summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/libcameraservice/Android.mk71
-rw-r--r--camera/libcameraservice/CameraHardwareStub.cpp402
-rw-r--r--camera/libcameraservice/CameraHardwareStub.h135
-rw-r--r--camera/libcameraservice/CameraService.cpp1417
-rw-r--r--camera/libcameraservice/CameraService.h226
-rw-r--r--camera/libcameraservice/CannedJpeg.h734
-rw-r--r--camera/libcameraservice/FakeCamera.cpp430
-rw-r--r--camera/libcameraservice/FakeCamera.h67
-rw-r--r--camera/tests/CameraServiceTest/Android.mk24
-rw-r--r--camera/tests/CameraServiceTest/CameraServiceTest.cpp849
-rw-r--r--cmds/surfaceflinger/Android.mk2
-rw-r--r--cmds/surfaceflinger/main_surfaceflinger.cpp31
-rw-r--r--include/binder/BinderService.h60
-rw-r--r--include/binder/IInterface.h17
-rw-r--r--include/binder/IPCThreadState.h6
-rw-r--r--include/binder/Parcel.h31
-rw-r--r--include/gui/ISensorEventConnection.h57
-rw-r--r--include/gui/ISensorServer.h57
-rw-r--r--include/gui/Sensor.h91
-rw-r--r--include/gui/SensorChannel.h (renamed from libs/audioflinger/AudioBufferProvider.h)40
-rw-r--r--include/gui/SensorEventQueue.h82
-rw-r--r--include/gui/SensorManager.h63
-rw-r--r--include/private/surfaceflinger/SharedBufferStack.h223
-rw-r--r--include/surfaceflinger/ISurface.h19
-rw-r--r--include/surfaceflinger/ISurfaceComposer.h8
-rw-r--r--include/surfaceflinger/ISurfaceComposerClient.h (renamed from include/surfaceflinger/ISurfaceFlingerClient.h)32
-rw-r--r--include/surfaceflinger/Surface.h144
-rw-r--r--include/surfaceflinger/SurfaceComposerClient.h56
-rw-r--r--include/ui/EventHub.h213
-rw-r--r--include/ui/FramebufferNativeWindow.h14
-rw-r--r--include/ui/GraphicBuffer.h13
-rw-r--r--include/ui/GraphicBufferAllocator.h2
-rw-r--r--include/ui/Input.h491
-rw-r--r--include/ui/InputDispatcher.h669
-rw-r--r--include/ui/InputManager.h172
-rw-r--r--include/ui/InputReader.h843
-rw-r--r--include/ui/InputTransport.h334
-rwxr-xr-x[-rw-r--r--]include/ui/KeycodeLabels.h125
-rw-r--r--include/ui/Rect.h33
-rw-r--r--include/ui/android_native_buffer.h9
-rw-r--r--include/ui/egl/android_natives.h133
-rw-r--r--include/utils/Asset.h11
-rw-r--r--include/utils/AssetManager.h20
-rw-r--r--include/utils/BitSet.h67
-rw-r--r--include/utils/Buffer.h107
-rw-r--r--include/utils/ObbFile.h87
-rw-r--r--include/utils/PollLoop.h197
-rw-r--r--include/utils/Pool.h71
-rw-r--r--include/utils/RefBase.h32
-rw-r--r--include/utils/ResourceTypes.h31
-rw-r--r--include/utils/Singleton.h8
-rw-r--r--include/utils/StopWatch.h2
-rw-r--r--include/utils/StreamingZipInflater.h82
-rw-r--r--include/utils/String8.h2
-rw-r--r--include/utils/Vector.h18
-rw-r--r--include/utils/VectorImpl.h6
-rw-r--r--include/utils/ZipFileCRO.h4
-rw-r--r--include/utils/ZipFileRO.h33
-rw-r--r--include/utils/threads.h90
-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/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.cpp13
-rw-r--r--libs/binder/IPermissionController.cpp8
-rw-r--r--libs/binder/IServiceManager.cpp9
-rw-r--r--libs/binder/Parcel.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.cpp135
-rw-r--r--libs/gui/SensorManager.cpp87
-rw-r--r--libs/surfaceflinger/Layer.cpp630
-rw-r--r--libs/surfaceflinger/Layer.h134
-rw-r--r--libs/surfaceflinger/LayerDim.cpp168
-rw-r--r--libs/surfaceflinger/Tokenizer.cpp173
-rw-r--r--libs/surfaceflinger/Tokenizer.h57
-rw-r--r--libs/surfaceflinger_client/Android.mk2
-rw-r--r--libs/surfaceflinger_client/ISurface.cpp30
-rw-r--r--libs/surfaceflinger_client/ISurfaceComposer.cpp18
-rw-r--r--libs/surfaceflinger_client/ISurfaceComposerClient.cpp (renamed from libs/surfaceflinger_client/ISurfaceFlingerClient.cpp)55
-rw-r--r--libs/surfaceflinger_client/SharedBufferStack.cpp384
-rw-r--r--libs/surfaceflinger_client/Surface.cpp565
-rw-r--r--libs/surfaceflinger_client/SurfaceComposerClient.cpp403
-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.mk17
-rw-r--r--libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp279
-rw-r--r--libs/ui/Android.mk15
-rw-r--r--libs/ui/EventHub.cpp403
-rw-r--r--libs/ui/FramebufferNativeWindow.cpp36
-rw-r--r--libs/ui/GraphicBuffer.cpp22
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp19
-rw-r--r--libs/ui/Input.cpp230
-rw-r--r--libs/ui/InputDispatcher.cpp1776
-rw-r--r--libs/ui/InputManager.cpp123
-rw-r--r--libs/ui/InputReader.cpp2706
-rw-r--r--libs/ui/InputTransport.cpp692
-rw-r--r--libs/ui/PixelFormat.cpp9
-rw-r--r--libs/ui/Rect.cpp8
-rw-r--r--libs/ui/tests/Android.mk48
-rw-r--r--libs/ui/tests/InputChannel_test.cpp158
-rw-r--r--libs/ui/tests/InputDispatcher_test.cpp18
-rw-r--r--libs/ui/tests/InputPublisherAndConsumer_test.cpp469
-rw-r--r--libs/ui/tests/region/Android.mk16
-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.cpp2
-rw-r--r--libs/utils/ObbFile.cpp296
-rw-r--r--libs/utils/PollLoop.cpp364
-rw-r--r--libs/utils/Pool.cpp37
-rw-r--r--libs/utils/ResourceTypes.cpp3
-rw-r--r--libs/utils/StopWatch.cpp11
-rw-r--r--libs/utils/StreamingZipInflater.cpp225
-rw-r--r--libs/utils/String8.cpp48
-rw-r--r--libs/utils/VectorImpl.cpp18
-rw-r--r--libs/utils/ZipFileCRO.cpp4
-rw-r--r--libs/utils/ZipFileRO.cpp460
-rw-r--r--libs/utils/tests/Android.mk44
-rw-r--r--libs/utils/tests/ObbFile_test.cpp82
-rw-r--r--libs/utils/tests/PollLoop_test.cpp370
-rw-r--r--libs/utils/tests/TestHelpers.h79
-rw-r--r--opengl/include/EGL/egl.h3
-rw-r--r--opengl/include/EGL/eglext.h98
-rw-r--r--opengl/include/EGL/eglplatform.h11
-rw-r--r--opengl/include/GLES/gl.h5
-rw-r--r--opengl/include/GLES/glext.h410
-rw-r--r--opengl/include/GLES/glplatform.h7
-rw-r--r--opengl/include/GLES2/gl2.h43
-rw-r--r--opengl/include/GLES2/gl2ext.h423
-rw-r--r--opengl/include/GLES2/gl2platform.h5
-rw-r--r--opengl/libagl/Android.mk14
-rw-r--r--opengl/libagl/TextureObjectManager.cpp3
-rw-r--r--opengl/libagl/TextureObjectManager.h3
-rw-r--r--opengl/libagl/array.cpp9
-rw-r--r--opengl/libagl/copybit.cpp618
-rw-r--r--opengl/libagl/copybit.h75
-rw-r--r--opengl/libagl/egl.cpp48
-rw-r--r--opengl/libagl/state.cpp38
-rw-r--r--opengl/libagl/texture.cpp29
-rw-r--r--opengl/libs/Android.mk9
-rw-r--r--opengl/libs/EGL/egl.cpp347
-rw-r--r--opengl/libs/EGL/getProcAddress.cpp176
-rw-r--r--opengl/libs/GLES2/gl2_api.in40
-rw-r--r--opengl/libs/GLES2/gl2ext_api.in97
-rw-r--r--opengl/libs/GLES_CM/gl_api.in2
-rw-r--r--opengl/libs/GLES_CM/glext_api.in110
-rw-r--r--opengl/libs/egl_impl.h1
-rw-r--r--opengl/libs/entries.in77
-rw-r--r--opengl/libs/hooks.h4
-rw-r--r--opengl/tests/gl2_basic/gl2_basic.cpp1
-rw-r--r--opengl/tests/gl_basic/gl_basic.cpp1
-rw-r--r--opengl/tests/gl_jni/jni/gl_code.cpp3
-rw-r--r--opengl/tools/glgen/specs/gles11/GLES20.spec4
-rw-r--r--opengl/tools/glgen/src/JniCodeEmitter.java2
-rw-r--r--services/surfaceflinger/Android.mk (renamed from libs/surfaceflinger/Android.mk)7
-rw-r--r--services/surfaceflinger/Barrier.h (renamed from libs/surfaceflinger/Barrier.h)4
-rw-r--r--services/surfaceflinger/BlurFilter.cpp (renamed from libs/surfaceflinger/BlurFilter.cpp)0
-rw-r--r--services/surfaceflinger/BlurFilter.h (renamed from libs/surfaceflinger/BlurFilter.h)0
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp (renamed from libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp)146
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h (renamed from libs/surfaceflinger/DisplayHardware/DisplayHardware.h)16
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp (renamed from libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp)0
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h (renamed from libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h)0
-rw-r--r--services/surfaceflinger/GLExtensions.cpp133
-rw-r--r--services/surfaceflinger/GLExtensions.h94
-rw-r--r--services/surfaceflinger/Layer.cpp859
-rw-r--r--services/surfaceflinger/Layer.h239
-rw-r--r--services/surfaceflinger/LayerBase.cpp (renamed from libs/surfaceflinger/LayerBase.cpp)386
-rw-r--r--services/surfaceflinger/LayerBase.h (renamed from libs/surfaceflinger/LayerBase.h)132
-rw-r--r--services/surfaceflinger/LayerBlur.cpp (renamed from libs/surfaceflinger/LayerBlur.cpp)68
-rw-r--r--services/surfaceflinger/LayerBlur.h (renamed from libs/surfaceflinger/LayerBlur.h)8
-rw-r--r--services/surfaceflinger/LayerBuffer.cpp (renamed from libs/surfaceflinger/LayerBuffer.cpp)89
-rw-r--r--services/surfaceflinger/LayerBuffer.h (renamed from libs/surfaceflinger/LayerBuffer.h)19
-rw-r--r--services/surfaceflinger/LayerDim.cpp103
-rw-r--r--services/surfaceflinger/LayerDim.h (renamed from libs/surfaceflinger/LayerDim.h)8
-rw-r--r--services/surfaceflinger/MODULE_LICENSE_APACHE2 (renamed from libs/surfaceflinger/MODULE_LICENSE_APACHE2)0
-rw-r--r--services/surfaceflinger/MessageQueue.cpp (renamed from libs/surfaceflinger/MessageQueue.cpp)13
-rw-r--r--services/surfaceflinger/MessageQueue.h (renamed from libs/surfaceflinger/MessageQueue.h)35
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp (renamed from libs/surfaceflinger/SurfaceFlinger.cpp)613
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h (renamed from libs/surfaceflinger/SurfaceFlinger.h)197
-rw-r--r--services/surfaceflinger/TextureManager.cpp342
-rw-r--r--services/surfaceflinger/TextureManager.h94
-rw-r--r--services/surfaceflinger/Transform.cpp (renamed from libs/surfaceflinger/Transform.cpp)7
-rw-r--r--services/surfaceflinger/Transform.h (renamed from libs/surfaceflinger/Transform.h)4
-rw-r--r--services/surfaceflinger/clz.cpp (renamed from libs/surfaceflinger/clz.cpp)0
-rw-r--r--services/surfaceflinger/clz.h (renamed from libs/surfaceflinger/clz.h)0
-rw-r--r--services/surfaceflinger/tests/Android.mk1
-rw-r--r--services/surfaceflinger/tests/overlays/Android.mk (renamed from libs/surfaceflinger/tests/overlays/Android.mk)0
-rw-r--r--services/surfaceflinger/tests/overlays/overlays.cpp (renamed from libs/surfaceflinger/tests/overlays/overlays.cpp)0
-rw-r--r--services/surfaceflinger/tests/resize/Android.mk (renamed from libs/surfaceflinger/tests/resize/Android.mk)0
-rw-r--r--services/surfaceflinger/tests/resize/resize.cpp (renamed from libs/surfaceflinger/tests/resize/resize.cpp)0
-rw-r--r--services/surfaceflinger/tests/surface/Android.mk18
-rw-r--r--services/surfaceflinger/tests/surface/surface.cpp54
-rw-r--r--vpn/java/android/net/vpn/VpnManager.java3
215 files changed, 18957 insertions, 22320 deletions
diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk
deleted file mode 100644
index df5c166..0000000
--- a/camera/libcameraservice/Android.mk
+++ /dev/null
@@ -1,71 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-#
-# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want
-# the camera service to use the fake camera. For emulator or simulator builds,
-# we always use the fake camera.
-
-ifeq ($(USE_CAMERA_STUB),)
-USE_CAMERA_STUB:=false
-ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
-USE_CAMERA_STUB:=true
-endif #libcamerastub
-endif
-
-ifeq ($(USE_CAMERA_STUB),true)
-#
-# libcamerastub
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- CameraHardwareStub.cpp \
- FakeCamera.cpp
-
-LOCAL_MODULE:= libcamerastub
-
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_CFLAGS += -DSINGLE_PROCESS
-endif
-
-LOCAL_SHARED_LIBRARIES:= libui
-
-include $(BUILD_STATIC_LIBRARY)
-endif # USE_CAMERA_STUB
-
-#
-# libcameraservice
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- CameraService.cpp
-
-LOCAL_SHARED_LIBRARIES:= \
- libui \
- libutils \
- libbinder \
- libcutils \
- libmedia \
- libcamera_client \
- libsurfaceflinger_client
-
-LOCAL_MODULE:= libcameraservice
-
-LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\"
-
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_CFLAGS += -DSINGLE_PROCESS
-endif
-
-ifeq ($(USE_CAMERA_STUB), true)
-LOCAL_STATIC_LIBRARIES += libcamerastub
-LOCAL_CFLAGS += -include CameraHardwareStub.h
-else
-LOCAL_SHARED_LIBRARIES += libcamera
-endif
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/camera/libcameraservice/CameraHardwareStub.cpp
deleted file mode 100644
index 8b66389..0000000
--- a/camera/libcameraservice/CameraHardwareStub.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "CameraHardwareStub"
-#include <utils/Log.h>
-
-#include "CameraHardwareStub.h"
-#include <utils/threads.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-
-#include "CannedJpeg.h"
-
-namespace android {
-
-CameraHardwareStub::CameraHardwareStub()
- : mParameters(),
- mPreviewHeap(0),
- mRawHeap(0),
- mFakeCamera(0),
- mPreviewFrameSize(0),
- mNotifyCb(0),
- mDataCb(0),
- mDataCbTimestamp(0),
- mCallbackCookie(0),
- mMsgEnabled(0),
- mCurrentPreviewFrame(0)
-{
- initDefaultParameters();
-}
-
-void CameraHardwareStub::initDefaultParameters()
-{
- CameraParameters p;
-
- p.set("preview-size-values","320x240");
- p.setPreviewSize(320, 240);
- p.setPreviewFrameRate(15);
- p.setPreviewFormat("yuv422sp");
-
- p.set("picture-size-values", "320x240");
- p.setPictureSize(320, 240);
- p.setPictureFormat("jpeg");
-
- if (setParameters(p) != NO_ERROR) {
- LOGE("Failed to set default parameters?!");
- }
-}
-
-void CameraHardwareStub::initHeapLocked()
-{
- // Create raw heap.
- int picture_width, picture_height;
- mParameters.getPictureSize(&picture_width, &picture_height);
- mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height);
-
- int preview_width, preview_height;
- mParameters.getPreviewSize(&preview_width, &preview_height);
- LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
-
- // Note that we enforce yuv422 in setParameters().
- int how_big = preview_width * preview_height * 2;
-
- // If we are being reinitialized to the same size as before, no
- // work needs to be done.
- if (how_big == mPreviewFrameSize)
- return;
-
- mPreviewFrameSize = how_big;
-
- // Make a new mmap'ed heap that can be shared across processes.
- // use code below to test with pmem
- mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount);
- // Make an IMemory for each frame so that we can reuse them in callbacks.
- for (int i = 0; i < kBufferCount; i++) {
- mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize);
- }
-
- // Recreate the fake camera to reflect the current size.
- delete mFakeCamera;
- mFakeCamera = new FakeCamera(preview_width, preview_height);
-}
-
-CameraHardwareStub::~CameraHardwareStub()
-{
- delete mFakeCamera;
- mFakeCamera = 0; // paranoia
- singleton.clear();
-}
-
-sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const
-{
- return mPreviewHeap;
-}
-
-sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
-{
- return mRawHeap;
-}
-
-void CameraHardwareStub::setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- void* user)
-{
- Mutex::Autolock lock(mLock);
- mNotifyCb = notify_cb;
- mDataCb = data_cb;
- mDataCbTimestamp = data_cb_timestamp;
- mCallbackCookie = user;
-}
-
-void CameraHardwareStub::enableMsgType(int32_t msgType)
-{
- Mutex::Autolock lock(mLock);
- mMsgEnabled |= msgType;
-}
-
-void CameraHardwareStub::disableMsgType(int32_t msgType)
-{
- Mutex::Autolock lock(mLock);
- mMsgEnabled &= ~msgType;
-}
-
-bool CameraHardwareStub::msgTypeEnabled(int32_t msgType)
-{
- Mutex::Autolock lock(mLock);
- return (mMsgEnabled & msgType);
-}
-
-// ---------------------------------------------------------------------------
-
-int CameraHardwareStub::previewThread()
-{
- mLock.lock();
- // the attributes below can change under our feet...
-
- int previewFrameRate = mParameters.getPreviewFrameRate();
-
- // Find the offset within the heap of the current buffer.
- ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize;
-
- sp<MemoryHeapBase> heap = mPreviewHeap;
-
- // this assumes the internal state of fake camera doesn't change
- // (or is thread safe)
- FakeCamera* fakeCamera = mFakeCamera;
-
- sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame];
-
- mLock.unlock();
-
- // TODO: here check all the conditions that could go wrong
- if (buffer != 0) {
- // Calculate how long to wait between frames.
- int delay = (int)(1000000.0f / float(previewFrameRate));
-
- // This is always valid, even if the client died -- the memory
- // is still mapped in our process.
- void *base = heap->base();
-
- // Fill the current frame with the fake camera.
- uint8_t *frame = ((uint8_t *)base) + offset;
- fakeCamera->getNextFrameAsYuv422(frame);
-
- //LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
-
- // Notify the client of a new frame.
- if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)
- mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);
-
- // Advance the buffer pointer.
- mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;
-
- // Wait for it...
- usleep(delay);
- }
-
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::startPreview()
-{
- Mutex::Autolock lock(mLock);
- if (mPreviewThread != 0) {
- // already running
- return INVALID_OPERATION;
- }
- mPreviewThread = new PreviewThread(this);
- return NO_ERROR;
-}
-
-void CameraHardwareStub::stopPreview()
-{
- sp<PreviewThread> previewThread;
-
- { // scope for the lock
- Mutex::Autolock lock(mLock);
- previewThread = mPreviewThread;
- }
-
- // don't hold the lock while waiting for the thread to quit
- if (previewThread != 0) {
- previewThread->requestExitAndWait();
- }
-
- Mutex::Autolock lock(mLock);
- mPreviewThread.clear();
-}
-
-bool CameraHardwareStub::previewEnabled() {
- return mPreviewThread != 0;
-}
-
-status_t CameraHardwareStub::startRecording()
-{
- return UNKNOWN_ERROR;
-}
-
-void CameraHardwareStub::stopRecording()
-{
-}
-
-bool CameraHardwareStub::recordingEnabled()
-{
- return false;
-}
-
-void CameraHardwareStub::releaseRecordingFrame(const sp<IMemory>& mem)
-{
-}
-
-// ---------------------------------------------------------------------------
-
-int CameraHardwareStub::beginAutoFocusThread(void *cookie)
-{
- CameraHardwareStub *c = (CameraHardwareStub *)cookie;
- return c->autoFocusThread();
-}
-
-int CameraHardwareStub::autoFocusThread()
-{
- if (mMsgEnabled & CAMERA_MSG_FOCUS)
- mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie);
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::autoFocus()
-{
- Mutex::Autolock lock(mLock);
- if (createThread(beginAutoFocusThread, this) == false)
- return UNKNOWN_ERROR;
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::cancelAutoFocus()
-{
- return NO_ERROR;
-}
-
-/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie)
-{
- CameraHardwareStub *c = (CameraHardwareStub *)cookie;
- return c->pictureThread();
-}
-
-int CameraHardwareStub::pictureThread()
-{
- if (mMsgEnabled & CAMERA_MSG_SHUTTER)
- mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie);
-
- if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {
- //FIXME: use a canned YUV image!
- // In the meantime just make another fake camera picture.
- int w, h;
- mParameters.getPictureSize(&w, &h);
- sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
- FakeCamera cam(w, h);
- cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base());
- mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);
- }
-
- if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
- sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize);
- sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize);
- memcpy(heap->base(), kCannedJpeg, kCannedJpegSize);
- mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie);
- }
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::takePicture()
-{
- stopPreview();
- if (createThread(beginPictureThread, this) == false)
- return -1;
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::cancelPicture()
-{
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::dump(int fd, const Vector<String16>& args) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- AutoMutex lock(&mLock);
- if (mFakeCamera != 0) {
- mFakeCamera->dump(fd);
- mParameters.dump(fd, args);
- snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false");
- result.append(buffer);
- } else {
- result.append("No camera client yet.\n");
- }
- write(fd, result.string(), result.size());
- return NO_ERROR;
-}
-
-status_t CameraHardwareStub::setParameters(const CameraParameters& params)
-{
- Mutex::Autolock lock(mLock);
- // XXX verify params
-
- if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) {
- LOGE("Only yuv422sp preview is supported");
- return -1;
- }
-
- if (strcmp(params.getPictureFormat(), "jpeg") != 0) {
- LOGE("Only jpeg still pictures are supported");
- return -1;
- }
-
- int w, h;
- params.getPictureSize(&w, &h);
- if (w != kCannedJpegWidth && h != kCannedJpegHeight) {
- LOGE("Still picture size must be size of canned JPEG (%dx%d)",
- kCannedJpegWidth, kCannedJpegHeight);
- return -1;
- }
-
- mParameters = params;
- initHeapLocked();
-
- return NO_ERROR;
-}
-
-CameraParameters CameraHardwareStub::getParameters() const
-{
- Mutex::Autolock lock(mLock);
- return mParameters;
-}
-
-status_t CameraHardwareStub::sendCommand(int32_t command, int32_t arg1,
- int32_t arg2)
-{
- return BAD_VALUE;
-}
-
-void CameraHardwareStub::release()
-{
-}
-
-wp<CameraHardwareInterface> CameraHardwareStub::singleton;
-
-sp<CameraHardwareInterface> CameraHardwareStub::createInstance()
-{
- if (singleton != 0) {
- sp<CameraHardwareInterface> hardware = singleton.promote();
- if (hardware != 0) {
- return hardware;
- }
- }
- sp<CameraHardwareInterface> hardware(new CameraHardwareStub());
- singleton = hardware;
- return hardware;
-}
-
-extern "C" sp<CameraHardwareInterface> openCameraHardware()
-{
- return CameraHardwareStub::createInstance();
-}
-
-}; // namespace android
diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h
deleted file mode 100644
index 957813a..0000000
--- a/camera/libcameraservice/CameraHardwareStub.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H
-#define ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H
-
-#include "FakeCamera.h"
-#include <utils/threads.h>
-#include <camera/CameraHardwareInterface.h>
-#include <binder/MemoryBase.h>
-#include <binder/MemoryHeapBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class CameraHardwareStub : public CameraHardwareInterface {
-public:
- virtual sp<IMemoryHeap> getPreviewHeap() const;
- virtual sp<IMemoryHeap> getRawHeap() const;
-
- virtual void setCallbacks(notify_callback notify_cb,
- data_callback data_cb,
- data_callback_timestamp data_cb_timestamp,
- void* user);
-
- virtual void enableMsgType(int32_t msgType);
- virtual void disableMsgType(int32_t msgType);
- virtual bool msgTypeEnabled(int32_t msgType);
-
- virtual status_t startPreview();
- virtual void stopPreview();
- virtual bool previewEnabled();
-
- virtual status_t startRecording();
- virtual void stopRecording();
- virtual bool recordingEnabled();
- virtual void releaseRecordingFrame(const sp<IMemory>& mem);
-
- virtual status_t autoFocus();
- virtual status_t cancelAutoFocus();
- virtual status_t takePicture();
- virtual status_t cancelPicture();
- virtual status_t dump(int fd, const Vector<String16>& args) const;
- virtual status_t setParameters(const CameraParameters& params);
- virtual CameraParameters getParameters() const;
- virtual status_t sendCommand(int32_t command, int32_t arg1,
- int32_t arg2);
- virtual void release();
-
- static sp<CameraHardwareInterface> createInstance();
-
-private:
- CameraHardwareStub();
- virtual ~CameraHardwareStub();
-
- static wp<CameraHardwareInterface> singleton;
-
- static const int kBufferCount = 4;
-
- class PreviewThread : public Thread {
- CameraHardwareStub* mHardware;
- public:
- PreviewThread(CameraHardwareStub* hw) :
-#ifdef SINGLE_PROCESS
- // In single process mode this thread needs to be a java thread,
- // since we won't be calling through the binder.
- Thread(true),
-#else
- Thread(false),
-#endif
- mHardware(hw) { }
- virtual void onFirstRef() {
- run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY);
- }
- virtual bool threadLoop() {
- mHardware->previewThread();
- // loop until we need to quit
- return true;
- }
- };
-
- void initDefaultParameters();
- void initHeapLocked();
-
- int previewThread();
-
- static int beginAutoFocusThread(void *cookie);
- int autoFocusThread();
-
- static int beginPictureThread(void *cookie);
- int pictureThread();
-
- mutable Mutex mLock;
-
- CameraParameters mParameters;
-
- sp<MemoryHeapBase> mPreviewHeap;
- sp<MemoryHeapBase> mRawHeap;
- sp<MemoryBase> mBuffers[kBufferCount];
-
- FakeCamera *mFakeCamera;
- bool mPreviewRunning;
- int mPreviewFrameSize;
-
- // protected by mLock
- sp<PreviewThread> mPreviewThread;
-
- notify_callback mNotifyCb;
- data_callback mDataCb;
- data_callback_timestamp mDataCbTimestamp;
- void *mCallbackCookie;
-
- int32_t mMsgEnabled;
-
- // only used from PreviewThread
- int mCurrentPreviewFrame;
-};
-
-}; // namespace android
-
-#endif
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
deleted file mode 100644
index 118249e..0000000
--- a/camera/libcameraservice/CameraService.cpp
+++ /dev/null
@@ -1,1417 +0,0 @@
-/*
-**
-** Copyright (C) 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "CameraService"
-#include <utils/Log.h>
-
-#include <binder/IServiceManager.h>
-#include <binder/IPCThreadState.h>
-#include <utils/String16.h>
-#include <utils/Errors.h>
-#include <binder/MemoryBase.h>
-#include <binder/MemoryHeapBase.h>
-#include <camera/ICameraService.h>
-#include <surfaceflinger/ISurface.h>
-#include <ui/Overlay.h>
-
-#include <hardware/hardware.h>
-
-#include <media/mediaplayer.h>
-#include <media/AudioSystem.h>
-#include "CameraService.h"
-
-#include <cutils/atomic.h>
-
-namespace android {
-
-extern "C" {
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-}
-
-// When you enable this, as well as DEBUG_REFS=1 and
-// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all
-// references to the CameraService::Client in order to catch the case where the
-// client is being destroyed while a callback from the CameraHardwareInterface
-// is outstanding. This is a serious bug because if we make another call into
-// CameraHardwreInterface that itself triggers a callback, we will deadlock.
-
-#define DEBUG_CLIENT_REFERENCES 0
-
-#define PICTURE_TIMEOUT seconds(5)
-
-#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */
-#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0
-#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0
-#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0
-
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
-static int debug_frame_cnt;
-#endif
-
-static int getCallingPid() {
- return IPCThreadState::self()->getCallingPid();
-}
-
-// ----------------------------------------------------------------------------
-
-void CameraService::instantiate() {
- defaultServiceManager()->addService(
- String16("media.camera"), new CameraService());
-}
-
-// ----------------------------------------------------------------------------
-
-CameraService::CameraService() :
- BnCameraService()
-{
- LOGI("CameraService started: pid=%d", getpid());
- mUsers = 0;
-}
-
-CameraService::~CameraService()
-{
- if (mClient != 0) {
- LOGE("mClient was still connected in destructor!");
- }
-}
-
-sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient)
-{
- int callingPid = getCallingPid();
- LOGV("CameraService::connect E (pid %d, client %p)", callingPid,
- cameraClient->asBinder().get());
-
- Mutex::Autolock lock(mServiceLock);
- sp<Client> client;
- if (mClient != 0) {
- sp<Client> currentClient = mClient.promote();
- if (currentClient != 0) {
- sp<ICameraClient> currentCameraClient(currentClient->getCameraClient());
- if (cameraClient->asBinder() == currentCameraClient->asBinder()) {
- // This is the same client reconnecting...
- LOGV("CameraService::connect X (pid %d, same client %p) is reconnecting...",
- callingPid, cameraClient->asBinder().get());
- return currentClient;
- } else {
- // It's another client... reject it
- LOGV("CameraService::connect X (pid %d, new client %p) rejected. "
- "(old pid %d, old client %p)",
- callingPid, cameraClient->asBinder().get(),
- currentClient->mClientPid, currentCameraClient->asBinder().get());
- if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) {
- LOGV("The old client is dead!");
- }
- return client;
- }
- } else {
- // can't promote, the previous client has died...
- LOGV("New client (pid %d) connecting, old reference was dangling...",
- callingPid);
- mClient.clear();
- }
- }
-
- if (mUsers > 0) {
- LOGV("Still have client, rejected");
- return client;
- }
-
- // create a new Client object
- client = new Client(this, cameraClient, callingPid);
- mClient = client;
-#if DEBUG_CLIENT_REFERENCES
- // Enable tracking for this object, and track increments and decrements of
- // the refcount.
- client->trackMe(true, true);
-#endif
- LOGV("CameraService::connect X");
- return client;
-}
-
-void CameraService::removeClient(const sp<ICameraClient>& cameraClient)
-{
- int callingPid = getCallingPid();
-
- // Declare this outside the lock to make absolutely sure the
- // destructor won't be called with the lock held.
- sp<Client> client;
-
- Mutex::Autolock lock(mServiceLock);
-
- if (mClient == 0) {
- // This happens when we have already disconnected.
- LOGV("removeClient (pid %d): already disconnected", callingPid);
- return;
- }
-
- // Promote mClient. It can fail if we are called from this path:
- // Client::~Client() -> disconnect() -> removeClient().
- client = mClient.promote();
- if (client == 0) {
- LOGV("removeClient (pid %d): no more strong reference", callingPid);
- mClient.clear();
- return;
- }
-
- if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) {
- // ugh! that's not our client!!
- LOGW("removeClient (pid %d): mClient doesn't match!", callingPid);
- } else {
- // okay, good, forget about mClient
- mClient.clear();
- }
-
- LOGV("removeClient (pid %d) done", callingPid);
-}
-
-// The reason we need this count is a new CameraService::connect() request may
-// come in while the previous Client's destructor has not been run or is still
-// running. If the last strong reference of the previous Client is gone but
-// destructor has not been run, we should not allow the new Client to be created
-// because we need to wait for the previous Client to tear down the hardware
-// first.
-void CameraService::incUsers() {
- android_atomic_inc(&mUsers);
-}
-
-void CameraService::decUsers() {
- android_atomic_dec(&mUsers);
-}
-
-static sp<MediaPlayer> newMediaPlayer(const char *file)
-{
- sp<MediaPlayer> mp = new MediaPlayer();
- if (mp->setDataSource(file, NULL /* headers */) == NO_ERROR) {
- mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
- mp->prepare();
- } else {
- mp.clear();
- LOGE("Failed to load CameraService sounds.");
- }
- return mp;
-}
-
-CameraService::Client::Client(const sp<CameraService>& cameraService,
- const sp<ICameraClient>& cameraClient, pid_t clientPid)
-{
- int callingPid = getCallingPid();
- LOGV("Client::Client E (pid %d)", callingPid);
- mCameraService = cameraService;
- mCameraClient = cameraClient;
- mClientPid = clientPid;
- mHardware = openCameraHardware();
- mUseOverlay = mHardware->useOverlay();
-
- mHardware->setCallbacks(notifyCallback,
- dataCallback,
- dataCallbackTimestamp,
- mCameraService.get());
-
- // Enable zoom, error, and focus messages by default
- mHardware->enableMsgType(CAMERA_MSG_ERROR |
- CAMERA_MSG_ZOOM |
- CAMERA_MSG_FOCUS);
-
- mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
- mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
- mOverlayW = 0;
- mOverlayH = 0;
-
- // Callback is disabled by default
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
- mOrientation = 0;
- cameraService->incUsers();
- LOGV("Client::Client X (pid %d)", callingPid);
-}
-
-status_t CameraService::Client::checkPid()
-{
- int callingPid = getCallingPid();
- if (mClientPid == callingPid) return NO_ERROR;
- LOGW("Attempt to use locked camera (client %p) from different process "
- " (old pid %d, new pid %d)",
- getCameraClient()->asBinder().get(), mClientPid, callingPid);
- return -EBUSY;
-}
-
-status_t CameraService::Client::lock()
-{
- int callingPid = getCallingPid();
- LOGV("lock from pid %d (mClientPid %d)", callingPid, mClientPid);
- Mutex::Autolock _l(mLock);
- // lock camera to this client if the the camera is unlocked
- if (mClientPid == 0) {
- mClientPid = callingPid;
- return NO_ERROR;
- }
- // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise
- return checkPid();
-}
-
-status_t CameraService::Client::unlock()
-{
- int callingPid = getCallingPid();
- LOGV("unlock from pid %d (mClientPid %d)", callingPid, mClientPid);
- Mutex::Autolock _l(mLock);
- // allow anyone to use camera
- status_t result = checkPid();
- if (result == NO_ERROR) {
- mClientPid = 0;
- LOGV("clear mCameraClient (pid %d)", callingPid);
- // we need to remove the reference so that when app goes
- // away, the reference count goes to 0.
- mCameraClient.clear();
- }
- return result;
-}
-
-status_t CameraService::Client::connect(const sp<ICameraClient>& client)
-{
- int callingPid = getCallingPid();
-
- // connect a new process to the camera
- LOGV("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get());
-
- // I hate this hack, but things get really ugly when the media recorder
- // service is handing back the camera to the app. The ICameraClient
- // destructor will be called during the same IPC, making it look like
- // the remote client is trying to disconnect. This hack temporarily
- // sets the mClientPid to an invalid pid to prevent the hardware from
- // being torn down.
- {
-
- // hold a reference to the old client or we will deadlock if the client is
- // in the same process and we hold the lock when we remove the reference
- sp<ICameraClient> oldClient;
- {
- Mutex::Autolock _l(mLock);
- if (mClientPid != 0 && checkPid() != NO_ERROR) {
- LOGW("Tried to connect to locked camera (old pid %d, new pid %d)",
- mClientPid, callingPid);
- return -EBUSY;
- }
- oldClient = mCameraClient;
-
- // did the client actually change?
- if ((mCameraClient != NULL) && (client->asBinder() == mCameraClient->asBinder())) {
- LOGV("Connect to the same client");
- return NO_ERROR;
- }
-
- mCameraClient = client;
- mClientPid = -1;
- mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
- LOGV("Connect to the new client (pid %d, client %p)",
- callingPid, mCameraClient->asBinder().get());
- }
-
- }
- // the old client destructor is called when oldClient goes out of scope
- // now we set the new PID to lock the interface again
- mClientPid = callingPid;
-
- return NO_ERROR;
-}
-
-#if HAVE_ANDROID_OS
-static void *unregister_surface(void *arg)
-{
- ISurface *surface = (ISurface *)arg;
- surface->unregisterBuffers();
- IPCThreadState::self()->flushCommands();
- return NULL;
-}
-#endif
-
-CameraService::Client::~Client()
-{
- int callingPid = getCallingPid();
-
- // tear down client
- LOGV("Client::~Client E (pid %d, client %p)",
- callingPid, getCameraClient()->asBinder().get());
- if (mSurface != 0 && !mUseOverlay) {
-#if HAVE_ANDROID_OS
- pthread_t thr;
- // We unregister the buffers in a different thread because binder does
- // not let us make sychronous transactions in a binder destructor (that
- // is, upon our reaching a refcount of zero.)
- pthread_create(&thr, NULL,
- unregister_surface,
- mSurface.get());
- pthread_join(thr, NULL);
-#else
- mSurface->unregisterBuffers();
-#endif
- }
-
- if (mMediaPlayerBeep.get() != NULL) {
- mMediaPlayerBeep->disconnect();
- mMediaPlayerBeep.clear();
- }
- if (mMediaPlayerClick.get() != NULL) {
- mMediaPlayerClick->disconnect();
- mMediaPlayerClick.clear();
- }
-
- // make sure we tear down the hardware
- mClientPid = callingPid;
- disconnect();
- LOGV("Client::~Client X (pid %d)", mClientPid);
-}
-
-void CameraService::Client::disconnect()
-{
- int callingPid = getCallingPid();
-
- LOGV("Client::disconnect() E (pid %d client %p)",
- callingPid, getCameraClient()->asBinder().get());
-
- Mutex::Autolock lock(mLock);
- if (mClientPid <= 0) {
- LOGV("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
- return;
- }
- if (checkPid() != NO_ERROR) {
- LOGV("Different client - don't disconnect");
- return;
- }
-
- // Make sure disconnect() is done once and once only, whether it is called
- // from the user directly, or called by the destructor.
- if (mHardware == 0) return;
-
- LOGV("hardware teardown");
- // Before destroying mHardware, we must make sure it's in the
- // idle state.
- mHardware->stopPreview();
- // Cancel all picture callbacks.
- mHardware->disableMsgType(CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_COMPRESSED_IMAGE);
- mHardware->cancelPicture();
- // Turn off remaining messages.
- mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
- // Release the hardware resources.
- mHardware->release();
- // Release the held overlay resources.
- if (mUseOverlay)
- {
- mOverlayRef = 0;
- }
- mHardware.clear();
-
- mCameraService->removeClient(mCameraClient);
- mCameraService->decUsers();
-
- LOGV("Client::disconnect() X (pid %d)", callingPid);
-}
-
-// pass the buffered ISurface to the camera service
-status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
-{
- LOGV("setPreviewDisplay(%p) (pid %d)",
- ((surface == NULL) ? NULL : surface.get()), getCallingPid());
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- Mutex::Autolock surfaceLock(mSurfaceLock);
- result = NO_ERROR;
- // asBinder() is safe on NULL (returns NULL)
- if (surface->asBinder() != mSurface->asBinder()) {
- if (mSurface != 0) {
- LOGV("clearing old preview surface %p", mSurface.get());
- if ( !mUseOverlay)
- {
- mSurface->unregisterBuffers();
- }
- else
- {
- // Force the destruction of any previous overlay
- sp<Overlay> dummy;
- mHardware->setOverlay( dummy );
- }
- }
- mSurface = surface;
- mOverlayRef = 0;
- // If preview has been already started, set overlay or register preview
- // buffers now.
- if (mHardware->previewEnabled()) {
- if (mUseOverlay) {
- result = setOverlay();
- } else if (mSurface != 0) {
- result = registerPreviewBuffers();
- }
- }
- }
- return result;
-}
-
-// set the preview callback flag to affect how the received frames from
-// preview are handled.
-void CameraService::Client::setPreviewCallbackFlag(int callback_flag)
-{
- LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid());
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
- mPreviewCallbackFlag = callback_flag;
-
- if(mUseOverlay) {
- if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)
- mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- else
- mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-}
-
-// start preview mode
-status_t CameraService::Client::startCameraMode(camera_mode mode)
-{
- int callingPid = getCallingPid();
-
- LOGV("startCameraMode(%d) (pid %d)", mode, callingPid);
-
- /* we cannot call into mHardware with mLock held because
- * mHardware has callbacks onto us which acquire this lock
- */
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- switch(mode) {
- case CAMERA_RECORDING_MODE:
- if (mSurface == 0) {
- LOGE("setPreviewDisplay must be called before startRecordingMode.");
- return INVALID_OPERATION;
- }
- return startRecordingMode();
-
- default: // CAMERA_PREVIEW_MODE
- if (mSurface == 0) {
- LOGV("mSurface is not set yet.");
- }
- return startPreviewMode();
- }
-}
-
-status_t CameraService::Client::startRecordingMode()
-{
- LOGV("startRecordingMode (pid %d)", getCallingPid());
-
- status_t ret = UNKNOWN_ERROR;
-
- // if preview has not been started, start preview first
- if (!mHardware->previewEnabled()) {
- ret = startPreviewMode();
- if (ret != NO_ERROR) {
- return ret;
- }
- }
-
- // if recording has been enabled, nothing needs to be done
- if (mHardware->recordingEnabled()) {
- return NO_ERROR;
- }
-
- // start recording mode
- ret = mHardware->startRecording();
- if (ret != NO_ERROR) {
- LOGE("mHardware->startRecording() failed with status %d", ret);
- }
- return ret;
-}
-
-status_t CameraService::Client::setOverlay()
-{
- LOGV("setOverlay");
- int w, h;
- CameraParameters params(mHardware->getParameters());
- params.getPreviewSize(&w, &h);
-
- if ( w != mOverlayW || h != mOverlayH )
- {
- // Force the destruction of any previous overlay
- sp<Overlay> dummy;
- mHardware->setOverlay( dummy );
- mOverlayRef = 0;
- }
-
- status_t ret = NO_ERROR;
- if (mSurface != 0) {
- if (mOverlayRef.get() == NULL) {
-
- // FIXME:
- // Surfaceflinger may hold onto the previous overlay reference for some
- // time after we try to destroy it. retry a few times. In the future, we
- // should make the destroy call block, or possibly specify that we can
- // wait in the createOverlay call if the previous overlay is in the
- // process of being destroyed.
- for (int retry = 0; retry < 50; ++retry) {
- mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT,
- mOrientation);
- if (mOverlayRef != NULL) break;
- LOGW("Overlay create failed - retrying");
- usleep(20000);
- }
- if ( mOverlayRef.get() == NULL )
- {
- LOGE("Overlay Creation Failed!");
- return -EINVAL;
- }
- ret = mHardware->setOverlay(new Overlay(mOverlayRef));
- }
- } else {
- ret = mHardware->setOverlay(NULL);
- }
- if (ret != NO_ERROR) {
- LOGE("mHardware->setOverlay() failed with status %d\n", ret);
- }
-
- mOverlayW = w;
- mOverlayH = h;
-
- return ret;
-}
-
-status_t CameraService::Client::registerPreviewBuffers()
-{
- int w, h;
- CameraParameters params(mHardware->getParameters());
- params.getPreviewSize(&w, &h);
-
- // don't use a hardcoded format here
- ISurface::BufferHeap buffers(w, h, w, h,
- HAL_PIXEL_FORMAT_YCrCb_420_SP,
- mOrientation,
- 0,
- mHardware->getPreviewHeap());
-
- status_t ret = mSurface->registerBuffers(buffers);
- if (ret != NO_ERROR) {
- LOGE("registerBuffers failed with status %d", ret);
- }
- return ret;
-}
-
-status_t CameraService::Client::startPreviewMode()
-{
- LOGV("startPreviewMode (pid %d)", getCallingPid());
-
- // if preview has been enabled, nothing needs to be done
- if (mHardware->previewEnabled()) {
- return NO_ERROR;
- }
-
- // start preview mode
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
- debug_frame_cnt = 0;
-#endif
- status_t ret = NO_ERROR;
-
- if (mUseOverlay) {
- // If preview display has been set, set overlay now.
- if (mSurface != 0) {
- ret = setOverlay();
- }
- if (ret != NO_ERROR) return ret;
- ret = mHardware->startPreview();
- } else {
- mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- ret = mHardware->startPreview();
- if (ret != NO_ERROR) return ret;
- // If preview display has been set, register preview buffers now.
- if (mSurface != 0) {
- // Unregister here because the surface registered with raw heap.
- mSurface->unregisterBuffers();
- ret = registerPreviewBuffers();
- }
- }
- return ret;
-}
-
-status_t CameraService::Client::startPreview()
-{
- LOGV("startPreview (pid %d)", getCallingPid());
-
- return startCameraMode(CAMERA_PREVIEW_MODE);
-}
-
-status_t CameraService::Client::startRecording()
-{
- LOGV("startRecording (pid %d)", getCallingPid());
-
- if (mMediaPlayerBeep.get() != NULL) {
- // do not play record jingle if stream volume is 0
- // (typically because ringer mode is silent).
- int index;
- AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
- if (index != 0) {
- mMediaPlayerBeep->seekTo(0);
- mMediaPlayerBeep->start();
- }
- }
-
- mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME);
-
- return startCameraMode(CAMERA_RECORDING_MODE);
-}
-
-// stop preview mode
-void CameraService::Client::stopPreview()
-{
- LOGV("stopPreview (pid %d)", getCallingPid());
-
- // hold main lock during state transition
- {
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return;
- }
-
- mHardware->stopPreview();
- mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- LOGV("stopPreview(), hardware stopped OK");
-
- if (mSurface != 0 && !mUseOverlay) {
- mSurface->unregisterBuffers();
- }
- }
-
- // hold preview buffer lock
- {
- Mutex::Autolock lock(mPreviewLock);
- mPreviewBuffer.clear();
- }
-}
-
-// stop recording mode
-void CameraService::Client::stopRecording()
-{
- LOGV("stopRecording (pid %d)", getCallingPid());
-
- // hold main lock during state transition
- {
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return;
- }
-
- if (mMediaPlayerBeep.get() != NULL) {
- mMediaPlayerBeep->seekTo(0);
- mMediaPlayerBeep->start();
- }
-
- mHardware->stopRecording();
- mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
- LOGV("stopRecording(), hardware stopped OK");
- }
-
- // hold preview buffer lock
- {
- Mutex::Autolock lock(mPreviewLock);
- mPreviewBuffer.clear();
- }
-}
-
-// release a recording frame
-void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem)
-{
- Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) return;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return;
- }
-
- mHardware->releaseRecordingFrame(mem);
-}
-
-bool CameraService::Client::previewEnabled()
-{
- Mutex::Autolock lock(mLock);
- if (mHardware == 0) return false;
- return mHardware->previewEnabled();
-}
-
-bool CameraService::Client::recordingEnabled()
-{
- Mutex::Autolock lock(mLock);
- if (mHardware == 0) return false;
- return mHardware->recordingEnabled();
-}
-
-// Safely retrieves a strong pointer to the client during a hardware callback.
-sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user)
-{
- sp<Client> client = 0;
- CameraService *service = static_cast<CameraService*>(user);
- if (service != NULL) {
- Mutex::Autolock ourLock(service->mServiceLock);
- if (service->mClient != 0) {
- client = service->mClient.promote();
- if (client == 0) {
- LOGE("getClientFromCookie: client appears to have died");
- service->mClient.clear();
- }
- } else {
- LOGE("getClientFromCookie: got callback but client was NULL");
- }
- }
- return client;
-}
-
-
-#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \
- DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \
- DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
-static void dump_to_file(const char *fname,
- uint8_t *buf, uint32_t size)
-{
- int nw, cnt = 0;
- uint32_t written = 0;
-
- LOGV("opening file [%s]\n", fname);
- int fd = open(fname, O_RDWR | O_CREAT);
- if (fd < 0) {
- LOGE("failed to create file [%s]: %s", fname, strerror(errno));
- return;
- }
-
- LOGV("writing %d bytes to file [%s]\n", size, fname);
- while (written < size) {
- nw = ::write(fd,
- buf + written,
- size - written);
- if (nw < 0) {
- LOGE("failed to write to file [%s]: %s",
- fname, strerror(errno));
- break;
- }
- written += nw;
- cnt++;
- }
- LOGV("done writing %d bytes to file [%s] in %d passes\n",
- size, fname, cnt);
- ::close(fd);
-}
-#endif
-
-status_t CameraService::Client::autoFocus()
-{
- LOGV("autoFocus (pid %d)", getCallingPid());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- return mHardware->autoFocus();
-}
-
-status_t CameraService::Client::cancelAutoFocus()
-{
- LOGV("cancelAutoFocus (pid %d)", getCallingPid());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- return mHardware->cancelAutoFocus();
-}
-
-// take a picture - image is returned in callback
-status_t CameraService::Client::takePicture()
-{
- LOGV("takePicture (pid %d)", getCallingPid());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- mHardware->enableMsgType(CAMERA_MSG_SHUTTER |
- CAMERA_MSG_POSTVIEW_FRAME |
- CAMERA_MSG_RAW_IMAGE |
- CAMERA_MSG_COMPRESSED_IMAGE);
-
- return mHardware->takePicture();
-}
-
-// snapshot taken
-void CameraService::Client::handleShutter(
- image_rect_type *size // The width and height of yuv picture for
- // registerBuffer. If this is NULL, use the picture
- // size from parameters.
-)
-{
- // Play shutter sound.
- if (mMediaPlayerClick.get() != NULL) {
- // do not play shutter sound if stream volume is 0
- // (typically because ringer mode is silent).
- int index;
- AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
- if (index != 0) {
- mMediaPlayerClick->seekTo(0);
- mMediaPlayerClick->start();
- }
- }
-
- // Screen goes black after the buffer is unregistered.
- if (mSurface != 0 && !mUseOverlay) {
- mSurface->unregisterBuffers();
- }
-
- sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
- c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
- }
- mHardware->disableMsgType(CAMERA_MSG_SHUTTER);
-
- // It takes some time before yuvPicture callback to be called.
- // Register the buffer for raw image here to reduce latency.
- if (mSurface != 0 && !mUseOverlay) {
- int w, h;
- CameraParameters params(mHardware->getParameters());
- if (size == NULL) {
- params.getPictureSize(&w, &h);
- } else {
- w = size->width;
- h = size->height;
- w &= ~1;
- h &= ~1;
- LOGV("Snapshot image width=%d, height=%d", w, h);
- }
- // FIXME: don't use hardcoded format constants here
- ISurface::BufferHeap buffers(w, h, w, h,
- HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0,
- mHardware->getRawHeap());
-
- mSurface->registerBuffers(buffers);
- IPCThreadState::self()->flushCommands();
- }
-}
-
-// preview callback - frame buffer update
-void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
-{
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-
-#if DEBUG_HEAP_LEAKS && 0 // debugging
- if (gWeakHeap == NULL) {
- if (gWeakHeap != heap) {
- LOGV("SETTING PREVIEW HEAP");
- heap->trackMe(true, true);
- gWeakHeap = heap;
- }
- }
-#endif
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
- {
- if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) {
- dump_to_file("/data/preview.yuv",
- (uint8_t *)heap->base() + offset, size);
- }
- }
-#endif
-
- if (!mUseOverlay)
- {
- Mutex::Autolock surfaceLock(mSurfaceLock);
- if (mSurface != NULL) {
- mSurface->postBuffer(offset);
- }
- }
-
- // local copy of the callback flags
- int flags = mPreviewCallbackFlag;
-
- // is callback enabled?
- if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
- // If the enable bit is off, the copy-out and one-shot bits are ignored
- LOGV("frame callback is diabled");
- return;
- }
-
- // hold a strong pointer to the client
- sp<ICameraClient> c = mCameraClient;
-
- // clear callback flags if no client or one-shot mode
- if ((c == NULL) || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
- LOGV("Disable preview callback");
- mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
- FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
- FRAME_CALLBACK_FLAG_ENABLE_MASK);
- // TODO: Shouldn't we use this API for non-overlay hardware as well?
- if (mUseOverlay)
- mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
- }
-
- // Is the received frame copied out or not?
- if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
- LOGV("frame is copied");
- copyFrameAndPostCopiedFrame(c, heap, offset, size);
- } else {
- LOGV("frame is forwarded");
- c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
- }
-}
-
-// picture callback - postview image ready
-void CameraService::Client::handlePostview(const sp<IMemory>& mem)
-{
-#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only
- {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- dump_to_file("/data/postview.yuv",
- (uint8_t *)heap->base() + offset, size);
- }
-#endif
-
- sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
- c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem);
- }
- mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
-}
-
-// picture callback - raw image ready
-void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)
-{
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
-#if DEBUG_HEAP_LEAKS && 0 // debugging
- gWeakHeap = heap; // debugging
-#endif
-
- //LOGV("handleRawPicture(%d, %d)", offset, size);
-#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only
- dump_to_file("/data/photo.yuv",
- (uint8_t *)heap->base() + offset, size);
-#endif
-
- // Put the YUV version of the snapshot in the preview display.
- if (mSurface != 0 && !mUseOverlay) {
- mSurface->postBuffer(offset);
- }
-
- sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
- c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
- }
- mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE);
-}
-
-// picture callback - compressed picture ready
-void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem)
-{
-#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only
- {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- dump_to_file("/data/photo.jpg",
- (uint8_t *)heap->base() + offset, size);
- }
-#endif
-
- sp<ICameraClient> c = mCameraClient;
- if (c != NULL) {
- c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
- }
- mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
-}
-
-void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user)
-{
- LOGV("notifyCallback(%d)", msgType);
-
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
-
- switch (msgType) {
- case CAMERA_MSG_SHUTTER:
- // ext1 is the dimension of the yuv picture.
- client->handleShutter((image_rect_type *)ext1);
- break;
- default:
- sp<ICameraClient> c = client->mCameraClient;
- if (c != NULL) {
- c->notifyCallback(msgType, ext1, ext2);
- }
- break;
- }
-
-#if DEBUG_CLIENT_REFERENCES
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
- }
-#endif
-}
-
-void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user)
-{
- LOGV("dataCallback(%d)", msgType);
-
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
-
- sp<ICameraClient> c = client->mCameraClient;
- if (dataPtr == NULL) {
- LOGE("Null data returned in data callback");
- if (c != NULL) {
- c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- c->dataCallback(msgType, NULL);
- }
- return;
- }
-
- switch (msgType) {
- case CAMERA_MSG_PREVIEW_FRAME:
- client->handlePreviewData(dataPtr);
- break;
- case CAMERA_MSG_POSTVIEW_FRAME:
- client->handlePostview(dataPtr);
- break;
- case CAMERA_MSG_RAW_IMAGE:
- client->handleRawPicture(dataPtr);
- break;
- case CAMERA_MSG_COMPRESSED_IMAGE:
- client->handleCompressedPicture(dataPtr);
- break;
- default:
- if (c != NULL) {
- c->dataCallback(msgType, dataPtr);
- }
- break;
- }
-
-#if DEBUG_CLIENT_REFERENCES
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
- }
-#endif
-}
-
-void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
- const sp<IMemory>& dataPtr, void* user)
-{
- LOGV("dataCallbackTimestamp(%d)", msgType);
-
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
- sp<ICameraClient> c = client->mCameraClient;
-
- if (dataPtr == NULL) {
- LOGE("Null data returned in data with timestamp callback");
- if (c != NULL) {
- c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
- c->dataCallbackTimestamp(0, msgType, NULL);
- }
- return;
- }
-
- if (c != NULL) {
- c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
- }
-
-#if DEBUG_CLIENT_REFERENCES
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
- }
-#endif
-}
-
-// set preview/capture parameters - key/value pairs
-status_t CameraService::Client::setParameters(const String8& params)
-{
- LOGV("setParameters(%s)", params.string());
-
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- CameraParameters p(params);
-
- return mHardware->setParameters(p);
-}
-
-// get preview/capture parameters - key/value pairs
-String8 CameraService::Client::getParameters() const
-{
- Mutex::Autolock lock(mLock);
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return String8();
- }
-
- String8 params(mHardware->getParameters().flatten());
- LOGV("getParameters(%s)", params.string());
- return params;
-}
-
-status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
-{
- LOGV("sendCommand (pid %d)", getCallingPid());
- Mutex::Autolock lock(mLock);
- status_t result = checkPid();
- if (result != NO_ERROR) return result;
-
- if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
- // The orientation cannot be set during preview.
- if (mHardware->previewEnabled()) {
- return INVALID_OPERATION;
- }
- switch (arg1) {
- case 0:
- mOrientation = ISurface::BufferHeap::ROT_0;
- break;
- case 90:
- mOrientation = ISurface::BufferHeap::ROT_90;
- break;
- case 180:
- mOrientation = ISurface::BufferHeap::ROT_180;
- break;
- case 270:
- mOrientation = ISurface::BufferHeap::ROT_270;
- break;
- default:
- return BAD_VALUE;
- }
- return OK;
- }
-
- if (mHardware == 0) {
- LOGE("mHardware is NULL, returning.");
- return INVALID_OPERATION;
- }
-
- return mHardware->sendCommand(cmd, arg1, arg2);
-}
-
-void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
- const sp<IMemoryHeap>& heap, size_t offset, size_t size)
-{
- LOGV("copyFrameAndPostCopiedFrame");
- // It is necessary to copy out of pmem before sending this to
- // the callback. For efficiency, reuse the same MemoryHeapBase
- // provided it's big enough. Don't allocate the memory or
- // perform the copy if there's no callback.
-
- // hold the preview lock while we grab a reference to the preview buffer
- sp<MemoryHeapBase> previewBuffer;
- {
- Mutex::Autolock lock(mPreviewLock);
- if (mPreviewBuffer == 0) {
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- } else if (size > mPreviewBuffer->virtualSize()) {
- mPreviewBuffer.clear();
- mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
- }
- if (mPreviewBuffer == 0) {
- LOGE("failed to allocate space for preview buffer");
- return;
- }
- previewBuffer = mPreviewBuffer;
- }
- memcpy(previewBuffer->base(),
- (uint8_t *)heap->base() + offset, size);
-
- sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
- if (frame == 0) {
- LOGE("failed to allocate space for frame callback");
- return;
- }
- client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
-}
-
-static const int kDumpLockRetries = 50;
-static const int kDumpLockSleep = 60000;
-
-static bool tryLock(Mutex& mutex)
-{
- bool locked = false;
- for (int i = 0; i < kDumpLockRetries; ++i) {
- if (mutex.tryLock() == NO_ERROR) {
- locked = true;
- break;
- }
- usleep(kDumpLockSleep);
- }
- return locked;
-}
-
-status_t CameraService::dump(int fd, const Vector<String16>& args)
-{
- static const char* kDeadlockedString = "CameraService may be deadlocked\n";
-
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
- "can't dump CameraService from pid=%d, uid=%d\n",
- getCallingPid(),
- IPCThreadState::self()->getCallingUid());
- result.append(buffer);
- write(fd, result.string(), result.size());
- } else {
- bool locked = tryLock(mServiceLock);
- // failed to lock - CameraService is probably deadlocked
- if (!locked) {
- String8 result(kDeadlockedString);
- write(fd, result.string(), result.size());
- }
-
- if (mClient != 0) {
- sp<Client> currentClient = mClient.promote();
- sprintf(buffer, "Client (%p) PID: %d\n",
- currentClient->getCameraClient()->asBinder().get(),
- currentClient->mClientPid);
- result.append(buffer);
- write(fd, result.string(), result.size());
- currentClient->mHardware->dump(fd, args);
- } else {
- result.append("No camera client yet.\n");
- write(fd, result.string(), result.size());
- }
-
- if (locked) mServiceLock.unlock();
- }
- return NO_ERROR;
-}
-
-
-status_t CameraService::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // permission checks...
- switch (code) {
- case BnCameraService::CONNECT:
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int self_pid = getpid();
- if (pid != self_pid) {
- // we're called from a different process, do the real check
- if (!checkCallingPermission(
- String16("android.permission.CAMERA")))
- {
- const int uid = ipc->getCallingUid();
- LOGE("Permission Denial: "
- "can't use the camera pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- }
- break;
- }
-
- status_t err = BnCameraService::onTransact(code, data, reply, flags);
-
-#if DEBUG_HEAP_LEAKS
- LOGV("+++ onTransact err %d code %d", err, code);
-
- if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
- // the 'service' command interrogates this binder for its name, and then supplies it
- // even for the debugging commands. that means we need to check for it here, using
- // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to
- // BnSurfaceComposer before falling through to this code).
-
- LOGV("+++ onTransact code %d", code);
-
- CHECK_INTERFACE(ICameraService, data, reply);
-
- switch(code) {
- case 1000:
- {
- if (gWeakHeap != 0) {
- sp<IMemoryHeap> h = gWeakHeap.promote();
- IMemoryHeap *p = gWeakHeap.unsafe_get();
- LOGV("CHECKING WEAK REFERENCE %p (%p)", h.get(), p);
- if (h != 0)
- h->printRefs();
- bool attempt_to_delete = data.readInt32() == 1;
- if (attempt_to_delete) {
- // NOT SAFE!
- LOGV("DELETING WEAK REFERENCE %p (%p)", h.get(), p);
- if (p) delete p;
- }
- return NO_ERROR;
- }
- }
- break;
- default:
- break;
- }
- }
-#endif // DEBUG_HEAP_LEAKS
-
- return err;
-}
-
-}; // namespace android
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
deleted file mode 100644
index 75e96c6..0000000
--- a/camera/libcameraservice/CameraService.h
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
-**
-** Copyright (C) 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
-#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
-
-#include <camera/ICameraService.h>
-#include <camera/CameraHardwareInterface.h>
-#include <camera/Camera.h>
-
-namespace android {
-
-class MemoryHeapBase;
-class MediaPlayer;
-
-// ----------------------------------------------------------------------------
-
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-// When enabled, this feature allows you to send an event to the CameraService
-// so that you can cause all references to the heap object gWeakHeap, defined
-// below, to be printed. You will also need to set DEBUG_REFS=1 and
-// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to
-// set gWeakHeap to the appropriate heap you want to track.
-
-#define DEBUG_HEAP_LEAKS 0
-
-// ----------------------------------------------------------------------------
-
-class CameraService : public BnCameraService
-{
- class Client;
-
-public:
- static void instantiate();
-
- // ICameraService interface
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient);
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- void removeClient(const sp<ICameraClient>& cameraClient);
-
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
-
-private:
-
-// ----------------------------------------------------------------------------
-
- class Client : public BnCamera {
-
- public:
- virtual void disconnect();
-
- // connect new client with existing camera remote
- virtual status_t connect(const sp<ICameraClient>& client);
-
- // prevent other processes from using this ICamera interface
- virtual status_t lock();
-
- // allow other processes to use this ICamera interface
- virtual status_t unlock();
-
- // pass the buffered ISurface to the camera service
- virtual status_t setPreviewDisplay(const sp<ISurface>& surface);
-
- // set the preview callback flag to affect how the received frames from
- // preview are handled.
- virtual void setPreviewCallbackFlag(int callback_flag);
-
- // start preview mode, must call setPreviewDisplay first
- virtual status_t startPreview();
-
- // stop preview mode
- virtual void stopPreview();
-
- // get preview state
- virtual bool previewEnabled();
-
- // start recording mode
- virtual status_t startRecording();
-
- // stop recording mode
- virtual void stopRecording();
-
- // get recording state
- virtual bool recordingEnabled();
-
- // release a recording frame
- virtual void releaseRecordingFrame(const sp<IMemory>& mem);
-
- // auto focus
- virtual status_t autoFocus();
-
- // cancel auto focus
- virtual status_t cancelAutoFocus();
-
- // take a picture - returns an IMemory (ref-counted mmap)
- virtual status_t takePicture();
-
- // set preview/capture parameters - key/value pairs
- virtual status_t setParameters(const String8& params);
-
- // get preview/capture parameters - key/value pairs
- virtual String8 getParameters() const;
-
- // send command to camera driver
- virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
-
- // our client...
- const sp<ICameraClient>& getCameraClient() const { return mCameraClient; }
-
- private:
- friend class CameraService;
- Client(const sp<CameraService>& cameraService,
- const sp<ICameraClient>& cameraClient,
- pid_t clientPid);
- Client();
- virtual ~Client();
-
- status_t checkPid();
-
- static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
- static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
- static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
- const sp<IMemory>& dataPtr, void* user);
-
- static sp<Client> getClientFromCookie(void* user);
-
- void handlePreviewData(const sp<IMemory>&);
- void handleShutter(image_rect_type *image);
- void handlePostview(const sp<IMemory>&);
- void handleRawPicture(const sp<IMemory>&);
- void handleCompressedPicture(const sp<IMemory>&);
-
- void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
- const sp<IMemoryHeap>& heap, size_t offset, size_t size);
-
- // camera operation mode
- enum camera_mode {
- CAMERA_PREVIEW_MODE = 0, // frame automatically released
- CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
- };
- status_t startCameraMode(camera_mode mode);
- status_t startPreviewMode();
- status_t startRecordingMode();
- status_t setOverlay();
- status_t registerPreviewBuffers();
-
- // Ensures atomicity among the public methods
- mutable Mutex mLock;
-
- // mSurfaceLock synchronizes access to mSurface between
- // setPreviewSurface() and postPreviewFrame(). Note that among
- // the public methods, all accesses to mSurface are
- // syncrhonized by mLock. However, postPreviewFrame() is called
- // by the CameraHardwareInterface callback, and needs to
- // access mSurface. It cannot hold mLock, however, because
- // stopPreview() may be holding that lock while attempting
- // to stop preview, and stopPreview itself will block waiting
- // for a callback from CameraHardwareInterface. If this
- // happens, it will cause a deadlock.
- mutable Mutex mSurfaceLock;
- mutable Condition mReady;
- sp<CameraService> mCameraService;
- sp<ISurface> mSurface;
- int mPreviewCallbackFlag;
- int mOrientation;
-
- sp<MediaPlayer> mMediaPlayerClick;
- sp<MediaPlayer> mMediaPlayerBeep;
-
- // these are immutable once the object is created,
- // they don't need to be protected by a lock
- sp<ICameraClient> mCameraClient;
- sp<CameraHardwareInterface> mHardware;
- pid_t mClientPid;
- bool mUseOverlay;
-
- sp<OverlayRef> mOverlayRef;
- int mOverlayW;
- int mOverlayH;
-
- mutable Mutex mPreviewLock;
- sp<MemoryHeapBase> mPreviewBuffer;
- };
-
-// ----------------------------------------------------------------------------
-
- CameraService();
- virtual ~CameraService();
-
- // We use a count for number of clients (shoule only be 0 or 1).
- volatile int32_t mUsers;
- virtual void incUsers();
- virtual void decUsers();
-
- mutable Mutex mServiceLock;
- wp<Client> mClient;
-
-#if DEBUG_HEAP_LEAKS
- wp<IMemoryHeap> gWeakHeap;
-#endif
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif
diff --git a/camera/libcameraservice/CannedJpeg.h b/camera/libcameraservice/CannedJpeg.h
deleted file mode 100644
index b6266fb..0000000
--- a/camera/libcameraservice/CannedJpeg.h
+++ /dev/null
@@ -1,734 +0,0 @@
-const int kCannedJpegWidth = 320;
-const int kCannedJpegHeight = 240;
-const int kCannedJpegSize = 8733;
-
-const char kCannedJpeg[] = {
- 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
- 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x66,
- 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00,
- 0x00, 0x00, 0x04, 0x00, 0x1a, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x3e, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x46, 0x00, 0x00, 0x00, 0x28, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x31, 0x01, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x50, 0x61, 0x69, 0x6e, 0x74, 0x2e, 0x4e, 0x45, 0x54, 0x20, 0x76, 0x33,
- 0x2e, 0x33, 0x36, 0x00, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02,
- 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05,
- 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, 0x07, 0x06, 0x08, 0x0c,
- 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d,
- 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15,
- 0x15, 0x15, 0x0c, 0x0f, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15,
- 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05,
- 0x09, 0x05, 0x05, 0x09, 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0,
- 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02,
- 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01,
- 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
- 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
- 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
- 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
- 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82,
- 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29,
- 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
- 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
- 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
- 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
- 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
- 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
- 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
- 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
- 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00,
- 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
- 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1,
- 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a,
- 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27,
- 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
- 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
- 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
- 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
- 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
- 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
- 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5,
- 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00,
- 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xd2, 0xa3, 0x95, 0xbb,
- 0x54, 0x84, 0xe0, 0x66, 0xa0, 0x27, 0x27, 0x35, 0xed, 0x9e, 0x50, 0x95,
- 0x2c, 0x4b, 0xc6, 0x6a, 0x35, 0x1b, 0x8e, 0x2a, 0x70, 0x30, 0x28, 0x00,
- 0xa8, 0xe5, 0x6e, 0x71, 0x52, 0x31, 0xda, 0x33, 0x50, 0x13, 0x93, 0x40,
- 0x09, 0x52, 0xc6, 0xb8, 0x19, 0xf5, 0xa6, 0x2a, 0xee, 0x6c, 0x54, 0xd4,
- 0x00, 0x54, 0x52, 0x36, 0x5b, 0x1e, 0x95, 0x23, 0xb6, 0xd5, 0xcd, 0x41,
- 0x40, 0x05, 0x4c, 0x8b, 0xb5, 0x7d, 0xea, 0x34, 0x5d, 0xcd, 0xed, 0x53,
- 0x50, 0x01, 0x50, 0xbb, 0x6e, 0x6f, 0x6a, 0x91, 0xdb, 0x6a, 0xfb, 0xd4,
- 0x34, 0x00, 0x54, 0xe8, 0xbb, 0x57, 0x15, 0x1c, 0x6b, 0x96, 0xcf, 0xa5,
- 0x4b, 0x40, 0x05, 0x42, 0xcd, 0xb9, 0xb3, 0x4f, 0x91, 0xb0, 0x31, 0xeb,
- 0x51, 0x50, 0x02, 0x81, 0x93, 0x53, 0xa8, 0xda, 0x31, 0x51, 0xc4, 0xbc,
- 0xe6, 0xa4, 0xa0, 0x00, 0x9c, 0x0a, 0x81, 0x8e, 0xe3, 0x9a, 0x92, 0x56,
- 0xe3, 0x15, 0x15, 0x00, 0x28, 0x19, 0x38, 0xa9, 0xc0, 0xc0, 0xc5, 0x47,
- 0x12, 0xf7, 0xa9, 0x28, 0x00, 0x27, 0x00, 0x9a, 0x80, 0x9c, 0x9c, 0xd3,
- 0xe5, 0x6e, 0xd5, 0x1d, 0x00, 0x2a, 0x8d, 0xc7, 0x15, 0x3d, 0x32, 0x35,
- 0xc0, 0xcf, 0xad, 0x3e, 0x80, 0x11, 0x8e, 0xd1, 0x9a, 0x82, 0x9f, 0x23,
- 0x64, 0xe3, 0xd2, 0x99, 0x40, 0x0e, 0x45, 0xdc, 0xde, 0xd5, 0x35, 0x36,
- 0x35, 0xc2, 0xfb, 0x9a, 0x75, 0x00, 0x35, 0xdb, 0x6a, 0xfb, 0xd4, 0x34,
- 0xe9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, 0x00, 0x3e, 0x35, 0xcb, 0x7b, 0x0a,
- 0x96, 0x91, 0x17, 0x6a, 0xd2, 0xd0, 0x03, 0x64, 0x6c, 0x2f, 0xb9, 0xa8,
- 0x69, 0xce, 0xdb, 0x9a, 0x9b, 0xd6, 0x80, 0x1f, 0x12, 0xe4, 0xe7, 0xd2,
- 0xa5, 0xa4, 0x51, 0xb4, 0x62, 0x97, 0xa5, 0x00, 0x67, 0xc9, 0xad, 0xd8,
- 0x91, 0x81, 0x72, 0x9f, 0x9d, 0x47, 0xfd, 0xb3, 0x65, 0xff, 0x00, 0x3f,
- 0x29, 0x5f, 0xa0, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad,
- 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43,
- 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd7, 0x3f, 0xb7, 0x87, 0x73, 0x6f,
- 0x63, 0x33, 0xe0, 0x28, 0xf5, 0x9b, 0x11, 0xc9, 0xb9, 0x4c, 0xfd, 0x69,
- 0xff, 0x00, 0xdb, 0x96, 0x1f, 0xf3, 0xf5, 0x1f, 0xe7, 0x5f, 0x7d, 0x7f,
- 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80,
- 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7,
- 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, 0x02, 0x93,
- 0x5b, 0xb1, 0x3c, 0x0b, 0x94, 0xc7, 0xd6, 0x99, 0xfd, 0xb3, 0x65, 0xff,
- 0x00, 0x3f, 0x29, 0xf9, 0xd7, 0xe8, 0x07, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f,
- 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09,
- 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0xbd, 0xbc, 0x03, 0xd8,
- 0xcc, 0xf8, 0x0e, 0x3d, 0x6a, 0xc1, 0x47, 0x37, 0x29, 0x9f, 0xad, 0x3b,
- 0xfb, 0x72, 0xc3, 0xfe, 0x7e, 0xa3, 0xfc, 0xeb, 0xef, 0xaf, 0xf8, 0x74,
- 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1,
- 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48,
- 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x19, 0x35, 0xbb, 0x26,
- 0x3c, 0x5c, 0xa6, 0x3e, 0xb4, 0xdf, 0xed, 0x9b, 0x2f, 0xf9, 0xf9, 0x4a,
- 0xfd, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
- 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f,
- 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x97, 0xb7, 0x80, 0x7b, 0x19, 0x9f, 0x01,
- 0xa6, 0xb5, 0x60, 0xab, 0xff, 0x00, 0x1f, 0x51, 0xe7, 0xeb, 0x4e, 0xfe,
- 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0xfb, 0xeb, 0xfe,
- 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f,
- 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a,
- 0x3d, 0xbc, 0x03, 0xd8, 0xcc, 0xf8, 0x05, 0xf5, 0xab, 0x26, 0x6f, 0xf8,
- 0xf9, 0x4c, 0x7d, 0x69, 0xbf, 0xdb, 0x36, 0x5f, 0xf3, 0xf2, 0x9f, 0x9d,
- 0x7e, 0x80, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5,
- 0xff, 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd,
- 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x80, 0x7b, 0x19, 0x9f,
- 0x02, 0x26, 0xb5, 0x60, 0xab, 0x8f, 0xb5, 0x47, 0xf9, 0xd2, 0xff, 0x00,
- 0x6e, 0x58, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x7d, 0xf5, 0xff, 0x00, 0x0e,
- 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc,
- 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e,
- 0xde, 0x01, 0xec, 0x66, 0x7c, 0x00, 0xda, 0xd5, 0x93, 0x1c, 0xfd, 0xa5,
- 0x3f, 0x3a, 0x4f, 0xed, 0x8b, 0x2f, 0xf9, 0xf9, 0x4f, 0xce, 0xbf, 0x40,
- 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00,
- 0xc0, 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd,
- 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa7, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf,
- 0x81, 0x57, 0x5a, 0xb0, 0x51, 0x8f, 0xb5, 0x47, 0xf9, 0xd1, 0xfd, 0xb9,
- 0x61, 0xff, 0x00, 0x3f, 0x49, 0xf9, 0xd7, 0xdf, 0x5f, 0xf0, 0xe9, 0x6f,
- 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5,
- 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd2,
- 0xf6, 0xf0, 0x0f, 0x63, 0x33, 0xe0, 0x06, 0xd6, 0xac, 0x98, 0xe7, 0xed,
- 0x29, 0xf9, 0xd2, 0x0d, 0x62, 0xcb, 0xfe, 0x7e, 0x53, 0xf3, 0xaf, 0xd0,
- 0x0f, 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12,
- 0x1a, 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff,
- 0x00, 0xc0, 0x48, 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x51,
- 0xad, 0xd8, 0x01, 0x8f, 0xb5, 0x47, 0xf9, 0xd0, 0x75, 0xcb, 0x0c, 0x7f,
- 0xc7, 0xca, 0x7e, 0x75, 0xf7, 0xd7, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, 0xf4,
- 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f,
- 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7b, 0x78, 0x77, 0x0f, 0x63,
- 0x33, 0xf3, 0xfc, 0xeb, 0x36, 0x44, 0xff, 0x00, 0xc7, 0xca, 0x7e, 0x74,
- 0xa3, 0x58, 0xb1, 0x24, 0x66, 0xe5, 0x31, 0xf5, 0xaf, 0xbf, 0xff, 0x00,
- 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0,
- 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
- 0xff, 0x00, 0x01, 0x21, 0xa3, 0xdb, 0xc3, 0xb8, 0x7b, 0x19, 0x9f, 0x02,
- 0xff, 0x00, 0x6d, 0xd8, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x07, 0x5c, 0xb1,
- 0x03, 0x8b, 0x94, 0xcf, 0xd6, 0xbe, 0xfa, 0xff, 0x00, 0x87, 0x4b, 0x78,
- 0x4b, 0xfe, 0x87, 0xdd, 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d,
- 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f,
- 0x0e, 0xe1, 0xec, 0x66, 0x7e, 0x7f, 0xff, 0x00, 0x6c, 0xd9, 0x7f, 0xcf,
- 0xca, 0x7e, 0x74, 0xab, 0xac, 0x58, 0xe7, 0x9b, 0x94, 0xc7, 0xd6, 0xbe,
- 0xff, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
- 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f,
- 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, 0x0e, 0xe1, 0xec, 0x66, 0x7c,
- 0x0b, 0xfd, 0xb9, 0x61, 0xff, 0x00, 0x3f, 0x51, 0xfe, 0x74, 0x8d, 0xae,
- 0x58, 0xed, 0x38, 0xb9, 0x4c, 0xfd, 0x6b, 0xef, 0xbf, 0xf8, 0x74, 0xb7,
- 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, 0xd2,
- 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, 0x68,
- 0xf6, 0xf0, 0xee, 0x1e, 0xc6, 0x67, 0xe7, 0xff, 0x00, 0xf6, 0xc5, 0x97,
- 0xfc, 0xfc, 0xa7, 0xe7, 0x4e, 0x4d, 0x62, 0xc7, 0x77, 0x37, 0x29, 0xf9,
- 0xd7, 0xdf, 0xdf, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f,
- 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee,
- 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf,
- 0x81, 0x7f, 0xb7, 0x2c, 0x3f, 0xe7, 0xea, 0x3f, 0xce, 0x91, 0xf5, 0xcb,
- 0x1c, 0x71, 0x72, 0x9f, 0x9d, 0x7d, 0xf7, 0xff, 0x00, 0x0e, 0x96, 0xf0,
- 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, 0x3a, 0x5b,
- 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, 0xde, 0x1d,
- 0xc3, 0xd8, 0xcc, 0xfc, 0xff, 0x00, 0xfe, 0xd9, 0xb2, 0xff, 0x00, 0x9f,
- 0x94, 0xfc, 0xe9, 0xd1, 0xeb, 0x36, 0x20, 0xe4, 0xdc, 0xa7, 0xe7, 0x5f,
- 0x7f, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff,
- 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f,
- 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e,
- 0x05, 0xfe, 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0x6c,
- 0x9a, 0xdd, 0x89, 0x18, 0x17, 0x29, 0xf9, 0xd7, 0xdf, 0x9f, 0xf0, 0xe9,
- 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3,
- 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90,
- 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, 0xbc, 0xa8, 0xa2, 0x8a, 0xf3,
- 0x0e, 0xf0, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
- 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
- 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0xa0, 0xbb, 0xbd, 0xb7, 0xb0,
- 0x88, 0x49, 0x73, 0x3c, 0x56, 0xf1, 0x96, 0x0a, 0x1e, 0x57, 0x0a, 0x09,
- 0x3d, 0x06, 0x4f, 0x7a, 0x9e, 0x95, 0xd3, 0x76, 0xea, 0x01, 0x45, 0x14,
- 0x53, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x82, 0xda, 0xf6, 0xde,
- 0xf0, 0xca, 0x2d, 0xe7, 0x8a, 0x73, 0x13, 0x98, 0xe4, 0xf2, 0xdc, 0x36,
- 0xc6, 0x1d, 0x54, 0xe3, 0xa1, 0xf6, 0xa4, 0xda, 0x4e, 0xcc, 0x09, 0xe8,
- 0xa2, 0x8a, 0x60, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14,
- 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14,
- 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x02, 0xb8,
- 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45,
- 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x18, 0x51, 0x45, 0x14, 0x0a, 0xe1, 0x45,
- 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb9, 0xca, 0xfc, 0x4a, 0xf0,
- 0x52, 0x78, 0xef, 0xc2, 0xb7, 0x1a, 0x76, 0xef, 0x2e, 0xe5, 0x4f, 0x9d,
- 0x6c, 0xe4, 0xe0, 0x09, 0x00, 0x38, 0xcf, 0xb1, 0xc9, 0x1f, 0x8e, 0x7b,
- 0x57, 0x3d, 0xf0, 0x5b, 0xc7, 0x53, 0x6b, 0xba, 0x6c, 0xda, 0x16, 0xaa,
- 0x5a, 0x3d, 0x73, 0x4a, 0xfd, 0xd4, 0x8b, 0x2f, 0xdf, 0x91, 0x01, 0xc0,
- 0x27, 0xdc, 0x1e, 0x0f, 0xe0, 0x7b, 0xd7, 0xa3, 0x5c, 0xdc, 0xc5, 0x67,
- 0x04, 0x93, 0xcf, 0x2a, 0x43, 0x0c, 0x60, 0xb3, 0xc9, 0x23, 0x05, 0x55,
- 0x1e, 0xa4, 0x9e, 0x95, 0xf3, 0x47, 0xc4, 0x8f, 0x1f, 0xe9, 0x36, 0xdf,
- 0x10, 0xed, 0x3c, 0x41, 0xe1, 0x39, 0x99, 0xaf, 0xa1, 0xe2, 0xea, 0x42,
- 0x98, 0x82, 0x72, 0x38, 0xe3, 0x90, 0x4e, 0x46, 0x41, 0xe9, 0x9c, 0x0c,
- 0x7a, 0xd7, 0xc4, 0x67, 0x98, 0x9a, 0x59, 0x3e, 0x26, 0x9e, 0x64, 0xa6,
- 0x93, 0x7e, 0xec, 0xe3, 0x7d, 0x65, 0x1e, 0xe9, 0x77, 0x8b, 0xd7, 0xd3,
- 0x4b, 0x99, 0x4d, 0xa8, 0xbe, 0x63, 0xe9, 0xca, 0x2b, 0xe4, 0x3d, 0x73,
- 0xe3, 0x3f, 0x8b, 0xb5, 0xc6, 0x6d, 0xfa, 0xb4, 0x96, 0x71, 0x9e, 0x91,
- 0x59, 0x0f, 0x28, 0x0f, 0xc4, 0x7c, 0xdf, 0x99, 0xae, 0x56, 0xe7, 0x5a,
- 0xd4, 0x6f, 0x18, 0xb5, 0xc5, 0xfd, 0xd4, 0xec, 0x7b, 0xc9, 0x33, 0x31,
- 0xfd, 0x4d, 0x78, 0x75, 0xf8, 0xfb, 0x0b, 0x07, 0x6a, 0x14, 0x65, 0x25,
- 0xe6, 0xd2, 0xff, 0x00, 0x32, 0x1d, 0x75, 0xd1, 0x1f, 0x73, 0x51, 0x5f,
- 0x0b, 0xdb, 0xea, 0xf7, 0xf6, 0xad, 0xba, 0x0b, 0xdb, 0x88, 0x58, 0x77,
- 0x8e, 0x56, 0x53, 0xfa, 0x1a, 0xe9, 0xf4, 0x5f, 0x8b, 0xfe, 0x2e, 0xd0,
- 0xd9, 0x7c, 0xad, 0x66, 0x7b, 0x84, 0x1f, 0xf2, 0xce, 0xec, 0xf9, 0xc0,
- 0xff, 0x00, 0xdf, 0x59, 0x23, 0xf0, 0x34, 0xa8, 0x71, 0xf6, 0x1a, 0x4e,
- 0xd5, 0xa8, 0x4a, 0x2b, 0xc9, 0xa7, 0xfe, 0x40, 0xab, 0xae, 0xa8, 0xfa,
- 0x13, 0xe2, 0xff, 0x00, 0x8f, 0xcf, 0x82, 0xfc, 0x3e, 0x21, 0xb3, 0x6d,
- 0xda, 0xcd, 0xfe, 0x62, 0xb5, 0x45, 0xe5, 0x97, 0xb1, 0x7c, 0x7b, 0x67,
- 0x8f, 0x72, 0x3d, 0xea, 0x5f, 0x84, 0x7e, 0x05, 0x6f, 0x04, 0x78, 0x60,
- 0x2d, 0xd1, 0x2d, 0xa9, 0xde, 0xb0, 0x9e, 0xe8, 0x93, 0x9d, 0xad, 0x8e,
- 0x17, 0xf0, 0x1d, 0x4f, 0xa9, 0x35, 0xe2, 0x5e, 0x13, 0xf8, 0x89, 0x61,
- 0xac, 0xfc, 0x49, 0x8f, 0xc4, 0x3e, 0x30, 0x76, 0xcc, 0x68, 0x16, 0xd8,
- 0x43, 0x19, 0x68, 0x61, 0x61, 0xd0, 0x91, 0x92, 0x40, 0x1c, 0x9e, 0x33,
- 0xc9, 0xcd, 0x7d, 0x3b, 0x63, 0x7f, 0x6d, 0xaa, 0x5a, 0x45, 0x75, 0x69,
- 0x3c, 0x77, 0x36, 0xd2, 0x8d, 0xc9, 0x2c, 0x4c, 0x19, 0x58, 0x7b, 0x11,
- 0x5e, 0xde, 0x4d, 0x8b, 0xa3, 0x9d, 0xe3, 0x2a, 0x66, 0x1c, 0xe9, 0xf2,
- 0x5e, 0x30, 0x8f, 0x58, 0xae, 0xb2, 0x6b, 0xbc, 0xbf, 0x05, 0xa1, 0x50,
- 0x6a, 0x6f, 0x98, 0xb1, 0x45, 0x14, 0x57, 0xdc, 0x9b, 0x5c, 0x28, 0xa2,
- 0x8a, 0x02, 0xe1, 0x45, 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb8,
- 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45,
- 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x0b, 0x8d, 0xcd, 0x19, 0xa6, 0xe4, 0x51,
- 0x91, 0x55, 0x62, 0x47, 0x66, 0x8c, 0xd3, 0x72, 0x28, 0xc8, 0xa2, 0xc0,
- 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x01, 0xd9, 0xa3, 0x34,
- 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, 0xcd, 0x19, 0xa6, 0xe4, 0x52, 0xe4,
- 0x51, 0x60, 0xb8, 0xb9, 0xa3, 0x34, 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e,
- 0xdd, 0x46, 0x69, 0xb9, 0x14, 0x64, 0x51, 0x60, 0x1d, 0x9a, 0xa7, 0xac,
- 0x6b, 0x16, 0x9a, 0x0e, 0x9b, 0x71, 0xa8, 0x5f, 0x4c, 0x20, 0xb5, 0x81,
- 0x37, 0xbb, 0x9e, 0xc3, 0xd0, 0x7a, 0x93, 0xd0, 0x0a, 0xb5, 0x91, 0x5f,
- 0x39, 0xfe, 0xd1, 0x1e, 0x37, 0x7d, 0x4b, 0x5a, 0x4f, 0x0f, 0x5b, 0x48,
- 0x45, 0xa5, 0x96, 0x1e, 0x70, 0xa7, 0xef, 0xca, 0x46, 0x40, 0x3f, 0xee,
- 0x83, 0xf9, 0x93, 0xe9, 0x5e, 0x06, 0x79, 0x9a, 0xc3, 0x27, 0xc1, 0x4b,
- 0x12, 0xd5, 0xe5, 0xb4, 0x57, 0x76, 0xff, 0x00, 0xab, 0xbf, 0x24, 0x44,
- 0xe5, 0xca, 0xae, 0x72, 0xbf, 0x12, 0xbe, 0x2a, 0xea, 0x3e, 0x3e, 0xbd,
- 0x78, 0xd5, 0x9e, 0xd3, 0x48, 0x46, 0xfd, 0xd5, 0xa2, 0x9f, 0xbd, 0xe8,
- 0xcf, 0xea, 0x7f, 0x41, 0xdb, 0xd4, 0xc3, 0xe0, 0x5f, 0x85, 0x1a, 0xd7,
- 0x8f, 0xed, 0xe6, 0xb9, 0xb1, 0xf2, 0x2d, 0xed, 0x22, 0x6d, 0x86, 0x7b,
- 0x96, 0x21, 0x59, 0xb1, 0x9c, 0x0c, 0x02, 0x4f, 0x51, 0xf9, 0xd7, 0x19,
- 0x5e, 0xcd, 0xf0, 0x73, 0xe3, 0x16, 0x97, 0xe1, 0x0d, 0x06, 0x4d, 0x23,
- 0x57, 0x49, 0x63, 0x44, 0x95, 0xa5, 0x86, 0x78, 0x53, 0x78, 0x21, 0xba,
- 0xab, 0x0e, 0xb9, 0xcf, 0x7f, 0x7f, 0x6a, 0xfc, 0x1b, 0x2e, 0xa9, 0x87,
- 0xcd, 0xb3, 0x2f, 0x69, 0x9c, 0xd5, 0x6a, 0x2e, 0xfa, 0xde, 0xda, 0xf4,
- 0x57, 0xe8, 0xbf, 0xe1, 0x8e, 0x48, 0xda, 0x52, 0xf7, 0x8f, 0x30, 0xf1,
- 0x57, 0x85, 0x75, 0x0f, 0x06, 0xeb, 0x12, 0x69, 0xba, 0x94, 0x42, 0x3b,
- 0x84, 0x01, 0x83, 0x21, 0xca, 0xba, 0x9e, 0x8c, 0xa7, 0xb8, 0xac, 0x8a,
- 0xed, 0x3e, 0x2c, 0xf8, 0xee, 0x1f, 0x1f, 0xf8, 0x9c, 0x5e, 0xda, 0xc2,
- 0xf0, 0xda, 0x41, 0x08, 0x82, 0x2f, 0x33, 0x01, 0xd8, 0x02, 0x49, 0x63,
- 0xe9, 0xc9, 0x3c, 0x57, 0x17, 0x5e, 0x26, 0x3e, 0x9e, 0x1e, 0x96, 0x2a,
- 0xa4, 0x30, 0xb2, 0xe6, 0xa6, 0x9b, 0xb3, 0xee, 0x88, 0x76, 0xbe, 0x81,
- 0x5a, 0x1a, 0x06, 0x83, 0x7b, 0xe2, 0x7d, 0x5e, 0xdf, 0x4d, 0xd3, 0xe2,
- 0xf3, 0xae, 0xa7, 0x38, 0x55, 0xce, 0x00, 0x00, 0x64, 0x92, 0x7b, 0x00,
- 0x39, 0xac, 0xfa, 0xea, 0x3e, 0x1b, 0x78, 0xc1, 0x7c, 0x0d, 0xe2, 0xcb,
- 0x5d, 0x52, 0x58, 0x4c, 0xf6, 0xe1, 0x5a, 0x39, 0x51, 0x3e, 0xf6, 0xd6,
- 0x18, 0x24, 0x7b, 0x8e, 0x0d, 0x67, 0x83, 0x85, 0x1a, 0x98, 0x8a, 0x70,
- 0xc4, 0x4b, 0x96, 0x0d, 0xae, 0x67, 0xd9, 0x5f, 0x50, 0x56, 0xbe, 0xa6,
- 0x97, 0x8d, 0x7e, 0x0e, 0xeb, 0xde, 0x06, 0xd3, 0x17, 0x50, 0xbb, 0x36,
- 0xf7, 0x56, 0x99, 0x0b, 0x24, 0x96, 0xae, 0x4f, 0x96, 0x4f, 0x4d, 0xc0,
- 0x81, 0xc1, 0x3c, 0x66, 0xa9, 0xfc, 0x3e, 0xf8, 0x93, 0xaa, 0x78, 0x03,
- 0x50, 0x0f, 0x6c, 0xe6, 0x7b, 0x07, 0x6f, 0xdf, 0xd9, 0x3b, 0x7c, 0x8e,
- 0x3d, 0x47, 0xa3, 0x7b, 0xfe, 0x79, 0xaf, 0x45, 0xf8, 0xad, 0xf1, 0xb3,
- 0x47, 0xf1, 0x27, 0x85, 0x26, 0xd2, 0x34, 0x84, 0x9a, 0x67, 0xbb, 0x2b,
- 0xe6, 0xcb, 0x34, 0x7b, 0x04, 0x6a, 0x18, 0x36, 0x07, 0xa9, 0xc8, 0x1e,
- 0xd5, 0xe1, 0x95, 0xf4, 0x39, 0xab, 0xc2, 0x65, 0x79, 0x84, 0x67, 0x93,
- 0x55, 0x6d, 0x24, 0x9d, 0xd3, 0xbd, 0x9f, 0x55, 0x7e, 0xaa, 0xd6, 0xbe,
- 0xfb, 0xd8, 0xb9, 0x5a, 0x32, 0xf7, 0x59, 0xf6, 0xef, 0x86, 0xbc, 0x49,
- 0x63, 0xe2, 0xbd, 0x1a, 0xdf, 0x53, 0xd3, 0xe5, 0xf3, 0x2d, 0xe6, 0x1d,
- 0xfe, 0xf2, 0x1e, 0xea, 0xc3, 0xb1, 0x15, 0xa9, 0x9a, 0xf9, 0x7b, 0xe0,
- 0x27, 0x8d, 0xe4, 0xf0, 0xef, 0x8a, 0x53, 0x4a, 0x9e, 0x43, 0xfd, 0x9f,
- 0xa9, 0x30, 0x8f, 0x69, 0x3c, 0x24, 0xdf, 0xc0, 0xc3, 0xeb, 0xf7, 0x7f,
- 0x11, 0xe9, 0x5f, 0x4f, 0xe4, 0x57, 0xee, 0x3c, 0x3f, 0x9b, 0xc7, 0x39,
- 0xc1, 0x2a, 0xed, 0x5a, 0x6b, 0x49, 0x2f, 0x3f, 0xf2, 0x7b, 0xfe, 0x1d,
- 0x0e, 0xb8, 0x4f, 0x99, 0x5c, 0x76, 0x4d, 0x19, 0xa6, 0xe4, 0x51, 0x91,
- 0x5f, 0x4b, 0x62, 0xc7, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58,
- 0x2e, 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x0b, 0x8e, 0xcd,
- 0x19, 0xa6, 0xe4, 0x51, 0x91, 0x45, 0x80, 0x76, 0x68, 0xcd, 0x37, 0x34,
- 0x64, 0x51, 0x60, 0xb8, 0xec, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58,
- 0x07, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, 0x06, 0x6e, 0xa3,
- 0x75, 0x37, 0x34, 0x66, 0xae, 0xc4, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73,
- 0x46, 0x68, 0xb0, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, 0x46, 0x68, 0xb0,
- 0x5c, 0x76, 0xea, 0x37, 0x53, 0x72, 0x28, 0xcd, 0x16, 0x0b, 0x8e, 0xdd,
- 0x46, 0xea, 0x6e, 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0x6e,
- 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0xc4, 0xf1, 0x57, 0x8c,
- 0x34, 0xaf, 0x06, 0x69, 0xff, 0x00, 0x6b, 0xd5, 0x2e, 0x44, 0x28, 0xc7,
- 0x08, 0x8a, 0x37, 0x3c, 0x87, 0xd1, 0x47, 0x7f, 0xe5, 0x5c, 0x0d, 0x9f,
- 0xed, 0x1f, 0xe1, 0xcb, 0x8b, 0xc1, 0x14, 0xd6, 0x97, 0xf6, 0xb0, 0x93,
- 0x81, 0x3b, 0xa2, 0xb0, 0x1e, 0xe4, 0x06, 0x27, 0xf2, 0xcd, 0x78, 0xf8,
- 0xac, 0xdf, 0x2f, 0xc0, 0xd4, 0x54, 0x71, 0x35, 0xa3, 0x19, 0x3e, 0x8d,
- 0xfe, 0x7d, 0xbe, 0x64, 0xb9, 0x25, 0xb9, 0xeb, 0x05, 0xf6, 0x82, 0x4f,
- 0x41, 0x5f, 0x10, 0xeb, 0x7a, 0x93, 0xeb, 0x3a, 0xcd, 0xf5, 0xfc, 0x84,
- 0x97, 0xb9, 0x9d, 0xe6, 0x39, 0xff, 0x00, 0x69, 0x89, 0xfe, 0xb5, 0xf6,
- 0xad, 0x8e, 0xa1, 0x6b, 0xab, 0x58, 0xc5, 0x75, 0x69, 0x34, 0x77, 0x36,
- 0xb3, 0x2e, 0xe4, 0x91, 0x0e, 0x55, 0x85, 0x78, 0x5f, 0xfc, 0x2d, 0x5f,
- 0x87, 0x3f, 0xf4, 0x25, 0x27, 0xfe, 0x00, 0xdb, 0xff, 0x00, 0x8d, 0x7c,
- 0x67, 0x18, 0xe1, 0xa8, 0x63, 0x61, 0x87, 0x55, 0x31, 0x31, 0xa7, 0x1f,
- 0x79, 0xab, 0xdd, 0xf3, 0x7c, 0x3a, 0xab, 0x76, 0xfd, 0x4c, 0xea, 0x59,
- 0xdb, 0x53, 0xc4, 0x68, 0xaf, 0x6e, 0xff, 0x00, 0x85, 0xab, 0xf0, 0xe7,
- 0xfe, 0x84, 0xa4, 0xff, 0x00, 0xc0, 0x1b, 0x7f, 0xf1, 0xa3, 0xfe, 0x16,
- 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00,
- 0xc6, 0xbf, 0x33, 0xfe, 0xc5, 0xc0, 0xff, 0x00, 0xd0, 0x7c, 0x3e, 0xe9,
- 0x7f, 0x91, 0x8f, 0x2a, 0xee, 0x78, 0x8d, 0x15, 0xed, 0xdf, 0xf0, 0xb5,
- 0x7e, 0x1c, 0xff, 0x00, 0xd0, 0x94, 0x9f, 0xf8, 0x03, 0x6f, 0xfe, 0x34,
- 0x7f, 0xc2, 0xd5, 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d,
- 0xbf, 0xf8, 0xd1, 0xfd, 0x8b, 0x81, 0xff, 0x00, 0xa0, 0xf8, 0x7d, 0xd2,
- 0xff, 0x00, 0x20, 0xe5, 0x5d, 0xcf, 0x11, 0xa2, 0xbd, 0xbb, 0xfe, 0x16,
- 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00,
- 0xc6, 0x8f, 0xf8, 0x5a, 0xbf, 0x0e, 0x7f, 0xe8, 0x4a, 0x4f, 0xfc, 0x01,
- 0xb7, 0xff, 0x00, 0x1a, 0x3f, 0xb1, 0x70, 0x3f, 0xf4, 0x1f, 0x0f, 0xba,
- 0x5f, 0xe4, 0x1c, 0xab, 0xb9, 0xe2, 0x34, 0x57, 0xb7, 0x7f, 0xc2, 0xd5,
- 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd1,
- 0xff, 0x00, 0x0b, 0x57, 0xe1, 0xcf, 0xfd, 0x09, 0x49, 0xff, 0x00, 0x80,
- 0x36, 0xff, 0x00, 0xe3, 0x47, 0xf6, 0x2e, 0x07, 0xfe, 0x83, 0xe1, 0xf7,
- 0x4b, 0xfc, 0x83, 0x95, 0x77, 0x3c, 0x52, 0x09, 0xde, 0xda, 0x78, 0xe6,
- 0x89, 0x8a, 0x49, 0x1b, 0x07, 0x56, 0x1d, 0x41, 0x07, 0x20, 0xd7, 0xdb,
- 0xfa, 0x5d, 0xf0, 0xd4, 0x74, 0xdb, 0x4b, 0xb0, 0x30, 0x27, 0x85, 0x25,
- 0x03, 0xfd, 0xe5, 0x07, 0xfa, 0xd7, 0x85, 0xff, 0x00, 0xc2, 0xd5, 0xf8,
- 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd7, 0xb6,
- 0x69, 0x1a, 0x95, 0xa5, 0xc7, 0x87, 0xec, 0xaf, 0xe1, 0x55, 0xb3, 0xb1,
- 0x7b, 0x54, 0x99, 0x11, 0xf0, 0x82, 0x28, 0xca, 0x02, 0x01, 0xec, 0x30,
- 0x3f, 0x0e, 0x2b, 0xf4, 0x7e, 0x0e, 0xc2, 0xd0, 0xc1, 0xce, 0xbc, 0x69,
- 0x62, 0x63, 0x51, 0x34, 0x9b, 0x4a, 0xfa, 0x5a, 0xfa, 0xeb, 0xea, 0x6d,
- 0x4e, 0xca, 0xfa, 0x9a, 0x5b, 0xa8, 0xdd, 0x5e, 0x57, 0xab, 0xfe, 0xd1,
- 0x3e, 0x1b, 0xd3, 0xaf, 0x1a, 0x0b, 0x68, 0x6e, 0xf5, 0x15, 0x53, 0x83,
- 0x34, 0x28, 0xaa, 0x87, 0xe9, 0xb8, 0x82, 0x7f, 0x2a, 0xeb, 0xbc, 0x1b,
- 0xf1, 0x07, 0x45, 0xf1, 0xcd, 0xbb, 0xbe, 0x99, 0x70, 0x7c, 0xe8, 0xc6,
- 0x64, 0xb6, 0x98, 0x6d, 0x91, 0x07, 0xa9, 0x1d, 0xc7, 0xb8, 0xc8, 0xaf,
- 0xb7, 0xc3, 0xe7, 0x19, 0x76, 0x2a, 0xb7, 0xd5, 0xe8, 0x56, 0x8c, 0xa7,
- 0xd9, 0x3f, 0xcb, 0xbf, 0xc8, 0xd1, 0x49, 0x3d, 0x2e, 0x74, 0xdb, 0xa8,
- 0xcd, 0x37, 0x34, 0x64, 0x57, 0xb2, 0x55, 0xc7, 0x6e, 0xa3, 0x75, 0x37,
- 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b,
- 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e,
- 0xa3, 0x75, 0x37, 0x34, 0x64, 0x51, 0x60, 0xb8, 0xed, 0xd4, 0x6e, 0xa6,
- 0xe4, 0x51, 0x9a, 0x2c, 0x17, 0x1b, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75,
- 0x55, 0x88, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, 0xd4, 0x58, 0x2e,
- 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, 0xc9, 0x32, 0x28,
- 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, 0x1e, 0xea,
- 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8c, 0xd3, 0xb0,
- 0x5c, 0xf9, 0x7b, 0xe3, 0xb6, 0xaf, 0x3e, 0xa5, 0xf1, 0x0e, 0xf2, 0xde,
- 0x47, 0x26, 0x1b, 0x24, 0x48, 0x62, 0x4e, 0xc0, 0x15, 0x0c, 0x4f, 0xe2,
- 0x58, 0xfe, 0x95, 0xe7, 0x95, 0xda, 0x7c, 0x64, 0xff, 0x00, 0x92, 0x97,
- 0xad, 0xff, 0x00, 0xbf, 0x1f, 0xfe, 0x8a, 0x4a, 0xe2, 0xeb, 0xf9, 0x47,
- 0x3a, 0x9c, 0xaa, 0x66, 0x78, 0x99, 0x49, 0xdd, 0xf3, 0xcb, 0xf0, 0x6d,
- 0x23, 0x92, 0x5b, 0x9e, 0xf7, 0xfb, 0x34, 0xeb, 0x13, 0xcd, 0x67, 0xac,
- 0xe9, 0xb2, 0x39, 0x6b, 0x78, 0x1a, 0x39, 0xa2, 0x04, 0xfd, 0xd2, 0xdb,
- 0x83, 0x0f, 0xc7, 0x68, 0xfd, 0x6b, 0xc1, 0x2b, 0xda, 0xff, 0x00, 0x66,
- 0x73, 0x8b, 0xed, 0x7f, 0xfe, 0xb9, 0xc3, 0xfc, 0xde, 0xbc, 0x52, 0xbd,
- 0x8c, 0xd2, 0x72, 0x9e, 0x4b, 0x97, 0x39, 0x3b, 0xdb, 0xda, 0xaf, 0x92,
- 0x92, 0xb1, 0x4f, 0xe1, 0x41, 0x45, 0x14, 0x57, 0xc6, 0x90, 0x14, 0x51,
- 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x15, 0xef,
- 0x7f, 0x13, 0xf5, 0x89, 0xf4, 0xef, 0x82, 0xbe, 0x19, 0xb6, 0x81, 0xca,
- 0x0b, 0xc8, 0x2d, 0x62, 0x94, 0x83, 0xd5, 0x04, 0x3b, 0x88, 0xfc, 0x48,
- 0x15, 0xe0, 0x95, 0xed, 0x7f, 0x17, 0x0f, 0xfc, 0x5a, 0x5f, 0x05, 0xff,
- 0x00, 0xd7, 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x96, 0x47, 0x39, 0x43, 0x03,
- 0x98, 0x4a, 0x2e, 0xcf, 0x91, 0x7e, 0x32, 0xb3, 0xfc, 0x0a, 0x8e, 0xcc,
- 0xf1, 0x4a, 0xe9, 0xbe, 0x1a, 0x6a, 0xf3, 0xe8, 0xbe, 0x3a, 0xd1, 0x67,
- 0x81, 0xca, 0x99, 0x2e, 0x52, 0x07, 0x00, 0xfd, 0xe4, 0x76, 0x0a, 0xc0,
- 0xfe, 0x07, 0xf4, 0xae, 0x66, 0xb6, 0x3c, 0x1b, 0xff, 0x00, 0x23, 0x7e,
- 0x87, 0xff, 0x00, 0x5f, 0xd0, 0x7f, 0xe8, 0xc5, 0xaf, 0x9b, 0xc0, 0xce,
- 0x54, 0xf1, 0x54, 0xa7, 0x07, 0x66, 0xa4, 0xbf, 0x31, 0x2d, 0xcf, 0xb4,
- 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xaf, 0xeb, 0x9b, 0x1d, 0x57, 0x24,
- 0xcd, 0x19, 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35,
- 0x1e, 0xea, 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d,
- 0xd4, 0x58, 0x2e, 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15,
- 0xc9, 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xa2, 0xc3, 0xb8, 0xdd, 0xd4,
- 0x9b, 0xa9, 0xbb, 0xa8, 0xdd, 0x5a, 0x19, 0xdc, 0x7e, 0xea, 0x4d, 0xd4,
- 0xdd, 0xd4, 0x6e, 0xa0, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e,
- 0xa0, 0x77, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, 0xa4, 0x2b, 0x8f,
- 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0xc2, 0xe3, 0xb7, 0x52, 0xee,
- 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb9, 0xf2, 0x9f, 0xc6, 0x3f, 0xf9, 0x29,
- 0x5a, 0xdf, 0xfb, 0xf1, 0xff, 0x00, 0xe8, 0xb4, 0xae, 0x32, 0xbb, 0x2f,
- 0x8c, 0x5c, 0xfc, 0x49, 0xd6, 0xff, 0x00, 0xdf, 0x8f, 0xff, 0x00, 0x45,
- 0xa5, 0x71, 0xb5, 0xfc, 0x99, 0x9c, 0x7f, 0xc8, 0xcb, 0x13, 0xfe, 0x39,
- 0xff, 0x00, 0xe9, 0x4c, 0xc1, 0xee, 0x7b, 0x57, 0xec, 0xd2, 0x71, 0x7d,
- 0xaf, 0x7f, 0xd7, 0x38, 0x7f, 0x9b, 0xd7, 0x8a, 0xd7, 0xb4, 0x7e, 0xcd,
- 0x67, 0x17, 0xda, 0xf7, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, 0xbd, 0x7b,
- 0x19, 0x97, 0xfc, 0x89, 0x32, 0xef, 0xfb, 0x8b, 0xff, 0x00, 0xa5, 0x21,
- 0xbd, 0x90, 0x51, 0x45, 0x15, 0xf2, 0x02, 0x0a, 0x28, 0xa2, 0x80, 0x0a,
- 0x28, 0xa2, 0x80, 0x0a, 0x28, 0xa2, 0x80, 0x0a, 0xf6, 0xaf, 0x8b, 0x47,
- 0x3f, 0x09, 0x7c, 0x17, 0xff, 0x00, 0x5c, 0xe0, 0xff, 0x00, 0xd1, 0x15,
- 0xe2, 0xb5, 0xed, 0x1f, 0x16, 0x4f, 0xfc, 0x5a, 0x6f, 0x06, 0x7f, 0xd7,
- 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x7e, 0x4d, 0xff, 0x00, 0x22, 0xfc, 0xc3,
- 0xfc, 0x11, 0xff, 0x00, 0xd2, 0x90, 0xd6, 0xcc, 0xf1, 0x7a, 0xd8, 0xf0,
- 0x67, 0xfc, 0x8e, 0x1a, 0x17, 0xfd, 0x7f, 0xc1, 0xff, 0x00, 0xa3, 0x16,
- 0xb1, 0xeb, 0x63, 0xc1, 0xdf, 0xf2, 0x37, 0x68, 0x7f, 0xf5, 0xfd, 0x07,
- 0xfe, 0x8c, 0x5a, 0xf9, 0xbc, 0x27, 0xfb, 0xcd, 0x3f, 0xf1, 0x2f, 0xcc,
- 0x48, 0xfb, 0x2b, 0x75, 0x1b, 0xa9, 0x9b, 0xa8, 0xdd, 0x5f, 0xd8, 0x16,
- 0x37, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb8, 0xed,
- 0xd4, 0xbb, 0xa9, 0x9b, 0xa8, 0xdd, 0x4c, 0x2e, 0x3f, 0x75, 0x26, 0xea,
- 0x6e, 0xea, 0x37, 0x52, 0x0b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d,
- 0xd4, 0xec, 0x3b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0x0a,
- 0xe4, 0x74, 0x53, 0x33, 0x4b, 0x93, 0x57, 0x63, 0x3b, 0x8e, 0xa2, 0x9b,
- 0x9a, 0x4c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa3, 0x34, 0x58, 0x2e,
- 0x3f, 0x34, 0x53, 0x33, 0x4b, 0x93, 0x45, 0x82, 0xe3, 0xa8, 0xcd, 0x33,
- 0x34, 0x66, 0x8b, 0x05, 0xc7, 0xd1, 0x4d, 0xc9, 0xa3, 0x26, 0x8b, 0x05,
- 0xcf, 0x96, 0x3e, 0x30, 0x7f, 0xc9, 0x48, 0xd6, 0xbf, 0xdf, 0x8f, 0xff,
- 0x00, 0x45, 0xa5, 0x71, 0xb5, 0xdd, 0xfc, 0x6c, 0xd3, 0xa6, 0xb1, 0xf8,
- 0x85, 0x7f, 0x2c, 0x8a, 0x44, 0x77, 0x4b, 0x1c, 0xd1, 0xb7, 0x66, 0x1b,
- 0x02, 0x9f, 0xd5, 0x4d, 0x70, 0x95, 0xfc, 0x97, 0x9d, 0x42, 0x50, 0xcc,
- 0xf1, 0x31, 0x92, 0xb7, 0xbf, 0x2f, 0xfd, 0x29, 0x90, 0x7b, 0x3f, 0xec,
- 0xdb, 0xff, 0x00, 0x1f, 0xba, 0xef, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78,
- 0xc5, 0x7b, 0x87, 0xec, 0xe3, 0xa7, 0x4d, 0x1c, 0x3a, 0xd5, 0xf3, 0x29,
- 0x58, 0x24, 0x31, 0xc2, 0x8d, 0xfd, 0xe2, 0x37, 0x16, 0xfc, 0xb2, 0x3f,
- 0x3a, 0xf0, 0xfa, 0xf6, 0x73, 0x58, 0x4a, 0x19, 0x26, 0x5b, 0xcc, 0xad,
- 0x7f, 0x6a, 0xff, 0x00, 0xf2, 0x64, 0x01, 0x45, 0x14, 0x57, 0xc6, 0x00,
- 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00,
- 0x57, 0xb3, 0xfc, 0x57, 0xff, 0x00, 0x92, 0x51, 0xe0, 0xdf, 0xfa, 0xe7,
- 0x07, 0xfe, 0x88, 0xaf, 0x18, 0xaf, 0x70, 0xf8, 0x9b, 0xa7, 0x4d, 0x79,
- 0xf0, 0x77, 0xc3, 0x37, 0x11, 0x29, 0x74, 0xb5, 0x8a, 0xd9, 0xe4, 0xc7,
- 0x65, 0x30, 0xed, 0xcf, 0xe6, 0x40, 0xfc, 0x6b, 0xec, 0xf2, 0x38, 0x4a,
- 0x78, 0x0c, 0xc1, 0x45, 0x5f, 0xdc, 0x5f, 0x84, 0xae, 0xc0, 0xf0, 0xfa,
- 0xd8, 0xf0, 0x6f, 0xfc, 0x8d, 0xfa, 0x1f, 0xfd, 0x7f, 0x41, 0xff, 0x00,
- 0xa3, 0x16, 0xb1, 0xeb, 0xa2, 0xf8, 0x79, 0xa7, 0x4d, 0xaa, 0x78, 0xdf,
- 0x45, 0x86, 0x15, 0x2c, 0xcb, 0x75, 0x1c, 0xad, 0x8e, 0xca, 0x8c, 0x18,
- 0x9f, 0xc8, 0x57, 0xcd, 0x60, 0x61, 0x29, 0xe2, 0xe9, 0x46, 0x2a, 0xed,
- 0xca, 0x3f, 0x9a, 0x03, 0xeb, 0x9c, 0xd1, 0x4c, 0xcd, 0x2e, 0x4d, 0x7f,
- 0x60, 0x58, 0xbb, 0x8e, 0xa2, 0x99, 0x9a, 0x33, 0x45, 0x82, 0xe3, 0xe8,
- 0xa6, 0x66, 0x8c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa5, 0xc9, 0xa2,
- 0xc1, 0x71, 0xd9, 0xa2, 0x9b, 0x93, 0x49, 0x9a, 0x2c, 0x17, 0x1f, 0x45,
- 0x33, 0x34, 0x66, 0x8b, 0x05, 0xc6, 0x6e, 0xa3, 0x75, 0x30, 0x90, 0x3a,
- 0x9c, 0x51, 0x9a, 0xd2, 0xc6, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xda, 0x4c,
- 0xd1, 0x60, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x02, 0x0f, 0x43, 0x9a, 0x37,
- 0x01, 0xdf, 0x14, 0x58, 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x94, 0x58,
- 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x80, 0x83, 0xd0, 0xe6, 0x8b, 0x05,
- 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x3a, 0x9c, 0x51, 0x9a, 0x2c, 0x17,
- 0x31, 0x3c, 0x5d, 0xe0, 0xcd, 0x2f, 0xc6, 0xb6, 0x2b, 0x6f, 0xa8, 0xc4,
- 0x4b, 0x26, 0x4c, 0x53, 0xc6, 0x71, 0x24, 0x64, 0xf5, 0xc1, 0xfe, 0x87,
- 0x8a, 0xe0, 0x6d, 0x3f, 0x67, 0x7d, 0x32, 0x2b, 0xb0, 0xf7, 0x1a, 0xad,
- 0xcc, 0xf6, 0xe0, 0xe7, 0xca, 0x58, 0xd5, 0x09, 0x1e, 0x85, 0xb2, 0x7f,
- 0x95, 0x7a, 0xd5, 0x25, 0x78, 0x38, 0xcc, 0x87, 0x2c, 0xcc, 0x2b, 0x2a,
- 0xf8, 0x9a, 0x2a, 0x52, 0xef, 0xaa, 0xfb, 0xec, 0xd5, 0xfe, 0x77, 0x0b,
- 0x95, 0xf4, 0xad, 0x32, 0xd3, 0x44, 0xb0, 0x86, 0xca, 0xc6, 0x05, 0xb7,
- 0xb6, 0x88, 0x61, 0x23, 0x4e, 0x83, 0xfc, 0x4f, 0xbd, 0x7c, 0x6b, 0x5f,
- 0x69, 0x57, 0xc5, 0xb5, 0xf9, 0x8f, 0x88, 0xb0, 0x8d, 0x38, 0xe0, 0xe1,
- 0x05, 0x64, 0xb9, 0xec, 0x97, 0xfd, 0xb8, 0x34, 0x14, 0x51, 0x45, 0x7e,
- 0x32, 0x30, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
- 0x28, 0x00, 0xaf, 0xad, 0xfc, 0x2b, 0x04, 0x57, 0x9e, 0x06, 0xd1, 0xad,
- 0xe7, 0x8d, 0x66, 0x86, 0x4d, 0x3a, 0x04, 0x78, 0xdc, 0x65, 0x58, 0x18,
- 0xd7, 0x20, 0x8a, 0xf9, 0x22, 0xbe, 0xba, 0xf0, 0x67, 0xfc, 0x89, 0xfa,
- 0x17, 0xfd, 0x78, 0x41, 0xff, 0x00, 0xa2, 0xd6, 0xbf, 0x5c, 0xf0, 0xee,
- 0x2a, 0x58, 0x9c, 0x42, 0x7b, 0x72, 0xaf, 0xcc, 0x47, 0x05, 0xab, 0x7e,
- 0xcf, 0x7a, 0x4d, 0xdd, 0xdb, 0x4b, 0x65, 0xa8, 0x4f, 0x63, 0x13, 0x1c,
- 0xf9, 0x25, 0x04, 0x80, 0x7b, 0x02, 0x48, 0x3f, 0x9e, 0x6b, 0xaf, 0xf0,
- 0x57, 0xc3, 0xcd, 0x27, 0xc0, 0xd1, 0xb9, 0xb3, 0x47, 0x9a, 0xee, 0x41,
- 0xb6, 0x4b, 0xa9, 0xb0, 0x5c, 0x8f, 0x41, 0xe8, 0x3d, 0x87, 0xe3, 0x5d,
- 0x36, 0x69, 0x6b, 0xf5, 0x7c, 0x37, 0x0f, 0xe5, 0x78, 0x3a, 0xff, 0x00,
- 0x59, 0xa1, 0x41, 0x46, 0x7d, 0xf5, 0xd3, 0xd1, 0x6c, 0xbe, 0x49, 0x0a,
- 0xe3, 0xb7, 0x51, 0xba, 0x99, 0x9a, 0x03, 0x03, 0xde, 0xbd, 0xfb, 0x05,
- 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x06, 0x4f, 0x14, 0x51, 0x60, 0xb8,
- 0xfd, 0xd4, 0x6e, 0xa6, 0xd1, 0x45, 0x82, 0xe3, 0xb7, 0x51, 0xba, 0x98,
- 0x08, 0x3d, 0x0e, 0x68, 0x24, 0x0e, 0xf4, 0x58, 0x2e, 0x3f, 0x75, 0x1b,
- 0xa9, 0x94, 0xb4, 0x58, 0x2e, 0x3b, 0x75, 0x1b, 0xa9, 0x99, 0xa0, 0x10,
- 0x7a, 0x1a, 0x2c, 0x17, 0x31, 0x62, 0xbd, 0x91, 0x46, 0xf7, 0x90, 0x3e,
- 0xd3, 0xf7, 0x1b, 0xa9, 0xad, 0x58, 0xa5, 0x13, 0x46, 0xae, 0xbd, 0x08,
- 0xac, 0x3f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, 0x91, 0x56, 0x52, 0xf1,
- 0xe3, 0x48, 0x55, 0x42, 0x80, 0x7a, 0x80, 0x3d, 0xeb, 0xb2, 0x70, 0xbe,
- 0xc7, 0x9d, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x36, 0x59, 0x44, 0x31,
- 0xb3, 0xb7, 0x41, 0x49, 0xba, 0xb3, 0x1e, 0xf2, 0x49, 0x12, 0x65, 0x60,
- 0x08, 0x1d, 0x01, 0x1e, 0xf5, 0x84, 0x61, 0xcc, 0x75, 0x4e, 0xa7, 0x22,
- 0x1b, 0x2d, 0xec, 0x8c, 0x0b, 0xa4, 0x81, 0x37, 0x1f, 0xb8, 0xbd, 0x7e,
- 0xb4, 0xb1, 0x5e, 0xc8, 0xa0, 0x3b, 0xb8, 0x7d, 0xa7, 0xee, 0x37, 0x5f,
- 0xad, 0x57, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x14, 0x79, 0xc7, 0xfb,
- 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xeb, 0xe5, 0x56, 0xb5, 0x8e, 0x0e, 0x77,
- 0x7b, 0xdc, 0xdd, 0x8e, 0x41, 0x2a, 0x2b, 0xaf, 0x42, 0x33, 0x4e, 0xac,
- 0xa8, 0xef, 0x1d, 0x3c, 0x85, 0x00, 0x05, 0x3d, 0x40, 0x1e, 0xe6, 0xb4,
- 0x77, 0x57, 0x24, 0xa3, 0xca, 0x77, 0xc2, 0x7c, 0xe8, 0x74, 0x92, 0x08,
- 0xd1, 0x99, 0x8f, 0x00, 0x66, 0xb2, 0xa5, 0xbd, 0x91, 0x81, 0x74, 0x70,
- 0x99, 0x38, 0xd8, 0x3a, 0xfd, 0x69, 0xd2, 0x5e, 0x3b, 0xf9, 0xea, 0x40,
- 0x2a, 0x3a, 0x02, 0x3d, 0xc5, 0x55, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9,
- 0x15, 0xbc, 0x21, 0x6d, 0xce, 0x6a, 0x95, 0x6f, 0xa2, 0x64, 0xf1, 0x5e,
- 0x48, 0xa3, 0x7b, 0x48, 0x1f, 0x69, 0xfb, 0x8d, 0xd4, 0xd6, 0xac, 0x52,
- 0x89, 0xa3, 0x57, 0x5e, 0x86, 0xb0, 0xfc, 0xe3, 0xfd, 0xc4, 0xff, 0x00,
- 0xbe, 0x45, 0x59, 0x4b, 0xc7, 0x8d, 0x61, 0x0a, 0x14, 0x03, 0xd4, 0x01,
- 0xef, 0x44, 0xe1, 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x15,
- 0x1e, 0xea, 0x5d, 0xd5, 0xcd, 0x63, 0xba, 0xe3, 0xeb, 0xc0, 0xbc, 0x71,
- 0xf0, 0x57, 0x55, 0x83, 0x57, 0x9e, 0xe7, 0x44, 0x85, 0x6f, 0x6c, 0xa6,
- 0x72, 0xe2, 0x25, 0x70, 0xaf, 0x16, 0x4e, 0x76, 0xe0, 0x91, 0x91, 0xe9,
- 0x8a, 0xf7, 0xad, 0xd4, 0x66, 0xbe, 0x7f, 0x39, 0xc8, 0xf0, 0x99, 0xe5,
- 0x18, 0xd2, 0xc4, 0xdd, 0x72, 0xbb, 0xa6, 0xb7, 0x5d, 0xfb, 0xef, 0xe8,
- 0x17, 0xb1, 0xf2, 0xc5, 0xff, 0x00, 0xc3, 0x5f, 0x12, 0xe9, 0x96, 0x53,
- 0x5d, 0xdd, 0x69, 0x52, 0x43, 0x6f, 0x0a, 0x97, 0x92, 0x42, 0xe8, 0x42,
- 0x81, 0xd4, 0xf0, 0x6b, 0x99, 0xaf, 0xaa, 0xfe, 0x21, 0x9c, 0xf8, 0x1f,
- 0x5c, 0xff, 0x00, 0xaf, 0x47, 0xfe, 0x55, 0xf2, 0xa5, 0x7e, 0x01, 0xc5,
- 0x59, 0x1e, 0x1f, 0x22, 0xc4, 0x53, 0xa3, 0x87, 0x93, 0x92, 0x94, 0x6f,
- 0xef, 0x5b, 0xbd, 0xba, 0x24, 0x5a, 0x77, 0x0a, 0xe9, 0xec, 0xbe, 0x19,
- 0xf8, 0x9b, 0x51, 0xb3, 0x86, 0xea, 0xdb, 0x49, 0x92, 0x5b, 0x79, 0x90,
- 0x49, 0x1b, 0x87, 0x40, 0x19, 0x48, 0xc8, 0x3d, 0x6b, 0x98, 0xaf, 0xac,
- 0x3c, 0x08, 0x71, 0xe0, 0xad, 0x0b, 0xfe, 0xbc, 0xa1, 0xff, 0x00, 0xd0,
- 0x05, 0x57, 0x0a, 0xe4, 0x58, 0x7c, 0xf6, 0xbd, 0x5a, 0x58, 0x89, 0x4a,
- 0x2a, 0x2a, 0xfe, 0xed, 0xbb, 0xdb, 0xaa, 0x60, 0xdd, 0x8f, 0x9e, 0xdb,
- 0xe1, 0x4f, 0x8a, 0xd1, 0x4b, 0x1d, 0x1a, 0x50, 0x00, 0xc9, 0x3e, 0x62,
- 0x7f, 0xf1, 0x55, 0xc9, 0xd7, 0xd9, 0x17, 0x4d, 0xfe, 0x8d, 0x37, 0xfb,
- 0x87, 0xf9, 0x57, 0xc6, 0xf5, 0xd1, 0xc5, 0x9c, 0x3d, 0x86, 0xc8, 0x5d,
- 0x05, 0x87, 0x9c, 0xa5, 0xcf, 0xcd, 0x7e, 0x6b, 0x74, 0xb6, 0xd6, 0x4b,
- 0xb8, 0x27, 0x70, 0xae, 0x87, 0x47, 0xf0, 0x07, 0x88, 0x35, 0xfb, 0x04,
- 0xbd, 0xb0, 0xd3, 0x64, 0xb9, 0xb5, 0x72, 0x42, 0xc8, 0xae, 0xa0, 0x12,
- 0x0e, 0x0f, 0x53, 0xeb, 0x5c, 0xf5, 0x7d, 0x27, 0xf0, 0x54, 0xe3, 0xe1,
- 0xed, 0x8f, 0xfd, 0x74, 0x97, 0xff, 0x00, 0x43, 0x35, 0xe7, 0x70, 0xbe,
- 0x4f, 0x43, 0x3c, 0xc6, 0xcb, 0x0d, 0x5e, 0x4d, 0x25, 0x16, 0xf4, 0xb5,
- 0xee, 0x9a, 0x5d, 0x53, 0xee, 0x0d, 0xd8, 0xf3, 0x1f, 0x0d, 0x7c, 0x12,
- 0xd7, 0xb5, 0x3d, 0x42, 0x31, 0xa9, 0x40, 0x34, 0xdb, 0x20, 0xc0, 0xc8,
- 0xee, 0xea, 0xce, 0x47, 0x70, 0xa0, 0x13, 0xcf, 0xb9, 0xe2, 0xbe, 0x87,
- 0xb7, 0x82, 0x3b, 0x4b, 0x78, 0xa0, 0x89, 0x42, 0x45, 0x12, 0x84, 0x45,
- 0x1d, 0x80, 0x18, 0x02, 0x97, 0x34, 0x9b, 0xab, 0xfa, 0x07, 0x25, 0xe1,
- 0xfc, 0x1e, 0x45, 0x09, 0x47, 0x0d, 0x76, 0xe5, 0xbb, 0x7a, 0xbd, 0x36,
- 0x5a, 0x24, 0xad, 0xf2, 0x22, 0xf7, 0x24, 0xcd, 0x19, 0xa8, 0xf7, 0x51,
- 0xba, 0xbe, 0x96, 0xc1, 0x71, 0xd2, 0x48, 0x22, 0x46, 0x76, 0xe0, 0x0e,
- 0x6b, 0x2a, 0x5b, 0xd9, 0x18, 0x17, 0x47, 0x11, 0xe4, 0xe3, 0x60, 0xeb,
- 0xf5, 0xa7, 0x49, 0x78, 0xef, 0xe7, 0xa9, 0x00, 0xa8, 0xe8, 0x08, 0xf7,
- 0x02, 0xaa, 0xf9, 0xc7, 0xfb, 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xe9, 0x84,
- 0x2d, 0xb9, 0xc5, 0x56, 0xaf, 0x36, 0x89, 0x93, 0xc5, 0x7b, 0x22, 0x8d,
- 0xef, 0x20, 0x7d, 0xa7, 0xee, 0x37, 0x53, 0x5a, 0xd1, 0x4a, 0x26, 0x8d,
- 0x5d, 0x7a, 0x1a, 0xc2, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x15, 0x65,
- 0x2f, 0x1e, 0x34, 0x84, 0x28, 0x50, 0x09, 0xe4, 0x01, 0xef, 0x44, 0xe1,
- 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xa6, 0xcb, 0x28, 0x86, 0x36,
- 0x76, 0xe8, 0x29, 0x37, 0x56, 0x63, 0xde, 0x3c, 0x89, 0x30, 0x60, 0xa4,
- 0x0e, 0x80, 0x8f, 0x7a, 0xc2, 0x30, 0xe6, 0x3a, 0xa7, 0x53, 0x91, 0x0d,
- 0x96, 0xf6, 0x46, 0x05, 0xd2, 0x40, 0x9b, 0x8f, 0xdc, 0x5e, 0xa3, 0xde,
- 0x96, 0x2b, 0xd9, 0x14, 0x07, 0x77, 0x0f, 0xb4, 0xfd, 0xc6, 0xeb, 0xf5,
- 0xaa, 0xfe, 0x71, 0xfe, 0xe2, 0x7f, 0xdf, 0x22, 0x8f, 0x38, 0xff, 0x00,
- 0x71, 0x3f, 0xef, 0x91, 0x5d, 0x7c, 0xaa, 0xd6, 0xb1, 0xc1, 0xce, 0xef,
- 0x7b, 0x9b, 0xb1, 0xc8, 0x25, 0x45, 0x75, 0xe8, 0x46, 0x69, 0xd5, 0x95,
- 0x1d, 0xe3, 0xa0, 0x81, 0x46, 0x02, 0x9e, 0xa0, 0x0f, 0x72, 0x2b, 0x4b,
- 0x35, 0xc9, 0x28, 0xf2, 0x9d, 0xf0, 0xa9, 0xce, 0x85, 0x92, 0x41, 0x1a,
- 0x33, 0xb7, 0x40, 0x32, 0x6b, 0x2a, 0x5b, 0xd9, 0x1c, 0x17, 0x47, 0x09,
- 0x93, 0x8d, 0x83, 0xaf, 0xd6, 0x9d, 0x25, 0xe3, 0xbf, 0x9e, 0xa4, 0x02,
- 0xa3, 0xa0, 0x23, 0xdc, 0x55, 0x5f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef,
- 0x91, 0x5b, 0xc2, 0x16, 0xdc, 0xe6, 0xa9, 0x57, 0x9b, 0x44, 0xc8, 0xea,
- 0x70, 0xa4, 0x88, 0x30, 0x09, 0xff, 0x00, 0xf5, 0xd3, 0x4d, 0xbb, 0x09,
- 0x84, 0x59, 0x1b, 0xbd, 0x7b, 0x56, 0x95, 0xb4, 0x66, 0x18, 0x42, 0x13,
- 0x92, 0x3d, 0x2a, 0xe5, 0x2b, 0x23, 0x2a, 0x70, 0x72, 0x6d, 0x32, 0x7a,
- 0xc8, 0x2a, 0x40, 0x9f, 0x20, 0x8f, 0xff, 0x00, 0x5d, 0x6a, 0xe6, 0xa3,
- 0xb9, 0x8c, 0xcf, 0x09, 0x40, 0x40, 0x27, 0xd6, 0xb1, 0x83, 0xe5, 0x3a,
- 0x6a, 0x47, 0x99, 0x5c, 0xc7, 0xa2, 0xa5, 0x5b, 0x76, 0x69, 0x8c, 0x59,
- 0x1b, 0xbf, 0x4a, 0x3e, 0xce, 0xde, 0x7f, 0x95, 0x91, 0xbb, 0xd7, 0xb5,
- 0x74, 0xdd, 0x1c, 0x3c, 0xac, 0x7a, 0xa9, 0x2d, 0x6f, 0x80, 0x7f, 0xcb,
- 0x1a, 0xd6, 0xa8, 0x6d, 0xe3, 0x30, 0xc2, 0xa8, 0x48, 0x24, 0x77, 0x15,
- 0x26, 0x6b, 0x9a, 0x6f, 0x99, 0x9d, 0xf4, 0xe3, 0xca, 0x8c, 0xb6, 0x52,
- 0x1a, 0xe3, 0x20, 0xff, 0x00, 0x96, 0x15, 0x5e, 0xb6, 0x2e, 0x23, 0x33,
- 0x44, 0xc8, 0x08, 0x04, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37,
- 0x7a, 0xf6, 0xad, 0xa1, 0x24, 0xd1, 0xcb, 0x52, 0x0e, 0x2d, 0x58, 0x8a,
- 0xa7, 0x0a, 0x48, 0x83, 0x00, 0x9f, 0xff, 0x00, 0x5d, 0x34, 0xdb, 0xb2,
- 0xcc, 0x22, 0xc8, 0xdd, 0xeb, 0xda, 0xb4, 0xad, 0xa3, 0x30, 0xc2, 0xaa,
- 0x4e, 0x48, 0xf4, 0xa2, 0x52, 0xb2, 0x0a, 0x70, 0x72, 0x6d, 0x32, 0x7a,
- 0x29, 0xa4, 0xd1, 0x9a, 0xe5, 0xb1, 0xde, 0x3a, 0x8a, 0x6e, 0x73, 0x41,
- 0x34, 0x58, 0x0e, 0x7f, 0xe2, 0x1f, 0xfc, 0x88, 0xfa, 0xe7, 0xfd, 0x7a,
- 0xbf, 0xf2, 0xaf, 0x95, 0xab, 0xeb, 0x2f, 0x18, 0x58, 0x4b, 0xaa, 0xf8,
- 0x57, 0x56, 0xb4, 0x80, 0x6e, 0x9a, 0x6b, 0x69, 0x15, 0x17, 0xd5, 0xb6,
- 0x9c, 0x0f, 0xce, 0xbe, 0x4e, 0x65, 0x2a, 0x48, 0x20, 0x82, 0x38, 0x20,
- 0xf6, 0xaf, 0xc1, 0x7c, 0x46, 0x84, 0x96, 0x32, 0x84, 0xed, 0xa3, 0x8b,
- 0x5f, 0x73, 0xff, 0x00, 0x82, 0x8d, 0x20, 0x25, 0x7d, 0x5d, 0xe0, 0x5f,
- 0xf9, 0x12, 0xf4, 0x2f, 0xfa, 0xf2, 0x87, 0xff, 0x00, 0x40, 0x15, 0xf2,
- 0x9a, 0x23, 0x48, 0xea, 0x88, 0xa5, 0x9d, 0x8e, 0x02, 0x81, 0x92, 0x4d,
- 0x7d, 0x69, 0xe1, 0x8b, 0x19, 0x34, 0xbf, 0x0d, 0xe9, 0x76, 0x73, 0x71,
- 0x2c, 0x16, 0xd1, 0xc6, 0xe3, 0xd0, 0x85, 0x00, 0xd5, 0x78, 0x73, 0x09,
- 0x3c, 0x56, 0x22, 0x76, 0xd1, 0x45, 0x2f, 0xc7, 0xfe, 0x00, 0x4c, 0xd0,
- 0xba, 0xff, 0x00, 0x8f, 0x69, 0xbf, 0xdc, 0x3f, 0xca, 0xbe, 0x39, 0xaf,
- 0xb1, 0xe5, 0x5f, 0x32, 0x27, 0x4c, 0xe3, 0x72, 0x91, 0x9a, 0xf9, 0x03,
- 0x50, 0xb1, 0x9b, 0x4c, 0xbe, 0xb8, 0xb4, 0x9d, 0x0a, 0x4d, 0x04, 0x86,
- 0x37, 0x53, 0xd8, 0x83, 0x8a, 0xed, 0xf1, 0x22, 0x12, 0xff, 0x00, 0x65,
- 0x9d, 0xb4, 0xf7, 0xd7, 0xfe, 0x92, 0x10, 0x2b, 0xd7, 0xd2, 0x5f, 0x05,
- 0xbf, 0xe4, 0x9f, 0x58, 0xff, 0x00, 0xd7, 0x49, 0x7f, 0xf4, 0x33, 0x5f,
- 0x36, 0xd7, 0xd3, 0x9f, 0x0a, 0x74, 0xe9, 0xb4, 0xbf, 0x01, 0xe9, 0x91,
- 0x4e, 0xa5, 0x24, 0x75, 0x69, 0x76, 0x9e, 0xa0, 0x33, 0x12, 0x3f, 0x42,
- 0x2b, 0xc3, 0xf0, 0xf6, 0x12, 0x96, 0x69, 0x52, 0x49, 0x68, 0xa0, 0xff,
- 0x00, 0x19, 0x44, 0x72, 0xd8, 0xeb, 0xe8, 0xa6, 0xe6, 0x8c, 0xd7, 0xf4,
- 0x3d, 0x8c, 0x87, 0x51, 0x4d, 0xcd, 0x19, 0xa2, 0xc0, 0x65, 0xb2, 0x90,
- 0xd7, 0x19, 0x04, 0x7f, 0xfb, 0x42, 0xab, 0xd6, 0xc5, 0xc4, 0x66, 0x68,
- 0x59, 0x07, 0x04, 0xfa, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37,
- 0x7a, 0xf6, 0xae, 0xa8, 0x4a, 0xe8, 0xe0, 0xa9, 0x07, 0x16, 0xac, 0x45,
- 0x53, 0x85, 0x24, 0x41, 0x80, 0x4f, 0xff, 0x00, 0xae, 0x9a, 0x6d, 0xd9,
- 0x66, 0x11, 0x64, 0x6e, 0xf5, 0xed, 0x5a, 0x76, 0xd1, 0x98, 0x21, 0x0a,
- 0x48, 0x27, 0xda, 0x89, 0x4a, 0xc8, 0x29, 0xc1, 0xc9, 0xb4, 0xc9, 0xab,
- 0x20, 0xa9, 0x02, 0x7c, 0x82, 0x3f, 0xfd, 0x75, 0xab, 0x9a, 0x8a, 0xe6,
- 0x33, 0x34, 0x45, 0x41, 0xc1, 0xf7, 0xac, 0x60, 0xf9, 0x4e, 0x9a, 0x91,
- 0xe6, 0x46, 0x45, 0x15, 0x28, 0xb7, 0x66, 0x98, 0xc5, 0x91, 0xb8, 0x77,
- 0xed, 0x47, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, 0x7a, 0xf6, 0xae, 0x9b,
- 0xa3, 0x87, 0x95, 0x8f, 0x55, 0x25, 0xad, 0xf0, 0x0f, 0xf9, 0x63, 0x5a,
- 0xd5, 0x0d, 0xba, 0x18, 0x61, 0x54, 0x24, 0x12, 0x3d, 0x2a, 0x4c, 0xd7,
- 0x34, 0xdf, 0x33, 0x3b, 0xe9, 0xc7, 0x95, 0x19, 0x6c, 0xa4, 0x35, 0xc6,
- 0x41, 0xff, 0x00, 0x2c, 0x2a, 0xbd, 0x6c, 0x5c, 0x46, 0x66, 0x85, 0x90,
- 0x1c, 0x13, 0xeb, 0x59, 0x9f, 0x67, 0x6f, 0x3f, 0xca, 0xc8, 0xdd, 0xfa,
- 0x56, 0xd0, 0x92, 0x68, 0xe5, 0xa9, 0x06, 0x9a, 0xb1, 0xa0, 0xd6, 0xc1,
- 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, 0x6a, 0x28, 0xae, 0x76, 0xdb, 0x3a,
- 0xd2, 0xb6, 0xc1, 0x45, 0x14, 0x52, 0x19, 0x0a, 0xdb, 0x05, 0xb8, 0x32,
- 0xe4, 0xe4, 0xf6, 0xa3, 0xec, 0xc3, 0xed, 0x1e, 0x6e, 0x4e, 0x7d, 0x2a,
- 0x6a, 0x2a, 0xb9, 0x99, 0x3c, 0xa8, 0x28, 0xa2, 0x8a, 0x92, 0x82, 0xa1,
- 0xfb, 0x30, 0xfb, 0x47, 0x9b, 0x93, 0x9f, 0x4a, 0x9a, 0x8a, 0x69, 0xd8,
- 0x4d, 0x5f, 0x72, 0x16, 0xb6, 0x0d, 0x70, 0x25, 0xc9, 0xc8, 0xed, 0x53,
- 0x51, 0x45, 0x0d, 0xb6, 0x09, 0x5b, 0x60, 0xa2, 0x8a, 0x29, 0x0c, 0x28,
- 0xa2, 0x8a, 0x00, 0x2b, 0x83, 0xf1, 0x57, 0xc1, 0xdd, 0x1b, 0xc4, 0xb7,
- 0xaf, 0x79, 0x1b, 0xcb, 0xa7, 0x5d, 0x48, 0x73, 0x21, 0x80, 0x02, 0x8e,
- 0x7d, 0x4a, 0x9e, 0xff, 0x00, 0x42, 0x2b, 0xbc, 0xa2, 0xbc, 0xfc, 0x76,
- 0x5f, 0x85, 0xcc, 0xa9, 0xfb, 0x1c, 0x5d, 0x35, 0x38, 0xf9, 0xfe, 0x8f,
- 0x75, 0xf2, 0x1a, 0x6d, 0x1c, 0x3f, 0x84, 0xbe, 0x11, 0xe8, 0xde, 0x16,
- 0xbb, 0x4b, 0xc2, 0xd2, 0x5f, 0xde, 0x27, 0x29, 0x24, 0xf8, 0xda, 0x87,
- 0xd5, 0x54, 0x77, 0xf7, 0x39, 0xae, 0xe2, 0x8a, 0x29, 0xe0, 0xb0, 0x18,
- 0x5c, 0xba, 0x97, 0xb1, 0xc2, 0x53, 0x50, 0x8f, 0x97, 0xeb, 0xd5, 0xfc,
- 0xc2, 0xed, 0x85, 0x71, 0xfe, 0x31, 0xf8, 0x5f, 0xa4, 0x78, 0xc6, 0x6f,
- 0xb4, 0xcd, 0xe6, 0x5a, 0x5e, 0xe3, 0x06, 0xe2, 0x0c, 0x65, 0xc7, 0x6d,
- 0xc0, 0xf5, 0xfe, 0x7e, 0xf5, 0xd8, 0x51, 0x55, 0x8c, 0xc1, 0x61, 0xf1,
- 0xf4, 0x9d, 0x0c, 0x54, 0x14, 0xe2, 0xfa, 0x3f, 0xeb, 0x46, 0x17, 0x68,
- 0xf3, 0xbf, 0x0f, 0x7c, 0x11, 0xd1, 0x74, 0x6b, 0xc4, 0xb9, 0xb9, 0x96,
- 0x5d, 0x49, 0xd0, 0xe5, 0x63, 0x94, 0x05, 0x8f, 0x3e, 0xa5, 0x47, 0x5f,
- 0xc4, 0xe3, 0xda, 0xbd, 0x13, 0xa5, 0x14, 0x56, 0x38, 0x1c, 0xb7, 0x07,
- 0x96, 0x41, 0xd3, 0xc1, 0xd3, 0x50, 0x4f, 0x7b, 0x75, 0xf5, 0x7b, 0xb0,
- 0x6d, 0xb0, 0xa2, 0x8a, 0x2b, 0xd3, 0x10, 0x51, 0x45, 0x14, 0x00, 0x54,
- 0x3f, 0x66, 0x1f, 0x68, 0xf3, 0x72, 0x73, 0xe9, 0x53, 0x51, 0x4d, 0x3b,
- 0x09, 0xab, 0xee, 0x42, 0xd6, 0xc1, 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa,
- 0x6a, 0x28, 0xa1, 0xb6, 0xc1, 0x2b, 0x6c, 0x14, 0x51, 0x45, 0x21, 0x90,
- 0xad, 0xb0, 0x5b, 0x83, 0x2e, 0x4e, 0x4f, 0x6a, 0x3e, 0xcc, 0x3e, 0xd1,
- 0xe6, 0xe4, 0xe7, 0xd2, 0xa6, 0xa2, 0xab, 0x99, 0x93, 0xca, 0x82, 0x8a,
- 0x28, 0xa9, 0x28, 0x2a, 0x1f, 0xb3, 0x0f, 0xb4, 0x79, 0xb9, 0x39, 0xf4,
- 0xa9, 0xa8, 0xa6, 0x9d, 0x84, 0xd5, 0xf7, 0x3e, 0x20, 0xff, 0x00, 0x87,
- 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3,
- 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a,
- 0xbe, 0x17, 0xfe, 0xc6, 0xb2, 0xff, 0x00, 0x9f, 0x64, 0xa7, 0x47, 0xa2,
- 0x59, 0x31, 0xe6, 0xd9, 0x31, 0x45, 0x8e, 0x9e, 0x44, 0x7d, 0xcd, 0xff,
- 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45,
- 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15,
- 0x15, 0x7c, 0x3b, 0xfd, 0x87, 0x61, 0xff, 0x00, 0x3e, 0xb1, 0xfe, 0x54,
- 0xd9, 0x34, 0x6b, 0x05, 0x1c, 0x5b, 0x47, 0x9f, 0xa5, 0x16, 0x0e, 0x44,
- 0x7d, 0xc9, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7,
- 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb,
- 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x2f, 0xfd, 0x8d, 0x65, 0xff, 0x00, 0x3e,
- 0xc9, 0xf9, 0x53, 0xe3, 0xd1, 0x2c, 0x4f, 0x26, 0xd9, 0x31, 0xf4, 0xa2,
- 0xc1, 0xc8, 0x8f, 0xb9, 0x7f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1,
- 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, 0xa6, 0xf8,
- 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, 0x87, 0x7f,
- 0xb0, 0xec, 0x3f, 0xe7, 0xd6, 0x3f, 0xca, 0x99, 0x26, 0x8d, 0x62, 0x38,
- 0x16, 0xc9, 0x9a, 0x2c, 0x1c, 0x88, 0xfb, 0x97, 0xfe, 0x1e, 0x9b, 0xe1,
- 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0x8f, 0xf8, 0x7a, 0x6f,
- 0x85, 0xbf, 0xe8, 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0xf8, 0x5f, 0xfb,
- 0x1a, 0xcb, 0xfe, 0x7d, 0x93, 0xf2, 0xa9, 0x23, 0xd1, 0x2c, 0x48, 0xc9,
- 0xb6, 0x4f, 0xca, 0x8b, 0x07, 0x22, 0x3e, 0xe4, 0xff, 0x00, 0x87, 0xa6,
- 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe,
- 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe,
- 0x1d, 0xfe, 0xc3, 0xb0, 0xff, 0x00, 0x9f, 0x58, 0xff, 0x00, 0x2a, 0x64,
- 0x9a, 0x35, 0x88, 0x38, 0x16, 0xc9, 0xf9, 0x51, 0x60, 0xe4, 0x47, 0xdc,
- 0xbf, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54,
- 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff,
- 0x00, 0x81, 0x51, 0x57, 0xc2, 0xff, 0x00, 0xd8, 0xd6, 0x5f, 0xf3, 0xec,
- 0x9f, 0x95, 0x48, 0x9a, 0x1d, 0x89, 0x19, 0x36, 0xc9, 0xf9, 0x51, 0x60,
- 0xe4, 0x47, 0xdc, 0x9f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac,
- 0x7f, 0xe0, 0x54, 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42,
- 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, 0x57, 0xc3, 0xbf, 0xd8, 0x76, 0x1f,
- 0xf3, 0xeb, 0x1f, 0xe5, 0x51, 0xbe, 0x8d, 0x63, 0x9c, 0x0b, 0x64, 0xfc,
- 0xa8, 0xb0, 0x72, 0x23, 0xee, 0x6f, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8,
- 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff,
- 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x7f, 0xec,
- 0x7b, 0x2f, 0xf9, 0xf6, 0x4f, 0xca, 0x9b, 0x2e, 0x9d, 0xa7, 0xdb, 0xed,
- 0x53, 0x67, 0xe6, 0xc8, 0xc0, 0x90, 0xb1, 0xae, 0x4e, 0x3d, 0x68, 0xb0,
- 0x72, 0x23, 0xee, 0xaf, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, 0x47, 0xd6,
- 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1,
- 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x1f, 0x23, 0x4b, 0xd8,
- 0x1b, 0xec, 0x44, 0x8d, 0xbb, 0x9b, 0x09, 0xf7, 0x06, 0x48, 0xc9, 0xe7,
- 0xd8, 0xfe, 0x54, 0x3d, 0x9e, 0x9e, 0x2e, 0x04, 0x66, 0xcb, 0x60, 0x27,
- 0x68, 0x72, 0xbf, 0x29, 0x3f, 0x5c, 0xd1, 0x60, 0xe4, 0x47, 0xdd, 0xdf,
- 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x54,
- 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00,
- 0x81, 0x51, 0x57, 0xc1, 0x82, 0xdf, 0x4e, 0x1b, 0xb7, 0xd9, 0x98, 0x88,
- 0x19, 0xda, 0xeb, 0xc9, 0xe7, 0x1c, 0x73, 0xea, 0x45, 0x4d, 0x1d, 0x9e,
- 0x9a, 0x46, 0x0d, 0x91, 0x12, 0x02, 0x41, 0x8c, 0xaf, 0xcd, 0x9c, 0x67,
- 0xd7, 0xd2, 0x8b, 0x07, 0x22, 0x3e, 0xed, 0xff, 0x00, 0x87, 0xa6, 0xf8,
- 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e,
- 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x16,
- 0xb7, 0xd3, 0xf4, 0xeb, 0x86, 0x65, 0xfb, 0x1f, 0x96, 0xea, 0x01, 0x2b,
- 0x22, 0xe0, 0xe0, 0xf7, 0xa7, 0x36, 0x8f, 0x62, 0x49, 0xc5, 0xb2, 0x62,
- 0x8b, 0x07, 0x22, 0x3e, 0xe7, 0xff, 0x00, 0x87, 0xa6, 0xf8, 0x5b, 0xfe,
- 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, 0x9b, 0xe1,
- 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x17, 0xfe, 0xc6,
- 0xb2, 0x3f, 0xf2, 0xec, 0x95, 0x30, 0xd0, 0xec, 0x40, 0xff, 0x00, 0x8f,
- 0x64, 0xa2, 0xc1, 0xc8, 0x8f, 0xb8, 0xbf, 0xe1, 0xe9, 0xbe, 0x16, 0xff,
- 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87,
- 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf,
- 0x87, 0x7f, 0xb1, 0x2c, 0x07, 0xfc, 0xba, 0xc7, 0xf9, 0x54, 0x27, 0x47,
- 0xb2, 0x27, 0xfe, 0x3d, 0x92, 0x8b, 0x07, 0x22, 0x3e, 0xe8, 0xff, 0x00,
- 0x87, 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2,
- 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a,
- 0x8a, 0xbe, 0x17, 0x1a, 0x2d, 0x91, 0x38, 0xfb, 0x32, 0x54, 0xdf, 0xd8,
- 0x76, 0x1f, 0xf3, 0xea, 0x9f, 0x95, 0x16, 0x0e, 0x44, 0x7d, 0xc5, 0xff,
- 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45,
- 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15,
- 0x15, 0x7c, 0x3a, 0x74, 0x4b, 0x00, 0x33, 0xf6, 0x58, 0xff, 0x00, 0x2a,
- 0x84, 0xe8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x47,
- 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15,
- 0x1f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54,
- 0x55, 0xf0, 0xc2, 0xe8, 0xb6, 0x4c, 0x40, 0xfb, 0x32, 0x54, 0xbf, 0xd8,
- 0x76, 0x1f, 0xf3, 0xea, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x17, 0xfc, 0x3d,
- 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x1f, 0xf0,
- 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x55, 0xf0,
- 0xe3, 0x68, 0xb6, 0x0a, 0x09, 0xfb, 0x2c, 0x7f, 0x95, 0x45, 0xfd, 0x8d,
- 0x65, 0xff, 0x00, 0x3e, 0xc9, 0x45, 0x83, 0x91, 0x1f, 0x74, 0x7f, 0xc3,
- 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51,
- 0x51, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe,
- 0x05, 0x45, 0x5f, 0x0c, 0x26, 0x89, 0x64, 0xcd, 0xff, 0x00, 0x1e, 0xc9,
- 0x52, 0xff, 0x00, 0x61, 0xd8, 0x7f, 0xcf, 0xac, 0x7f, 0x95, 0x16, 0x0e,
- 0x44, 0x7d, 0xc5, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa,
- 0xc7, 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23,
- 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x38, 0xfa, 0x2d, 0x82, 0xaf, 0xfc,
- 0x7a, 0xc7, 0xf9, 0x54, 0x5f, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58,
- 0x39, 0x11, 0x72, 0xa7, 0x45, 0xda, 0xb8, 0xa8, 0xe3, 0x5c, 0xb6, 0x7d,
- 0x2a, 0x5a, 0xa2, 0xc2, 0xa1, 0x66, 0xdc, 0xd9, 0xa7, 0xc8, 0xd8, 0x18,
- 0xf5, 0xa8, 0xa8, 0x01, 0x40, 0xc9, 0xa9, 0xd4, 0x6d, 0x18, 0xa8, 0xe2,
- 0x5e, 0x73, 0x52, 0x50, 0x00, 0x4e, 0x05, 0x40, 0xc7, 0x71, 0xcd, 0x49,
- 0x2b, 0x71, 0x8a, 0x8a, 0x80, 0x14, 0x0c, 0x9c, 0x54, 0xe0, 0x60, 0x62,
- 0xa3, 0x89, 0x7b, 0xd4, 0x94, 0x00, 0x13, 0x80, 0x4d, 0x40, 0x4e, 0x4e,
- 0x69, 0xf2, 0xb7, 0x6a, 0x8e, 0x80, 0x15, 0x46, 0xe3, 0x8a, 0x9e, 0x99,
- 0x1a, 0xe0, 0x67, 0xd6, 0x9f, 0x40, 0x08, 0xc7, 0x68, 0xcd, 0x41, 0x4f,
- 0x91, 0xb2, 0x71, 0xe9, 0x4c, 0xa0, 0x07, 0x22, 0xee, 0x6f, 0x6a, 0x27,
- 0xb5, 0x59, 0xa4, 0x49, 0x37, 0xbc, 0x6e, 0xbc, 0x06, 0x43, 0x8c, 0x8f,
- 0x43, 0xed, 0x52, 0x46, 0xb8, 0x5f, 0x73, 0x4e, 0xa0, 0x0a, 0xa6, 0xca,
- 0x38, 0xa2, 0x95, 0x41, 0x6f, 0xde, 0x2e, 0xc3, 0xcf, 0x6c, 0x93, 0xff,
- 0x00, 0xb3, 0x1a, 0x87, 0xec, 0x60, 0xca, 0xae, 0xd2, 0x48, 0xe1, 0x4e,
- 0x42, 0x12, 0x36, 0x83, 0xf9, 0x55, 0xa9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d,
- 0x00, 0x57, 0x8b, 0x49, 0xb7, 0x0c, 0xfb, 0x53, 0xcb, 0x0c, 0x00, 0x21,
- 0x38, 0xe8, 0x72, 0x0f, 0xd6, 0xa6, 0x5d, 0x39, 0x17, 0x90, 0xf2, 0x79,
- 0x99, 0x2c, 0x64, 0x27, 0x2c, 0x4e, 0xdc, 0x7e, 0x82, 0xac, 0xa2, 0xed,
- 0x5a, 0x5a, 0x00, 0xad, 0x1d, 0xaa, 0xdb, 0x16, 0x6f, 0x31, 0xe5, 0x91,
- 0xc0, 0x05, 0xe4, 0x39, 0x38, 0x1d, 0xbf, 0x5a, 0x5a, 0x73, 0xb6, 0xe6,
- 0xa6, 0xf5, 0xa0, 0x07, 0xc4, 0xb9, 0x39, 0xf4, 0xa9, 0x69, 0x14, 0x6d,
- 0x18, 0xa5, 0xe9, 0x40, 0x0c, 0x95, 0xb0, 0x31, 0x51, 0x52, 0xb1, 0xdc,
- 0x49, 0xa0, 0x0c, 0x9c, 0x50, 0x03, 0xe2, 0x5e, 0xf5, 0x25, 0x00, 0x60,
- 0x62, 0x82, 0x70, 0x33, 0x40, 0x11, 0xca, 0xdd, 0xaa, 0x3a, 0x52, 0x72,
- 0x73, 0x42, 0x8d, 0xc4, 0x0a, 0x00, 0x92, 0x25, 0xc0, 0xcd, 0x3e, 0x8e,
- 0x94, 0x8c, 0x76, 0x82, 0x68, 0x02, 0x39, 0x5b, 0x27, 0x1e, 0x94, 0xca,
- 0x3a, 0xd3, 0x91, 0x77, 0x35, 0x00, 0x49, 0x1a, 0xe1, 0x7d, 0xcd, 0x3a,
- 0x8a, 0x47, 0x6d, 0xab, 0x40, 0x11, 0xc8, 0xd9, 0x6f, 0x61, 0x4c, 0xa2,
- 0x9d, 0x1a, 0xee, 0x6f, 0x61, 0x40, 0x1f, 0xff, 0xd9
-};
diff --git a/camera/libcameraservice/FakeCamera.cpp b/camera/libcameraservice/FakeCamera.cpp
deleted file mode 100644
index 6749899..0000000
--- a/camera/libcameraservice/FakeCamera.cpp
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "FakeCamera"
-#include <utils/Log.h>
-
-#include <string.h>
-#include <stdlib.h>
-#include <utils/String8.h>
-
-#include "FakeCamera.h"
-
-
-namespace android {
-
-// TODO: All this rgb to yuv should probably be in a util class.
-
-// TODO: I think something is wrong in this class because the shadow is kBlue
-// and the square color should alternate between kRed and kGreen. However on the
-// emulator screen these are all shades of gray. Y seems ok but the U and V are
-// probably not.
-
-static int tables_initialized = 0;
-uint8_t *gYTable, *gCbTable, *gCrTable;
-
-static int
-clamp(int x)
-{
- if (x > 255) return 255;
- if (x < 0) return 0;
- return x;
-}
-
-/* the equation used by the video code to translate YUV to RGB looks like this
- *
- * Y = (Y0 - 16)*k0
- * Cb = Cb0 - 128
- * Cr = Cr0 - 128
- *
- * G = ( Y - k1*Cr - k2*Cb )
- * R = ( Y + k3*Cr )
- * B = ( Y + k4*Cb )
- *
- */
-
-static const double k0 = 1.164;
-static const double k1 = 0.813;
-static const double k2 = 0.391;
-static const double k3 = 1.596;
-static const double k4 = 2.018;
-
-/* let's try to extract the value of Y
- *
- * G + k1/k3*R + k2/k4*B = Y*( 1 + k1/k3 + k2/k4 )
- *
- * Y = ( G + k1/k3*R + k2/k4*B ) / (1 + k1/k3 + k2/k4)
- * Y0 = ( G0 + k1/k3*R0 + k2/k4*B0 ) / ((1 + k1/k3 + k2/k4)*k0) + 16
- *
- * let define:
- * kYr = k1/k3
- * kYb = k2/k4
- * kYy = k0 * ( 1 + kYr + kYb )
- *
- * we have:
- * Y = ( G + kYr*R + kYb*B )
- * Y0 = clamp[ Y/kYy + 16 ]
- */
-
-static const double kYr = k1/k3;
-static const double kYb = k2/k4;
-static const double kYy = k0*( 1. + kYr + kYb );
-
-static void
-initYtab( void )
-{
- const int imax = (int)( (kYr + kYb)*(31 << 2) + (61 << 3) + 0.1 );
- int i;
-
- gYTable = (uint8_t *)malloc(imax);
-
- for(i=0; i<imax; i++) {
- int x = (int)(i/kYy + 16.5);
- if (x < 16) x = 16;
- else if (x > 235) x = 235;
- gYTable[i] = (uint8_t) x;
- }
-}
-
-/*
- * the source is RGB565, so adjust for 8-bit range of input values:
- *
- * G = (pixels >> 3) & 0xFC;
- * R = (pixels >> 8) & 0xF8;
- * B = (pixels & 0x1f) << 3;
- *
- * R2 = (pixels >> 11) R = R2*8
- * B2 = (pixels & 0x1f) B = B2*8
- *
- * kYr*R = kYr2*R2 => kYr2 = kYr*8
- * kYb*B = kYb2*B2 => kYb2 = kYb*8
- *
- * we want to use integer multiplications:
- *
- * SHIFT1 = 9
- *
- * (ALPHA*R2) >> SHIFT1 == R*kYr => ALPHA = kYr*8*(1 << SHIFT1)
- *
- * ALPHA = kYr*(1 << (SHIFT1+3))
- * BETA = kYb*(1 << (SHIFT1+3))
- */
-
-static const int SHIFT1 = 9;
-static const int ALPHA = (int)( kYr*(1 << (SHIFT1+3)) + 0.5 );
-static const int BETA = (int)( kYb*(1 << (SHIFT1+3)) + 0.5 );
-
-/*
- * now let's try to get the values of Cb and Cr
- *
- * R-B = (k3*Cr - k4*Cb)
- *
- * k3*Cr = k4*Cb + (R-B)
- * k4*Cb = k3*Cr - (R-B)
- *
- * R-G = (k1+k3)*Cr + k2*Cb
- * = (k1+k3)*Cr + k2/k4*(k3*Cr - (R-B)/k0)
- * = (k1 + k3 + k2*k3/k4)*Cr - k2/k4*(R-B)
- *
- * kRr*Cr = (R-G) + kYb*(R-B)
- *
- * Cr = ((R-G) + kYb*(R-B))/kRr
- * Cr0 = clamp(Cr + 128)
- */
-
-static const double kRr = (k1 + k3 + k2*k3/k4);
-
-static void
-initCrtab( void )
-{
- uint8_t *pTable;
- int i;
-
- gCrTable = (uint8_t *)malloc(768*2);
-
- pTable = gCrTable + 384;
- for(i=-384; i<384; i++)
- pTable[i] = (uint8_t) clamp( i/kRr + 128.5 );
-}
-
-/*
- * B-G = (k2 + k4)*Cb + k1*Cr
- * = (k2 + k4)*Cb + k1/k3*(k4*Cb + (R-B))
- * = (k2 + k4 + k1*k4/k3)*Cb + k1/k3*(R-B)
- *
- * kBb*Cb = (B-G) - kYr*(R-B)
- *
- * Cb = ((B-G) - kYr*(R-B))/kBb
- * Cb0 = clamp(Cb + 128)
- *
- */
-
-static const double kBb = (k2 + k4 + k1*k4/k3);
-
-static void
-initCbtab( void )
-{
- uint8_t *pTable;
- int i;
-
- gCbTable = (uint8_t *)malloc(768*2);
-
- pTable = gCbTable + 384;
- for(i=-384; i<384; i++)
- pTable[i] = (uint8_t) clamp( i/kBb + 128.5 );
-}
-
-/*
- * SHIFT2 = 16
- *
- * DELTA = kYb*(1 << SHIFT2)
- * GAMMA = kYr*(1 << SHIFT2)
- */
-
-static const int SHIFT2 = 16;
-static const int DELTA = kYb*(1 << SHIFT2);
-static const int GAMMA = kYr*(1 << SHIFT2);
-
-int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[])
-{
- uint16_t *inputRGB = (uint16_t*)rgb16;
- uint8_t *outYUV = yuv422;
- int32_t width_dst = param[0];
- int32_t height_dst = param[1];
- int32_t pitch_dst = param[2];
- int32_t mheight_dst = param[3];
- int32_t pitch_src = param[4];
- uint8_t *y_tab = table[0];
- uint8_t *cb_tab = table[1];
- uint8_t *cr_tab = table[2];
-
- int32_t size16 = pitch_dst*mheight_dst;
- int32_t i,j,count;
- int32_t ilimit,jlimit;
- uint8_t *tempY,*tempU,*tempV;
- uint16_t pixels;
- int tmp;
-uint32_t temp;
-
- tempY = outYUV;
- tempU = outYUV + (height_dst * pitch_dst);
- tempV = tempU + 1;
-
- jlimit = height_dst;
- ilimit = width_dst;
-
- for(j=0; j<jlimit; j+=1)
- {
- for (i=0; i<ilimit; i+=2)
- {
- int32_t G_ds = 0, B_ds = 0, R_ds = 0;
- uint8_t y0, y1, u, v;
-
- pixels = inputRGB[i];
- temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) );
- y0 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)];
-
- G_ds += (pixels>>1) & 0x03E0;
- B_ds += (pixels<<5) & 0x03E0;
- R_ds += (pixels>>6) & 0x03E0;
-
- pixels = inputRGB[i+1];
- temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) );
- y1 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)];
-
- G_ds += (pixels>>1) & 0x03E0;
- B_ds += (pixels<<5) & 0x03E0;
- R_ds += (pixels>>6) & 0x03E0;
-
- R_ds >>= 1;
- B_ds >>= 1;
- G_ds >>= 1;
-
- tmp = R_ds - B_ds;
-
- u = cb_tab[(((B_ds-G_ds)<<SHIFT2) - GAMMA*tmp)>>(SHIFT2+2)];
- v = cr_tab[(((R_ds-G_ds)<<SHIFT2) + DELTA*tmp)>>(SHIFT2+2)];
-
- tempY[0] = y0;
- tempY[1] = y1;
- tempU[0] = u;
- tempV[0] = v;
-
- tempY += 2;
- tempU += 2;
- tempV += 2;
- }
-
- inputRGB += pitch_src;
- }
-
- return 1;
-}
-
-#define min(a,b) ((a)<(b)?(a):(b))
-#define max(a,b) ((a)>(b)?(a):(b))
-
-static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height)
-{
- if (!tables_initialized) {
- initYtab();
- initCrtab();
- initCbtab();
- tables_initialized = 1;
- }
-
- uint32_t param[6];
- param[0] = (uint32_t) width;
- param[1] = (uint32_t) height;
- param[2] = (uint32_t) width;
- param[3] = (uint32_t) height;
- param[4] = (uint32_t) width;
- param[5] = (uint32_t) 0;
-
- uint8_t *table[3];
- table[0] = gYTable;
- table[1] = gCbTable + 384;
- table[2] = gCrTable + 384;
-
- ccrgb16toyuv_wo_colorkey(rgb, yuv, param, table);
-}
-
-const int FakeCamera::kRed;
-const int FakeCamera::kGreen;
-const int FakeCamera::kBlue;
-
-FakeCamera::FakeCamera(int width, int height)
- : mTmpRgb16Buffer(0)
-{
- setSize(width, height);
-}
-
-FakeCamera::~FakeCamera()
-{
- delete[] mTmpRgb16Buffer;
-}
-
-void FakeCamera::setSize(int width, int height)
-{
- mWidth = width;
- mHeight = height;
- mCounter = 0;
- mCheckX = 0;
- mCheckY = 0;
-
- // This will cause it to be reallocated on the next call
- // to getNextFrameAsYuv422().
- delete[] mTmpRgb16Buffer;
- mTmpRgb16Buffer = 0;
-}
-
-void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer)
-{
- int size = mWidth / 10;
-
- drawCheckerboard(buffer, size);
-
- int x = ((mCounter*3)&255);
- if(x>128) x = 255 - x;
- int y = ((mCounter*5)&255);
- if(y>128) y = 255 - y;
-
- drawSquare(buffer, x*size/32, y*size/32, (size*5)>>1, (mCounter&0x100)?kRed:kGreen, kBlue);
-
- mCounter++;
-}
-
-void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer)
-{
- if (mTmpRgb16Buffer == 0)
- mTmpRgb16Buffer = new uint16_t[mWidth * mHeight];
-
- getNextFrameAsRgb565(mTmpRgb16Buffer);
- convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
-}
-
-void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow)
-{
- int square_xstop, square_ystop, shadow_xstop, shadow_ystop;
-
- square_xstop = min(mWidth, x+size);
- square_ystop = min(mHeight, y+size);
- shadow_xstop = min(mWidth, x+size+(size/4));
- shadow_ystop = min(mHeight, y+size+(size/4));
-
- // Do the shadow.
- uint16_t *sh = &dst[(y+(size/4))*mWidth];
- for (int j = y + (size/4); j < shadow_ystop; j++) {
- for (int i = x + (size/4); i < shadow_xstop; i++) {
- sh[i] &= shadow;
- }
- sh += mWidth;
- }
-
- // Draw the square.
- uint16_t *sq = &dst[y*mWidth];
- for (int j = y; j < square_ystop; j++) {
- for (int i = x; i < square_xstop; i++) {
- sq[i] = color;
- }
- sq += mWidth;
- }
-}
-
-void FakeCamera::drawCheckerboard(uint16_t *dst, int size)
-{
- bool black = true;
-
- if((mCheckX/size)&1)
- black = false;
- if((mCheckY/size)&1)
- black = !black;
-
- int county = mCheckY%size;
- int checkxremainder = mCheckX%size;
-
- for(int y=0;y<mHeight;y++) {
- int countx = checkxremainder;
- bool current = black;
- for(int x=0;x<mWidth;x++) {
- dst[y*mWidth+x] = current?0:0xffff;
- if(countx++ >= size) {
- countx=0;
- current = !current;
- }
- }
- if(county++ >= size) {
- county=0;
- black = !black;
- }
- }
- mCheckX += 3;
- mCheckY++;
-}
-
-
-void FakeCamera::dump(int fd) const
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
- snprintf(buffer, 255, " width x height (%d x %d), counter (%d), check x-y coordinate(%d, %d)\n", mWidth, mHeight, mCounter, mCheckX, mCheckY);
- result.append(buffer);
- ::write(fd, result.string(), result.size());
-}
-
-
-}; // namespace android
diff --git a/camera/libcameraservice/FakeCamera.h b/camera/libcameraservice/FakeCamera.h
deleted file mode 100644
index f7f8803..0000000
--- a/camera/libcameraservice/FakeCamera.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_HARDWARE_FAKECAMERA_H
-#define ANDROID_HARDWARE_FAKECAMERA_H
-
-#include <sys/types.h>
-#include <stdint.h>
-
-namespace android {
-
-/*
- * FakeCamera is used in the CameraHardwareStub to provide a fake video feed
- * when the system does not have a camera in hardware.
- * The fake video is a moving black and white checkerboard background with a
- * bouncing gray square in the foreground.
- * This class is not thread-safe.
- *
- * TODO: Since the major methods provides a raw/uncompressed video feed, rename
- * this class to RawVideoSource.
- */
-
-class FakeCamera {
-public:
- FakeCamera(int width, int height);
- ~FakeCamera();
-
- void setSize(int width, int height);
- void getNextFrameAsYuv422(uint8_t *buffer);
- // Write to the fd a string representing the current state.
- void dump(int fd) const;
-
-private:
- // TODO: remove the uint16_t buffer param everywhere since it is a field of
- // this class.
- void getNextFrameAsRgb565(uint16_t *buffer);
-
- void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow);
- void drawCheckerboard(uint16_t *buffer, int size);
-
- static const int kRed = 0xf800;
- static const int kGreen = 0x07c0;
- static const int kBlue = 0x003e;
-
- int mWidth, mHeight;
- int mCounter;
- int mCheckX, mCheckY;
- uint16_t *mTmpRgb16Buffer;
-};
-
-}; // namespace android
-
-#endif // ANDROID_HARDWARE_FAKECAMERA_H
diff --git a/camera/tests/CameraServiceTest/Android.mk b/camera/tests/CameraServiceTest/Android.mk
deleted file mode 100644
index 9bb190a..0000000
--- a/camera/tests/CameraServiceTest/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= CameraServiceTest.cpp
-
-LOCAL_MODULE:= CameraServiceTest
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_C_INCLUDES += \
- frameworks/base/libs
-
-LOCAL_CFLAGS :=
-
-LOCAL_SHARED_LIBRARIES += \
- libbinder \
- libcutils \
- libutils \
- libui \
- libcamera_client \
- libsurfaceflinger_client
-
-include $(BUILD_EXECUTABLE)
diff --git a/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/camera/tests/CameraServiceTest/CameraServiceTest.cpp
deleted file mode 100644
index 9fc795b..0000000
--- a/camera/tests/CameraServiceTest/CameraServiceTest.cpp
+++ /dev/null
@@ -1,849 +0,0 @@
-#define LOG_TAG "CameraServiceTest"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <surfaceflinger/ISurface.h>
-#include <camera/Camera.h>
-#include <camera/CameraParameters.h>
-#include <ui/GraphicBuffer.h>
-#include <camera/ICamera.h>
-#include <camera/ICameraClient.h>
-#include <camera/ICameraService.h>
-#include <ui/Overlay.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <utils/KeyedVector.h>
-#include <utils/Log.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-using namespace android;
-
-//
-// Assertion and Logging utilities
-//
-#define INFO(...) \
- do { \
- printf(__VA_ARGS__); \
- printf("\n"); \
- LOGD(__VA_ARGS__); \
- } while(0)
-
-void assert_fail(const char *file, int line, const char *func, const char *expr) {
- INFO("assertion failed at file %s, line %d, function %s:",
- file, line, func);
- INFO("%s", expr);
- exit(1);
-}
-
-void assert_eq_fail(const char *file, int line, const char *func,
- const char *expr, int actual) {
- INFO("assertion failed at file %s, line %d, function %s:",
- file, line, func);
- INFO("(expected) %s != (actual) %d", expr, actual);
- exit(1);
-}
-
-#define ASSERT(e) \
- do { \
- if (!(e)) \
- assert_fail(__FILE__, __LINE__, __func__, #e); \
- } while(0)
-
-#define ASSERT_EQ(expected, actual) \
- do { \
- int _x = (actual); \
- if (_x != (expected)) \
- assert_eq_fail(__FILE__, __LINE__, __func__, #expected, _x); \
- } while(0)
-
-//
-// Holder service for pass objects between processes.
-//
-class IHolder : public IInterface {
-protected:
- enum {
- HOLDER_PUT = IBinder::FIRST_CALL_TRANSACTION,
- HOLDER_GET,
- HOLDER_CLEAR
- };
-public:
- DECLARE_META_INTERFACE(Holder);
-
- virtual void put(sp<IBinder> obj) = 0;
- virtual sp<IBinder> get() = 0;
- virtual void clear() = 0;
-};
-
-class BnHolder : public BnInterface<IHolder> {
- virtual status_t onTransact(uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-class BpHolder : public BpInterface<IHolder> {
-public:
- BpHolder(const sp<IBinder>& impl)
- : BpInterface<IHolder>(impl) {
- }
-
- virtual void put(sp<IBinder> obj) {
- Parcel data, reply;
- data.writeStrongBinder(obj);
- remote()->transact(HOLDER_PUT, data, &reply, IBinder::FLAG_ONEWAY);
- }
-
- virtual sp<IBinder> get() {
- Parcel data, reply;
- remote()->transact(HOLDER_GET, data, &reply);
- return reply.readStrongBinder();
- }
-
- virtual void clear() {
- Parcel data, reply;
- remote()->transact(HOLDER_CLEAR, data, &reply);
- }
-};
-
-IMPLEMENT_META_INTERFACE(Holder, "CameraServiceTest.Holder");
-
-status_t BnHolder::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch(code) {
- case HOLDER_PUT: {
- put(data.readStrongBinder());
- return NO_ERROR;
- } break;
- case HOLDER_GET: {
- reply->writeStrongBinder(get());
- return NO_ERROR;
- } break;
- case HOLDER_CLEAR: {
- clear();
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-class HolderService : public BnHolder {
- virtual void put(sp<IBinder> obj) {
- mObj = obj;
- }
- virtual sp<IBinder> get() {
- return mObj;
- }
- virtual void clear() {
- mObj.clear();
- }
-private:
- sp<IBinder> mObj;
-};
-
-//
-// A mock CameraClient
-//
-class MCameraClient : public BnCameraClient {
-public:
- virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
- virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
- virtual void dataCallbackTimestamp(nsecs_t timestamp,
- int32_t msgType, const sp<IMemory>& data) {}
-
- // new functions
- void clearStat();
- enum OP { EQ, GE, LE, GT, LT };
- void assertNotify(int32_t msgType, OP op, int count);
- void assertData(int32_t msgType, OP op, int count);
- void waitNotify(int32_t msgType, OP op, int count);
- void waitData(int32_t msgType, OP op, int count);
- void assertDataSize(int32_t msgType, OP op, int dataSize);
-
- void setReleaser(ICamera *releaser) {
- mReleaser = releaser;
- }
-private:
- Mutex mLock;
- Condition mCond;
- DefaultKeyedVector<int32_t, int> mNotifyCount;
- DefaultKeyedVector<int32_t, int> mDataCount;
- DefaultKeyedVector<int32_t, int> mDataSize;
- bool test(OP op, int v1, int v2);
-
- ICamera *mReleaser;
-};
-
-void MCameraClient::clearStat() {
- Mutex::Autolock _l(mLock);
- mNotifyCount.clear();
- mDataCount.clear();
- mDataSize.clear();
-}
-
-bool MCameraClient::test(OP op, int v1, int v2) {
- switch (op) {
- case EQ: return v1 == v2;
- case GT: return v1 > v2;
- case LT: return v1 < v2;
- case GE: return v1 >= v2;
- case LE: return v1 <= v2;
- default: ASSERT(0); break;
- }
- return false;
-}
-
-void MCameraClient::assertNotify(int32_t msgType, OP op, int count) {
- Mutex::Autolock _l(mLock);
- int v = mNotifyCount.valueFor(msgType);
- ASSERT(test(op, v, count));
-}
-
-void MCameraClient::assertData(int32_t msgType, OP op, int count) {
- Mutex::Autolock _l(mLock);
- int v = mDataCount.valueFor(msgType);
- ASSERT(test(op, v, count));
-}
-
-void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) {
- Mutex::Autolock _l(mLock);
- int v = mDataSize.valueFor(msgType);
- ASSERT(test(op, v, dataSize));
-}
-
-void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
- INFO(__func__);
- Mutex::Autolock _l(mLock);
- ssize_t i = mNotifyCount.indexOfKey(msgType);
- if (i < 0) {
- mNotifyCount.add(msgType, 1);
- } else {
- ++mNotifyCount.editValueAt(i);
- }
- mCond.signal();
-}
-
-void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) {
- INFO(__func__);
- int dataSize = data->size();
- INFO("data type = %d, size = %d", msgType, dataSize);
- Mutex::Autolock _l(mLock);
- ssize_t i = mDataCount.indexOfKey(msgType);
- if (i < 0) {
- mDataCount.add(msgType, 1);
- mDataSize.add(msgType, dataSize);
- } else {
- ++mDataCount.editValueAt(i);
- mDataSize.editValueAt(i) = dataSize;
- }
- mCond.signal();
-
- if (msgType == CAMERA_MSG_VIDEO_FRAME) {
- ASSERT(mReleaser != NULL);
- mReleaser->releaseRecordingFrame(data);
- }
-}
-
-void MCameraClient::waitNotify(int32_t msgType, OP op, int count) {
- INFO("waitNotify: %d, %d, %d", msgType, op, count);
- Mutex::Autolock _l(mLock);
- while (true) {
- int v = mNotifyCount.valueFor(msgType);
- if (test(op, v, count)) {
- break;
- }
- mCond.wait(mLock);
- }
-}
-
-void MCameraClient::waitData(int32_t msgType, OP op, int count) {
- INFO("waitData: %d, %d, %d", msgType, op, count);
- Mutex::Autolock _l(mLock);
- while (true) {
- int v = mDataCount.valueFor(msgType);
- if (test(op, v, count)) {
- break;
- }
- mCond.wait(mLock);
- }
-}
-
-//
-// A mock Surface
-//
-class MSurface : public BnSurface {
-public:
- virtual status_t registerBuffers(const BufferHeap& buffers);
- virtual void postBuffer(ssize_t offset);
- virtual void unregisterBuffers();
- virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format, int32_t orientation);
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage);
-
- // new functions
- void clearStat();
- void waitUntil(int c0, int c1, int c2);
-
-private:
- // check callback count
- Condition mCond;
- Mutex mLock;
- int registerBuffersCount;
- int postBufferCount;
- int unregisterBuffersCount;
-};
-
-status_t MSurface::registerBuffers(const BufferHeap& buffers) {
- INFO(__func__);
- Mutex::Autolock _l(mLock);
- ++registerBuffersCount;
- mCond.signal();
- return NO_ERROR;
-}
-
-void MSurface::postBuffer(ssize_t offset) {
- // INFO(__func__);
- Mutex::Autolock _l(mLock);
- ++postBufferCount;
- mCond.signal();
-}
-
-void MSurface::unregisterBuffers() {
- INFO(__func__);
- Mutex::Autolock _l(mLock);
- ++unregisterBuffersCount;
- mCond.signal();
-}
-
-sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) {
- INFO(__func__);
- return NULL;
-}
-
-void MSurface::clearStat() {
- Mutex::Autolock _l(mLock);
- registerBuffersCount = 0;
- postBufferCount = 0;
- unregisterBuffersCount = 0;
-}
-
-void MSurface::waitUntil(int c0, int c1, int c2) {
- INFO("waitUntil: %d %d %d", c0, c1, c2);
- Mutex::Autolock _l(mLock);
- while (true) {
- if (registerBuffersCount >= c0 &&
- postBufferCount >= c1 &&
- unregisterBuffersCount >= c2) {
- break;
- }
- mCond.wait(mLock);
- }
-}
-
-sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format,
- int32_t orientation) {
- // We don't expect this to be called in current hardware.
- ASSERT(0);
- sp<OverlayRef> dummy;
- return dummy;
-}
-
-//
-// Utilities to use the Holder service
-//
-sp<IHolder> getHolder() {
- sp<IServiceManager> sm = defaultServiceManager();
- ASSERT(sm != 0);
- sp<IBinder> binder = sm->getService(String16("CameraServiceTest.Holder"));
- ASSERT(binder != 0);
- sp<IHolder> holder = interface_cast<IHolder>(binder);
- ASSERT(holder != 0);
- return holder;
-}
-
-void putTempObject(sp<IBinder> obj) {
- INFO(__func__);
- getHolder()->put(obj);
-}
-
-sp<IBinder> getTempObject() {
- INFO(__func__);
- return getHolder()->get();
-}
-
-void clearTempObject() {
- INFO(__func__);
- getHolder()->clear();
-}
-
-//
-// Get a Camera Service
-//
-sp<ICameraService> getCameraService() {
- sp<IServiceManager> sm = defaultServiceManager();
- ASSERT(sm != 0);
- sp<IBinder> binder = sm->getService(String16("media.camera"));
- ASSERT(binder != 0);
- sp<ICameraService> cs = interface_cast<ICameraService>(binder);
- ASSERT(cs != 0);
- return cs;
-}
-
-//
-// Various Connect Tests
-//
-void testConnect() {
- INFO(__func__);
- sp<ICameraService> cs = getCameraService();
- sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
- ASSERT(c != 0);
- c->disconnect();
-}
-
-void testAllowConnectOnceOnly() {
- INFO(__func__);
- sp<ICameraService> cs = getCameraService();
- // Connect the first client.
- sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
- ASSERT(c != 0);
- // Same client -- ok.
- ASSERT(cs->connect(cc) != 0);
- // Different client -- not ok.
- sp<MCameraClient> cc2 = new MCameraClient();
- ASSERT(cs->connect(cc2) == 0);
- c->disconnect();
-}
-
-void testReconnectFailed() {
- INFO(__func__);
- sp<ICamera> c = interface_cast<ICamera>(getTempObject());
- sp<MCameraClient> cc2 = new MCameraClient();
- ASSERT(c->connect(cc2) != NO_ERROR);
-}
-
-void testReconnectSuccess() {
- INFO(__func__);
- sp<ICamera> c = interface_cast<ICamera>(getTempObject());
- sp<MCameraClient> cc = new MCameraClient();
- ASSERT(c->connect(cc) == NO_ERROR);
-}
-
-void testLockFailed() {
- INFO(__func__);
- sp<ICamera> c = interface_cast<ICamera>(getTempObject());
- ASSERT(c->lock() != NO_ERROR);
-}
-
-void testLockUnlockSuccess() {
- INFO(__func__);
- sp<ICamera> c = interface_cast<ICamera>(getTempObject());
- ASSERT(c->lock() == NO_ERROR);
- ASSERT(c->unlock() == NO_ERROR);
-}
-
-void testLockSuccess() {
- INFO(__func__);
- sp<ICamera> c = interface_cast<ICamera>(getTempObject());
- ASSERT(c->lock() == NO_ERROR);
-}
-
-//
-// Run the connect tests in another process.
-//
-const char *gExecutable;
-
-struct FunctionTableEntry {
- const char *name;
- void (*func)();
-};
-
-FunctionTableEntry function_table[] = {
-#define ENTRY(x) {#x, &x}
- ENTRY(testReconnectFailed),
- ENTRY(testReconnectSuccess),
- ENTRY(testLockUnlockSuccess),
- ENTRY(testLockFailed),
- ENTRY(testLockSuccess),
-#undef ENTRY
-};
-
-void runFunction(const char *tag) {
- INFO("runFunction: %s", tag);
- int entries = sizeof(function_table) / sizeof(function_table[0]);
- for (int i = 0; i < entries; i++) {
- if (strcmp(function_table[i].name, tag) == 0) {
- (*function_table[i].func)();
- return;
- }
- }
- ASSERT(0);
-}
-
-void runInAnotherProcess(const char *tag) {
- pid_t pid = fork();
- if (pid == 0) {
- execlp(gExecutable, gExecutable, tag, NULL);
- ASSERT(0);
- } else {
- int status;
- ASSERT_EQ(pid, wait(&status));
- ASSERT_EQ(0, status);
- }
-}
-
-void testReconnect() {
- INFO(__func__);
- sp<ICameraService> cs = getCameraService();
- sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
- ASSERT(c != 0);
- // Reconnect to the same client -- ok.
- ASSERT(c->connect(cc) == NO_ERROR);
- // Reconnect to a different client (but the same pid) -- ok.
- sp<MCameraClient> cc2 = new MCameraClient();
- ASSERT(c->connect(cc2) == NO_ERROR);
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
-}
-
-void testLockUnlock() {
- sp<ICameraService> cs = getCameraService();
- sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
- ASSERT(c != 0);
- // We can lock as many times as we want.
- ASSERT(c->lock() == NO_ERROR);
- ASSERT(c->lock() == NO_ERROR);
- // Lock from a different process -- not ok.
- putTempObject(c->asBinder());
- runInAnotherProcess("testLockFailed");
- // Unlock then lock from a different process -- ok.
- ASSERT(c->unlock() == NO_ERROR);
- runInAnotherProcess("testLockUnlockSuccess");
- // Unlock then lock from a different process -- ok.
- runInAnotherProcess("testLockSuccess");
- c->disconnect();
- clearTempObject();
-}
-
-void testReconnectFromAnotherProcess() {
- INFO(__func__);
-
- sp<ICameraService> cs = getCameraService();
- sp<MCameraClient> cc = new MCameraClient();
- sp<ICamera> c = cs->connect(cc);
- ASSERT(c != 0);
- // Reconnect from a different process -- not ok.
- putTempObject(c->asBinder());
- runInAnotherProcess("testReconnectFailed");
- // Unlock then reconnect from a different process -- ok.
- ASSERT(c->unlock() == NO_ERROR);
- runInAnotherProcess("testReconnectSuccess");
- c->disconnect();
- clearTempObject();
-}
-
-// We need to flush the command buffer after the reference
-// to ICamera is gone. The sleep is for the server to run
-// the destructor for it.
-static void flushCommands() {
- IPCThreadState::self()->flushCommands();
- usleep(200000); // 200ms
-}
-
-// Run a test case
-#define RUN(class_name) do { \
- { \
- INFO(#class_name); \
- class_name instance; \
- instance.run(); \
- } \
- flushCommands(); \
-} while(0)
-
-// Base test case after the the camera is connected.
-class AfterConnect {
-protected:
- sp<ICameraService> cs;
- sp<MCameraClient> cc;
- sp<ICamera> c;
-
- AfterConnect() {
- cs = getCameraService();
- cc = new MCameraClient();
- c = cs->connect(cc);
- ASSERT(c != 0);
- }
-
- ~AfterConnect() {
- c.clear();
- cc.clear();
- cs.clear();
- }
-};
-
-class TestSetPreviewDisplay : public AfterConnect {
-public:
- void run() {
- sp<MSurface> surface = new MSurface();
- ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-class TestStartPreview : public AfterConnect {
-public:
- void run() {
- sp<MSurface> surface = new MSurface();
- ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
-
- ASSERT(c->startPreview() == NO_ERROR);
- ASSERT(c->previewEnabled() == true);
-
- surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer
- surface->clearStat();
-
- c->disconnect();
- // TODO: CameraService crashes for this. Fix it.
-#if 0
- sp<MSurface> another_surface = new MSurface();
- c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers
- // is called.
- surface->waitUntil(0, 0, 1); // needs unregisterBuffers
-#endif
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-class TestStartPreviewWithoutDisplay : AfterConnect {
-public:
- void run() {
- ASSERT(c->startPreview() == NO_ERROR);
- ASSERT(c->previewEnabled() == true);
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-// Base test case after the the camera is connected and the preview is started.
-class AfterStartPreview : public AfterConnect {
-protected:
- sp<MSurface> surface;
-
- AfterStartPreview() {
- surface = new MSurface();
- ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
- ASSERT(c->startPreview() == NO_ERROR);
- }
-
- ~AfterStartPreview() {
- surface.clear();
- }
-};
-
-class TestAutoFocus : public AfterStartPreview {
-public:
- void run() {
- cc->assertNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 0);
- c->autoFocus();
- cc->waitNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 1);
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-class TestStopPreview : public AfterStartPreview {
-public:
- void run() {
- ASSERT(c->previewEnabled() == true);
- c->stopPreview();
- ASSERT(c->previewEnabled() == false);
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-class TestTakePicture: public AfterStartPreview {
-public:
- void run() {
- ASSERT(c->takePicture() == NO_ERROR);
- cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1);
- cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
- cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
- c->stopPreview();
-#if 1 // TODO: It crashes if we don't have this. Fix it.
- usleep(100000);
-#endif
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-class TestTakeMultiplePictures: public AfterStartPreview {
-public:
- void run() {
- for (int i = 0; i < 10; i++) {
- cc->clearStat();
- ASSERT(c->takePicture() == NO_ERROR);
- cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1);
- cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
- cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
- usleep(100000); // 100ms
- }
- c->disconnect();
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- }
-};
-
-class TestGetParameters: public AfterStartPreview {
-public:
- void run() {
- String8 param_str = c->getParameters();
- INFO(param_str);
- }
-};
-
-class TestPictureSize : public AfterStartPreview {
-public:
- void checkOnePicture(int w, int h) {
- const float rate = 0.5; // byte per pixel limit
- int pixels = w * h;
-
- CameraParameters param(c->getParameters());
- param.setPictureSize(w, h);
- c->setParameters(param.flatten());
-
- cc->clearStat();
- ASSERT(c->takePicture() == NO_ERROR);
- cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
- cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2);
- cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
- cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT,
- int(pixels * rate));
- cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0);
- cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
- usleep(100000); // 100ms
- }
-
- void run() {
- checkOnePicture(2048, 1536);
- checkOnePicture(1600, 1200);
- checkOnePicture(1024, 768);
- }
-};
-
-class TestPreviewCallbackFlag : public AfterConnect {
-public:
- void run() {
- sp<MSurface> surface = new MSurface();
- ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
-
- // Try all flag combinations.
- for (int v = 0; v < 8; v++) {
- cc->clearStat();
- c->setPreviewCallbackFlag(v);
- ASSERT(c->previewEnabled() == false);
- ASSERT(c->startPreview() == NO_ERROR);
- ASSERT(c->previewEnabled() == true);
- sleep(2);
- c->stopPreview();
- if ((v & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) {
- cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0);
- } else {
- if ((v & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) {
- cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10);
- } else {
- cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1);
- }
- }
- }
- }
-};
-
-class TestRecording : public AfterConnect {
-public:
- void run() {
- ASSERT(c->recordingEnabled() == false);
- sp<MSurface> surface = new MSurface();
- ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
- c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
- cc->setReleaser(c.get());
- c->startRecording();
- ASSERT(c->recordingEnabled() == true);
- sleep(2);
- c->stopRecording();
- cc->setReleaser(NULL);
- cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10);
- }
-};
-
-class TestPreviewSize : public AfterStartPreview {
-public:
- void checkOnePicture(int w, int h) {
- int size = w*h*3/2; // should read from parameters
-
- c->stopPreview();
-
- CameraParameters param(c->getParameters());
- param.setPreviewSize(w, h);
- c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
- c->setParameters(param.flatten());
-
- c->startPreview();
-
- cc->clearStat();
- cc->waitData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 1);
- cc->assertDataSize(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, size);
- }
-
- void run() {
- checkOnePicture(480, 320);
- checkOnePicture(352, 288);
- checkOnePicture(176, 144);
- }
-};
-
-void runHolderService() {
- defaultServiceManager()->addService(
- String16("CameraServiceTest.Holder"), new HolderService());
- ProcessState::self()->startThreadPool();
-}
-
-int main(int argc, char **argv)
-{
- if (argc != 1) {
- runFunction(argv[1]);
- return 0;
- }
- INFO("CameraServiceTest start");
- gExecutable = argv[0];
- runHolderService();
-
- testConnect(); flushCommands();
- testAllowConnectOnceOnly(); flushCommands();
- testReconnect(); flushCommands();
- testLockUnlock(); flushCommands();
- testReconnectFromAnotherProcess(); flushCommands();
-
- RUN(TestSetPreviewDisplay);
- RUN(TestStartPreview);
- RUN(TestStartPreviewWithoutDisplay);
- RUN(TestAutoFocus);
- RUN(TestStopPreview);
- RUN(TestTakePicture);
- RUN(TestTakeMultiplePictures);
- RUN(TestGetParameters);
- RUN(TestPictureSize);
- RUN(TestPreviewCallbackFlag);
- RUN(TestRecording);
- RUN(TestPreviewSize);
-}
diff --git a/cmds/surfaceflinger/Android.mk b/cmds/surfaceflinger/Android.mk
index bfa58a1..1df32bb 100644
--- a/cmds/surfaceflinger/Android.mk
+++ b/cmds/surfaceflinger/Android.mk
@@ -10,7 +10,7 @@ LOCAL_SHARED_LIBRARIES := \
libutils
LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/../../libs/surfaceflinger
+ $(LOCAL_PATH)/../../services/surfaceflinger
LOCAL_MODULE:= surfaceflinger
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index d650721..78b1007 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -1,18 +1,25 @@
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <utils/Log.h>
+/*
+ * 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 <binder/BinderService.h>
#include <SurfaceFlinger.h>
using namespace android;
-int main(int argc, char** argv)
-{
- sp<ProcessState> proc(ProcessState::self());
- sp<IServiceManager> sm = defaultServiceManager();
- LOGI("ServiceManager: %p", sm.get());
- SurfaceFlinger::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
+int main(int argc, char** argv) {
+ SurfaceFlinger::publishAndJoinThreadPool();
+ return 0;
}
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 100644
index 0000000..2316fef
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -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.
+ */
+
+#ifndef ANDROID_BINDER_SERVICE_H
+#define ANDROID_BINDER_SERVICE_H
+
+#include <stdint.h>
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+template<typename SERVICE>
+class BinderService
+{
+public:
+ static status_t publish() {
+ sp<IServiceManager> sm(defaultServiceManager());
+ return sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+ }
+
+ static void publishAndJoinThreadPool() {
+ sp<ProcessState> proc(ProcessState::self());
+ sp<IServiceManager> sm(defaultServiceManager());
+ sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
+ ProcessState::self()->startThreadPool();
+ IPCThreadState::self()->joinThreadPool();
+ }
+
+ static void instantiate() { publish(); }
+
+ static status_t shutdown() {
+ return NO_ERROR;
+ }
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+#endif // ANDROID_BINDER_SERVICE_H
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
index 273d922..5f9f69c 100644
--- a/include/binder/IInterface.h
+++ b/include/binder/IInterface.h
@@ -72,21 +72,24 @@ protected:
// ----------------------------------------------------------------------
#define DECLARE_META_INTERFACE(INTERFACE) \
- static const String16 descriptor; \
- static sp<I##INTERFACE> asInterface(const sp<IBinder>& obj); \
- virtual const String16& getInterfaceDescriptor() const; \
+ static const android::String16 descriptor; \
+ static android::sp<I##INTERFACE> asInterface( \
+ const android::sp<android::IBinder>& obj); \
+ virtual const android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
- const String16 I##INTERFACE::descriptor(NAME); \
- const String16& I##INTERFACE::getInterfaceDescriptor() const { \
+ const android::String16 I##INTERFACE::descriptor(NAME); \
+ const android::String16& \
+ I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
- sp<I##INTERFACE> I##INTERFACE::asInterface(const sp<IBinder>& obj) \
+ android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
+ const android::sp<android::IBinder>& obj) \
{ \
- sp<I##INTERFACE> intr; \
+ android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 3ab985d..04e24d2 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -40,6 +40,9 @@ public:
int getCallingPid();
int getCallingUid();
+
+ void setStrictModePolicy(int32_t policy);
+ int32_t getStrictModePolicy() const;
int64_t clearCallingIdentity();
void restoreCallingIdentity(int64_t token);
@@ -109,8 +112,9 @@ private:
status_t mLastError;
pid_t mCallingPid;
uid_t mCallingUid;
+ int32_t mStrictModePolicy;
};
-
+
}; // namespace android
// ---------------------------------------------------------------------------
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 66c34b2..32c9a1d 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -26,11 +26,12 @@
// ---------------------------------------------------------------------------
namespace android {
+class Flattenable;
class IBinder;
+class IPCThreadState;
class ProcessState;
class String8;
class TextOutput;
-class Flattenable;
struct flat_binder_object; // defined in support_p/binder_module.h
@@ -56,9 +57,19 @@ public:
bool hasFileDescriptors() const;
+ // Writes the RPC header.
status_t writeInterfaceToken(const String16& interface);
- bool enforceInterface(const String16& interface) const;
- bool checkInterface(IBinder*) const;
+
+ // Parses the RPC header, returning true if the interface name
+ // in the header matches the expected interface from the caller.
+ //
+ // Additionally, enforceInterface does part of the work of
+ // propagating the StrictMode policy mask, populating the current
+ // IPCThreadState, which as an optimization may optionally be
+ // passed in.
+ bool enforceInterface(const String16& interface,
+ IPCThreadState* threadState = NULL) const;
+ bool checkInterface(IBinder*) const;
void freeData();
@@ -100,6 +111,11 @@ public:
status_t writeObject(const flat_binder_object& val, bool nullMetaData);
+ // Like Parcel.java's writeNoException(). Just writes a zero int32.
+ // Currently the native implementation doesn't do any of the StrictMode
+ // stack gathering and serialization that the Java implementation does.
+ status_t writeNoException();
+
void remove(size_t start, size_t amt);
status_t read(void* outData, size_t len) const;
@@ -122,7 +138,14 @@ public:
sp<IBinder> readStrongBinder() const;
wp<IBinder> readWeakBinder() const;
status_t read(Flattenable& val) const;
-
+
+ // Like Parcel.java's readExceptionCode(). Reads the first int32
+ // off of a Parcel's header, returning 0 or the negative error
+ // code on exceptions, but also deals with skipping over rich
+ // response headers. Callers should use this to read & parse the
+ // response headers rather than doing it by hand.
+ int32_t readExceptionCode() const;
+
// Retrieve native_handle from the parcel. This returns a copy of the
// parcel's native_handle (the caller takes ownership). The caller
// must free the native_handle with native_handle_close() and
diff --git a/include/gui/ISensorEventConnection.h b/include/gui/ISensorEventConnection.h
new file mode 100644
index 0000000..ed4e4cc
--- /dev/null
+++ b/include/gui/ISensorEventConnection.h
@@ -0,0 +1,57 @@
+/*
+ * 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 ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
+#define ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class SensorChannel;
+
+class ISensorEventConnection : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SensorEventConnection);
+
+ virtual sp<SensorChannel> getSensorChannel() const = 0;
+ virtual status_t enableDisable(int handle, bool enabled) = 0;
+ virtual status_t setEventRate(int handle, nsecs_t ns) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorEventConnection : public BnInterface<ISensorEventConnection>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_ISENSOR_EVENT_CONNECTION_H
diff --git a/include/gui/ISensorServer.h b/include/gui/ISensorServer.h
new file mode 100644
index 0000000..9c8afc5
--- /dev/null
+++ b/include/gui/ISensorServer.h
@@ -0,0 +1,57 @@
+/*
+ * 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 ANDROID_GUI_ISENSORSERVER_H
+#define ANDROID_GUI_ISENSORSERVER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <binder/IInterface.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Sensor;
+class ISensorEventConnection;
+
+class ISensorServer : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(SensorServer);
+
+ virtual Vector<Sensor> getSensorList() = 0;
+ virtual sp<ISensorEventConnection> createSensorEventConnection() = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnSensorServer : public BnInterface<ISensorServer>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_ISENSORSERVER_H
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
new file mode 100644
index 0000000..2de07b1
--- /dev/null
+++ b/include/gui/Sensor.h
@@ -0,0 +1,91 @@
+/*
+ * 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 ANDROID_GUI_SENSOR_H
+#define ANDROID_GUI_SENSOR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Flattenable.h>
+
+#include <hardware/sensors.h>
+
+#include <android/sensor.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensor { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class Parcel;
+
+// ----------------------------------------------------------------------------
+
+class Sensor : public ASensor, public Flattenable
+{
+public:
+ enum {
+ TYPE_ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER,
+ TYPE_MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+ TYPE_GYROSCOPE = ASENSOR_TYPE_GYROSCOPE,
+ TYPE_LIGHT = ASENSOR_TYPE_LIGHT,
+ TYPE_PROXIMITY = ASENSOR_TYPE_PROXIMITY
+ };
+
+ Sensor();
+ Sensor(struct sensor_t const* hwSensor);
+ virtual ~Sensor();
+
+ const String8& getName() const;
+ const String8& getVendor() const;
+ int32_t getHandle() const;
+ int32_t getType() const;
+ float getMinValue() const;
+ float getMaxValue() const;
+ float getResolution() const;
+ float getPowerUsage() const;
+ int32_t getMinDelay() const;
+
+ // Flattenable interface
+ virtual size_t getFlattenedSize() const;
+ virtual size_t getFdCount() const;
+ virtual status_t flatten(void* buffer, size_t size,
+ int fds[], size_t count) const;
+ virtual status_t unflatten(void const* buffer, size_t size,
+ int fds[], size_t count);
+
+private:
+ String8 mName;
+ String8 mVendor;
+ int32_t mHandle;
+ int32_t mType;
+ float mMinValue;
+ float mMaxValue;
+ float mResolution;
+ float mPower;
+ int32_t mMinDelay;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_H
diff --git a/libs/audioflinger/AudioBufferProvider.h b/include/gui/SensorChannel.h
index 81c5c39..bb54618 100644
--- a/libs/audioflinger/AudioBufferProvider.h
+++ b/include/gui/SensorChannel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,36 +14,40 @@
* limitations under the License.
*/
-#ifndef ANDROID_AUDIO_BUFFER_PROVIDER_H
-#define ANDROID_AUDIO_BUFFER_PROVIDER_H
+#ifndef ANDROID_GUI_SENSOR_CHANNEL_H
+#define ANDROID_GUI_SENSOR_CHANNEL_H
#include <stdint.h>
#include <sys/types.h>
+
#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
namespace android {
// ----------------------------------------------------------------------------
+class Parcel;
-class AudioBufferProvider
+class SensorChannel : public RefBase
{
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;
+ SensorChannel();
+ SensorChannel(const Parcel& data);
+ virtual ~SensorChannel();
+
+ int getFd() const;
+ ssize_t write(void const* vaddr, size_t size);
+ ssize_t read(void* vaddr, size_t size);
+
+ status_t writeToParcel(Parcel* reply) const;
+
+private:
+ int mSendFd;
+ mutable int mReceiveFd;
};
// ----------------------------------------------------------------------------
}; // namespace android
-#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H
+#endif // ANDROID_GUI_SENSOR_CHANNEL_H
diff --git a/include/gui/SensorEventQueue.h b/include/gui/SensorEventQueue.h
new file mode 100644
index 0000000..6581ae3
--- /dev/null
+++ b/include/gui/SensorEventQueue.h
@@ -0,0 +1,82 @@
+/*
+ * 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 ANDROID_SENSOR_EVENT_QUEUE_H
+#define ANDROID_SENSOR_EVENT_QUEUE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <gui/SensorChannel.h>
+
+// ----------------------------------------------------------------------------
+
+struct ALooper;
+struct ASensorEvent;
+
+// Concrete types for the NDK
+struct ASensorEventQueue {
+ ALooper* looper;
+};
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorEventConnection;
+class Sensor;
+class PollLoop;
+
+// ----------------------------------------------------------------------------
+
+class SensorEventQueue : public ASensorEventQueue, public RefBase
+{
+public:
+ SensorEventQueue(const sp<ISensorEventConnection>& connection);
+ virtual ~SensorEventQueue();
+ virtual void onFirstRef();
+
+ int getFd() const;
+ ssize_t write(ASensorEvent const* events, size_t numEvents);
+ ssize_t read(ASensorEvent* events, size_t numEvents);
+
+ status_t waitForEvent() const;
+ status_t wake() const;
+
+ status_t enableSensor(Sensor const* sensor) const;
+ status_t disableSensor(Sensor const* sensor) const;
+ status_t setEventRate(Sensor const* sensor, nsecs_t ns) const;
+
+ // these are here only to support SensorManager.java
+ status_t enableSensor(int32_t handle, int32_t us) const;
+ status_t disableSensor(int32_t handle) const;
+
+private:
+ sp<PollLoop> getPollLoop() const;
+ sp<ISensorEventConnection> mSensorEventConnection;
+ sp<SensorChannel> mSensorChannel;
+ mutable Mutex mLock;
+ mutable sp<PollLoop> mPollLoop;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SENSOR_EVENT_QUEUE_H
diff --git a/include/gui/SensorManager.h b/include/gui/SensorManager.h
new file mode 100644
index 0000000..e1b1a7b
--- /dev/null
+++ b/include/gui/SensorManager.h
@@ -0,0 +1,63 @@
+/*
+ * 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 ANDROID_GUI_SENSOR_MANAGER_H
+#define ANDROID_GUI_SENSOR_MANAGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+#include <utils/Vector.h>
+
+#include <gui/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+// Concrete types for the NDK
+struct ASensorManager { };
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class ISensorServer;
+class Sensor;
+class SensorEventQueue;
+
+// ----------------------------------------------------------------------------
+
+class SensorManager : public ASensorManager, public Singleton<SensorManager>
+{
+public:
+ SensorManager();
+ ~SensorManager();
+
+ ssize_t getSensorList(Sensor const* const** list) const;
+ Sensor const* getDefaultSensor(int type);
+ sp<SensorEventQueue> createEventQueue();
+
+private:
+ sp<ISensorServer> mSensorServer;
+ Sensor const** mSensorList;
+ Vector<Sensor> mSensors;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_GUI_SENSOR_MANAGER_H
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 9b5a1e0..1eb178e 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -43,24 +43,9 @@ namespace android {
* unless they are in use by the server, which is only the case for the last
* dequeue-able buffer. When these various conditions are not met, the caller
* waits until the condition is met.
- *
- *
- * CAVEATS:
- *
- * In the current implementation there are several limitations:
- * - buffers must be locked in the same order they've been dequeued
- * - buffers must be enqueued in the same order they've been locked
- * - dequeue() is not reentrant
- * - no error checks are done on the condition above
*
*/
-// When changing these values, the COMPILE_TIME_ASSERT at the end of this
-// file need to be updated.
-const unsigned int NUM_LAYERS_MAX = 31;
-const unsigned int NUM_BUFFER_MAX = 4;
-const unsigned int NUM_DISPLAY_MAX = 4;
-
// ----------------------------------------------------------------------------
class Region;
@@ -69,7 +54,11 @@ class SharedClient;
// ----------------------------------------------------------------------------
-// should be 128 bytes (32 longs)
+// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
+// 4 * (11 + 7 + (1 + 2*7)*16) * 31
+// 1032 * 31
+// = ~27 KiB (31992)
+
class SharedBufferStack
{
friend class SharedClient;
@@ -78,21 +67,38 @@ class SharedBufferStack
friend class SharedBufferServer;
public:
- struct FlatRegion { // 12 bytes
- static const unsigned int NUM_RECT_MAX = 1;
- uint32_t count;
- uint16_t rects[4*NUM_RECT_MAX];
- };
-
+ // When changing these values, the COMPILE_TIME_ASSERT at the end of this
+ // file need to be updated.
+ static const unsigned int NUM_LAYERS_MAX = 31;
+ static const unsigned int NUM_BUFFER_MAX = 16;
+ static const unsigned int NUM_BUFFER_MIN = 2;
+ static const unsigned int NUM_DISPLAY_MAX = 4;
+
struct Statistics { // 4 longs
typedef int32_t usecs_t;
usecs_t totalTime;
usecs_t reserved[3];
};
+
+ struct SmallRect {
+ uint16_t l, t, r, b;
+ };
+
+ struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
+ static const unsigned int NUM_RECT_MAX = 6;
+ uint32_t count;
+ SmallRect rects[NUM_RECT_MAX];
+ };
+
+ struct BufferData {
+ FlatRegion dirtyRegion;
+ SmallRect crop;
+ };
SharedBufferStack();
void init(int32_t identity);
status_t setDirtyRegion(int buffer, const Region& reg);
+ status_t setCrop(int buffer, const Rect& reg);
Region getDirtyRegion(int buffer) const;
// these attributes are part of the conditions/updates
@@ -104,24 +110,25 @@ public:
// not part of the conditions
volatile int32_t reallocMask;
+ volatile int8_t index[NUM_BUFFER_MAX];
int32_t identity; // surface's identity (const)
- int32_t reserved32[9];
+ int32_t token; // surface's token (for debugging)
+ int32_t reserved32[1];
Statistics stats;
- FlatRegion dirtyRegion[NUM_BUFFER_MAX]; // 12*4=48 bytes
+ int32_t reserved;
+ BufferData buffers[NUM_BUFFER_MAX]; // 960 bytes
};
// ----------------------------------------------------------------------------
-// 4 KB max
+// 32 KB max
class SharedClient
{
public:
SharedClient();
~SharedClient();
-
status_t validate(size_t token) const;
- uint32_t getIdentity(size_t token) const;
private:
friend class SharedBufferBase;
@@ -131,7 +138,7 @@ private:
// FIXME: this should be replaced by a lock-less primitive
Mutex lock;
Condition cv;
- SharedBufferStack surfaces[ NUM_LAYERS_MAX ];
+ SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ];
};
// ============================================================================
@@ -139,18 +146,17 @@ private:
class SharedBufferBase
{
public:
- SharedBufferBase(SharedClient* sharedClient, int surface, int num,
+ SharedBufferBase(SharedClient* sharedClient, int surface,
int32_t identity);
~SharedBufferBase();
- uint32_t getIdentity();
status_t getStatus() const;
+ int32_t getIdentity() const;
size_t getFrontBuffer() const;
String8 dump(char const* prefix) const;
protected:
SharedClient* const mSharedClient;
SharedBufferStack* const mSharedStack;
- const int mNumBuffers;
const int mIdentity;
friend struct Update;
@@ -160,61 +166,22 @@ protected:
SharedBufferStack& stack;
inline ConditionBase(SharedBufferBase* sbc)
: stack(*sbc->mSharedStack) { }
+ virtual ~ConditionBase() { };
+ virtual bool operator()() const = 0;
+ virtual const char* name() const = 0;
};
+ status_t waitForCondition(const ConditionBase& condition);
struct UpdateBase {
SharedBufferStack& stack;
inline UpdateBase(SharedBufferBase* sbb)
: stack(*sbb->mSharedStack) { }
};
-
- template <typename T>
- status_t waitForCondition(T condition);
-
template <typename T>
status_t updateCondition(T update);
};
template <typename T>
-status_t SharedBufferBase::waitForCondition(T condition)
-{
- const SharedBufferStack& stack( *mSharedStack );
- SharedClient& client( *mSharedClient );
- const nsecs_t TIMEOUT = s2ns(1);
- Mutex::Autolock _l(client.lock);
- while ((condition()==false) &&
- (stack.identity == mIdentity) &&
- (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." , T::name(),
- stack.identity);
- break;
- } else {
- LOGW("waitForCondition(%s) timed out "
- "(identity=%d, status=%d). "
- "CPU may be pegged. trying again.", T::name(),
- stack.identity, stack.status);
- }
- } else {
- LOGE("waitForCondition(%s) error (%s) ",
- T::name(), strerror(-err));
- return err;
- }
- }
- }
- return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;
-}
-
-
-template <typename T>
status_t SharedBufferBase::updateCondition(T update) {
SharedClient& client( *mSharedClient );
Mutex::Autolock _l(client.lock);
@@ -238,13 +205,21 @@ public:
status_t queue(int buf);
bool needNewBuffer(int buffer) const;
status_t setDirtyRegion(int buffer, const Region& reg);
+ status_t setCrop(int buffer, const Rect& reg);
+
+ class SetBufferCountCallback {
+ friend class SharedBufferClient;
+ virtual status_t operator()(int bufferCount) const = 0;
+ protected:
+ virtual ~SetBufferCountCallback() { }
+ };
+ status_t setBufferCount(int bufferCount, const SetBufferCountCallback& ipc);
+
private:
friend struct Condition;
friend struct DequeueCondition;
friend struct LockCondition;
-
- int32_t computeTail() const;
struct QueueUpdate : public UpdateBase {
inline QueueUpdate(SharedBufferBase* sbb);
@@ -260,25 +235,34 @@ private:
struct DequeueCondition : public ConditionBase {
inline DequeueCondition(SharedBufferClient* sbc);
- inline bool operator()();
- static inline const char* name() { return "DequeueCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "DequeueCondition"; }
};
struct LockCondition : public ConditionBase {
int buf;
inline LockCondition(SharedBufferClient* sbc, int buf);
- inline bool operator()();
- static inline const char* name() { return "LockCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "LockCondition"; }
};
+ int32_t computeTail() const;
+
+ mutable RWLock mLock;
+ int mNumBuffers;
+
int32_t tail;
+ int32_t undoDequeueTail;
+ int32_t queued_head;
// statistics...
- nsecs_t mDequeueTime[NUM_BUFFER_MAX];
+ nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX];
};
// ----------------------------------------------------------------------------
-class SharedBufferServer : public SharedBufferBase
+class SharedBufferServer
+ : public SharedBufferBase,
+ public LightRefBase<SharedBufferServer>
{
public:
SharedBufferServer(SharedClient* sharedClient, int surface, int num,
@@ -287,16 +271,73 @@ public:
ssize_t retireAndLock();
status_t unlock(int buffer);
void setStatus(status_t status);
- status_t reallocate();
- status_t assertReallocate(int buffer);
+ status_t reallocateAll();
+ status_t reallocateAllExcept(int buffer);
int32_t getQueuedCount() const;
-
Region getDirtyRegion(int buffer) const;
+ status_t resize(int newNumBuffers);
+
SharedBufferStack::Statistics getStats() const;
private:
+ friend class LightRefBase<SharedBufferServer>;
+ ~SharedBufferServer();
+
+ /*
+ * BufferList is basically a fixed-capacity sorted-vector of
+ * unsigned 5-bits ints using a 32-bits int as storage.
+ * it has efficient iterators to find items in the list and not in the list.
+ */
+ class BufferList {
+ size_t mCapacity;
+ uint32_t mList;
+ public:
+ BufferList(size_t c = SharedBufferStack::NUM_BUFFER_MAX)
+ : mCapacity(c), mList(0) { }
+ status_t add(int value);
+ status_t remove(int value);
+ uint32_t getMask() const { return mList; }
+
+ class const_iterator {
+ friend class BufferList;
+ uint32_t mask, curr;
+ const_iterator(uint32_t mask) :
+ mask(mask), curr(__builtin_clz(mask)) {
+ }
+ public:
+ inline bool operator == (const const_iterator& rhs) const {
+ return mask == rhs.mask;
+ }
+ inline bool operator != (const const_iterator& rhs) const {
+ return mask != rhs.mask;
+ }
+ inline int operator *() const { return curr; }
+ inline const const_iterator& operator ++() {
+ mask &= ~(1<<(31-curr));
+ curr = __builtin_clz(mask);
+ return *this;
+ }
+ };
+
+ inline const_iterator begin() const {
+ return const_iterator(mList);
+ }
+ inline const_iterator end() const {
+ return const_iterator(0);
+ }
+ inline const_iterator free_begin() const {
+ uint32_t mask = (1 << (32-mCapacity)) - 1;
+ return const_iterator( ~(mList | mask) );
+ }
+ };
+
+ // this protects mNumBuffers and mBufferList
+ mutable RWLock mLock;
+ int mNumBuffers;
+ BufferList mBufferList;
+
struct UnlockUpdate : public UpdateBase {
const int lockedBuffer;
inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer);
@@ -314,13 +355,6 @@ private:
inline StatusUpdate(SharedBufferBase* sbb, status_t status);
inline ssize_t operator()();
};
-
- struct ReallocateCondition : public ConditionBase {
- int buf;
- inline ReallocateCondition(SharedBufferBase* sbb, int buf);
- inline bool operator()();
- static inline const char* name() { return "ReallocateCondition"; }
- };
};
// ===========================================================================
@@ -344,13 +378,12 @@ struct surface_flinger_cblk_t // 4KB max
uint8_t connected;
uint8_t reserved[3];
uint32_t pad[7];
- display_cblk_t displays[NUM_DISPLAY_MAX];
+ display_cblk_t displays[SharedBufferStack::NUM_DISPLAY_MAX];
};
// ---------------------------------------------------------------------------
-COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 4096)
-COMPILE_TIME_ASSERT(sizeof(SharedBufferStack) == 128)
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768)
COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
// ---------------------------------------------------------------------------
diff --git a/include/surfaceflinger/ISurface.h b/include/surfaceflinger/ISurface.h
index 472f759..ddbe03d 100644
--- a/include/surfaceflinger/ISurface.h
+++ b/include/surfaceflinger/ISurface.h
@@ -47,13 +47,30 @@ protected:
POST_BUFFER, // one-way transaction
CREATE_OVERLAY,
REQUEST_BUFFER,
+ SET_BUFFER_COUNT,
};
public:
DECLARE_META_INTERFACE(Surface);
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) = 0;
+ /*
+ * requests a new buffer for the given index. If w, h, or format are
+ * null the buffer is created with the parameters assigned to the
+ * surface it is bound to. Otherwise the buffer's parameters are
+ * set to those specified.
+ */
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0;
+
+ /*
+ * sets the number of buffers dequeuable for this surface.
+ */
+ virtual status_t setBufferCount(int bufferCount) = 0;
+ // ------------------------------------------------------------------------
+ // Deprecated...
+ // ------------------------------------------------------------------------
+
class BufferHeap {
public:
enum {
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index d1e7785..dd44aa5 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -27,7 +27,7 @@
#include <ui/PixelFormat.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -85,8 +85,11 @@ public:
/* create connection with surface flinger, requires
* ACCESS_SURFACE_FLINGER permission
*/
+ virtual sp<ISurfaceComposerClient> createConnection() = 0;
- virtual sp<ISurfaceFlingerClient> createConnection() = 0;
+ /* create a client connection with surface flinger
+ */
+ virtual sp<ISurfaceComposerClient> createClientConnection() = 0;
/* retrieve the control block */
virtual sp<IMemoryHeap> getCblk() const = 0;
@@ -123,6 +126,7 @@ public:
// Java by ActivityManagerService.
BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
CREATE_CONNECTION,
+ CREATE_CLIENT_CONNECTION,
GET_CBLK,
OPEN_GLOBAL_TRANSACTION,
CLOSE_GLOBAL_TRANSACTION,
diff --git a/include/surfaceflinger/ISurfaceFlingerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h
index d257645..a1e9e04 100644
--- a/include/surfaceflinger/ISurfaceFlingerClient.h
+++ b/include/surfaceflinger/ISurfaceComposerClient.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_ISURFACE_FLINGER_CLIENT_H
-#define ANDROID_SF_ISURFACE_FLINGER_CLIENT_H
+#ifndef ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H
+#define ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H
#include <stdint.h>
#include <sys/types.h>
@@ -26,7 +26,7 @@
#include <binder/IInterface.h>
#include <ui/PixelFormat.h>
-
+
#include <surfaceflinger/ISurface.h>
namespace android {
@@ -42,10 +42,10 @@ typedef int32_t DisplayID;
class layer_state_t;
-class ISurfaceFlingerClient : public IInterface
+class ISurfaceComposerClient : public IInterface
{
-public:
- DECLARE_META_INTERFACE(SurfaceFlingerClient);
+public:
+ DECLARE_META_INTERFACE(SurfaceComposerClient);
struct surface_data_t {
int32_t token;
@@ -56,26 +56,36 @@ public:
status_t readFromParcel(const Parcel& parcel);
status_t writeToParcel(Parcel* parcel) const;
};
-
+
virtual sp<IMemoryHeap> getControlBlock() const = 0;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const = 0;
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
virtual sp<ISurface> createSurface( surface_data_t* data,
- int pid,
+ int pid,
const String8& name,
DisplayID display,
uint32_t w,
uint32_t h,
PixelFormat format,
uint32_t flags) = 0;
-
+
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
virtual status_t destroySurface(SurfaceID sid) = 0;
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
virtual status_t setState(int32_t count, const layer_state_t* states) = 0;
};
// ----------------------------------------------------------------------------
-class BnSurfaceFlingerClient : public BnInterface<ISurfaceFlingerClient>
+class BnSurfaceComposerClient : public BnInterface<ISurfaceComposerClient>
{
public:
virtual status_t onTransact( uint32_t code,
@@ -88,4 +98,4 @@ public:
}; // namespace android
-#endif // ANDROID_SF_ISURFACE_FLINGER_CLIENT_H
+#endif // ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 0279d84..294c867 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
@@ -28,12 +29,15 @@
#include <ui/egl/android_natives.h>
#include <surfaceflinger/ISurface.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
+
+#define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface"
namespace android {
// ---------------------------------------------------------------------------
+class GraphicBuffer;
class GraphicBufferMapper;
class IOMX;
class Rect;
@@ -41,6 +45,7 @@ class Surface;
class SurfaceComposerClient;
class SharedClient;
class SharedBufferClient;
+class SurfaceClient;
// ---------------------------------------------------------------------------
@@ -56,7 +61,6 @@ public:
static bool isSameSurface(
const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
- SurfaceID ID() const { return mToken; }
uint32_t getFlags() const { return mFlags; }
uint32_t getIdentity() const { return mIdentity; }
@@ -104,7 +108,7 @@ private:
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);
~SurfaceControl();
@@ -128,7 +132,7 @@ private:
// ---------------------------------------------------------------------------
class Surface
- : public EGLNativeBase<android_native_window_t, Surface, RefBase>
+ : public EGLNativeBase<ANativeWindow, Surface, RefBase>
{
public:
struct SurfaceInfo {
@@ -141,17 +145,16 @@ public:
uint32_t reserved[2];
};
- Surface(const Parcel& data);
+ static status_t writeToParcel(
+ const sp<Surface>& control, Parcel* parcel);
+
+ static sp<Surface> readFromParcel(const Parcel& data);
static bool isValid(const sp<Surface>& surface) {
return (surface != 0) && surface->isValid();
}
- static bool isSameSurface(
- const sp<Surface>& lhs, const sp<Surface>& rhs);
-
bool isValid();
- SurfaceID ID() const { return mToken; }
uint32_t getFlags() const { return mFlags; }
uint32_t getIdentity() const { return mIdentity; }
@@ -163,44 +166,43 @@ public:
// setSwapRectangle() is intended to be used by GL ES clients
void setSwapRectangle(const Rect& r);
-private:
- // can't be copied
- Surface& operator = (Surface& rhs);
- Surface(const Surface& rhs);
-
- Surface(const sp<SurfaceControl>& control);
- void init();
- ~Surface();
-
- friend class SurfaceComposerClient;
- friend class SurfaceControl;
-
+private:
+ /*
+ * Android frameworks friends
+ * (eventually this should go away and be replaced by proper APIs)
+ */
// camera and camcorder need access to the ISurface binder interface for preview
friend class Camera;
friend class MediaRecorder;
- // mediaplayer needs access to ISurface for display
+ // MediaPlayer needs access to ISurface for display
friend class MediaPlayer;
friend class IOMX;
// this is just to be able to write some unit tests
friend class Test;
- sp<SurfaceComposerClient> getClient() const;
- sp<ISurface> getISurface() const;
+private:
+ friend class SurfaceComposerClient;
+ friend class SurfaceControl;
- status_t getBufferLocked(int index, int usage);
-
- status_t validate() const;
+ // can't be copied
+ Surface& operator = (Surface& rhs);
+ Surface(const Surface& rhs);
- inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
- inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
-
- static int setSwapInterval(android_native_window_t* window, int interval);
- static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
- static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
- static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
- static int query(android_native_window_t* window, int what, int* value);
- static int perform(android_native_window_t* window, int operation, ...);
+ Surface(const sp<SurfaceControl>& control);
+ Surface(const Parcel& data, const sp<IBinder>& ref);
+ ~Surface();
+
+
+ /*
+ * ANativeWindow hooks
+ */
+ static int setSwapInterval(ANativeWindow* window, int interval);
+ static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+ static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+ static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+ static int query(ANativeWindow* window, int what, int* value);
+ static int perform(ANativeWindow* window, int operation, ...);
int dequeueBuffer(android_native_buffer_t** buffer);
int lockBuffer(android_native_buffer_t* buffer);
@@ -208,44 +210,88 @@ private:
int query(int what, int* value);
int perform(int operation, va_list args);
- status_t dequeueBuffer(sp<GraphicBuffer>* buffer);
-
void dispatch_setUsage(va_list args);
int dispatch_connect(va_list args);
int dispatch_disconnect(va_list args);
+ int dispatch_crop(va_list args);
+ int dispatch_set_buffer_count(va_list args);
+ int dispatch_set_buffers_geometry(va_list args);
void setUsage(uint32_t reqUsage);
int connect(int api);
int disconnect(int api);
+ int crop(Rect const* rect);
+ int setBufferCount(int bufferCount);
+ int setBuffersGeometry(int w, int h, int format);
+
+ /*
+ * private stuff...
+ */
+ void init();
+ status_t validate() const;
+ sp<ISurface> getISurface() const;
+
+ inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
+ inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
+
+ status_t getBufferLocked(int index,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ int getBufferIndex(const sp<GraphicBuffer>& buffer) const;
- uint32_t getUsage() const;
- int getConnectedApi() const;
+ int getConnectedApi() const;
+ bool needNewBuffer(int bufIdx,
+ uint32_t *pWidth, uint32_t *pHeight,
+ uint32_t *pFormat, uint32_t *pUsage) const;
+
+ static void cleanCachedSurfaces();
+
+ class BufferInfo {
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mFormat;
+ uint32_t mUsage;
+ mutable uint32_t mDirty;
+ enum {
+ GEOMETRY = 0x01
+ };
+ public:
+ BufferInfo();
+ void set(uint32_t w, uint32_t h, uint32_t format);
+ void set(uint32_t usage);
+ void get(uint32_t *pWidth, uint32_t *pHeight,
+ uint32_t *pFormat, uint32_t *pUsage) const;
+ bool validateBuffer(const sp<GraphicBuffer>& buffer) const;
+ };
+
// constants
- sp<SurfaceComposerClient> mClient;
+ GraphicBufferMapper& mBufferMapper;
+ SurfaceClient& mClient;
+ SharedBufferClient* mSharedBufferClient;
+ status_t mInitCheck;
sp<ISurface> mSurface;
- SurfaceID mToken;
uint32_t mIdentity;
PixelFormat mFormat;
uint32_t mFlags;
- GraphicBufferMapper& mBufferMapper;
- SharedBufferClient* mSharedBufferClient;
// protected by mSurfaceLock
Rect mSwapRectangle;
- uint32_t mUsage;
int mConnected;
+ Rect mNextBufferCrop;
+ BufferInfo mBufferInfo;
// protected by mSurfaceLock. These are also used from lock/unlock
// but in that case, they must be called form the same thread.
- sp<GraphicBuffer> mBuffers[2];
mutable Region mDirtyRegion;
// must be used from the lock/unlock thread
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
mutable Region mOldDirtyRegion;
- bool mNeedFullUpdate;
+ bool mReserved;
+
+ // only used from dequeueBuffer()
+ Vector< sp<GraphicBuffer> > mBuffers;
// query() must be called from dequeueBuffer() thread
uint32_t mWidth;
@@ -254,6 +300,10 @@ private:
// Inherently thread-safe
mutable Mutex mSurfaceLock;
mutable Mutex mApiLock;
+
+ // A cache of Surface objects that have been deserialized into this process.
+ static Mutex sCachedSurfacesLock;
+ static DefaultKeyedVector<wp<IBinder>, wp<Surface> > sCachedSurfaces;
};
}; // namespace android
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 9d0f0cb..8773d71 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -22,8 +22,9 @@
#include <binder/IBinder.h>
-#include <utils/SortedVector.h>
#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+#include <utils/SortedVector.h>
#include <utils/threads.h>
#include <ui/PixelFormat.h>
@@ -39,8 +40,26 @@ class Region;
class SharedClient;
class ISurfaceComposer;
class DisplayInfo;
+class surface_flinger_cblk_t;
+
+// ---------------------------------------------------------------------------
+
+class ComposerService : public Singleton<ComposerService>
+{
+ // these are constants
+ sp<ISurfaceComposer> mComposerService;
+ sp<IMemoryHeap> mServerCblkMemory;
+ surface_flinger_cblk_t volatile* mServerCblk;
+ ComposerService();
+ friend class Singleton<ComposerService>;
+public:
+ static sp<ISurfaceComposer> getComposerService();
+ static surface_flinger_cblk_t const volatile * getControlBlock();
+};
-class SurfaceComposerClient : virtual public RefBase
+// ---------------------------------------------------------------------------
+
+class SurfaceComposerClient : public RefBase
{
public:
SurfaceComposerClient();
@@ -52,10 +71,6 @@ public:
// Return the connection of this client
sp<IBinder> connection() const;
- // Retrieve a client for an existing connection.
- static sp<SurfaceComposerClient>
- clientForConnection(const sp<IBinder>& conn);
-
// Forcibly remove connection before all references have gone away.
void dispose();
@@ -123,13 +138,6 @@ public:
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
void* cookie = NULL, uint32_t flags = 0);
-private:
- friend class Surface;
- friend class SurfaceControl;
-
- SurfaceComposerClient(const sp<ISurfaceComposer>& sm,
- const sp<IBinder>& conn);
-
status_t hide(SurfaceID id);
status_t show(SurfaceID id, int32_t layer = -1);
status_t freeze(SurfaceID id);
@@ -142,32 +150,26 @@ private:
status_t setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy);
status_t setPosition(SurfaceID id, int32_t x, int32_t y);
status_t setSize(SurfaceID id, uint32_t w, uint32_t h);
-
- void signalServer();
-
status_t destroySurface(SurfaceID sid);
- void _init(const sp<ISurfaceComposer>& sm,
- const sp<ISurfaceFlingerClient>& conn);
-
- inline layer_state_t* _get_state_l(SurfaceID id);
- layer_state_t* _lockLayerState(SurfaceID id);
- inline void _unlockLayerState();
+private:
+ virtual void onFirstRef();
+ inline layer_state_t* get_state_l(SurfaceID id);
+ layer_state_t* lockLayerState(SurfaceID id);
+ inline void unlockLayerState();
mutable Mutex mLock;
- layer_state_t* mPrebuiltLayerState;
SortedVector<layer_state_t> mStates;
int32_t mTransactionOpen;
+ layer_state_t* mPrebuiltLayerState;
// these don't need to be protected because they never change
// after assignment
status_t mStatus;
- SharedClient* mControl;
- sp<IMemoryHeap> mControlMemory;
- sp<ISurfaceFlingerClient> mClient;
- sp<ISurfaceComposer> mSignalServer;
+ sp<ISurfaceComposerClient> mClient;
};
+// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 3b18c77..dab35b3 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -18,6 +18,7 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
+#include <android/input.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Log.h>
@@ -27,6 +28,31 @@
#include <linux/input.h>
+/* These constants are not defined in linux/input.h but they are part of the multitouch
+ * input protocol. */
+
+#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
+#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
+#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
+#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
+#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
+#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
+#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
+#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device (finger, pen, ...) */
+#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
+#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
+#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
+
+#define MT_TOOL_FINGER 0 /* Identifies a finger */
+#define MT_TOOL_PEN 1 /* Identifies a pen */
+
+#define SYN_MT_REPORT 2
+
+/* Convenience constants. */
+
+#define BTN_FIRST 0x100 // first button scancode
+#define BTN_LAST 0x15f // last button scancode
+
struct pollfd;
namespace android {
@@ -34,73 +60,154 @@ namespace android {
class KeyLayoutMap;
/*
- * Grand Central Station for events. With a single call to waitEvent()
- * you can wait for:
- * - input events from the keypad of a real device
- * - input events and meta-events (e.g. "quit") from the simulator
- * - synthetic events from the runtime (e.g. "URL fetch completed")
- * - real or forged "vsync" events
+ * A raw event as retrieved from the EventHub.
+ */
+struct RawEvent {
+ nsecs_t when;
+ int32_t deviceId;
+ int32_t type;
+ int32_t scanCode;
+ int32_t keyCode;
+ int32_t value;
+ uint32_t flags;
+};
+
+/* Describes an absolute axis. */
+struct RawAbsoluteAxisInfo {
+ bool valid; // true if the information is valid, false otherwise
+
+ int32_t minValue; // minimum value
+ int32_t maxValue; // maximum value
+ int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
+ int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
+
+ inline int32_t getRange() { return maxValue - minValue; }
+};
+
+/*
+ * Input device classes.
+ */
+enum {
+ /* The input device is a keyboard. */
+ INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
+
+ /* The input device is an alpha-numeric keyboard (not just a dial pad). */
+ INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
+
+ /* The input device is a touchscreen (either single-touch or multi-touch). */
+ INPUT_DEVICE_CLASS_TOUCHSCREEN = 0x00000004,
+
+ /* The input device is a trackball. */
+ INPUT_DEVICE_CLASS_TRACKBALL = 0x00000008,
+
+ /* The input device is a multi-touch touchscreen. */
+ INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010,
+
+ /* The input device is a directional pad. */
+ INPUT_DEVICE_CLASS_DPAD = 0x00000020,
+
+ /* The input device is a gamepad (implies keyboard). */
+ INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
+
+ /* The input device has switches. */
+ INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
+};
+
+/*
+ * Grand Central Station for events.
*
- * Do not instantiate this class. Instead, call startUp().
+ * The event hub aggregates input events received across all known input
+ * devices on the system, including devices that may be emulated by the simulator
+ * environment. In addition, the event hub generates fake input events to indicate
+ * when devices are added or removed.
+ *
+ * The event hub provies a stream of input events (via the getEvent function).
+ * It also supports querying the current actual state of input devices such as identifying
+ * which keys are currently down. Finally, the event hub keeps track of the capabilities of
+ * individual input devices, such as their class and the set of key codes that they support.
*/
-class EventHub : public RefBase
+class EventHubInterface : public virtual RefBase {
+protected:
+ EventHubInterface() { }
+ virtual ~EventHubInterface() { }
+
+public:
+ // Synthetic raw event type codes produced when devices are added or removed.
+ enum {
+ DEVICE_ADDED = 0x10000000,
+ DEVICE_REMOVED = 0x20000000
+ };
+
+ virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
+
+ virtual String8 getDeviceName(int32_t deviceId) const = 0;
+
+ virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const = 0;
+
+ virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
+ int32_t* outKeycode, uint32_t* outFlags) const = 0;
+
+ // exclude a particular device from opening
+ // this can be used to ignore input devices for sensors
+ virtual void addExcludedDevice(const char* deviceName) = 0;
+
+ /*
+ * Wait for the next event to become available and return it.
+ * After returning, the EventHub holds onto a wake lock until the next call to getEvent.
+ * This ensures that the device will not go to sleep while the event is being processed.
+ * If the device needs to remain awake longer than that, then the caller is responsible
+ * for taking care of it (say, by poking the power manager user activity timer).
+ */
+ virtual bool getEvent(RawEvent* outEvent) = 0;
+
+ /*
+ * Query current input state.
+ */
+ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
+
+ /*
+ * Examine key input devices for specific framework keycode support
+ */
+ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const = 0;
+};
+
+class EventHub : public EventHubInterface
{
public:
EventHub();
-
+
status_t errorCheck() const;
+
+ virtual uint32_t getDeviceClasses(int32_t deviceId) const;
- // bit fields for classes of devices.
- enum {
- CLASS_KEYBOARD = 0x00000001,
- CLASS_ALPHAKEY = 0x00000002,
- CLASS_TOUCHSCREEN = 0x00000004,
- CLASS_TRACKBALL = 0x00000008,
- CLASS_TOUCHSCREEN_MT= 0x00000010,
- CLASS_DPAD = 0x00000020
- };
- uint32_t getDeviceClasses(int32_t deviceId) const;
-
- String8 getDeviceName(int32_t deviceId) const;
-
- int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
- int* outMaxValue, int* outFlat, int* outFuzz) const;
-
- int getSwitchState(int sw) const;
- int getSwitchState(int32_t deviceId, int sw) const;
-
- int getScancodeState(int key) const;
- int getScancodeState(int32_t deviceId, int key) const;
-
- int getKeycodeState(int key) const;
- int getKeycodeState(int32_t deviceId, int key) const;
+ virtual String8 getDeviceName(int32_t deviceId) const;
- status_t scancodeToKeycode(int32_t deviceId, int scancode,
+ virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const;
+
+ virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const;
- // exclude a particular device from opening
- // this can be used to ignore input devices for sensors
- void addExcludedDevice(const char* deviceName);
+ virtual void addExcludedDevice(const char* deviceName);
- // special type codes when devices are added/removed.
- enum {
- DEVICE_ADDED = 0x10000000,
- DEVICE_REMOVED = 0x20000000
- };
-
- // examine key input devices for specific framework keycode support
- bool hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags);
+ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
+ virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
+ virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
- virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
- int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
- int32_t* outValue, nsecs_t* outWhen);
+ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const;
+
+ virtual bool getEvent(RawEvent* outEvent);
protected:
virtual ~EventHub();
private:
bool openPlatformInput(void);
- int32_t convertDeviceKey_TI_P2(int code);
int open_device(const char *device);
int close_device(const char *device);
@@ -126,6 +233,12 @@ private:
device_t* getDevice(int32_t deviceId) const;
bool hasKeycode(device_t* device, int keycode) const;
+ int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
+ int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
+ int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
+ bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const;
+
// Protect all internal state.
mutable Mutex mLock;
@@ -151,7 +264,7 @@ private:
// device ids that report particular switches.
#ifdef EV_SW
- int32_t mSwitches[SW_MAX+1];
+ int32_t mSwitches[SW_MAX + 1];
#endif
};
diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h
index 8ea3ab9..0f4594f 100644
--- a/include/ui/FramebufferNativeWindow.h
+++ b/include/ui/FramebufferNativeWindow.h
@@ -43,7 +43,7 @@ class NativeBuffer;
class FramebufferNativeWindow
: public EGLNativeBase<
- android_native_window_t,
+ ANativeWindow,
FramebufferNativeWindow,
LightRefBase<FramebufferNativeWindow> >
{
@@ -59,12 +59,12 @@ public:
private:
friend class LightRefBase<FramebufferNativeWindow>;
~FramebufferNativeWindow(); // this class cannot be overloaded
- static int setSwapInterval(android_native_window_t* window, int interval);
- static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
- static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
- static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
- static int query(android_native_window_t* window, int what, int* value);
- static int perform(android_native_window_t* window, int operation, ...);
+ static int setSwapInterval(ANativeWindow* window, int interval);
+ static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+ static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+ static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
+ static int query(ANativeWindow* window, int what, int* value);
+ static int perform(ANativeWindow* window, int operation, ...);
framebuffer_device_t* fbDev;
alloc_device_t* grDev;
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index e72b6b3..a3e85a9 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -93,10 +93,8 @@ public:
void setIndex(int index);
int getIndex() const;
- void setVerticalStride(uint32_t vstride);
- uint32_t getVerticalStride() const;
-protected:
+private:
virtual ~GraphicBuffer();
enum {
@@ -105,8 +103,12 @@ protected:
ownData = 2,
};
- inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
- inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
+ inline const GraphicBufferMapper& getBufferMapper() const {
+ return mBufferMapper;
+ }
+ inline GraphicBufferMapper& getBufferMapper() {
+ return mBufferMapper;
+ }
uint8_t mOwner;
private:
@@ -134,7 +136,6 @@ private:
GraphicBufferMapper& mBufferMapper;
ssize_t mInitCheck;
- uint32_t mVStride;
int mIndex;
};
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 741d763..54b8236 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -73,9 +73,9 @@ private:
struct alloc_rec_t {
uint32_t w;
uint32_t h;
+ uint32_t s;
PixelFormat format;
uint32_t usage;
- void* vaddr;
size_t size;
};
diff --git a/include/ui/Input.h b/include/ui/Input.h
new file mode 100644
index 0000000..2385973
--- /dev/null
+++ b/include/ui/Input.h
@@ -0,0 +1,491 @@
+/*
+ * 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 _UI_INPUT_H
+#define _UI_INPUT_H
+
+/**
+ * Native input event structures.
+ */
+
+#include <android/input.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+/*
+ * Additional private constants not defined in ndk/ui/input.h.
+ */
+enum {
+ /*
+ * Private control to determine when an app is tracking a key sequence.
+ */
+ AKEY_EVENT_FLAG_START_TRACKING = 0x40000000
+};
+
+/*
+ * Maximum number of pointers supported per motion event.
+ */
+#define MAX_POINTERS 10
+
+/*
+ * Declare a concrete type for the NDK's input event forward declaration.
+ */
+struct AInputEvent {
+ virtual ~AInputEvent() { }
+};
+
+/*
+ * Declare a concrete type for the NDK's input device forward declaration.
+ */
+struct AInputDevice {
+ virtual ~AInputDevice() { }
+};
+
+
+namespace android {
+
+/*
+ * Flags that flow alongside events in the input dispatch system to help with certain
+ * policy decisions such as waking from device sleep.
+ */
+enum {
+ /* These flags originate in RawEvents and are generally set in the key map. */
+
+ POLICY_FLAG_WAKE = 0x00000001,
+ POLICY_FLAG_WAKE_DROPPED = 0x00000002,
+ POLICY_FLAG_SHIFT = 0x00000004,
+ POLICY_FLAG_CAPS_LOCK = 0x00000008,
+ POLICY_FLAG_ALT = 0x00000010,
+ POLICY_FLAG_ALT_GR = 0x00000020,
+ POLICY_FLAG_MENU = 0x00000040,
+ POLICY_FLAG_LAUNCHER = 0x00000080,
+
+ POLICY_FLAG_RAW_MASK = 0x0000ffff,
+
+ /* These flags are set by the input reader policy as it intercepts each event. */
+
+ // Indicates that the screen was off when the event was received and the event
+ // should wake the device.
+ POLICY_FLAG_WOKE_HERE = 0x10000000,
+
+ // Indicates that the screen was dim when the event was received and the event
+ // should brighten the device.
+ POLICY_FLAG_BRIGHT_HERE = 0x20000000,
+};
+
+/*
+ * Describes the basic configuration of input devices that are present.
+ */
+struct InputConfiguration {
+ enum {
+ TOUCHSCREEN_UNDEFINED = 0,
+ TOUCHSCREEN_NOTOUCH = 1,
+ TOUCHSCREEN_STYLUS = 2,
+ TOUCHSCREEN_FINGER = 3
+ };
+
+ enum {
+ KEYBOARD_UNDEFINED = 0,
+ KEYBOARD_NOKEYS = 1,
+ KEYBOARD_QWERTY = 2,
+ KEYBOARD_12KEY = 3
+ };
+
+ enum {
+ NAVIGATION_UNDEFINED = 0,
+ NAVIGATION_NONAV = 1,
+ NAVIGATION_DPAD = 2,
+ NAVIGATION_TRACKBALL = 3,
+ NAVIGATION_WHEEL = 4
+ };
+
+ int32_t touchScreen;
+ int32_t keyboard;
+ int32_t navigation;
+};
+
+/*
+ * Pointer coordinate data.
+ */
+struct PointerCoords {
+ float x;
+ float y;
+ float pressure;
+ float size;
+ float touchMajor;
+ float touchMinor;
+ float toolMajor;
+ float toolMinor;
+ float orientation;
+};
+
+/*
+ * Input events.
+ */
+class InputEvent : public AInputEvent {
+public:
+ virtual ~InputEvent() { }
+
+ virtual int32_t getType() const = 0;
+
+ inline int32_t getDeviceId() const { return mDeviceId; }
+
+ inline int32_t getSource() const { return mSource; }
+
+protected:
+ void initialize(int32_t deviceId, int32_t source);
+ void initialize(const InputEvent& from);
+
+private:
+ int32_t mDeviceId;
+ int32_t mSource;
+};
+
+/*
+ * Key events.
+ */
+class KeyEvent : public InputEvent {
+public:
+ virtual ~KeyEvent() { }
+
+ virtual int32_t getType() const { return AINPUT_EVENT_TYPE_KEY; }
+
+ inline int32_t getAction() const { return mAction; }
+
+ inline int32_t getFlags() const { return mFlags; }
+
+ inline int32_t getKeyCode() const { return mKeyCode; }
+
+ inline int32_t getScanCode() const { return mScanCode; }
+
+ inline int32_t getMetaState() const { return mMetaState; }
+
+ inline int32_t getRepeatCount() const { return mRepeatCount; }
+
+ inline nsecs_t getDownTime() const { return mDownTime; }
+
+ inline nsecs_t getEventTime() const { return mEventTime; }
+
+ // Return true if this event may have a default action implementation.
+ static bool hasDefaultAction(int32_t keyCode);
+ bool hasDefaultAction() const;
+
+ // Return true if this event represents a system key.
+ static bool isSystemKey(int32_t keyCode);
+ bool isSystemKey() const;
+
+ void 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);
+ void initialize(const KeyEvent& from);
+
+private:
+ int32_t mAction;
+ int32_t mFlags;
+ int32_t mKeyCode;
+ int32_t mScanCode;
+ int32_t mMetaState;
+ int32_t mRepeatCount;
+ nsecs_t mDownTime;
+ nsecs_t mEventTime;
+};
+
+/*
+ * Motion events.
+ */
+class MotionEvent : public InputEvent {
+public:
+ virtual ~MotionEvent() { }
+
+ virtual int32_t getType() const { return AINPUT_EVENT_TYPE_MOTION; }
+
+ inline int32_t getAction() const { return mAction; }
+
+ inline int32_t getEdgeFlags() const { return mEdgeFlags; }
+
+ inline int32_t getMetaState() const { return mMetaState; }
+
+ inline float getXOffset() const { return mXOffset; }
+
+ inline float getYOffset() const { return mYOffset; }
+
+ inline float getXPrecision() const { return mXPrecision; }
+
+ inline float getYPrecision() const { return mYPrecision; }
+
+ inline nsecs_t getDownTime() const { return mDownTime; }
+
+ inline size_t getPointerCount() const { return mPointerIds.size(); }
+
+ inline int32_t getPointerId(size_t pointerIndex) const { return mPointerIds[pointerIndex]; }
+
+ inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
+
+ inline float getRawX(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).x;
+ }
+
+ inline float getRawY(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).y;
+ }
+
+ inline float getX(size_t pointerIndex) const {
+ return getRawX(pointerIndex) + mXOffset;
+ }
+
+ inline float getY(size_t pointerIndex) const {
+ return getRawY(pointerIndex) + mYOffset;
+ }
+
+ inline float getPressure(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).pressure;
+ }
+
+ inline float getSize(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).size;
+ }
+
+ inline float getTouchMajor(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).touchMajor;
+ }
+
+ inline float getTouchMinor(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).touchMinor;
+ }
+
+ inline float getToolMajor(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).toolMajor;
+ }
+
+ inline float getToolMinor(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).toolMinor;
+ }
+
+ inline float getOrientation(size_t pointerIndex) const {
+ return getCurrentPointerCoords(pointerIndex).orientation;
+ }
+
+ inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
+
+ inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const {
+ return mSampleEventTimes[historicalIndex];
+ }
+
+ inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).x;
+ }
+
+ inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).y;
+ }
+
+ inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset;
+ }
+
+ inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset;
+ }
+
+ inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure;
+ }
+
+ inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).size;
+ }
+
+ inline float getHistoricalTouchMajor(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMajor;
+ }
+
+ inline float getHistoricalTouchMinor(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).touchMinor;
+ }
+
+ inline float getHistoricalToolMajor(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMajor;
+ }
+
+ inline float getHistoricalToolMinor(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).toolMinor;
+ }
+
+ inline float getHistoricalOrientation(size_t pointerIndex, size_t historicalIndex) const {
+ return getHistoricalPointerCoords(pointerIndex, historicalIndex).orientation;
+ }
+
+ void initialize(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ 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);
+
+ void addSample(
+ nsecs_t eventTime,
+ const PointerCoords* pointerCoords);
+
+ void offsetLocation(float xOffset, float yOffset);
+
+ // Low-level accessors.
+ inline const int32_t* getPointerIds() const { return mPointerIds.array(); }
+ inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
+ inline const PointerCoords* getSamplePointerCoords() const {
+ return mSamplePointerCoords.array();
+ }
+
+private:
+ int32_t mAction;
+ int32_t mEdgeFlags;
+ int32_t mMetaState;
+ float mXOffset;
+ float mYOffset;
+ float mXPrecision;
+ float mYPrecision;
+ nsecs_t mDownTime;
+ Vector<int32_t> mPointerIds;
+ Vector<nsecs_t> mSampleEventTimes;
+ Vector<PointerCoords> mSamplePointerCoords;
+
+ inline const PointerCoords& getCurrentPointerCoords(size_t pointerIndex) const {
+ return mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
+ }
+
+ inline const PointerCoords& getHistoricalPointerCoords(
+ size_t pointerIndex, size_t historicalIndex) const {
+ return mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
+ }
+};
+
+/*
+ * Input event factory.
+ */
+class InputEventFactoryInterface {
+protected:
+ virtual ~InputEventFactoryInterface() { }
+
+public:
+ InputEventFactoryInterface() { }
+
+ virtual KeyEvent* createKeyEvent() = 0;
+ virtual MotionEvent* createMotionEvent() = 0;
+};
+
+/*
+ * A simple input event factory implementation that uses a single preallocated instance
+ * of each type of input event that are reused for each request.
+ */
+class PreallocatedInputEventFactory : public InputEventFactoryInterface {
+public:
+ PreallocatedInputEventFactory() { }
+ virtual ~PreallocatedInputEventFactory() { }
+
+ virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
+ virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
+
+private:
+ KeyEvent mKeyEvent;
+ MotionEvent mMotionEvent;
+};
+
+/*
+ * Describes the characteristics and capabilities of an input device.
+ */
+class InputDeviceInfo {
+public:
+ InputDeviceInfo();
+ InputDeviceInfo(const InputDeviceInfo& other);
+ ~InputDeviceInfo();
+
+ struct MotionRange {
+ float min;
+ float max;
+ float flat;
+ float fuzz;
+ };
+
+ void initialize(int32_t id, const String8& name);
+
+ inline int32_t getId() const { return mId; }
+ inline const String8 getName() const { return mName; }
+ inline uint32_t getSources() const { return mSources; }
+
+ const MotionRange* getMotionRange(int32_t rangeType) const;
+
+ void addSource(uint32_t source);
+ void addMotionRange(int32_t rangeType, float min, float max, float flat, float fuzz);
+ void addMotionRange(int32_t rangeType, const MotionRange& range);
+
+ inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
+ inline int32_t getKeyboardType() const { return mKeyboardType; }
+
+private:
+ int32_t mId;
+ String8 mName;
+ uint32_t mSources;
+ int32_t mKeyboardType;
+
+ KeyedVector<int32_t, MotionRange> mMotionRanges;
+};
+
+/*
+ * Provides remote access to information about an input device.
+ *
+ * Note: This is essentially a wrapper for Binder calls into the Window Manager Service.
+ */
+class InputDeviceProxy : public RefBase, public AInputDevice {
+protected:
+ InputDeviceProxy();
+ virtual ~InputDeviceProxy();
+
+public:
+ static void getDeviceIds(Vector<int32_t>& outIds);
+
+ static sp<InputDeviceProxy> getDevice(int32_t id);
+
+ inline const InputDeviceInfo* getInfo() { return & mInfo; }
+
+ // TODO add hasKeys, keymap, etc...
+
+private:
+ InputDeviceInfo mInfo;
+};
+
+
+} // namespace android
+
+#endif // _UI_INPUT_H
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
new file mode 100644
index 0000000..d3495fe
--- /dev/null
+++ b/include/ui/InputDispatcher.h
@@ -0,0 +1,669 @@
+/*
+ * 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 _UI_INPUT_DISPATCHER_H
+#define _UI_INPUT_DISPATCHER_H
+
+#include <ui/Input.h>
+#include <ui/InputTransport.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/PollLoop.h>
+#include <utils/Pool.h>
+
+#include <stddef.h>
+#include <unistd.h>
+
+
+namespace android {
+
+/*
+ * Constants used to report the outcome of input event injection.
+ */
+enum {
+ /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+ INPUT_EVENT_INJECTION_PENDING = -1,
+
+ /* Injection succeeded. */
+ INPUT_EVENT_INJECTION_SUCCEEDED = 0,
+
+ /* Injection failed because the injector did not have permission to inject
+ * into the application with input focus. */
+ INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
+
+ /* Injection failed because there were no available input targets. */
+ INPUT_EVENT_INJECTION_FAILED = 2,
+
+ /* Injection failed due to a timeout. */
+ INPUT_EVENT_INJECTION_TIMED_OUT = 3
+};
+
+/*
+ * Constants used to determine the input event injection synchronization mode.
+ */
+enum {
+ /* Injection is asynchronous and is assumed always to be successful. */
+ INPUT_EVENT_INJECTION_SYNC_NONE = 0,
+
+ /* Waits for previous events to be dispatched so that the input dispatcher can determine
+ * whether input event injection willbe permitted based on the current input focus.
+ * Does not wait for the input event to finish processing. */
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1,
+
+ /* Waits for the input event to be completely processed. */
+ INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2,
+};
+
+
+/*
+ * An input target specifies how an input event is to be dispatched to a particular window
+ * including the window's input channel, control flags, a timeout, and an X / Y offset to
+ * be added to input event coordinates to compensate for the absolute position of the
+ * window area.
+ */
+struct InputTarget {
+ enum {
+ /* This flag indicates that subsequent event delivery should be held until the
+ * current event is delivered to this target or a timeout occurs. */
+ FLAG_SYNC = 0x01,
+
+ /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
+ * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */
+ FLAG_OUTSIDE = 0x02,
+
+ /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
+ * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
+ * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */
+ FLAG_CANCEL = 0x04
+ };
+
+ // The input channel to be targeted.
+ sp<InputChannel> inputChannel;
+
+ // Flags for the input target.
+ int32_t flags;
+
+ // The timeout for event delivery to this target in nanoseconds. Or -1 if none.
+ nsecs_t timeout;
+
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float xOffset, yOffset;
+};
+
+
+/*
+ * Input dispatcher policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI. This interface is also mocked in the unit tests.
+ */
+class InputDispatcherPolicyInterface : public virtual RefBase {
+protected:
+ InputDispatcherPolicyInterface() { }
+ virtual ~InputDispatcherPolicyInterface() { }
+
+public:
+ /* Notifies the system that a configuration change has occurred. */
+ virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+
+ /* Notifies the system that an input channel is unrecoverably broken. */
+ virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
+
+ /* Notifies the system that an input channel is not responding.
+ * Returns true and a new timeout value if the dispatcher should keep waiting.
+ * Otherwise returns false. */
+ virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+ nsecs_t& outNewTimeout) = 0;
+
+ /* Notifies the system that an input channel recovered from ANR. */
+ virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
+
+ /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
+ virtual nsecs_t getKeyRepeatTimeout() = 0;
+
+ /* Waits for key event input targets to become available.
+ * If the event is being injected, injectorPid and injectorUid should specify the
+ * process id and used id of the injecting application, otherwise they should both
+ * be -1.
+ * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
+ virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) = 0;
+
+ /* Waits for motion event targets to become available.
+ * If the event is being injected, injectorPid and injectorUid should specify the
+ * process id and used id of the injecting application, otherwise they should both
+ * be -1.
+ * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
+ virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) = 0;
+};
+
+
+/* Notifies the system about input events generated by the input reader.
+ * The dispatcher is expected to be mostly asynchronous. */
+class InputDispatcherInterface : public virtual RefBase {
+protected:
+ InputDispatcherInterface() { }
+ virtual ~InputDispatcherInterface() { }
+
+public:
+ /* Runs a single iteration of the dispatch loop.
+ * Nominally processes one queued event, a timeout, or a response from an input consumer.
+ *
+ * This method should only be called on the input dispatcher thread.
+ */
+ virtual void dispatchOnce() = 0;
+
+ /* Notifies the dispatcher about new events.
+ *
+ * These methods should only be called on the input reader thread.
+ */
+ virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
+ virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
+ 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) = 0;
+ virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
+ uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
+ float xPrecision, float yPrecision, nsecs_t downTime) = 0;
+
+ /* Injects an input event and optionally waits for sync.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
+ * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual int32_t injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
+
+ /* Preempts input dispatch in progress by making pending synchronous
+ * dispatches asynchronous instead. This method is generally called during a focus
+ * transition from one application to the next so as to enable the new application
+ * to start receiving input as soon as possible without having to wait for the
+ * old application to finish up.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void preemptInputDispatch() = 0;
+
+ /* Registers or unregister input channels that may be used as targets for input events.
+ *
+ * These methods may be called on any thread (usually by the input manager).
+ */
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+};
+
+/* Dispatches events to input targets. Some functions of the input dispatcher, such as
+ * identifying input targets, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ * Because the policy can potentially block or cause re-entrance into the input dispatcher,
+ * the input dispatcher never calls into the policy while holding its internal locks.
+ * The implementation is also carefully designed to recover from scenarios such as an
+ * input channel becoming unregistered while identifying input targets or processing timeouts.
+ *
+ * Methods marked 'Locked' must be called with the lock acquired.
+ *
+ * Methods marked 'LockedInterruptible' must be called with the lock acquired but
+ * may during the course of their execution release the lock, call into the policy, and
+ * then reacquire the lock. The caller is responsible for recovering gracefully.
+ *
+ * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
+ */
+class InputDispatcher : public InputDispatcherInterface {
+protected:
+ virtual ~InputDispatcher();
+
+public:
+ explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+
+ virtual void dispatchOnce();
+
+ virtual void notifyConfigurationChanged(nsecs_t eventTime);
+ virtual void notifyAppSwitchComing(nsecs_t eventTime);
+ 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);
+ virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
+ uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
+ float xPrecision, float yPrecision, nsecs_t downTime);
+
+ virtual int32_t injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
+
+ virtual void preemptInputDispatch();
+
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
+
+private:
+ template <typename T>
+ struct Link {
+ T* next;
+ T* prev;
+ };
+
+ struct EventEntry : Link<EventEntry> {
+ enum {
+ TYPE_SENTINEL,
+ TYPE_CONFIGURATION_CHANGED,
+ TYPE_KEY,
+ TYPE_MOTION
+ };
+
+ int32_t refCount;
+ int32_t type;
+ nsecs_t eventTime;
+
+ int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+ bool injectionIsAsync; // set to true if injection is not waiting for the result
+ int32_t injectorPid; // -1 if not injected
+ int32_t injectorUid; // -1 if not injected
+
+ bool dispatchInProgress; // initially false, set to true while dispatching
+ int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
+
+ inline bool isInjected() { return injectorPid >= 0; }
+ };
+
+ struct ConfigurationChangedEntry : EventEntry {
+ };
+
+ struct KeyEntry : EventEntry {
+ 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;
+ };
+
+ struct MotionSample {
+ MotionSample* next;
+
+ nsecs_t eventTime;
+ PointerCoords pointerCoords[MAX_POINTERS];
+ };
+
+ struct MotionEntry : EventEntry {
+ int32_t deviceId;
+ int32_t source;
+ uint32_t policyFlags;
+ int32_t action;
+ int32_t metaState;
+ int32_t edgeFlags;
+ float xPrecision;
+ float yPrecision;
+ nsecs_t downTime;
+ uint32_t pointerCount;
+ int32_t pointerIds[MAX_POINTERS];
+
+ // Linked list of motion samples associated with this motion event.
+ MotionSample firstSample;
+ MotionSample* lastSample;
+ };
+
+ // Tracks the progress of dispatching a particular event to a particular connection.
+ struct DispatchEntry : Link<DispatchEntry> {
+ EventEntry* eventEntry; // the event to dispatch
+ int32_t targetFlags;
+ float xOffset;
+ float yOffset;
+ nsecs_t timeout;
+
+ // True if dispatch has started.
+ bool inProgress;
+
+ // For motion events:
+ // Pointer to the first motion sample to dispatch in this cycle.
+ // Usually NULL to indicate that the list of motion samples begins at
+ // MotionEntry::firstSample. Otherwise, some samples were dispatched in a previous
+ // cycle and this pointer indicates the location of the first remainining sample
+ // to dispatch during the current cycle.
+ MotionSample* headMotionSample;
+ // Pointer to a motion sample to dispatch in the next cycle if the dispatcher was
+ // unable to send all motion samples during this cycle. On the next cycle,
+ // headMotionSample will be initialized to tailMotionSample and tailMotionSample
+ // will be set to NULL.
+ MotionSample* tailMotionSample;
+
+ inline bool isSyncTarget() {
+ return targetFlags & InputTarget::FLAG_SYNC;
+ }
+ };
+
+ // A command entry captures state and behavior for an action to be performed in the
+ // dispatch loop after the initial processing has taken place. It is essentially
+ // a kind of continuation used to postpone sensitive policy interactions to a point
+ // in the dispatch loop where it is safe to release the lock (generally after finishing
+ // the critical parts of the dispatch cycle).
+ //
+ // The special thing about commands is that they can voluntarily release and reacquire
+ // the dispatcher lock at will. Initially when the command starts running, the
+ // dispatcher lock is held. However, if the command needs to call into the policy to
+ // do some work, it can release the lock, do the work, then reacquire the lock again
+ // before returning.
+ //
+ // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+ // never calls into the policy while holding its lock.
+ //
+ // Commands are implicitly 'LockedInterruptible'.
+ struct CommandEntry;
+ typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
+
+ class Connection;
+ struct CommandEntry : Link<CommandEntry> {
+ CommandEntry();
+ ~CommandEntry();
+
+ Command command;
+
+ // parameters for the command (usage varies by command)
+ sp<Connection> connection;
+ };
+
+ // Generic queue implementation.
+ template <typename T>
+ struct Queue {
+ T head;
+ T tail;
+
+ inline Queue() {
+ head.prev = NULL;
+ head.next = & tail;
+ tail.prev = & head;
+ tail.next = NULL;
+ }
+
+ inline bool isEmpty() {
+ return head.next == & tail;
+ }
+
+ inline void enqueueAtTail(T* entry) {
+ T* last = tail.prev;
+ last->next = entry;
+ entry->prev = last;
+ entry->next = & tail;
+ tail.prev = entry;
+ }
+
+ inline void enqueueAtHead(T* entry) {
+ T* first = head.next;
+ head.next = entry;
+ entry->prev = & head;
+ entry->next = first;
+ first->prev = entry;
+ }
+
+ inline void dequeue(T* entry) {
+ entry->prev->next = entry->next;
+ entry->next->prev = entry->prev;
+ }
+
+ inline T* dequeueAtHead() {
+ T* first = head.next;
+ dequeue(first);
+ return first;
+ }
+ };
+
+ /* Allocates queue entries and performs reference counting as needed. */
+ class Allocator {
+ public:
+ Allocator();
+
+ ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
+ KeyEntry* 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);
+ MotionEntry* obtainMotionEntry(nsecs_t eventTime,
+ int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
+ int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+ nsecs_t downTime, uint32_t pointerCount,
+ const int32_t* pointerIds, const PointerCoords* pointerCoords);
+ DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
+ CommandEntry* obtainCommandEntry(Command command);
+
+ void releaseEventEntry(EventEntry* entry);
+ void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
+ void releaseKeyEntry(KeyEntry* entry);
+ void releaseMotionEntry(MotionEntry* entry);
+ void releaseDispatchEntry(DispatchEntry* entry);
+ void releaseCommandEntry(CommandEntry* entry);
+
+ void appendMotionSample(MotionEntry* motionEntry,
+ nsecs_t eventTime, const PointerCoords* pointerCoords);
+
+ private:
+ Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
+ Pool<KeyEntry> mKeyEntryPool;
+ Pool<MotionEntry> mMotionEntryPool;
+ Pool<MotionSample> mMotionSamplePool;
+ Pool<DispatchEntry> mDispatchEntryPool;
+ Pool<CommandEntry> mCommandEntryPool;
+
+ void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
+ };
+
+ /* Manages the dispatch state associated with a single input channel. */
+ class Connection : public RefBase {
+ protected:
+ virtual ~Connection();
+
+ public:
+ enum Status {
+ // Everything is peachy.
+ STATUS_NORMAL,
+ // An unrecoverable communication error has occurred.
+ STATUS_BROKEN,
+ // The client is not responding.
+ STATUS_NOT_RESPONDING,
+ // The input channel has been unregistered.
+ STATUS_ZOMBIE
+ };
+
+ Status status;
+ sp<InputChannel> inputChannel;
+ InputPublisher inputPublisher;
+ Queue<DispatchEntry> outboundQueue;
+ nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
+
+ nsecs_t lastEventTime; // the time when the event was originally captured
+ nsecs_t lastDispatchTime; // the time when the last event was dispatched
+ nsecs_t lastANRTime; // the time when the last ANR was recorded
+
+ explicit Connection(const sp<InputChannel>& inputChannel);
+
+ inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
+
+ const char* getStatusLabel() const;
+
+ // Finds a DispatchEntry in the outbound queue associated with the specified event.
+ // Returns NULL if not found.
+ DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
+
+ // Determine whether this connection has a pending synchronous dispatch target.
+ // Since there can only ever be at most one such target at a time, if there is one,
+ // it must be at the tail because nothing else can be enqueued after it.
+ inline bool hasPendingSyncTarget() {
+ return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
+ }
+
+ // Gets the time since the current event was originally obtained from the input driver.
+ inline double getEventLatencyMillis(nsecs_t currentTime) {
+ return (currentTime - lastEventTime) / 1000000.0;
+ }
+
+ // Gets the time since the current event entered the outbound dispatch queue.
+ inline double getDispatchLatencyMillis(nsecs_t currentTime) {
+ return (currentTime - lastDispatchTime) / 1000000.0;
+ }
+
+ // Gets the time since the current event ANR was declared, if applicable.
+ inline double getANRLatencyMillis(nsecs_t currentTime) {
+ return (currentTime - lastANRTime) / 1000000.0;
+ }
+
+ status_t initialize();
+
+ void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
+ };
+
+ sp<InputDispatcherPolicyInterface> mPolicy;
+
+ Mutex mLock;
+
+ Allocator mAllocator;
+ sp<PollLoop> mPollLoop;
+
+ Queue<EventEntry> mInboundQueue;
+ Queue<CommandEntry> mCommandQueue;
+
+ // All registered connections mapped by receive pipe file descriptor.
+ KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
+
+ // Active connections are connections that have a non-empty outbound queue.
+ // We don't use a ref-counted pointer here because we explicitly abort connections
+ // during unregistration which causes the connection's outbound queue to be cleared
+ // and the connection itself to be deactivated.
+ Vector<Connection*> mActiveConnections;
+
+ // List of connections that have timed out. Only used by dispatchOnce()
+ // We don't use a ref-counted pointer here because it is not possible for a connection
+ // to be unregistered while processing timed out connections since we hold the lock for
+ // the duration.
+ Vector<Connection*> mTimedOutConnections;
+
+ // Preallocated key and motion event objects used only to ask the input dispatcher policy
+ // for the targets of an event that is to be dispatched.
+ KeyEvent mReusableKeyEvent;
+ MotionEvent mReusableMotionEvent;
+
+ // The input targets that were most recently identified for dispatch.
+ // If there is a synchronous event dispatch in progress, the current input targets will
+ // remain unchanged until the dispatch has completed or been aborted.
+ Vector<InputTarget> mCurrentInputTargets;
+ bool mCurrentInputTargetsValid; // false while targets are being recomputed
+
+ // Event injection and synchronization.
+ Condition mInjectionResultAvailableCondition;
+ EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
+ void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
+
+ Condition mInjectionSyncFinishedCondition;
+ void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+
+ // Key repeat tracking.
+ // XXX Move this up to the input reader instead.
+ struct KeyRepeatState {
+ KeyEntry* lastKeyEntry; // or null if no repeat
+ nsecs_t nextRepeatTime;
+ } mKeyRepeatState;
+
+ void resetKeyRepeatLocked();
+
+ // Deferred command processing.
+ bool runCommandsLockedInterruptible();
+ CommandEntry* postCommandLocked(Command command);
+
+ // Process events that have just been dequeued from the head of the input queue.
+ void processConfigurationChangedLockedInterruptible(
+ nsecs_t currentTime, ConfigurationChangedEntry* entry);
+ void processKeyLockedInterruptible(
+ nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout);
+ void processKeyRepeatLockedInterruptible(
+ nsecs_t currentTime, nsecs_t keyRepeatTimeout);
+ void processMotionLockedInterruptible(
+ nsecs_t currentTime, MotionEntry* entry);
+
+ // Identify input targets for an event and dispatch to them.
+ void identifyInputTargetsAndDispatchKeyLockedInterruptible(
+ nsecs_t currentTime, KeyEntry* entry);
+ void identifyInputTargetsAndDispatchMotionLockedInterruptible(
+ nsecs_t currentTime, MotionEntry* entry);
+ void dispatchEventToCurrentInputTargetsLocked(
+ nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
+
+ // Manage the dispatch cycle for a single connection.
+ // These methods are deliberately not Interruptible because doing all of the work
+ // with the mutex held makes it easier to ensure that connection invariants are maintained.
+ // If needed, the methods post commands to run later once the critical bits are done.
+ void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget,
+ bool resumeWithAppendedMotionSample);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, nsecs_t newTimeout);
+ void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ bool broken);
+ static bool handleReceiveCallback(int receiveFd, int events, void* data);
+
+ // Add or remove a connection to the mActiveConnections vector.
+ void activateConnectionLocked(Connection* connection);
+ void deactivateConnectionLocked(Connection* connection);
+
+ // Interesting events that we might like to log or tell the framework about.
+ void onDispatchCycleStartedLocked(
+ nsecs_t currentTime, const sp<Connection>& connection);
+ void onDispatchCycleFinishedLocked(
+ nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
+ void onDispatchCycleANRLocked(
+ nsecs_t currentTime, const sp<Connection>& connection);
+ void onDispatchCycleBrokenLocked(
+ nsecs_t currentTime, const sp<Connection>& connection);
+
+ // Outbound policy interactions.
+ void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
+ void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
+ void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+};
+
+/* Enqueues and dispatches input events, endlessly. */
+class InputDispatcherThread : public Thread {
+public:
+ explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
+ ~InputDispatcherThread();
+
+private:
+ virtual bool threadLoop();
+
+ sp<InputDispatcherInterface> mDispatcher;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_DISPATCHER_PRIV_H
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
new file mode 100644
index 0000000..4012c69
--- /dev/null
+++ b/include/ui/InputManager.h
@@ -0,0 +1,172 @@
+/*
+ * 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 _UI_INPUT_MANAGER_H
+#define _UI_INPUT_MANAGER_H
+
+/**
+ * Native input manager.
+ */
+
+#include <ui/EventHub.h>
+#include <ui/Input.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class InputChannel;
+
+class InputReaderInterface;
+class InputReaderPolicyInterface;
+class InputReaderThread;
+
+class InputDispatcherInterface;
+class InputDispatcherPolicyInterface;
+class InputDispatcherThread;
+
+/*
+ * The input manager is the core of the system event processing.
+ *
+ * The input manager uses two threads.
+ *
+ * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
+ * applies policy, and posts messages to a queue managed by the DispatcherThread.
+ * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
+ * queue and asynchronously dispatches them to applications.
+ *
+ * By design, the InputReaderThread class and InputDispatcherThread class do not share any
+ * internal state. Moreover, all communication is done one way from the InputReaderThread
+ * into the InputDispatcherThread and never the reverse. Both classes may interact with the
+ * InputDispatchPolicy, however.
+ *
+ * The InputManager class never makes any calls into Java itself. Instead, the
+ * InputDispatchPolicy is responsible for performing all external interactions with the
+ * system, including calling DVM services.
+ */
+class InputManagerInterface : public virtual RefBase {
+protected:
+ InputManagerInterface() { }
+ virtual ~InputManagerInterface() { }
+
+public:
+ /* Starts the input manager threads. */
+ virtual status_t start() = 0;
+
+ /* Stops the input manager threads and waits for them to exit. */
+ virtual status_t stop() = 0;
+
+ /* Registers an input channel prior to using it as the target of an event. */
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+
+ /* Unregisters an input channel. */
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
+
+ /* Injects an input event and optionally waits for sync.
+ * The synchronization mode determines whether the method blocks while waiting for
+ * input injection to proceed.
+ * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+ */
+ virtual int32_t injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
+
+ /* Preempts input dispatch in progress by making pending synchronous
+ * dispatches asynchronous instead. This method is generally called during a focus
+ * transition from one application to the next so as to enable the new application
+ * to start receiving input as soon as possible without having to wait for the
+ * old application to finish up.
+ */
+ virtual void preemptInputDispatch() = 0;
+
+ /* Gets input device configuration. */
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
+
+ /* Gets information about the specified input device.
+ * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
+ * was no such device.
+ */
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
+
+ /* Gets the list of all registered device ids. */
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+
+ /* Queries current input state. */
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw) = 0;
+
+ /* Determines whether physical keys exist for the given framework-domain key codes. */
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+};
+
+class InputManager : public InputManagerInterface {
+protected:
+ virtual ~InputManager();
+
+public:
+ InputManager(
+ const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& readerPolicy,
+ const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
+
+ // (used for testing purposes)
+ InputManager(
+ const sp<InputReaderInterface>& reader,
+ const sp<InputDispatcherInterface>& dispatcher);
+
+ virtual status_t start();
+ virtual status_t stop();
+
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
+
+ virtual int32_t injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
+
+ virtual void preemptInputDispatch();
+
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration);
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode);
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode);
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw);
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
+
+private:
+ sp<InputReaderInterface> mReader;
+ sp<InputReaderThread> mReaderThread;
+
+ sp<InputDispatcherInterface> mDispatcher;
+ sp<InputDispatcherThread> mDispatcherThread;
+
+ void initialize();
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_MANAGER_H
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
new file mode 100644
index 0000000..f162231
--- /dev/null
+++ b/include/ui/InputReader.h
@@ -0,0 +1,843 @@
+/*
+ * 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 _UI_INPUT_READER_H
+#define _UI_INPUT_READER_H
+
+#include <ui/EventHub.h>
+#include <ui/Input.h>
+#include <ui/InputDispatcher.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/BitSet.h>
+
+#include <stddef.h>
+#include <unistd.h>
+
+namespace android {
+
+class InputDevice;
+class InputMapper;
+
+
+/*
+ * Input reader policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI. This interface is also mocked in the unit tests.
+ */
+class InputReaderPolicyInterface : public virtual RefBase {
+protected:
+ InputReaderPolicyInterface() { }
+ virtual ~InputReaderPolicyInterface() { }
+
+public:
+ /* Display orientations. */
+ enum {
+ ROTATION_0 = 0,
+ ROTATION_90 = 1,
+ ROTATION_180 = 2,
+ ROTATION_270 = 3
+ };
+
+ /* Actions returned by interceptXXX methods. */
+ enum {
+ // The input dispatcher should do nothing and discard the input unless other
+ // flags are set.
+ ACTION_NONE = 0,
+
+ // The input dispatcher should dispatch the input to the application.
+ ACTION_DISPATCH = 0x00000001,
+
+ // The input dispatcher should perform special filtering in preparation for
+ // a pending app switch.
+ ACTION_APP_SWITCH_COMING = 0x00000002,
+ };
+
+ /* Describes a virtual key. */
+ struct VirtualKeyDefinition {
+ int32_t scanCode;
+
+ // configured position data, specified in display coords
+ int32_t centerX;
+ int32_t centerY;
+ int32_t width;
+ int32_t height;
+ };
+
+ /* Gets information about the display with the specified id.
+ * Returns true if the display info is available, false otherwise.
+ */
+ virtual bool getDisplayInfo(int32_t displayId,
+ int32_t* width, int32_t* height, int32_t* orientation) = 0;
+
+ /* Provides feedback for a virtual key down.
+ */
+ virtual void virtualKeyDownFeedback() = 0;
+
+ /* Intercepts a key event.
+ * The policy can use this method as an opportunity to perform power management functions
+ * and early event preprocessing such as updating policy flags.
+ *
+ * Returns a policy action constant such as ACTION_DISPATCH.
+ */
+ virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
+ bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) = 0;
+
+ /* Intercepts a switch event.
+ * The policy can use this method as an opportunity to perform power management functions
+ * and early event preprocessing such as updating policy flags.
+ *
+ * Switches are not dispatched to applications so this method should
+ * usually return ACTION_NONE.
+ */
+ virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+ uint32_t& policyFlags) = 0;
+
+ /* Intercepts a generic touch, trackball or other event.
+ * The policy can use this method as an opportunity to perform power management functions
+ * and early event preprocessing such as updating policy flags.
+ *
+ * Returns a policy action constant such as ACTION_DISPATCH.
+ */
+ virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags) = 0;
+
+ /* Determines whether to turn on some hacks we have to improve the touch interaction with a
+ * certain device whose screen currently is not all that good.
+ */
+ virtual bool filterTouchEvents() = 0;
+
+ /* Determines whether to turn on some hacks to improve touch interaction with another device
+ * where touch coordinate data can get corrupted.
+ */
+ virtual bool filterJumpyTouchEvents() = 0;
+
+ /* Gets the configured virtual key definitions for an input device. */
+ virtual void getVirtualKeyDefinitions(const String8& deviceName,
+ Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
+
+ /* Gets the excluded device names for the platform. */
+ virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
+};
+
+
+/* Processes raw input events and sends cooked event data to an input dispatcher. */
+class InputReaderInterface : public virtual RefBase {
+protected:
+ InputReaderInterface() { }
+ virtual ~InputReaderInterface() { }
+
+public:
+ /* Runs a single iteration of the processing loop.
+ * Nominally reads and processes one incoming message from the EventHub.
+ *
+ * This method should be called on the input reader thread.
+ */
+ virtual void loopOnce() = 0;
+
+ /* Gets the current input device configuration.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
+
+ /* Gets information about the specified input device.
+ * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
+ * was no such device.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
+
+ /* Gets the list of all registered device ids. */
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
+
+ /* Query current input state. */
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) = 0;
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) = 0;
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw) = 0;
+
+ /* Determine whether physical keys exist for the given framework-domain key codes. */
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+};
+
+
+/* Internal interface used by individual input devices to access global input device state
+ * and parameters maintained by the input reader.
+ */
+class InputReaderContext {
+protected:
+ InputReaderContext() { }
+ virtual ~InputReaderContext() { }
+
+public:
+ virtual void updateGlobalMetaState() = 0;
+ virtual int32_t getGlobalMetaState() = 0;
+
+ virtual InputReaderPolicyInterface* getPolicy() = 0;
+ virtual InputDispatcherInterface* getDispatcher() = 0;
+ virtual EventHubInterface* getEventHub() = 0;
+};
+
+
+/* The input reader reads raw event data from the event hub and processes it into input events
+ * that it sends to the input dispatcher. Some functions of the input reader, such as early
+ * event filtering in low power states, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ * Because the policy and dispatcher can potentially block or cause re-entrance into
+ * the input reader, the input reader never calls into other components while holding
+ * an exclusive internal lock whenever re-entrance can happen.
+ */
+class InputReader : public InputReaderInterface, private InputReaderContext {
+public:
+ InputReader(const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputDispatcherInterface>& dispatcher);
+ virtual ~InputReader();
+
+ virtual void loopOnce();
+
+ virtual void getInputConfiguration(InputConfiguration* outConfiguration);
+
+ virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
+ virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
+
+ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode);
+ virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode);
+ virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
+ int32_t sw);
+
+ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
+
+private:
+ sp<EventHubInterface> mEventHub;
+ sp<InputReaderPolicyInterface> mPolicy;
+ sp<InputDispatcherInterface> mDispatcher;
+
+ virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); }
+ virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
+ virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
+
+ // This reader/writer lock guards the list of input devices.
+ // The writer lock must be held whenever the list of input devices is modified
+ // and then promptly released.
+ // The reader lock must be held whenever the list of input devices is traversed or an
+ // input device in the list is accessed.
+ // This lock only protects the registry and prevents inadvertent deletion of device objects
+ // that are in use. Individual devices are responsible for guarding their own internal state
+ // as needed for concurrent operation.
+ RWLock mDeviceRegistryLock;
+ KeyedVector<int32_t, InputDevice*> mDevices;
+
+ // low-level input event decoding and device management
+ void process(const RawEvent* rawEvent);
+
+ void addDevice(nsecs_t when, int32_t deviceId);
+ void removeDevice(nsecs_t when, int32_t deviceId);
+ InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
+ void configureExcludedDevices();
+
+ void consumeEvent(const RawEvent* rawEvent);
+
+ void handleConfigurationChanged(nsecs_t when);
+
+ // state management for all devices
+ Mutex mStateLock;
+
+ int32_t mGlobalMetaState;
+ virtual void updateGlobalMetaState();
+ virtual int32_t getGlobalMetaState();
+
+ InputConfiguration mInputConfiguration;
+ void updateInputConfiguration();
+
+ // state queries
+ typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+ int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc);
+ bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+};
+
+
+/* Reads raw events from the event hub and processes them, endlessly. */
+class InputReaderThread : public Thread {
+public:
+ InputReaderThread(const sp<InputReaderInterface>& reader);
+ virtual ~InputReaderThread();
+
+private:
+ sp<InputReaderInterface> mReader;
+
+ virtual bool threadLoop();
+};
+
+
+/* Represents the state of a single input device. */
+class InputDevice {
+public:
+ InputDevice(InputReaderContext* context, int32_t id, const String8& name);
+ ~InputDevice();
+
+ inline InputReaderContext* getContext() { return mContext; }
+ inline int32_t getId() { return mId; }
+ inline const String8& getName() { return mName; }
+ inline uint32_t getSources() { return mSources; }
+
+ inline bool isIgnored() { return mMappers.isEmpty(); }
+
+ void addMapper(InputMapper* mapper);
+ void configure();
+ void reset();
+ void process(const RawEvent* rawEvent);
+
+ void getDeviceInfo(InputDeviceInfo* outDeviceInfo);
+ int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+ bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+ int32_t getMetaState();
+
+private:
+ InputReaderContext* mContext;
+ int32_t mId;
+
+ Vector<InputMapper*> mMappers;
+
+ String8 mName;
+ uint32_t mSources;
+
+ typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
+ int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
+};
+
+
+/* An input mapper transforms raw input events into cooked event data.
+ * A single input device can have multiple associated input mappers in order to interpret
+ * different classes of events.
+ */
+class InputMapper {
+public:
+ InputMapper(InputDevice* device);
+ virtual ~InputMapper();
+
+ inline InputDevice* getDevice() { return mDevice; }
+ inline int32_t getDeviceId() { return mDevice->getId(); }
+ inline const String8 getDeviceName() { return mDevice->getName(); }
+ inline InputReaderContext* getContext() { return mContext; }
+ inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
+ inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); }
+ inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+
+ virtual uint32_t getSources() = 0;
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void configure();
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent) = 0;
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+ virtual int32_t getMetaState();
+
+protected:
+ InputDevice* mDevice;
+ InputReaderContext* mContext;
+
+ bool applyStandardPolicyActions(nsecs_t when, int32_t policyActions);
+};
+
+
+class SwitchInputMapper : public InputMapper {
+public:
+ SwitchInputMapper(InputDevice* device);
+ virtual ~SwitchInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void process(const RawEvent* rawEvent);
+
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
+
+private:
+ void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
+};
+
+
+class KeyboardInputMapper : public InputMapper {
+public:
+ KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, uint32_t sources,
+ int32_t keyboardType);
+ virtual ~KeyboardInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+ virtual int32_t getMetaState();
+
+private:
+ Mutex mLock;
+
+ struct KeyDown {
+ int32_t keyCode;
+ int32_t scanCode;
+ };
+
+ int32_t mAssociatedDisplayId;
+ uint32_t mSources;
+ int32_t mKeyboardType;
+
+ struct LockedState {
+ Vector<KeyDown> keyDowns; // keys that are down
+ int32_t metaState;
+ nsecs_t downTime; // time of most recent key down
+ } mLocked;
+
+ void initializeLocked();
+
+ bool isKeyboardOrGamepadKey(int32_t scanCode);
+
+ void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
+ uint32_t policyFlags);
+ void applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags,
+ bool down, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime);
+
+ ssize_t findKeyDownLocked(int32_t scanCode);
+};
+
+
+class TrackballInputMapper : public InputMapper {
+public:
+ TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~TrackballInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+private:
+ // Amount that trackball needs to move in order to generate a key event.
+ static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+ Mutex mLock;
+
+ int32_t mAssociatedDisplayId;
+
+ struct Accumulator {
+ enum {
+ FIELD_BTN_MOUSE = 1,
+ FIELD_REL_X = 2,
+ FIELD_REL_Y = 4
+ };
+
+ uint32_t fields;
+
+ bool btnMouse;
+ int32_t relX;
+ int32_t relY;
+
+ inline void clear() {
+ fields = 0;
+ }
+
+ inline bool isDirty() {
+ return fields != 0;
+ }
+ } mAccumulator;
+
+ float mXScale;
+ float mYScale;
+ float mXPrecision;
+ float mYPrecision;
+
+ struct LockedState {
+ bool down;
+ nsecs_t downTime;
+ } mLocked;
+
+ void initializeLocked();
+
+ void sync(nsecs_t when);
+ void applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
+ PointerCoords* pointerCoords, nsecs_t downTime);
+};
+
+
+class TouchInputMapper : public InputMapper {
+public:
+ TouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~TouchInputMapper();
+
+ virtual uint32_t getSources();
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+ virtual void configure();
+ virtual void reset();
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags);
+
+protected:
+ /* Maximum pointer id value supported.
+ * (This is limited by our use of BitSet32 to track pointer assignments.) */
+ static const uint32_t MAX_POINTER_ID = 31;
+
+ Mutex mLock;
+
+ struct VirtualKey {
+ int32_t keyCode;
+ int32_t scanCode;
+ uint32_t flags;
+
+ // computed hit box, specified in touch screen coords based on known display size
+ int32_t hitLeft;
+ int32_t hitTop;
+ int32_t hitRight;
+ int32_t hitBottom;
+
+ inline bool isHit(int32_t x, int32_t y) const {
+ return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
+ }
+ };
+
+ struct PointerData {
+ uint32_t id;
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ int32_t size;
+ int32_t touchMajor;
+ int32_t touchMinor;
+ int32_t toolMajor;
+ int32_t toolMinor;
+ int32_t orientation;
+ };
+
+ struct TouchData {
+ uint32_t pointerCount;
+ PointerData pointers[MAX_POINTERS];
+ BitSet32 idBits;
+ uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+ void copyFrom(const TouchData& other) {
+ pointerCount = other.pointerCount;
+ idBits = other.idBits;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointers[i] = other.pointers[i];
+
+ int id = pointers[i].id;
+ idToIndex[id] = other.idToIndex[id];
+ }
+ }
+
+ inline void clear() {
+ pointerCount = 0;
+ idBits.clear();
+ }
+ };
+
+ int32_t mAssociatedDisplayId;
+
+ // Immutable configuration parameters.
+ struct Parameters {
+ bool useBadTouchFilter;
+ bool useJumpyTouchFilter;
+ bool useAveragingTouchFilter;
+ } mParameters;
+
+ // Raw axis information.
+ struct Axes {
+ RawAbsoluteAxisInfo x;
+ RawAbsoluteAxisInfo y;
+ RawAbsoluteAxisInfo pressure;
+ RawAbsoluteAxisInfo size;
+ RawAbsoluteAxisInfo touchMajor;
+ RawAbsoluteAxisInfo touchMinor;
+ RawAbsoluteAxisInfo toolMajor;
+ RawAbsoluteAxisInfo toolMinor;
+ RawAbsoluteAxisInfo orientation;
+ } mAxes;
+
+ // Current and previous touch sample data.
+ TouchData mCurrentTouch;
+ TouchData mLastTouch;
+
+ // The time the primary pointer last went down.
+ nsecs_t mDownTime;
+
+ struct LockedState {
+ Vector<VirtualKey> virtualKeys;
+
+ // The surface orientation and width and height set by configureSurfaceLocked().
+ int32_t surfaceOrientation;
+ int32_t surfaceWidth, surfaceHeight;
+
+ // Translation and scaling factors, orientation-independent.
+ int32_t xOrigin;
+ float xScale;
+ float xPrecision;
+
+ int32_t yOrigin;
+ float yScale;
+ float yPrecision;
+
+ int32_t pressureOrigin;
+ float pressureScale;
+
+ int32_t sizeOrigin;
+ float sizeScale;
+
+ float orientationScale;
+
+ // Oriented motion ranges for input device info.
+ struct OrientedRanges {
+ InputDeviceInfo::MotionRange x;
+ InputDeviceInfo::MotionRange y;
+ InputDeviceInfo::MotionRange pressure;
+ InputDeviceInfo::MotionRange size;
+ InputDeviceInfo::MotionRange touchMajor;
+ InputDeviceInfo::MotionRange touchMinor;
+ InputDeviceInfo::MotionRange toolMajor;
+ InputDeviceInfo::MotionRange toolMinor;
+ InputDeviceInfo::MotionRange orientation;
+ } orientedRanges;
+
+ // Oriented dimensions and precision.
+ float orientedSurfaceWidth, orientedSurfaceHeight;
+ float orientedXPrecision, orientedYPrecision;
+
+ struct CurrentVirtualKeyState {
+ bool down;
+ nsecs_t downTime;
+ int32_t keyCode;
+ int32_t scanCode;
+ } currentVirtualKey;
+ } mLocked;
+
+ virtual void configureAxes();
+ virtual bool configureSurfaceLocked();
+ virtual void configureVirtualKeysLocked();
+
+ enum TouchResult {
+ // Dispatch the touch normally.
+ DISPATCH_TOUCH,
+ // Do not dispatch the touch, but keep tracking the current stroke.
+ SKIP_TOUCH,
+ // Do not dispatch the touch, and drop all information associated with the current stoke
+ // so the next movement will appear as a new down.
+ DROP_STROKE
+ };
+
+ void syncTouch(nsecs_t when, bool havePointerIds);
+
+private:
+ /* Maximum number of historical samples to average. */
+ static const uint32_t AVERAGING_HISTORY_SIZE = 5;
+
+ /* Slop distance for jumpy pointer detection.
+ * The vertical range of the screen divided by this is our epsilon value. */
+ static const uint32_t JUMPY_EPSILON_DIVISOR = 212;
+
+ /* Number of jumpy points to drop for touchscreens that need it. */
+ static const uint32_t JUMPY_TRANSITION_DROPS = 3;
+ static const uint32_t JUMPY_DROP_LIMIT = 3;
+
+ /* Maximum squared distance for averaging.
+ * If moving farther than this, turn of averaging to avoid lag in response. */
+ static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75;
+
+ struct AveragingTouchFilterState {
+ // Individual history tracks are stored by pointer id
+ uint32_t historyStart[MAX_POINTERS];
+ uint32_t historyEnd[MAX_POINTERS];
+ struct {
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t pressure;
+ } pointers[MAX_POINTERS];
+ } historyData[AVERAGING_HISTORY_SIZE];
+ } mAveragingTouchFilter;
+
+ struct JumpTouchFilterState {
+ uint32_t jumpyPointsDropped;
+ } mJumpyTouchFilter;
+
+ struct PointerDistanceHeapElement {
+ uint32_t currentPointerIndex : 8;
+ uint32_t lastPointerIndex : 8;
+ uint64_t distance : 48; // squared distance
+ };
+
+ void initializeLocked();
+
+ TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags);
+ void dispatchTouches(nsecs_t when, uint32_t policyFlags);
+ void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch,
+ BitSet32 idBits, uint32_t changedId, int32_t motionEventAction);
+
+ void applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+ int32_t keyEventAction, int32_t keyEventFlags,
+ int32_t keyCode, int32_t scanCode, nsecs_t downTime);
+
+ bool isPointInsideSurfaceLocked(int32_t x, int32_t y);
+ const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y);
+
+ bool applyBadTouchFilter();
+ bool applyJumpyTouchFilter();
+ void applyAveragingTouchFilter();
+ void calculatePointerIds();
+};
+
+
+class SingleTouchInputMapper : public TouchInputMapper {
+public:
+ SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~SingleTouchInputMapper();
+
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+protected:
+ virtual void configureAxes();
+
+private:
+ struct Accumulator {
+ enum {
+ FIELD_BTN_TOUCH = 1,
+ FIELD_ABS_X = 2,
+ FIELD_ABS_Y = 4,
+ FIELD_ABS_PRESSURE = 8,
+ FIELD_ABS_TOOL_WIDTH = 16
+ };
+
+ uint32_t fields;
+
+ bool btnTouch;
+ int32_t absX;
+ int32_t absY;
+ int32_t absPressure;
+ int32_t absToolWidth;
+
+ inline void clear() {
+ fields = 0;
+ }
+
+ inline bool isDirty() {
+ return fields != 0;
+ }
+ } mAccumulator;
+
+ bool mDown;
+ int32_t mX;
+ int32_t mY;
+ int32_t mPressure;
+ int32_t mSize;
+
+ void initialize();
+
+ void sync(nsecs_t when);
+};
+
+
+class MultiTouchInputMapper : public TouchInputMapper {
+public:
+ MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId);
+ virtual ~MultiTouchInputMapper();
+
+ virtual void reset();
+ virtual void process(const RawEvent* rawEvent);
+
+protected:
+ virtual void configureAxes();
+
+private:
+ struct Accumulator {
+ enum {
+ FIELD_ABS_MT_POSITION_X = 1,
+ FIELD_ABS_MT_POSITION_Y = 2,
+ FIELD_ABS_MT_TOUCH_MAJOR = 4,
+ FIELD_ABS_MT_TOUCH_MINOR = 8,
+ FIELD_ABS_MT_WIDTH_MAJOR = 16,
+ FIELD_ABS_MT_WIDTH_MINOR = 32,
+ FIELD_ABS_MT_ORIENTATION = 64,
+ FIELD_ABS_MT_TRACKING_ID = 128
+ };
+
+ uint32_t pointerCount;
+ struct Pointer {
+ uint32_t fields;
+
+ int32_t absMTPositionX;
+ int32_t absMTPositionY;
+ int32_t absMTTouchMajor;
+ int32_t absMTTouchMinor;
+ int32_t absMTWidthMajor;
+ int32_t absMTWidthMinor;
+ int32_t absMTOrientation;
+ int32_t absMTTrackingId;
+
+ inline void clear() {
+ fields = 0;
+ }
+ } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
+
+ inline void clear() {
+ pointerCount = 0;
+ pointers[0].clear();
+ }
+
+ inline bool isDirty() {
+ return pointerCount != 0;
+ }
+ } mAccumulator;
+
+ void initialize();
+
+ void sync(nsecs_t when);
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_READER_H
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
new file mode 100644
index 0000000..31ec701
--- /dev/null
+++ b/include/ui/InputTransport.h
@@ -0,0 +1,334 @@
+/*
+ * 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 _UI_INPUT_TRANSPORT_H
+#define _UI_INPUT_TRANSPORT_H
+
+/**
+ * Native input transport.
+ *
+ * Uses anonymous shared memory as a whiteboard for sending input events from an
+ * InputPublisher to an InputConsumer and ensuring appropriate synchronization.
+ * One interesting feature is that published events can be updated in place as long as they
+ * have not yet been consumed.
+ *
+ * The InputPublisher and InputConsumer only take care of transferring event data
+ * over an InputChannel and sending synchronization signals. The InputDispatcher and InputQueue
+ * build on these abstractions to add multiplexing and queueing.
+ */
+
+#include <semaphore.h>
+#include <ui/Input.h>
+#include <utils/Errors.h>
+#include <utils/PollLoop.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+
+/*
+ * An input channel consists of a shared memory buffer and a pair of pipes
+ * used to send input messages from an InputPublisher to an InputConsumer
+ * across processes. Each channel has a descriptive name for debugging purposes.
+ *
+ * Each endpoint has its own InputChannel object that specifies its own file descriptors.
+ *
+ * The input channel is closed when all references to it are released.
+ */
+class InputChannel : public RefBase {
+protected:
+ virtual ~InputChannel();
+
+public:
+ InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
+ int32_t sendPipeFd);
+
+ /* Creates a pair of input channels and their underlying shared memory buffers
+ * and pipes.
+ *
+ * Returns OK on success.
+ */
+ static status_t openInputChannelPair(const String8& name,
+ sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
+
+ inline String8 getName() const { return mName; }
+ inline int32_t getAshmemFd() const { return mAshmemFd; }
+ inline int32_t getReceivePipeFd() const { return mReceivePipeFd; }
+ inline int32_t getSendPipeFd() const { return mSendPipeFd; }
+
+ /* Sends a signal to the other endpoint.
+ *
+ * Returns OK on success.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t sendSignal(char signal);
+
+ /* Receives a signal send by the other endpoint.
+ * (Should only call this after poll() indicates that the receivePipeFd has available input.)
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no signal present.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t receiveSignal(char* outSignal);
+
+private:
+ String8 mName;
+ int32_t mAshmemFd;
+ int32_t mReceivePipeFd;
+ int32_t mSendPipeFd;
+};
+
+/*
+ * Private intermediate representation of input events as messages written into an
+ * ashmem buffer.
+ */
+struct InputMessage {
+ /* Semaphore count is set to 1 when the message is published.
+ * It becomes 0 transiently while the publisher updates the message.
+ * It becomes 0 permanently when the consumer consumes the message.
+ */
+ sem_t semaphore;
+
+ /* Initialized to false by the publisher.
+ * Set to true by the consumer when it consumes the message.
+ */
+ bool consumed;
+
+ int32_t type;
+
+ struct SampleData {
+ nsecs_t eventTime;
+ PointerCoords coords[0]; // variable length
+ };
+
+ int32_t deviceId;
+ int32_t source;
+
+ union {
+ struct {
+ int32_t action;
+ int32_t flags;
+ int32_t keyCode;
+ int32_t scanCode;
+ int32_t metaState;
+ int32_t repeatCount;
+ nsecs_t downTime;
+ nsecs_t eventTime;
+ } key;
+
+ struct {
+ int32_t action;
+ int32_t metaState;
+ int32_t edgeFlags;
+ nsecs_t downTime;
+ float xOffset;
+ float yOffset;
+ float xPrecision;
+ float yPrecision;
+ size_t pointerCount;
+ int32_t pointerIds[MAX_POINTERS];
+ size_t sampleCount;
+ SampleData sampleData[0]; // variable length
+ } motion;
+ };
+
+ /* Gets the number of bytes to add to step to the next SampleData object in a motion
+ * event message for a given number of pointers.
+ */
+ static inline size_t sampleDataStride(size_t pointerCount) {
+ return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
+ }
+
+ /* Adds the SampleData stride to the given pointer. */
+ static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) {
+ return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride);
+ }
+};
+
+/*
+ * Publishes input events to an anonymous shared memory buffer.
+ * Uses atomic operations to coordinate shared access with a single concurrent consumer.
+ */
+class InputPublisher {
+public:
+ /* Creates a publisher associated with an input channel. */
+ explicit InputPublisher(const sp<InputChannel>& channel);
+
+ /* Destroys the publisher and releases its input channel. */
+ ~InputPublisher();
+
+ /* Gets the underlying input channel. */
+ inline sp<InputChannel> getChannel() { return mChannel; }
+
+ /* Prepares the publisher for use. Must be called before it is used.
+ * Returns OK on success.
+ *
+ * This method implicitly calls reset(). */
+ status_t initialize();
+
+ /* Resets the publisher to its initial state and unpins its ashmem buffer.
+ * Returns OK on success.
+ *
+ * Should be called after an event has been consumed to release resources used by the
+ * publisher until the next event is ready to be published.
+ */
+ status_t reset();
+
+ /* Publishes a key event to the ashmem buffer.
+ *
+ * Returns OK on success.
+ * Returns INVALID_OPERATION if the publisher has not been reset.
+ */
+ status_t 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);
+
+ /* Publishes a motion event to the ashmem buffer.
+ *
+ * Returns OK on success.
+ * Returns INVALID_OPERATION if the publisher has not been reset.
+ * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS.
+ */
+ status_t publishMotionEvent(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ 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);
+
+ /* Appends a motion sample to a motion event unless already consumed.
+ *
+ * Returns OK on success.
+ * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event.
+ * Returns FAILED_TRANSACTION if the current event has already been consumed.
+ * Returns NO_MEMORY if the buffer is full and no additional samples can be added.
+ */
+ status_t appendMotionSample(
+ nsecs_t eventTime,
+ const PointerCoords* pointerCoords);
+
+ /* Sends a dispatch signal to the consumer to inform it that a new message is available.
+ *
+ * Returns OK on success.
+ * Errors probably indicate that the channel is broken.
+ */
+ status_t sendDispatchSignal();
+
+ /* Receives the finished signal from the consumer in reply to the original dispatch signal.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no signal present.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t receiveFinishedSignal();
+
+private:
+ sp<InputChannel> mChannel;
+
+ size_t mAshmemSize;
+ InputMessage* mSharedMessage;
+ bool mPinned;
+ bool mSemaphoreInitialized;
+ bool mWasDispatched;
+
+ size_t mMotionEventPointerCount;
+ InputMessage::SampleData* mMotionEventSampleDataTail;
+ size_t mMotionEventSampleDataStride;
+
+ status_t publishInputEvent(
+ int32_t type,
+ int32_t deviceId,
+ int32_t source);
+};
+
+/*
+ * Consumes input events from an anonymous shared memory buffer.
+ * Uses atomic operations to coordinate shared access with a single concurrent publisher.
+ */
+class InputConsumer {
+public:
+ /* Creates a consumer associated with an input channel. */
+ explicit InputConsumer(const sp<InputChannel>& channel);
+
+ /* Destroys the consumer and releases its input channel. */
+ ~InputConsumer();
+
+ /* Gets the underlying input channel. */
+ inline sp<InputChannel> getChannel() { return mChannel; }
+
+ /* Prepares the consumer for use. Must be called before it is used. */
+ status_t initialize();
+
+ /* Consumes the input event in the buffer and copies its contents into
+ * an InputEvent object created using the specified factory.
+ * This operation will block if the publisher is updating the event.
+ *
+ * Returns OK on success.
+ * Returns INVALID_OPERATION if there is no currently published event.
+ * Returns NO_MEMORY if the event could not be created.
+ */
+ status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);
+
+ /* Sends a finished signal to the publisher to inform it that the current message is
+ * finished processing.
+ *
+ * Returns OK on success.
+ * Errors probably indicate that the channel is broken.
+ */
+ status_t sendFinishedSignal();
+
+ /* Receives the dispatched signal from the publisher.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no signal present.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t receiveDispatchSignal();
+
+private:
+ sp<InputChannel> mChannel;
+
+ size_t mAshmemSize;
+ InputMessage* mSharedMessage;
+
+ void populateKeyEvent(KeyEvent* keyEvent) const;
+ void populateMotionEvent(MotionEvent* motionEvent) const;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_TRANSPORT_H
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index 571e47b..c8d6ffc 100644..100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -17,6 +17,8 @@
#ifndef _UI_KEYCODE_LABELS_H
#define _UI_KEYCODE_LABELS_H
+#include <android/keycodes.h>
+
struct KeycodeLabel {
const char *literal;
int value;
@@ -114,113 +116,32 @@ static const KeycodeLabel KEYCODES[] = {
{ "MEDIA_REWIND", 89 },
{ "MEDIA_FAST_FORWARD", 90 },
{ "MUTE", 91 },
+ { "PAGE_UP", 92 },
+ { "PAGE_DOWN", 93 },
+ { "PICTSYMBOLS", 94 },
+ { "SWITCH_CHARSET", 95 },
+ { "BUTTON_A", 96 },
+ { "BUTTON_B", 97 },
+ { "BUTTON_C", 98 },
+ { "BUTTON_X", 99 },
+ { "BUTTON_Y", 100 },
+ { "BUTTON_Z", 101 },
+ { "BUTTON_L1", 102 },
+ { "BUTTON_R1", 103 },
+ { "BUTTON_L2", 104 },
+ { "BUTTON_R2", 105 },
+ { "BUTTON_THUMBL", 106 },
+ { "BUTTON_THUMBR", 107 },
+ { "BUTTON_START", 108 },
+ { "BUTTON_SELECT", 109 },
+ { "BUTTON_MODE", 110 },
- // NOTE: If you add a new keycode here you must also add it to:
- // (enum KeyCode, in this file)
- // frameworks/base/core/java/android/view/KeyEvent.java
- // tools/puppet_master/PuppetMaster.nav_keys.py
- // frameworks/base/core/res/res/values/attrs.xml
+ // NOTE: If you add a new keycode here you must also add it to several other files.
+ // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
{ NULL, 0 }
};
-// These constants need to match the above mappings.
-typedef enum KeyCode {
- kKeyCodeUnknown = 0,
-
- kKeyCodeSoftLeft = 1,
- kKeyCodeSoftRight = 2,
- kKeyCodeHome = 3,
- kKeyCodeBack = 4,
- kKeyCodeCall = 5,
- kKeyCodeEndCall = 6,
- kKeyCode0 = 7,
- kKeyCode1 = 8,
- kKeyCode2 = 9,
- kKeyCode3 = 10,
- kKeyCode4 = 11,
- kKeyCode5 = 12,
- kKeyCode6 = 13,
- kKeyCode7 = 14,
- kKeyCode8 = 15,
- kKeyCode9 = 16,
- kKeyCodeStar = 17,
- kKeyCodePound = 18,
- kKeyCodeDpadUp = 19,
- kKeyCodeDpadDown = 20,
- kKeyCodeDpadLeft = 21,
- kKeyCodeDpadRight = 22,
- kKeyCodeDpadCenter = 23,
- kKeyCodeVolumeUp = 24,
- kKeyCodeVolumeDown = 25,
- kKeyCodePower = 26,
- kKeyCodeCamera = 27,
- kKeyCodeClear = 28,
- kKeyCodeA = 29,
- kKeyCodeB = 30,
- kKeyCodeC = 31,
- kKeyCodeD = 32,
- kKeyCodeE = 33,
- kKeyCodeF = 34,
- kKeyCodeG = 35,
- kKeyCodeH = 36,
- kKeyCodeI = 37,
- kKeyCodeJ = 38,
- kKeyCodeK = 39,
- kKeyCodeL = 40,
- kKeyCodeM = 41,
- kKeyCodeN = 42,
- kKeyCodeO = 43,
- kKeyCodeP = 44,
- kKeyCodeQ = 45,
- kKeyCodeR = 46,
- kKeyCodeS = 47,
- kKeyCodeT = 48,
- kKeyCodeU = 49,
- kKeyCodeV = 50,
- kKeyCodeW = 51,
- kKeyCodeX = 52,
- kKeyCodeY = 53,
- kKeyCodeZ = 54,
- kKeyCodeComma = 55,
- kKeyCodePeriod = 56,
- kKeyCodeAltLeft = 57,
- kKeyCodeAltRight = 58,
- kKeyCodeShiftLeft = 59,
- kKeyCodeShiftRight = 60,
- kKeyCodeTab = 61,
- kKeyCodeSpace = 62,
- kKeyCodeSym = 63,
- kKeyCodeExplorer = 64,
- kKeyCodeEnvelope = 65,
- kKeyCodeNewline = 66,
- kKeyCodeDel = 67,
- kKeyCodeGrave = 68,
- kKeyCodeMinus = 69,
- kKeyCodeEquals = 70,
- kKeyCodeLeftBracket = 71,
- kKeyCodeRightBracket = 72,
- kKeyCodeBackslash = 73,
- kKeyCodeSemicolon = 74,
- kKeyCodeApostrophe = 75,
- kKeyCodeSlash = 76,
- kKeyCodeAt = 77,
- kKeyCodeNum = 78,
- kKeyCodeHeadSetHook = 79,
- kKeyCodeFocus = 80,
- kKeyCodePlus = 81,
- kKeyCodeMenu = 82,
- kKeyCodeNotification = 83,
- kKeyCodeSearch = 84,
- kKeyCodePlayPause = 85,
- kKeyCodeStop = 86,
- kKeyCodeNextSong = 87,
- kKeyCodePreviousSong = 88,
- kKeyCodeRewind = 89,
- kKeyCodeForward = 90,
- kKeyCodeMute = 91
-} KeyCode;
-
static const KeycodeLabel FLAGS[] = {
{ "WAKE", 0x00000001 },
{ "WAKE_DROPPED", 0x00000002 },
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index a213c09..4e65a2d 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -20,31 +20,28 @@
#include <utils/TypeHelpers.h>
#include <ui/Point.h>
+#include <android/rect.h>
+
namespace android {
-class Rect
+class Rect : public ARect
{
public:
- int left;
- int top;
- int right;
- int bottom;
-
- typedef int value_type;
+ typedef int32_t value_type;
// we don't provide copy-ctor and operator= on purpose
// because we want the compiler generated versions
inline Rect() {
}
- inline Rect(int w, int h)
- : left(0), top(0), right(w), bottom(h) {
+ inline Rect(int32_t w, int32_t h) {
+ left = top = 0; right = w; bottom = h;
}
- inline Rect(int l, int t, int r, int b)
- : left(l), top(t), right(r), bottom(b) {
+ inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) {
+ left = l; top = t; right = r; bottom = b;
}
- inline Rect(const Point& lt, const Point& rb)
- : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) {
+ inline Rect(const Point& lt, const Point& rb) {
+ left = lt.x; top = lt.y; right = rb.x; bottom = rb.y;
}
void makeInvalid();
@@ -68,12 +65,12 @@ public:
}
// rectangle's width
- inline int width() const {
+ inline int32_t width() const {
return right-left;
}
// rectangle's height
- inline int height() const {
+ inline int32_t height() const {
return bottom-top;
}
@@ -136,12 +133,12 @@ public:
const Rect operator + (const Point& rhs) const;
const Rect operator - (const Point& rhs) const;
- void translate(int dx, int dy) { // legacy, don't use.
+ void translate(int32_t dx, int32_t dy) { // legacy, don't use.
offsetBy(dx, dy);
}
- Rect& offsetTo(int x, int y);
- Rect& offsetBy(int x, int y);
+ Rect& offsetTo(int32_t x, int32_t y);
+ Rect& offsetBy(int32_t x, int32_t y);
bool intersect(const Rect& with, Rect* result) const;
};
diff --git a/include/ui/android_native_buffer.h b/include/ui/android_native_buffer.h
index 9c92af8..402843e 100644
--- a/include/ui/android_native_buffer.h
+++ b/include/ui/android_native_buffer.h
@@ -33,6 +33,15 @@ typedef struct android_native_buffer_t
common.version = sizeof(android_native_buffer_t);
memset(common.reserved, 0, sizeof(common.reserved));
}
+
+ // Implement the methods that sp<android_native_buffer_t> expects so that it
+ // can be used to automatically refcount android_native_buffer_t's.
+ void incStrong(const void* id) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* id) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
#endif
struct android_native_base_t common;
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index 773fd93..ca89b06 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -22,6 +22,8 @@
#include <hardware/gralloc.h>
+#include <android/native_window.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -41,6 +43,14 @@ extern "C" {
struct android_native_buffer_t;
+typedef struct android_native_rect_t
+{
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} android_native_rect_t;
+
// ---------------------------------------------------------------------------
typedef struct android_native_base_t
@@ -63,15 +73,18 @@ typedef struct android_native_base_t
/* attributes queriable with query() */
enum {
NATIVE_WINDOW_WIDTH = 0,
- NATIVE_WINDOW_HEIGHT = 1,
- NATIVE_WINDOW_FORMAT = 2,
+ NATIVE_WINDOW_HEIGHT,
+ NATIVE_WINDOW_FORMAT,
};
/* valid operations for the (*perform)() hook */
enum {
NATIVE_WINDOW_SET_USAGE = 0,
- NATIVE_WINDOW_CONNECT = 1,
- NATIVE_WINDOW_DISCONNECT = 2
+ NATIVE_WINDOW_CONNECT,
+ NATIVE_WINDOW_DISCONNECT,
+ NATIVE_WINDOW_SET_CROP,
+ NATIVE_WINDOW_SET_BUFFER_COUNT,
+ NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
};
/* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -79,16 +92,25 @@ enum {
NATIVE_WINDOW_API_EGL = 1
};
-typedef struct android_native_window_t
+struct ANativeWindow
{
#ifdef __cplusplus
- android_native_window_t()
+ ANativeWindow()
: flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
{
common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
- common.version = sizeof(android_native_window_t);
+ common.version = sizeof(ANativeWindow);
memset(common.reserved, 0, sizeof(common.reserved));
}
+
+ // Implement the methods that sp<ANativeWindow> expects so that it
+ // can be used to automatically refcount ANativeWindow's.
+ void incStrong(const void* id) const {
+ common.incRef(const_cast<android_native_base_t*>(&common));
+ }
+ void decStrong(const void* id) const {
+ common.decRef(const_cast<android_native_base_t*>(&common));
+ }
#endif
struct android_native_base_t common;
@@ -115,7 +137,7 @@ typedef struct android_native_window_t
*
* Returns 0 on success or -errno on error.
*/
- int (*setSwapInterval)(struct android_native_window_t* window,
+ int (*setSwapInterval)(struct ANativeWindow* window,
int interval);
/*
@@ -125,7 +147,7 @@ typedef struct android_native_window_t
*
* Returns 0 on success or -errno on error.
*/
- int (*dequeueBuffer)(struct android_native_window_t* window,
+ int (*dequeueBuffer)(struct ANativeWindow* window,
struct android_native_buffer_t** buffer);
/*
@@ -135,7 +157,7 @@ typedef struct android_native_window_t
*
* Returns 0 on success or -errno on error.
*/
- int (*lockBuffer)(struct android_native_window_t* window,
+ int (*lockBuffer)(struct ANativeWindow* window,
struct android_native_buffer_t* buffer);
/*
* hook called by EGL when modifications to the render buffer are done.
@@ -145,7 +167,7 @@ typedef struct android_native_window_t
*
* Returns 0 on success or -errno on error.
*/
- int (*queueBuffer)(struct android_native_window_t* window,
+ int (*queueBuffer)(struct ANativeWindow* window,
struct android_native_buffer_t* buffer);
/*
@@ -153,13 +175,13 @@ typedef struct android_native_window_t
*
* Returns 0 on success or -errno on error.
*/
- int (*query)(struct android_native_window_t* window,
+ int (*query)(struct ANativeWindow* window,
int what, int* value);
/*
* hook used to perform various operations on the surface.
* (*perform)() is a generic mechanism to add functionality to
- * android_native_window_t while keeping backward binary compatibility.
+ * ANativeWindow while keeping backward binary compatibility.
*
* This hook should not be called directly, instead use the helper functions
* defined below.
@@ -171,19 +193,25 @@ typedef struct android_native_window_t
* NATIVE_WINDOW_SET_USAGE
* NATIVE_WINDOW_CONNECT
* NATIVE_WINDOW_DISCONNECT
+ * NATIVE_WINDOW_SET_CROP
+ * NATIVE_WINDOW_SET_BUFFER_COUNT
+ * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
*
*/
- int (*perform)(struct android_native_window_t* window,
+ int (*perform)(struct ANativeWindow* window,
int operation, ... );
void* reserved_proc[3];
-} android_native_window_t;
+};
+// Backwards compatibility... please switch to ANativeWindow.
+typedef struct ANativeWindow android_native_window_t;
/*
- * native_window_set_usage() sets the intended usage flags for the next
- * buffers acquired with (*lockBuffer)() and on.
+ * native_window_set_usage(..., usage)
+ * Sets the intended usage flags for the next buffers
+ * acquired with (*lockBuffer)() and on.
* By default (if this function is never called), a usage of
* GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
* is assumed.
@@ -192,35 +220,83 @@ typedef struct android_native_window_t
*/
static inline int native_window_set_usage(
- android_native_window_t* window, int usage)
+ ANativeWindow* window, int usage)
{
return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
}
/*
- * native_window_connect(..., NATIVE_WINDOW_API_EGL) must be called
- * by EGL when the window is made current.
+ * native_window_connect(..., NATIVE_WINDOW_API_EGL)
+ * Must be called by EGL when the window is made current.
* Returns -EINVAL if for some reason the window cannot be connected, which
* can happen if it's connected to some other API.
*/
static inline int native_window_connect(
- android_native_window_t* window, int api)
+ ANativeWindow* window, int api)
{
return window->perform(window, NATIVE_WINDOW_CONNECT, api);
}
/*
- * native_window_disconnect(..., NATIVE_WINDOW_API_EGL) must be called
- * by EGL when the window is made not current.
+ * native_window_disconnect(..., NATIVE_WINDOW_API_EGL)
+ * Must be called by EGL when the window is made not current.
* An error is returned if for instance the window wasn't connected in the
* first place.
*/
static inline int native_window_disconnect(
- android_native_window_t* window, int api)
+ ANativeWindow* window, int api)
{
return window->perform(window, NATIVE_WINDOW_DISCONNECT, api);
}
+/*
+ * native_window_set_crop(..., crop)
+ * Sets which region of the next queued buffers needs to be considered.
+ * A buffer's crop region is scaled to match the surface's size.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * if 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid,
+ * out of the buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_crop(
+ ANativeWindow* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
+}
+
+/*
+ * native_window_set_buffer_count(..., count)
+ * Sets the number of buffers associated with this native window.
+ */
+static inline int native_window_set_buffer_count(
+ ANativeWindow* window,
+ size_t bufferCount)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
+}
+
+/*
+ * native_window_set_buffers_geometry(..., int w, int h, int format)
+ * All buffers dequeued after this call will have the geometry specified.
+ * In particular, all buffers will have a fixed-size, independent form the
+ * native-window size. They will be appropriately scaled to the window-size
+ * upon composition.
+ *
+ * If all parameters are 0, the normal behavior is restored. That is,
+ * dequeued buffers following this call will be sized to the window's size.
+ *
+ */
+static inline int native_window_set_buffers_geometry(
+ ANativeWindow* window,
+ int w, int h, int format)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+ w, h, format);
+}
// ---------------------------------------------------------------------------
@@ -263,6 +339,15 @@ namespace android {
template <typename NATIVE_TYPE, typename TYPE, typename REF>
class EGLNativeBase : public NATIVE_TYPE, public REF
{
+public:
+ // Disambiguate between the incStrong in REF and NATIVE_TYPE
+ void incStrong(const void* id) const {
+ REF::incStrong(id);
+ }
+ void decStrong(const void* id) const {
+ REF::decStrong(id);
+ }
+
protected:
typedef EGLNativeBase<NATIVE_TYPE, TYPE, REF> BASE;
EGLNativeBase() : NATIVE_TYPE(), REF() {
diff --git a/include/utils/Asset.h b/include/utils/Asset.h
index 5908bcc..2a09095 100644
--- a/include/utils/Asset.h
+++ b/include/utils/Asset.h
@@ -61,15 +61,6 @@ public:
ACCESS_BUFFER,
} AccessMode;
- enum {
- /* data larger than this does not get uncompressed into a buffer */
-#ifdef HAVE_ANDROID_OS
- UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024
-#else
- UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024
-#endif
- };
-
/*
* Read data from the current offset. Returns the actual number of
* bytes read, 0 on EOF, or -1 on error.
@@ -317,6 +308,8 @@ private:
FileMap* mMap; // for memory-mapped input
int mFd; // for file input
+ class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
+
unsigned char* mBuf; // for getBuffer()
};
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index d8994e0..97694ff 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -29,6 +29,24 @@
#include <utils/ZipFileRO.h>
#include <utils/threads.h>
+/*
+ * Native-app access is via the opaque typedef struct AAssetManager in the C namespace.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AAssetManager { };
+
+#ifdef __cplusplus
+};
+#endif
+
+
+/*
+ * Now the proper C++ android-namespace definitions
+ */
+
namespace android {
class Asset; // fwd decl for things that include Asset.h first
@@ -48,7 +66,7 @@ struct ResTable_config;
* The asset hierarchy may be examined like a filesystem, using
* AssetDir objects to peruse a single directory.
*/
-class AssetManager {
+class AssetManager : public AAssetManager {
public:
typedef enum CacheMode {
CACHE_UNKNOWN = 0,
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
new file mode 100644
index 0000000..19c8bf0
--- /dev/null
+++ b/include/utils/BitSet.h
@@ -0,0 +1,67 @@
+/*
+ * 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 UTILS_BITSET_H
+#define UTILS_BITSET_H
+
+#include <stdint.h>
+
+/*
+ * Contains some bit manipulation helpers.
+ */
+
+namespace android {
+
+// A simple set of 32 bits that can be individually marked or cleared.
+struct BitSet32 {
+ uint32_t value;
+
+ inline BitSet32() : value(0) { }
+ explicit inline BitSet32(uint32_t value) : value(value) { }
+
+ // Gets the value associated with a particular bit index.
+ static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; }
+
+ // Clears the bit set.
+ inline void clear() { value = 0; }
+
+ // Returns true if the bit set does not contain any marked bits.
+ inline bool isEmpty() const { return ! value; }
+
+ // Returns true if the specified bit is marked.
+ inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
+
+ // Marks the specified bit.
+ inline void markBit(uint32_t n) { value |= valueForBit(n); }
+
+ // Clears the specified bit.
+ inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); }
+
+ // Finds the first marked bit in the set.
+ // Result is undefined if all bits are unmarked.
+ inline uint32_t firstMarkedBit() const { return __builtin_clz(value); }
+
+ // Finds the first unmarked bit in the set.
+ // Result is undefined if all bits are marked.
+ inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
+
+ inline bool operator== (const BitSet32& other) const { return value == other.value; }
+ inline bool operator!= (const BitSet32& other) const { return value != other.value; }
+};
+
+} // namespace android
+
+#endif // UTILS_BITSET_H
diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h
deleted file mode 100644
index 8e22b0f..0000000
--- a/include/utils/Buffer.h
+++ /dev/null
@@ -1,107 +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 __UTILS_BUFFER_H__
-#define __UTILS_BUFFER_H__ 1
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-namespace android {
-
-class Buffer
-{
-private:
- char *buf;
- int bufsiz;
- int used;
- void ensureCapacity(int len);
-
- void
- makeRoomFor(int len)
- {
- if (len + used >= bufsiz) {
- bufsiz = (len + used) * 3/2 + 2;
- char *blah = new char[bufsiz];
-
- memcpy(blah, buf, used);
- delete[] buf;
- buf = blah;
- }
- }
-
-public:
- Buffer()
- {
- bufsiz = 16;
- buf = new char[bufsiz];
- clear();
- }
-
- ~Buffer()
- {
- delete[] buf;
- }
-
- void
- clear()
- {
- buf[0] = '\0';
- used = 0;
- }
-
- int
- length()
- {
- return used;
- }
-
- void
- append(const char c)
- {
- makeRoomFor(1);
- buf[used] = c;
- used++;
- buf[used] = '\0';
- }
-
- void
- append(const char *s, int len)
- {
- makeRoomFor(len);
-
- memcpy(buf + used, s, len);
- used += len;
- buf[used] = '\0';
- }
-
- void
- append(const char *s)
- {
- append(s, strlen(s));
- }
-
- char *
- getBytes()
- {
- return buf;
- }
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
new file mode 100644
index 0000000..075927c
--- /dev/null
+++ b/include/utils/ObbFile.h
@@ -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.
+ */
+
+#ifndef OBBFILE_H_
+#define OBBFILE_H_
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class ObbFile : public RefBase {
+protected:
+ virtual ~ObbFile();
+
+public:
+ ObbFile();
+
+ bool readFrom(const char* filename);
+ bool readFrom(int fd);
+ bool writeTo(const char* filename);
+ bool writeTo(int fd);
+
+ const char* getFileName() const {
+ return mFileName;
+ }
+
+ const String8 getPackageName() const {
+ return mPackageName;
+ }
+
+ int32_t getVersion() const {
+ return mVersion;
+ }
+
+ void setPackageName(String8 packageName) {
+ mPackageName = packageName;
+ }
+
+ void setVersion(int32_t version) {
+ mVersion = version;
+ }
+
+ static inline uint32_t get4LE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ }
+
+ static inline void put4LE(unsigned char* buf, uint32_t val) {
+ buf[0] = val & 0xFF;
+ buf[1] = (val >> 8) & 0xFF;
+ buf[2] = (val >> 16) & 0xFF;
+ buf[3] = (val >> 24) & 0xFF;
+ }
+
+private:
+ /* Package name this ObbFile is associated with */
+ String8 mPackageName;
+
+ /* Package version this ObbFile is associated with */
+ int32_t mVersion;
+
+ const char* mFileName;
+
+ size_t mFileSize;
+
+ unsigned char* mReadBuf;
+
+ bool parseObbFile(int fd);
+};
+
+}
+#endif /* OBBFILE_H_ */
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
new file mode 100644
index 0000000..81230e8
--- /dev/null
+++ b/include/utils/PollLoop.h
@@ -0,0 +1,197 @@
+/*
+ * 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 UTILS_POLL_LOOP_H
+#define UTILS_POLL_LOOP_H
+
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include <sys/poll.h>
+
+#include <android/looper.h>
+
+struct ALooper : public android::RefBase {
+protected:
+ virtual ~ALooper() { }
+
+public:
+ ALooper() { }
+};
+
+namespace android {
+
+/**
+ * A basic file descriptor polling loop based on poll() with callbacks.
+ */
+class PollLoop : public ALooper {
+protected:
+ virtual ~PollLoop();
+
+public:
+ PollLoop(bool allowNonCallbacks);
+
+ /**
+ * A callback that it to be invoked when an event occurs on a file descriptor.
+ * Specifies the events that were triggered and the user data provided when the
+ * callback was set.
+ *
+ * Returns true if the callback should be kept, false if it should be removed automatically
+ * after the callback returns.
+ */
+ typedef bool (*Callback)(int fd, int events, void* data);
+
+ enum {
+ POLL_CALLBACK = ALOOPER_POLL_CALLBACK,
+ POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT,
+ POLL_ERROR = ALOOPER_POLL_ERROR,
+ };
+
+ /**
+ * Performs a single call to poll() with optional timeout in milliseconds.
+ * Invokes callbacks for all file descriptors on which an event occurred.
+ *
+ * If the timeout is zero, returns immediately without blocking.
+ * If the timeout is negative, waits indefinitely until awoken.
+ *
+ * Returns ALOOPER_POLL_CALLBACK if a callback was invoked.
+ *
+ * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
+ * timeout expired.
+ *
+ * Returns ALOPER_POLL_ERROR if an error occurred.
+ *
+ * Returns a value >= 0 containing a file descriptor if it has data
+ * and it has no callback function (requiring the caller here to handle it).
+ * In this (and only this) case outEvents and outData will contain the poll
+ * events and data associated with the fd.
+ *
+ * This method must only be called on the thread owning the PollLoop.
+ * This method blocks until either a file descriptor is signalled, a timeout occurs,
+ * or wake() is called.
+ * This method does not return until it has finished invoking the appropriate callbacks
+ * for all file descriptors that were signalled.
+ */
+ int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL);
+
+ /**
+ * Wakes the loop asynchronously.
+ *
+ * This method can be called on any thread.
+ * This method returns immediately.
+ */
+ void wake();
+
+ /**
+ * Control whether this PollLoop instance allows using IDs instead
+ * of callbacks.
+ */
+ bool getAllowNonCallbacks() const;
+
+ /**
+ * Sets the callback for a file descriptor, replacing the existing one, if any.
+ * It is an error to call this method with events == 0 or callback == NULL.
+ *
+ * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events
+ * even if it is not explicitly requested when registered.
+ *
+ * This method can be called on any thread.
+ * This method may block briefly if it needs to wake the poll loop.
+ */
+ void setCallback(int fd, int events, Callback callback, void* data = NULL);
+
+ /**
+ * Like setCallback(), but for the NDK callback function.
+ */
+ void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+ void* data);
+
+ /**
+ * Removes the callback for a file descriptor, if one exists.
+ *
+ * When this method returns, it is safe to close the file descriptor since the poll loop
+ * will no longer have a reference to it. However, it is possible for the callback to
+ * already be running or for it to run one last time if the file descriptor was already
+ * signalled. Calling code is responsible for ensuring that this case is safely handled.
+ * For example, if the callback takes care of removing itself during its own execution either
+ * by returning false or calling this method, then it can be guaranteed to not be invoked
+ * again at any later time unless registered anew.
+ *
+ * This method can be called on any thread.
+ * This method may block briefly if it needs to wake the poll loop.
+ *
+ * Returns true if a callback was actually removed, false if none was registered.
+ */
+ bool removeCallback(int fd);
+
+ /**
+ * Set the given PollLoop to be associated with the
+ * calling thread. There must be a 1:1 relationship between
+ * PollLoop and thread.
+ */
+ static void setForThread(const sp<PollLoop>& pollLoop);
+
+ /**
+ * Return the PollLoop associated with the calling thread.
+ */
+ static sp<PollLoop> getForThread();
+
+private:
+ struct RequestedCallback {
+ Callback callback;
+ ALooper_callbackFunc* looperCallback;
+ void* data;
+ };
+
+ struct PendingCallback {
+ int fd;
+ int events;
+ Callback callback;
+ ALooper_callbackFunc* looperCallback;
+ void* data;
+ };
+
+ const bool mAllowNonCallbacks;
+
+ Mutex mLock;
+ bool mPolling;
+ uint32_t mWaiters;
+ Condition mAwake;
+ Condition mResume;
+
+ int mWakeReadPipeFd;
+ int mWakeWritePipeFd;
+
+ Vector<struct pollfd> mRequestedFds;
+ Vector<RequestedCallback> mRequestedCallbacks;
+
+ Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
+ Vector<PendingCallback> mPendingFds; // used privately by pollOnce
+ size_t mPendingFdsPos;
+
+ void openWakePipe();
+ void closeWakePipe();
+
+ void setCallbackCommon(int fd, int events, Callback callback,
+ ALooper_callbackFunc* looperCallback, void* data);
+ ssize_t getRequestIndexLocked(int fd);
+ void wakeAndLock();
+ static void threadDestructor(void *st);
+};
+
+} // namespace android
+
+#endif // UTILS_POLL_LOOP_H
diff --git a/include/utils/Pool.h b/include/utils/Pool.h
new file mode 100644
index 0000000..2ee768e
--- /dev/null
+++ b/include/utils/Pool.h
@@ -0,0 +1,71 @@
+/*
+ * 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 UTILS_POOL_H
+#define UTILS_POOL_H
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+class PoolImpl {
+public:
+ PoolImpl(size_t objSize);
+ ~PoolImpl();
+
+ void* allocImpl();
+ void freeImpl(void* obj);
+
+private:
+ size_t mObjSize;
+};
+
+/*
+ * A homogeneous typed memory pool for fixed size objects.
+ * Not intended to be thread-safe.
+ */
+template<typename T>
+class Pool : private PoolImpl {
+public:
+ /* Creates an initially empty pool. */
+ Pool() : PoolImpl(sizeof(T)) { }
+
+ /* Destroys the pool.
+ * Assumes that the pool is empty. */
+ ~Pool() { }
+
+ /* Allocates an object from the pool, growing the pool if needed. */
+ inline T* alloc() {
+ void* mem = allocImpl();
+ if (! traits<T>::has_trivial_ctor) {
+ return new (mem) T();
+ } else {
+ return static_cast<T*>(mem);
+ }
+ }
+
+ /* Frees an object from the pool. */
+ inline void free(T* obj) {
+ if (! traits<T>::has_trivial_dtor) {
+ obj->~T();
+ }
+ freeImpl(obj);
+ }
+};
+
+} // namespace android
+
+#endif // UTILS_POOL_H
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
index bd7f28c..9c64ac0 100644
--- a/include/utils/RefBase.h
+++ b/include/utils/RefBase.h
@@ -333,9 +333,10 @@ sp<T>::~sp()
template<typename T>
sp<T>& sp<T>::operator = (const sp<T>& other) {
- if (other.m_ptr) other.m_ptr->incStrong(this);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherPtr->incStrong(this);
if (m_ptr) m_ptr->decStrong(this);
- m_ptr = other.m_ptr;
+ m_ptr = otherPtr;
return *this;
}
@@ -351,9 +352,10 @@ sp<T>& sp<T>::operator = (T* other)
template<typename T> template<typename U>
sp<T>& sp<T>::operator = (const sp<U>& other)
{
- if (other.m_ptr) other.m_ptr->incStrong(this);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherPtr->incStrong(this);
if (m_ptr) m_ptr->decStrong(this);
- m_ptr = other.m_ptr;
+ m_ptr = otherPtr;
return *this;
}
@@ -466,10 +468,12 @@ wp<T>& wp<T>::operator = (T* other)
template<typename T>
wp<T>& wp<T>::operator = (const wp<T>& other)
{
- if (other.m_ptr) other.m_refs->incWeak(this);
+ weakref_type* otherRefs(other.m_refs);
+ T* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.m_ptr;
- m_refs = other.m_refs;
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
return *this;
}
@@ -478,8 +482,9 @@ wp<T>& wp<T>::operator = (const sp<T>& other)
{
weakref_type* newRefs =
other != NULL ? other->createWeak(this) : 0;
+ T* otherPtr(other.m_ptr);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.get();
+ m_ptr = otherPtr;
m_refs = newRefs;
return *this;
}
@@ -498,10 +503,12 @@ wp<T>& wp<T>::operator = (U* other)
template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const wp<U>& other)
{
- if (other.m_ptr) other.m_refs->incWeak(this);
+ weakref_type* otherRefs(other.m_refs);
+ U* otherPtr(other.m_ptr);
+ if (otherPtr) otherRefs->incWeak(this);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.m_ptr;
- m_refs = other.m_refs;
+ m_ptr = otherPtr;
+ m_refs = otherRefs;
return *this;
}
@@ -510,8 +517,9 @@ wp<T>& wp<T>::operator = (const sp<U>& other)
{
weakref_type* newRefs =
other != NULL ? other->createWeak(this) : 0;
+ U* otherPtr(other.m_ptr);
if (m_ptr) m_refs->decWeak(this);
- m_ptr = other.get();
+ m_ptr = otherPtr;
m_refs = newRefs;
return *this;
}
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index b701ce7..c7d9ff1 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -933,6 +933,7 @@ struct ResTable_config
SCREENSIZE_SMALL = 0x01,
SCREENSIZE_NORMAL = 0x02,
SCREENSIZE_LARGE = 0x03,
+ SCREENSIZE_XLARGE = 0x04,
// screenLayout bits for wide/long screen variation.
MASK_SCREENLONG = 0x30,
@@ -1208,7 +1209,28 @@ struct ResTable_config
if (screenLayout || o.screenLayout) {
if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0
&& (requested->screenLayout & MASK_SCREENSIZE)) {
- return (screenLayout & MASK_SCREENSIZE);
+ // A little backwards compatibility here: undefined is
+ // considered equivalent to normal. But only if the
+ // requested size is at least normal; otherwise, small
+ // is better than the default.
+ int mySL = (screenLayout & MASK_SCREENSIZE);
+ int oSL = (o.screenLayout & MASK_SCREENSIZE);
+ int fixedMySL = mySL;
+ int fixedOSL = oSL;
+ if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) {
+ if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL;
+ if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL;
+ }
+ // For screen size, the best match is the one that is
+ // closest to the requested screen size, but not over
+ // (the not over part is dealt with in match() below).
+ if (fixedMySL == fixedOSL) {
+ // If the two are the same, but 'this' is actually
+ // undefined, then the other is really a better match.
+ if (mySL == 0) return false;
+ return true;
+ }
+ return fixedMySL >= fixedOSL;
}
if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0
&& (requested->screenLayout & MASK_SCREENLONG)) {
@@ -1370,8 +1392,11 @@ struct ResTable_config
if (screenConfig != 0) {
const int screenSize = screenLayout&MASK_SCREENSIZE;
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
- if (setScreenSize != 0 && screenSize != 0
- && screenSize != setScreenSize) {
+ // Any screen sizes for larger screens than the setting do not
+ // match.
+ if ((setScreenSize != 0 && screenSize != 0
+ && screenSize > setScreenSize) ||
+ (setScreenSize == 0 && screenSize != 0)) {
return false;
}
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
index bc7626a..3b975b4 100644
--- a/include/utils/Singleton.h
+++ b/include/utils/Singleton.h
@@ -54,11 +54,13 @@ private:
* (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,
* and avoid to have a copy of them in each compilation units Singleton<TYPE>
* is used.
+ * NOTE: we use a version of Mutex ctor that takes a parameter, because
+ * for some unknown reason using the default ctor doesn't emit the variable!
*/
-#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
- template class Singleton< TYPE >; \
- template< class TYPE > Mutex Singleton< TYPE >::sLock; \
+#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
+ template class Singleton< TYPE >; \
+ template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \
template<> TYPE* Singleton< TYPE >::sInstance(0);
diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h
index cc0bebc..693dd3c 100644
--- a/include/utils/StopWatch.h
+++ b/include/utils/StopWatch.h
@@ -37,6 +37,8 @@ public:
const char* name() const;
nsecs_t lap();
nsecs_t elapsedTime() const;
+
+ void reset();
private:
const char* mName;
diff --git a/include/utils/StreamingZipInflater.h b/include/utils/StreamingZipInflater.h
new file mode 100644
index 0000000..16867d8
--- /dev/null
+++ b/include/utils/StreamingZipInflater.h
@@ -0,0 +1,82 @@
+/*
+ * 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 __LIBS_STREAMINGZIPINFLATER_H
+#define __LIBS_STREAMINGZIPINFLATER_H
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <zlib.h>
+
+namespace android {
+
+class StreamingZipInflater {
+public:
+ static const size_t INPUT_CHUNK_SIZE = 64 * 1024;
+ static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024;
+
+ // Flavor that pages in the compressed data from a fd
+ StreamingZipInflater(int fd, off_t compDataStart, size_t uncompSize, size_t compSize);
+
+ // Flavor that gets the compressed data from an in-memory buffer
+ StreamingZipInflater(class FileMap* dataMap, size_t uncompSize);
+
+ ~StreamingZipInflater();
+
+ // read 'count' bytes of uncompressed data from the current position. outBuf may
+ // be NULL, in which case the data is consumed and discarded.
+ ssize_t read(void* outBuf, size_t count);
+
+ // 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 seekAbsolute(off_t absoluteInputPosition);
+
+private:
+ void initInflateState();
+ int readNextChunk();
+
+ // where to find the uncompressed data
+ int mFd;
+ off_t mInFileStart; // where the compressed data lives in the file
+ class FileMap* mDataMap;
+
+ z_stream mInflateState;
+ bool mStreamNeedsInit;
+
+ // output invariants for this asset
+ uint8_t* mOutBuf; // output buf for decompressed bytes
+ size_t mOutBufSize; // allocated size of mOutBuf
+ size_t mOutTotalSize; // total uncompressed size of the blob
+
+ // current output state bookkeeping
+ off_t mOutCurPosition; // current position in total offset
+ size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf
+ size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf
+
+ // input invariants
+ uint8_t* mInBuf;
+ size_t mInBufSize; // allocated size of mInBuf;
+ size_t mInTotalSize; // total size of compressed data for this blob
+
+ // input state bookkeeping
+ size_t mInNextChunkOffset; // offset from start of blob at which the next input chunk lies
+ // the z_stream contains state about input block consumption
+};
+
+}
+
+#endif
diff --git a/include/utils/String8.h b/include/utils/String8.h
index c4b18a4..0b18fe3 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -171,6 +171,8 @@ public:
status_t append(const char* other);
status_t append(const char* other, size_t numChars);
+ status_t appendFormat(const char* fmt, ...);
+
// Note that this function takes O(N) time to calculate the value.
// No cache value is stored.
size_t getUtf32Length() const;
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index ad59fd6..ec851bd 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -114,13 +114,19 @@ public:
ssize_t appendVector(const Vector<TYPE>& vector);
+ //! insert an array at a given index
+ ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length);
+
+ //! append an array at the end of this vector
+ ssize_t appendArray(const TYPE* array, size_t length);
+
/*!
* add/insert/replace items
*/
//! insert one or several items initialized with their default constructor
inline ssize_t insertAt(size_t index, size_t numItems = 1);
- //! insert on onr several items initialized from a prototype item
+ //! insert one or several items initialized from a prototype item
ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
//! pop the top of the stack (removes the last element). No-op if the stack's empty
inline void pop();
@@ -259,6 +265,16 @@ ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
}
template<class TYPE> inline
+ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
+ return VectorImpl::insertArrayAt(array, index, length);
+}
+
+template<class TYPE> inline
+ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
+ return VectorImpl::appendArray(array, length);
+}
+
+template<class TYPE> inline
ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
return VectorImpl::insertAt(&item, index, numItems);
}
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
index 49b03f1..c4ec2ff 100644
--- a/include/utils/VectorImpl.h
+++ b/include/utils/VectorImpl.h
@@ -65,9 +65,11 @@ public:
size_t capacity() const;
ssize_t setCapacity(size_t size);
- /*! append/insert another vector */
+ /*! append/insert another vector or array */
ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
/*! add/insert/replace items */
ssize_t insertAt(size_t where, size_t numItems = 1);
@@ -184,6 +186,8 @@ private:
void push(const void* item);
ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
ssize_t appendVector(const VectorImpl& vector);
+ ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+ ssize_t appendArray(const void* array, size_t length);
ssize_t insertAt(size_t where, size_t numItems = 1);
ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
ssize_t replaceAt(size_t index);
diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h
index 30e0036..e38bf66 100644
--- a/include/utils/ZipFileCRO.h
+++ b/include/utils/ZipFileCRO.h
@@ -47,8 +47,8 @@ extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip,
const char* fileName);
extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry,
- 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);
extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd);
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 51c4f2f..97d31f4 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -58,14 +58,19 @@ typedef void* ZipEntryRO;
class ZipFileRO {
public:
ZipFileRO()
- : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL)
+ : mFd(-1), mFileName(NULL), mFileLength(-1),
+ mDirectoryMap(NULL),
+ mNumEntries(-1), mDirectoryOffset(-1),
+ mHashTableSize(-1), mHashTable(NULL)
{}
~ZipFileRO() {
free(mHashTable);
- if (mFileMap)
- mFileMap->release();
+ if (mDirectoryMap)
+ mDirectoryMap->release();
if (mFd >= 0)
close(mFd);
+ if (mFileName)
+ free(mFileName);
}
/*
@@ -118,8 +123,8 @@ public:
* Returns "false" if "entry" is bogus or if the data in the Zip file
* appears to be bad.
*/
- bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen,
- long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const;
+ bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const;
/*
* Create a new FileMap object that maps a subset of the archive. For
@@ -155,13 +160,13 @@ public:
* Utility function: uncompress deflated data, buffer to buffer.
*/
static bool inflateBuffer(void* outBuf, const void* inBuf,
- long uncompLen, long compLen);
+ size_t uncompLen, size_t compLen);
/*
* Utility function: uncompress deflated data, buffer to fd.
*/
static bool inflateBuffer(int fd, const void* inBuf,
- long uncompLen, long compLen);
+ size_t uncompLen, size_t compLen);
/*
* Some basic functions for raw data manipulation. "LE" means
@@ -179,6 +184,9 @@ private:
ZipFileRO(const ZipFileRO& src);
ZipFileRO& operator=(const ZipFileRO& src);
+ /* locate and parse the central directory */
+ bool mapCentralDirectory(void);
+
/* parse the archive, prepping internal structures */
bool parseZipArchive(void);
@@ -203,12 +211,21 @@ private:
/* open Zip archive */
int mFd;
+ /* zip file name */
+ char* mFileName;
+
+ /* length of file */
+ size_t mFileLength;
+
/* mapped file */
- FileMap* mFileMap;
+ FileMap* mDirectoryMap;
/* number of entries in the Zip archive */
int mNumEntries;
+ /* CD directory offset in the Zip archive */
+ off_t mDirectoryOffset;
+
/*
* We know how many entries are in the Zip archive, so we have a
* fixed-size hash table. We probe for an empty slot.
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 5ac0c5e..1bcfaed 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -295,6 +295,96 @@ typedef Mutex::Autolock AutoMutex;
/*****************************************************************************/
+#if defined(HAVE_PTHREADS)
+
+/*
+ * Simple mutex class. The implementation is system-dependent.
+ *
+ * The mutex must be unlocked by the thread that locked it. They are not
+ * recursive, i.e. the same thread can't lock it multiple times.
+ */
+class RWLock {
+public:
+ enum {
+ PRIVATE = 0,
+ SHARED = 1
+ };
+
+ RWLock();
+ RWLock(const char* name);
+ RWLock(int type, const char* name = NULL);
+ ~RWLock();
+
+ status_t readLock();
+ status_t tryReadLock();
+ status_t writeLock();
+ status_t tryWriteLock();
+ void unlock();
+
+ class AutoRLock {
+ public:
+ inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); }
+ inline ~AutoRLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+ class AutoWLock {
+ public:
+ inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); }
+ inline ~AutoWLock() { mLock.unlock(); }
+ private:
+ RWLock& mLock;
+ };
+
+private:
+ // A RWLock cannot be copied
+ RWLock(const RWLock&);
+ RWLock& operator = (const RWLock&);
+
+ pthread_rwlock_t mRWLock;
+};
+
+inline RWLock::RWLock() {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(const char* name) {
+ pthread_rwlock_init(&mRWLock, NULL);
+}
+inline RWLock::RWLock(int type, const char* name) {
+ if (type == SHARED) {
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init(&attr);
+ pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_rwlock_init(&mRWLock, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ } else {
+ pthread_rwlock_init(&mRWLock, NULL);
+ }
+}
+inline RWLock::~RWLock() {
+ pthread_rwlock_destroy(&mRWLock);
+}
+inline status_t RWLock::readLock() {
+ return -pthread_rwlock_rdlock(&mRWLock);
+}
+inline status_t RWLock::tryReadLock() {
+ return -pthread_rwlock_tryrdlock(&mRWLock);
+}
+inline status_t RWLock::writeLock() {
+ return -pthread_rwlock_wrlock(&mRWLock);
+}
+inline status_t RWLock::tryWriteLock() {
+ return -pthread_rwlock_trywrlock(&mRWLock);
+}
+inline void RWLock::unlock() {
+ pthread_rwlock_unlock(&mRWLock);
+}
+
+#endif // HAVE_PTHREADS
+
+/*****************************************************************************/
+
/*
* Condition variable class. The implementation is system-dependent.
*
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/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..f6582e6 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -367,6 +367,16 @@ int64_t IPCThreadState::clearCallingIdentity()
return token;
}
+void IPCThreadState::setStrictModePolicy(int32_t policy)
+{
+ mStrictModePolicy = policy;
+}
+
+int32_t IPCThreadState::getStrictModePolicy() const
+{
+ return mStrictModePolicy;
+}
+
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
@@ -588,7 +598,8 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy)
}
IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self()), mMyThreadId(androidGetTid())
+ : mProcess(ProcessState::self()), mMyThreadId(androidGetTid()),
+ mStrictModePolicy(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
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..18f75df 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,28 @@ 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();
+ }
+ threadState->setStrictModePolicy(strictPolicy);
const String16 str(readString16());
if (str == interface) {
return true;
@@ -457,7 +473,7 @@ bool Parcel::enforceInterface(const String16& interface) const
String8(interface).string(), String8(str).string());
return false;
}
-}
+}
const size_t* Parcel::objects() const
{
@@ -750,6 +766,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 +959,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/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..3396f25
--- /dev/null
+++ b/libs/gui/SensorEventQueue.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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/PollLoop.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]));
+ if (size >= 0) {
+ if (size % sizeof(events[0])) {
+ // partial read!!! should never happen.
+ return -EINVAL;
+ }
+ // returns number of events read
+ size /= sizeof(events[0]);
+ }
+ return size;
+}
+
+sp<PollLoop> SensorEventQueue::getPollLoop() const
+{
+ Mutex::Autolock _l(mLock);
+ if (mPollLoop == 0) {
+ mPollLoop = new PollLoop(true);
+ mPollLoop->setCallback(getFd(), POLLIN, NULL, NULL);
+ }
+ return mPollLoop;
+}
+
+status_t SensorEventQueue::waitForEvent() const
+{
+ const int fd = getFd();
+ sp<PollLoop> pollLoop(getPollLoop());
+ int32_t result = pollLoop->pollOnce(-1, NULL, NULL);
+ return (result == fd) ? NO_ERROR : -1;
+}
+
+status_t SensorEventQueue::wake() const
+{
+ sp<PollLoop> pollLoop(getPollLoop());
+ pollLoop->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/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/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/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_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..5c111f6 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
@@ -136,6 +145,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();
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..156a7db 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,52 @@ 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::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 +113,37 @@ 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;
}
// ----------------------------------------------------------------------------
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,16 +151,16 @@ SharedBufferBase::~SharedBufferBase()
{
}
-uint32_t SharedBufferBase::getIdentity()
+status_t SharedBufferBase::getStatus() const
{
SharedBufferStack& stack( *mSharedStack );
- return stack.identity;
+ return stack.status;
}
-status_t SharedBufferBase::getStatus() const
+int32_t SharedBufferBase::getIdentity() const
{
SharedBufferStack& stack( *mSharedStack );
- return stack.status;
+ return stack.identity;
}
size_t SharedBufferBase::getFrontBuffer() const
@@ -132,16 +175,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 +228,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)
@@ -193,8 +267,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 +282,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.index[head], &stack.inUse);
// Decrement the number of queued buffers
int32_t queued;
@@ -221,16 +298,15 @@ 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;
+ android_atomic_write(stack.index[head], &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 +326,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), undoDequeueTail(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",
@@ -301,9 +367,10 @@ ssize_t SharedBufferClient::dequeue()
LOGW("dequeue probably called from multiple threads!");
}
- int dequeued = tail;
+ undoDequeueTail = 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 +380,23 @@ ssize_t SharedBufferClient::dequeue()
status_t SharedBufferClient::undoDequeue(int buf)
{
+ RWLock::AutoRLock _rd(mLock);
+
+ // TODO: we can only undo the previous dequeue, we should
+ // enforce that in the api
UndoDequeueUpdate update(this);
status_t err = updateCondition( update );
if (err == NO_ERROR) {
- tail = computeTail();
+ tail = undoDequeueTail;
}
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 +404,105 @@ 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.setCrop(buf, crop);
+}
+
+status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
{
SharedBufferStack& stack( *mSharedStack );
- return stack.setDirtyRegion(buffer, reg);
+ 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 +515,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 = (1<<mNumBuffers)-1;
- android_atomic_or(mask, &stack.reallocMask);
+ 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 );
+ BufferList temp(mBufferList);
+ temp.remove(buffer);
+ uint32_t mask = temp.getMask();
+ android_atomic_or(mask, &stack.reallocMask);
return NO_ERROR;
}
@@ -403,17 +543,62 @@ 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);
}
-Region SharedBufferServer::getDirtyRegion(int buffer) const
+/*
+ * 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 );
- return stack.getDirtyRegion(buffer);
+ 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 +607,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..5ab72cd 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,8 +26,6 @@
#include <utils/CallStack.h>
#include <utils/Log.h>
-#include <pixelflinger/pixelflinger.h>
-
#include <binder/IPCThreadState.h>
#include <binder/IMemory.h>
@@ -55,6 +51,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 +65,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 +104,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 +151,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 +230,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 +272,178 @@ 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);
+}
+
+
+Mutex Surface::sCachedSurfacesLock;
+DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces(wp<Surface>(0));
- mSharedBufferClient = new SharedBufferClient(
- mClient->mControl, mToken, 2, mIdentity);
+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::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;
+
mConnected = 0;
- mNeedFullUpdate = false;
+ mSwapRectangle.makeInvalid();
+ // 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 +451,88 @@ 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::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,21 +544,24 @@ 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;
@@ -492,24 +572,28 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer)
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;
}
@@ -526,12 +610,11 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer)
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));
err = mSharedBufferClient->lock(bufIdx);
LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
return err;
@@ -539,7 +622,6 @@ int Surface::lockBuffer(android_native_buffer_t* buffer)
int Surface::queueBuffer(android_native_buffer_t* buffer)
{
- sp<SurfaceComposerClient> client(getClient());
status_t err = validate();
if (err != NO_ERROR)
return err;
@@ -548,14 +630,15 @@ 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));
+ 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 +661,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 +676,15 @@ 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;
default:
res = NAME_NOT_FOUND;
break;
@@ -608,12 +704,25 @@ 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);
+}
void Surface::setUsage(uint32_t reqUsage)
{
Mutex::Autolock _l(mSurfaceLock);
- mUsage = reqUsage;
+ mBufferInfo.set(reqUsage);
}
int Surface::connect(int api)
@@ -654,19 +763,55 @@ int Surface::disconnect(int api)
return err;
}
-uint32_t Surface::getUsage() const
+int Surface::crop(Rect const* rect)
+{
+ Mutex::Autolock _l(mSurfaceLock);
+ // 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);
- return mUsage;
+ mBufferInfo.set(w, h, format);
+ return NO_ERROR;
}
+// ----------------------------------------------------------------------------
+
int Surface::getConnectedApi() const
{
Mutex::Autolock _l(mSurfaceLock);
return mConnected;
}
-
// ----------------------------------------------------------------------------
status_t Surface::lock(SurfaceInfo* info, bool blocking) {
@@ -677,7 +822,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 +848,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 +924,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 +936,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 +950,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 +974,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..4096ac6 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,20 @@ 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;
}
+// ----------------------------------------------------------------------------
}; // 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_client/tests/SharedBufferStack/Android.mk b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk
new file mode 100644
index 0000000..d3dfe04
--- /dev/null
+++ b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ SharedBufferStackTest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-sharedbufferstack
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
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..9f49348 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -11,6 +11,11 @@ LOCAL_SRC_FILES:= \
GraphicBufferMapper.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
+ Input.cpp \
+ InputDispatcher.cpp \
+ InputManager.cpp \
+ InputReader.cpp \
+ InputTransport.cpp \
IOverlay.cpp \
Overlay.cpp \
PixelFormat.cpp \
@@ -33,3 +38,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..124f7b3 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -54,6 +54,9 @@
*/
#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
@@ -134,9 +137,14 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
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->valid = false;
+ outAxisInfo->minValue = 0;
+ outAxisInfo->maxValue = 0;
+ outAxisInfo->flat = 0;
+ outAxisInfo->fuzz = 0;
+
AutoMutex _l(mLock);
device_t* device = getDevice(deviceId);
if (device == NULL) return -1;
@@ -144,88 +152,58 @@ int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
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",
+ LOGW("Error reading absolute controller %d for device %s fd %d\n",
axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
- return -1;
+ 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 = getDevice(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(mFDs[id_to_index(device->id)].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;
- }
+ if (device != NULL) {
+ return getKeyCodeStateLocked(device, keyCode);
}
-
- return -1;
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getKeycodeState(int code) const
-{
- return getKeycodeState(mFirstKeyboardId, code);
-}
-
-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) {
@@ -239,12 +217,72 @@ 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 = getDevice(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(mFDs[id_to_index(device->id)].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 = getDevice(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,
@@ -295,23 +333,18 @@ 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;
+ outEvent->deviceId = 0;
+ outEvent->type = 0;
+ outEvent->scanCode = 0;
+ outEvent->keyCode = 0;
+ outEvent->flags = 0;
+ outEvent->value = 0;
+ outEvent->when = 0;
status_t err;
- fd_set readfds;
- int maxFd = -1;
- int cc;
int i;
int res;
int pollres;
@@ -333,20 +366,27 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
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;
delete device;
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;
return true;
}
@@ -373,27 +413,36 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
+ device_t* device = mDevices[i];
LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
- mDevices[i]->path.string(),
+ device->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 (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = iev.type;
+ outEvent->scanCode = 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);
+ 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) {
- *outKeycode = 0;
- *outFlags = 0;
+ outEvent->keyCode = AKEYCODE_UNKNOWN;
+ outEvent->flags = 0;
}
} else {
- *outKeycode = iev.code;
+ outEvent->keyCode = iev.code;
}
- *outValue = iev.value;
- *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
+ outEvent->value = iev.value;
+
+ // 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;
} else {
if (res<0) {
@@ -453,37 +502,27 @@ bool EventHub::openPlatformInput(void)
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)
{
@@ -602,27 +641,27 @@ int EventHub::open_device(const char *deviceName)
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
- // 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 +673,58 @@ 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_TOUCH_MAJOR, abs_bitmask)
+ && 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 +746,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,19 +769,27 @@ 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 (hasKeycode(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 (hasKeycode(device, AKEYCODE_DPAD_UP) &&
+ hasKeycode(device, AKEYCODE_DPAD_DOWN) &&
+ hasKeycode(device, AKEYCODE_DPAD_LEFT) &&
+ hasKeycode(device, AKEYCODE_DPAD_RIGHT) &&
+ hasKeycode(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); i++) {
+ if (hasKeycode(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);
}
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 52380a0..6f8948d 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -67,7 +67,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 +117,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,13 +168,13 @@ 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,
+int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
android_native_buffer_t** buffer)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -196,7 +196,7 @@ int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window,
return 0;
}
-int FramebufferNativeWindow::lockBuffer(android_native_window_t* window,
+int FramebufferNativeWindow::lockBuffer(ANativeWindow* window,
android_native_buffer_t* buffer)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -210,7 +210,7 @@ int FramebufferNativeWindow::lockBuffer(android_native_window_t* window,
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);
@@ -224,7 +224,7 @@ int FramebufferNativeWindow::queueBuffer(android_native_window_t* window,
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 +245,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/Input.cpp b/libs/ui/Input.cpp
new file mode 100644
index 0000000..5fbaf09
--- /dev/null
+++ b/libs/ui/Input.cpp
@@ -0,0 +1,230 @@
+//
+// 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 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;
+ 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);
+}
+
+// class InputDeviceProxy
+
+InputDeviceProxy::InputDeviceProxy() {
+}
+
+InputDeviceProxy::~InputDeviceProxy() {
+}
+
+void InputDeviceProxy::getDeviceIds(Vector<int32_t>& outIds) {
+ // TODO use Binder
+}
+
+sp<InputDeviceProxy> InputDeviceProxy::getDevice(int32_t id) {
+ // TODO use Binder
+ return NULL;
+}
+
+} // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
new file mode 100644
index 0000000..b53f140
--- /dev/null
+++ b/libs/ui/InputDispatcher.cpp
@@ -0,0 +1,1776 @@
+//
+// 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
+
+#include <cutils/log.h>
+#include <ui/InputDispatcher.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+namespace android {
+
+// TODO, this needs to be somewhere else, perhaps in the policy
+static inline bool isMovementKey(int32_t keyCode) {
+ return keyCode == AKEYCODE_DPAD_UP
+ || keyCode == AKEYCODE_DPAD_DOWN
+ || keyCode == AKEYCODE_DPAD_LEFT
+ || keyCode == AKEYCODE_DPAD_RIGHT;
+}
+
+static inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+// --- InputDispatcher ---
+
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
+ mPolicy(policy) {
+ mPollLoop = new PollLoop(false);
+
+ mInboundQueue.head.refCount = -1;
+ mInboundQueue.head.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.head.eventTime = LONG_LONG_MIN;
+
+ mInboundQueue.tail.refCount = -1;
+ mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.tail.eventTime = LONG_LONG_MAX;
+
+ mKeyRepeatState.lastKeyEntry = NULL;
+
+ mCurrentInputTargetsValid = false;
+}
+
+InputDispatcher::~InputDispatcher() {
+ resetKeyRepeatLocked();
+
+ while (mConnectionsByReceiveFd.size() != 0) {
+ unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
+ }
+
+ for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) {
+ EventEntry* next = entry->next;
+ mAllocator.releaseEventEntry(next);
+ entry = next;
+ }
+}
+
+void InputDispatcher::dispatchOnce() {
+ nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
+
+ bool skipPoll = false;
+ nsecs_t currentTime;
+ nsecs_t nextWakeupTime = LONG_LONG_MAX;
+ { // acquire lock
+ AutoMutex _l(mLock);
+ 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.
+ // XXX we should handle resetting input state coming out of sleep more generally elsewhere
+ if (keyRepeatTimeout < 0) {
+ resetKeyRepeatLocked();
+ }
+
+ // Detect and process timeouts for all connections and determine if there are any
+ // synchronous event dispatches pending. This step is entirely non-interruptible.
+ bool hasPendingSyncTarget = false;
+ size_t activeConnectionCount = mActiveConnections.size();
+ for (size_t i = 0; i < activeConnectionCount; i++) {
+ Connection* connection = mActiveConnections.itemAt(i);
+
+ if (connection->hasPendingSyncTarget()) {
+ hasPendingSyncTarget = true;
+ }
+
+ nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
+ if (connectionTimeoutTime <= currentTime) {
+ mTimedOutConnections.add(connection);
+ } else if (connectionTimeoutTime < nextWakeupTime) {
+ nextWakeupTime = connectionTimeoutTime;
+ }
+ }
+
+ size_t timedOutConnectionCount = mTimedOutConnections.size();
+ for (size_t i = 0; i < timedOutConnectionCount; i++) {
+ Connection* connection = mTimedOutConnections.itemAt(i);
+ timeoutDispatchCycleLocked(currentTime, connection);
+ skipPoll = true;
+ }
+ mTimedOutConnections.clear();
+
+ // If we don't have a pending sync target, then we can begin delivering a new event.
+ // (Otherwise we wait for dispatch to complete for that target.)
+ if (! hasPendingSyncTarget) {
+ if (mInboundQueue.isEmpty()) {
+ if (mKeyRepeatState.lastKeyEntry) {
+ if (currentTime >= mKeyRepeatState.nextRepeatTime) {
+ processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout);
+ skipPoll = true;
+ } else {
+ if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
+ nextWakeupTime = mKeyRepeatState.nextRepeatTime;
+ }
+ }
+ }
+ } else {
+ // Inbound queue has at least one entry.
+ // Start processing it but leave it on the queue until later so that the
+ // input reader can keep appending samples onto a motion event between the
+ // time we started processing it and the time we finally enqueue dispatch
+ // entries for it.
+ EventEntry* entry = mInboundQueue.head.next;
+
+ switch (entry->type) {
+ case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ ConfigurationChangedEntry* typedEntry =
+ static_cast<ConfigurationChangedEntry*>(entry);
+ processConfigurationChangedLockedInterruptible(currentTime, typedEntry);
+ break;
+ }
+
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* typedEntry = static_cast<KeyEntry*>(entry);
+ processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout);
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* typedEntry = static_cast<MotionEntry*>(entry);
+ processMotionLockedInterruptible(currentTime, typedEntry);
+ break;
+ }
+
+ default:
+ assert(false);
+ break;
+ }
+
+ // Dequeue and release the event entry that we just processed.
+ mInboundQueue.dequeue(entry);
+ mAllocator.releaseEventEntry(entry);
+ skipPoll = true;
+ }
+ }
+
+ // Run any deferred commands.
+ skipPoll |= runCommandsLockedInterruptible();
+ } // release lock
+
+ // If we dispatched anything, don't poll just now. Wait for the next iteration.
+ // Contents may have shifted during flight.
+ if (skipPoll) {
+ return;
+ }
+
+ // Wait for callback or timeout or wake.
+ nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
+ int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
+ mPollLoop->pollOnce(timeoutMillis);
+}
+
+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::processConfigurationChangedLockedInterruptible(
+ nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+ // Reset key repeating in case a keyboard device was added or removed or something.
+ resetKeyRepeatLocked();
+
+ mLock.unlock();
+
+ mPolicy->notifyConfigurationChanged(entry->eventTime);
+
+ mLock.lock();
+}
+
+void InputDispatcher::processKeyLockedInterruptible(
+ nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("processKey - 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",
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action,
+ entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+ entry->downTime);
+#endif
+
+ if (entry->action == AKEY_EVENT_ACTION_DOWN && ! 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 {
+ resetKeyRepeatLocked();
+ }
+
+ identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
+}
+
+void InputDispatcher::processKeyRepeatLockedInterruptible(
+ nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
+ KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+
+ // Search the inbound queue for a key up corresponding to this device.
+ // It doesn't make sense to generate a key repeat event if the key is already up.
+ for (EventEntry* queuedEntry = mInboundQueue.head.next;
+ queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
+ if (queuedEntry->type == EventEntry::TYPE_KEY) {
+ KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
+ if (queuedKeyEntry->deviceId == entry->deviceId
+ && entry->action == AKEY_EVENT_ACTION_UP) {
+ resetKeyRepeatLocked();
+ return;
+ }
+ }
+ }
+
+ // Synthesize a key repeat after the repeat timeout expired.
+ // Reuse the repeated key entry if it is otherwise unreferenced.
+ uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
+ if (entry->refCount == 1) {
+ 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;
+ }
+
+ if (entry->repeatCount == 1) {
+ entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
+ }
+
+ mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("processKeyRepeat - 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, "
+ "repeatCount=%d, downTime=%lld",
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+ entry->repeatCount, entry->downTime);
+#endif
+
+ identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
+}
+
+void InputDispatcher::processMotionLockedInterruptible(
+ nsecs_t currentTime, MotionEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
+ "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action,
+ 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;
+ 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",
+ i, entry->pointerIds[i],
+ sample->pointerCoords[i].x,
+ sample->pointerCoords[i].y,
+ sample->pointerCoords[i].pressure,
+ sample->pointerCoords[i].size);
+ }
+
+ // 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
+
+ identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry);
+}
+
+void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
+ nsecs_t currentTime, KeyEntry* entry) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("identifyInputTargetsAndDispatchKey");
+#endif
+
+ entry->dispatchInProgress = true;
+ mCurrentInputTargetsValid = false;
+ mLock.unlock();
+
+ mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+ entry->downTime, entry->eventTime);
+
+ mCurrentInputTargets.clear();
+ int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
+ entry->policyFlags, entry->injectorPid, entry->injectorUid,
+ mCurrentInputTargets);
+
+ mLock.lock();
+ mCurrentInputTargetsValid = true;
+
+ setInjectionResultLocked(entry, injectionResult);
+
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
+}
+
+void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
+ nsecs_t currentTime, MotionEntry* entry) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("identifyInputTargetsAndDispatchMotion");
+#endif
+
+ entry->dispatchInProgress = true;
+ mCurrentInputTargetsValid = false;
+ mLock.unlock();
+
+ mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action,
+ entry->edgeFlags, entry->metaState,
+ 0, 0, entry->xPrecision, entry->yPrecision,
+ entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
+ entry->firstSample.pointerCoords);
+
+ mCurrentInputTargets.clear();
+ int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
+ entry->policyFlags, entry->injectorPid, entry->injectorUid,
+ mCurrentInputTargets);
+
+ mLock.lock();
+ mCurrentInputTargetsValid = true;
+
+ setInjectionResultLocked(entry, injectionResult);
+
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
+}
+
+void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
+ EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("dispatchEventToCurrentInputTargets - "
+ "resumeWithAppendedMotionSample=%s",
+ resumeWithAppendedMotionSample ? "true" : "false");
+#endif
+
+ assert(eventEntry->dispatchInProgress); // should already have been set to true
+
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
+
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(
+ inputTarget.inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
+ resumeWithAppendedMotionSample);
+ } else {
+ LOGW("Framework requested delivery of an input event to channel '%s' but it "
+ "is not registered with the input dispatcher.",
+ inputTarget.inputChannel->getName().string());
+ }
+ }
+}
+
+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, timeout=%lldns, "
+ "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
+ connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
+ inputTarget->xOffset, inputTarget->yOffset,
+ resumeWithAppendedMotionSample ? "true" : "false");
+#endif
+
+ // Skip this event if the connection status is not normal.
+ // We don't want to queue outbound events at all if the connection is broken or
+ // not responding.
+ if (connection->status != Connection::STATUS_NORMAL) {
+ LOGV("channel '%s' ~ Dropping event because the channel status is %s",
+ connection->getStatusLabel());
+ return;
+ }
+
+ // 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).
+ MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->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
+ dispatchEntry->targetFlags = inputTarget->flags;
+ dispatchEntry->xOffset = inputTarget->xOffset;
+ dispatchEntry->yOffset = inputTarget->yOffset;
+ dispatchEntry->timeout = inputTarget->timeout;
+ dispatchEntry->inProgress = false;
+ dispatchEntry->headMotionSample = NULL;
+ dispatchEntry->tailMotionSample = NULL;
+
+ if (dispatchEntry->isSyncTarget()) {
+ eventEntry->pendingSyncDispatches += 1;
+ }
+
+ // 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.head.next;
+ assert(! dispatchEntry->inProgress);
+
+ // TODO throttle successive ACTION_MOVE motion events for the same device
+ // possible implementation could set a brief poll timeout here and resume starting the
+ // dispatch cycle when elapsed
+
+ // Publish the event.
+ status_t status;
+ switch (dispatchEntry->eventEntry->type) {
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+
+ // Apply target flags.
+ int32_t action = keyEntry->action;
+ int32_t flags = keyEntry->flags;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
+ flags |= AKEY_EVENT_FLAG_CANCELED;
+ }
+
+ // 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);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+
+ // Apply target flags.
+ int32_t action = motionEntry->action;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
+ action = AMOTION_EVENT_ACTION_OUTSIDE;
+ }
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
+ action = AMOTION_EVENT_ACTION_CANCEL;
+ }
+
+ // 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, 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);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ 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);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ 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);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+
+ // Record information about the newly started dispatch cycle.
+ dispatchEntry->inProgress = true;
+
+ connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
+ connection->lastDispatchTime = currentTime;
+
+ nsecs_t timeout = dispatchEntry->timeout;
+ connection->setNextTimeoutTime(currentTime, timeout);
+
+ // 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;
+ }
+
+ // Clear the pending timeout.
+ connection->nextTimeoutTime = LONG_LONG_MAX;
+
+ if (connection->status == Connection::STATUS_NOT_RESPONDING) {
+ // Recovering from an ANR.
+ connection->status = Connection::STATUS_NORMAL;
+
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection, true /*recoveredFromANR*/);
+ } else {
+ // Normal finish. Not much to do here.
+
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection, false /*recoveredFromANR*/);
+ }
+
+ // 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);
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ return;
+ }
+
+ // Start the next dispatch cycle for this connection.
+ while (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.head.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->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(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 (due to ANR).
+ // 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::timeoutDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ timeoutDispatchCycle",
+ connection->getInputChannelName());
+#endif
+
+ if (connection->status != Connection::STATUS_NORMAL) {
+ return;
+ }
+
+ // Enter the not responding state.
+ connection->status = Connection::STATUS_NOT_RESPONDING;
+ connection->lastANRTime = currentTime;
+
+ // Notify other system components.
+ // This enqueues a command which will eventually either call
+ // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
+ onDispatchCycleANRLocked(currentTime, connection);
+}
+
+void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, nsecs_t newTimeout) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked",
+ connection->getInputChannelName());
+#endif
+
+ if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+ return;
+ }
+
+ // Resume normal dispatch.
+ connection->status = Connection::STATUS_NORMAL;
+ connection->setNextTimeoutTime(currentTime, newTimeout);
+}
+
+void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, bool broken) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
+ connection->getInputChannelName(), broken ? "true" : "false");
+#endif
+
+ // Clear the pending timeout.
+ connection->nextTimeoutTime = LONG_LONG_MAX;
+
+ // Clear the outbound queue.
+ if (! connection->outboundQueue.isEmpty()) {
+ do {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ }
+ mAllocator.releaseDispatchEntry(dispatchEntry);
+ } while (! connection->outboundQueue.isEmpty());
+
+ deactivateConnectionLocked(connection.get());
+ }
+
+ // Handle the case where the connection appears to be unrecoverably broken.
+ // Ignore already broken or zombie connections.
+ if (broken) {
+ if (connection->status == Connection::STATUS_NORMAL
+ || connection->status == Connection::STATUS_NOT_RESPONDING) {
+ connection->status = Connection::STATUS_BROKEN;
+
+ // Notify other system components.
+ onDispatchCycleBrokenLocked(currentTime, connection);
+ }
+ }
+}
+
+bool 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 false; // remove the callback
+ }
+
+ nsecs_t currentTime = now();
+
+ sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (events & (POLLERR | POLLHUP | POLLNVAL)) {
+ LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ d->runCommandsLockedInterruptible();
+ return false; // remove the callback
+ }
+
+ if (! (events & POLLIN)) {
+ LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ return true;
+ }
+
+ status_t status = connection->inputPublisher.receiveFinishedSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
+ connection->getInputChannelName(), status);
+ d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+ d->runCommandsLockedInterruptible();
+ return false; // remove the callback
+ }
+
+ d->finishDispatchCycleLocked(currentTime, connection);
+ d->runCommandsLockedInterruptible();
+ return true;
+ } // release lock
+}
+
+void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
+#endif
+
+ bool wasEmpty;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
+
+ wasEmpty = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(newEntry);
+ } // release lock
+
+ if (wasEmpty) {
+ mPollLoop->wake();
+ }
+}
+
+void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime);
+#endif
+
+ // Remove movement keys from the queue from most recent to least recent, stopping at the
+ // first non-movement key.
+ // TODO: Include a detailed description of why we do this...
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) {
+ EventEntry* prev = entry->prev;
+
+ if (entry->type == EventEntry::TYPE_KEY) {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+ if (isMovementKey(keyEntry->keyCode)) {
+ LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
+ keyEntry->keyCode, keyEntry->action);
+ mInboundQueue.dequeue(keyEntry);
+
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+
+ mAllocator.releaseKeyEntry(keyEntry);
+ } else {
+ // stop at last non-movement key
+ break;
+ }
+ }
+
+ entry = prev;
+ }
+ } // release lock
+}
+
+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
+
+ bool wasEmpty;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ int32_t repeatCount = 0;
+ KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
+ deviceId, source, policyFlags, action, flags, keyCode, scanCode,
+ metaState, repeatCount, downTime);
+
+ wasEmpty = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(newEntry);
+ } // release lock
+
+ if (wasEmpty) {
+ mPollLoop->wake();
+ }
+}
+
+void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, 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, metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, "
+ "downTime=%lld",
+ eventTime, deviceId, source, policyFlags, action, 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",
+ i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y,
+ pointerCoords[i].pressure, pointerCoords[i].size);
+ }
+#endif
+
+ bool wasEmpty;
+ { // 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.tail.prev;
+ entry != & mInboundQueue.head; 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
+
+ // Sanity check for special case because dispatch is interruptible.
+ // The dispatch logic is partially interruptible and releases its lock while
+ // identifying targets. However, as soon as the targets have been identified,
+ // the dispatcher proceeds to write a dispatch entry into all relevant outbound
+ // queues and then promptly removes the motion entry from the queue.
+ //
+ // Consequently, we should never observe the case where the inbound queue contains
+ // an in-progress motion entry unless the current input targets are invalid
+ // (currently being computed). Check for this!
+ assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid));
+
+ return; // done!
+ }
+
+ // STREAMING CASE
+ //
+ // There is no pending motion event (of any kind) for this device in the inbound queue.
+ // Search the outbound queues for a synchronously dispatched motion event for this
+ // device. If found, then we append the new sample to that event and then try to
+ // push it out to all current targets. It is possible that some targets will already
+ // have consumed the motion event. This case is automatically handled by the
+ // logic in prepareDispatchCycleLocked by tracking where resumption takes place.
+ //
+ // The reason we look for a synchronously dispatched motion event is because we
+ // want to be sure that no other motion events have been dispatched since the move.
+ // It's also convenient because it means that the input targets are still valid.
+ // This code could be improved to support streaming of asynchronously dispatched
+ // motion events (which might be significantly more efficient) but it may become
+ // a little more complicated as a result.
+ //
+ // Note: This code crucially depends on the invariant that an outbound queue always
+ // contains at most one synchronous event and it is always last (but it might
+ // not be first!).
+ if (mCurrentInputTargetsValid) {
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections.itemAt(i);
+ if (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
+ if (dispatchEntry->isSyncTarget()) {
+ if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+ goto NoBatchingOrStreaming;
+ }
+
+ MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
+ dispatchEntry->eventEntry);
+ if (syncedMotionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || syncedMotionEntry->deviceId != deviceId
+ || syncedMotionEntry->pointerCount != pointerCount
+ || syncedMotionEntry->isInjected()) {
+ goto NoBatchingOrStreaming;
+ }
+
+ // Found synced move entry. Append sample and resume dispatch.
+ mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
+ pointerCoords);
+ #if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recent synchronously "
+ "dispatched motion event for this device in the outbound queues.");
+ #endif
+ nsecs_t currentTime = now();
+ dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
+ true /*resumeWithAppendedMotionSample*/);
+
+ runCommandsLockedInterruptible();
+ return; // done!
+ }
+ }
+ }
+ }
+
+NoBatchingOrStreaming:;
+ }
+
+ // Just enqueue a new motion event.
+ MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
+ deviceId, source, policyFlags, action, metaState, edgeFlags,
+ xPrecision, yPrecision, downTime,
+ pointerCount, pointerIds, pointerCoords);
+
+ wasEmpty = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(newEntry);
+ } // release lock
+
+ if (wasEmpty) {
+ mPollLoop->wake();
+ }
+}
+
+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);
+
+ EventEntry* injectedEntry;
+ bool wasEmpty;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ injectedEntry = createEntryFromInputEventLocked(event);
+ injectedEntry->refCount += 1;
+ injectedEntry->injectorPid = injectorPid;
+ injectedEntry->injectorUid = injectorUid;
+
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectedEntry->injectionIsAsync = true;
+ }
+
+ wasEmpty = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(injectedEntry);
+
+ } // release lock
+
+ if (wasEmpty) {
+ mPollLoop->wake();
+ }
+
+ int32_t injectionResult;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ } else {
+ for (;;) {
+ injectionResult = injectedEntry->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 (injectedEntry->pendingSyncDispatches != 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.",
+ injectedEntry->pendingSyncDispatches);
+#endif
+ nsecs_t remainingTimeout = endTime - now();
+ if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for pending synchronous "
+ "dispatches to finish.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+
+ mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ }
+ }
+ }
+
+ mAllocator.releaseEventEntry(injectedEntry);
+ } // release lock
+
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Finished with result %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectorPid, injectorUid);
+#endif
+
+ return injectionResult;
+}
+
+void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+ if (entry->isInjected()) {
+#if DEBUG_INJECTION
+ LOGD("Setting input event injection result to %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, entry->injectorPid, entry->injectorUid);
+#endif
+
+ if (entry->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;
+ }
+ }
+
+ entry->injectionResult = injectionResult;
+ mInjectionResultAvailableCondition.broadcast();
+ }
+}
+
+void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) {
+ entry->pendingSyncDispatches -= 1;
+
+ if (entry->isInjected() && entry->pendingSyncDispatches == 0) {
+ mInjectionSyncFinishedCondition.broadcast();
+ }
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
+ const InputEvent* event) {
+ switch (event->getType()) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
+ uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+
+ KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
+ keyEvent->getDeviceId(), keyEvent->getSource(), policyFlags,
+ keyEvent->getAction(), keyEvent->getFlags(),
+ keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), keyEvent->getDownTime());
+ return keyEntry;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+ uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+
+ const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+ const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+ size_t pointerCount = motionEvent->getPointerCount();
+
+ MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
+ motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+ motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+ motionEvent->getDownTime(), uint32_t(pointerCount),
+ motionEvent->getPointerIds(), samplePointerCoords);
+ for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+ sampleEventTimes += 1;
+ samplePointerCoords += pointerCount;
+ mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords);
+ }
+ return motionEntry;
+ }
+
+ default:
+ assert(false);
+ return NULL;
+ }
+}
+
+void InputDispatcher::resetKeyRepeatLocked() {
+ if (mKeyRepeatState.lastKeyEntry) {
+ mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
+ mKeyRepeatState.lastKeyEntry = NULL;
+ }
+}
+
+void InputDispatcher::preemptInputDispatch() {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("preemptInputDispatch");
+#endif
+
+ bool preemptedOne = false;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+ connection->getInputChannelName());
+#endif
+ connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
+ preemptedOne = true;
+ }
+ }
+ } // release lock
+
+ if (preemptedOne) {
+ // Wake up the poll loop so it can get a head start dispatching the next event.
+ mPollLoop->wake();
+ }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+ LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
+#endif
+
+ int receiveFd;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ receiveFd = inputChannel->getReceivePipeFd();
+ if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 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;
+ }
+
+ mConnectionsByReceiveFd.add(receiveFd, connection);
+
+ runCommandsLockedInterruptible();
+ } // release lock
+
+ mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
+ return OK;
+}
+
+status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+ LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
+#endif
+
+ int32_t receiveFd;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ receiveFd = inputChannel->getReceivePipeFd();
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ 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;
+
+ nsecs_t currentTime = now();
+ abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+
+ runCommandsLockedInterruptible();
+ } // release lock
+
+ mPollLoop->removeCallback(receiveFd);
+
+ // Wake the poll loop because removing the connection may have changed the current
+ // synchronization state.
+ mPollLoop->wake();
+ return OK;
+}
+
+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, bool recoveredFromANR) {
+ if (recoveredFromANR) {
+ LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, "
+ "%01.1fms since dispatch, %01.1fms since ANR",
+ connection->getInputChannelName(),
+ connection->getEventLatencyMillis(currentTime),
+ connection->getDispatchLatencyMillis(currentTime),
+ connection->getANRLatencyMillis(currentTime));
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
+ commandEntry->connection = connection;
+ }
+}
+
+void InputDispatcher::onDispatchCycleANRLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+ LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch",
+ connection->getInputChannelName(),
+ connection->getEventLatencyMillis(currentTime),
+ connection->getDispatchLatencyMillis(currentTime));
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
+ commandEntry->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::doNotifyInputChannelBrokenLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ mPolicy->notifyInputChannelBroken(connection->inputChannel);
+
+ mLock.lock();
+ }
+}
+
+void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ nsecs_t newTimeout;
+ bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
+
+ mLock.lock();
+
+ nsecs_t currentTime = now();
+ if (resume) {
+ resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
+ } else {
+ abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
+ }
+ }
+}
+
+void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
+
+ mLock.lock();
+ }
+}
+
+
+// --- InputDispatcher::Allocator ---
+
+InputDispatcher::Allocator::Allocator() {
+}
+
+void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
+ nsecs_t eventTime) {
+ entry->type = type;
+ entry->refCount = 1;
+ entry->dispatchInProgress = false;
+ entry->eventTime = eventTime;
+ entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ entry->injectionIsAsync = false;
+ entry->injectorPid = -1;
+ entry->injectorUid = -1;
+ entry->pendingSyncDispatches = 0;
+}
+
+InputDispatcher::ConfigurationChangedEntry*
+InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) {
+ ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime);
+ 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);
+
+ entry->deviceId = deviceId;
+ entry->source = source;
+ entry->policyFlags = policyFlags;
+ entry->action = action;
+ entry->flags = flags;
+ entry->keyCode = keyCode;
+ entry->scanCode = scanCode;
+ entry->metaState = metaState;
+ entry->repeatCount = repeatCount;
+ entry->downTime = downTime;
+ return entry;
+}
+
+InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
+ int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
+ 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);
+
+ entry->eventTime = eventTime;
+ entry->deviceId = deviceId;
+ entry->source = source;
+ entry->policyFlags = policyFlags;
+ entry->action = action;
+ 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) {
+ DispatchEntry* entry = mDispatchEntryPool.alloc();
+ entry->eventEntry = eventEntry;
+ eventEntry->refCount += 1;
+ return entry;
+}
+
+InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) {
+ CommandEntry* entry = mCommandEntryPool.alloc();
+ entry->command = command;
+ return entry;
+}
+
+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) {
+ mConfigurationChangeEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ mKeyEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ 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;
+}
+
+// --- InputDispatcher::Connection ---
+
+InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
+ status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
+ nextTimeoutTime(LONG_LONG_MAX),
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX),
+ lastANRTime(LONG_LONG_MAX) {
+}
+
+InputDispatcher::Connection::~Connection() {
+}
+
+status_t InputDispatcher::Connection::initialize() {
+ return inputPublisher.initialize();
+}
+
+void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
+ nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
+}
+
+const char* InputDispatcher::Connection::getStatusLabel() const {
+ switch (status) {
+ case STATUS_NORMAL:
+ return "NORMAL";
+
+ case STATUS_BROKEN:
+ return "BROKEN";
+
+ case STATUS_NOT_RESPONDING:
+ return "NOT_RESPONDING";
+
+ case STATUS_ZOMBIE:
+ return "ZOMBIE";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
+ const EventEntry* eventEntry) const {
+ for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev;
+ dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) {
+ if (dispatchEntry->eventEntry == eventEntry) {
+ return dispatchEntry;
+ }
+ }
+ return NULL;
+}
+
+// --- InputDispatcher::CommandEntry ---
+
+InputDispatcher::CommandEntry::CommandEntry() {
+}
+
+InputDispatcher::CommandEntry::~CommandEntry() {
+}
+
+
+// --- 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..ed4f07b
--- /dev/null
+++ b/libs/ui/InputManager.cpp
@@ -0,0 +1,123 @@
+//
+// 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;
+}
+
+status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) {
+ return mDispatcher->registerInputChannel(inputChannel);
+}
+
+status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+ return mDispatcher->unregisterInputChannel(inputChannel);
+}
+
+int32_t InputManager::injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+}
+
+void InputManager::preemptInputDispatch() {
+ mDispatcher->preemptInputDispatch();
+}
+
+void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) {
+ mReader->getInputConfiguration(outConfiguration);
+}
+
+status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+ return mReader->getInputDeviceInfo(deviceId, outDeviceInfo);
+}
+
+void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+ mReader->getInputDeviceIds(outDeviceIds);
+}
+
+int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) {
+ return mReader->getScanCodeState(deviceId, sourceMask, scanCode);
+}
+
+int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) {
+ return mReader->getKeyCodeState(deviceId, sourceMask, keyCode);
+}
+
+int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
+ return mReader->getSwitchState(deviceId, sourceMask, sw);
+}
+
+bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+ return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
+
+} // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
new file mode 100644
index 0000000..6618702
--- /dev/null
+++ b/libs/ui/InputReader.cpp
@@ -0,0 +1,2706 @@
+//
+// 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 <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+
+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;
+}
+
+
+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;
+}
+
+
+// --- 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->when, rawEvent->deviceId);
+ break;
+
+ case EventHubInterface::DEVICE_REMOVED:
+ removeDevice(rawEvent->when, rawEvent->deviceId);
+ break;
+
+ default:
+ consumeEvent(rawEvent);
+ break;
+ }
+}
+
+void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
+ String8 name = mEventHub->getDeviceName(deviceId);
+ uint32_t classes = mEventHub->getDeviceClasses(deviceId);
+
+ InputDevice* device = createDevice(deviceId, name, classes);
+ device->configure();
+
+ 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;
+ }
+
+ 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());
+ }
+
+ handleConfigurationChanged(when);
+}
+
+void InputReader::removeDevice(nsecs_t when, 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;
+ }
+
+ device->reset();
+
+ 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());
+ }
+
+ delete device;
+
+ handleConfigurationChanged(when);
+}
+
+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 (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+ keyboardSources |= AINPUT_SOURCE_GAMEPAD;
+ }
+
+ 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
+}
+
+
+// --- 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();
+}
+
+void InputDevice::addMapper(InputMapper* mapper) {
+ mMappers.add(mapper);
+}
+
+void InputDevice::configure() {
+ 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::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;
+}
+
+bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
+ if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
+ getDispatcher()->notifyAppSwitchComing(when);
+ }
+
+ return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
+}
+
+
+// --- 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) {
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptSwitch(
+ when, switchCode, switchValue, policyFlags);
+
+ applyStandardPolicyActions(when, policyActions);
+}
+
+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::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.top().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.top().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();
+ }
+
+ applyPolicyAndDispatch(when, policyFlags, down, keyCode, scanCode, newMetaState, downTime);
+}
+
+void KeyboardInputMapper::applyPolicyAndDispatch(nsecs_t when, uint32_t policyFlags, bool down,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+ int32_t policyActions = getPolicy()->interceptKey(when,
+ getDeviceId(), down, keyCode, scanCode, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ return; // event dropped
+ }
+
+ int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP;
+ int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+ if (policyFlags & POLICY_FLAG_WOKE_HERE) {
+ keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
+ }
+
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, 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::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);
+ mAccumulator.clear();
+ }
+
+ 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(rawEvent->when);
+ mAccumulator.clear();
+ 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:
+ if (mAccumulator.isDirty()) {
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+void TrackballInputMapper::sync(nsecs_t when) {
+ int motionEventAction;
+ PointerCoords pointerCoords;
+ nsecs_t downTime;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ uint32_t fields = mAccumulator.fields;
+ 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
+
+ applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
+}
+
+void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
+ PointerCoords* pointerCoords, nsecs_t downTime) {
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ return; // event dropped
+ }
+
+ int32_t metaState = mContext->getGlobalMetaState();
+ int32_t pointerId = 0;
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, policyFlags,
+ motionEventAction, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
+}
+
+
+// --- 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);
+ info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mLocked.orientedRanges.pressure);
+ info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mLocked.orientedRanges.size);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mLocked.orientedRanges.touchMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mLocked.orientedRanges.touchMinor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mLocked.orientedRanges.toolMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mLocked.orientedRanges.toolMinor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mLocked.orientedRanges.orientation);
+ } // 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;
+}
+
+void TouchInputMapper::configure() {
+ InputMapper::configure();
+
+ // Configure basic parameters.
+ mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
+
+ // Configure absolute axis information.
+ configureAxes();
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Configure pressure factors.
+ if (mAxes.pressure.valid) {
+ mLocked.pressureOrigin = mAxes.pressure.minValue;
+ mLocked.pressureScale = 1.0f / mAxes.pressure.getRange();
+ } else {
+ mLocked.pressureOrigin = 0;
+ mLocked.pressureScale = 1.0f;
+ }
+
+ mLocked.orientedRanges.pressure.min = 0.0f;
+ mLocked.orientedRanges.pressure.max = 1.0f;
+ mLocked.orientedRanges.pressure.flat = 0.0f;
+ mLocked.orientedRanges.pressure.fuzz = mLocked.pressureScale;
+
+ // Configure size factors.
+ if (mAxes.size.valid) {
+ mLocked.sizeOrigin = mAxes.size.minValue;
+ mLocked.sizeScale = 1.0f / mAxes.size.getRange();
+ } else {
+ mLocked.sizeOrigin = 0;
+ mLocked.sizeScale = 1.0f;
+ }
+
+ mLocked.orientedRanges.size.min = 0.0f;
+ mLocked.orientedRanges.size.max = 1.0f;
+ mLocked.orientedRanges.size.flat = 0.0f;
+ mLocked.orientedRanges.size.fuzz = mLocked.sizeScale;
+
+ // Configure orientation factors.
+ if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) {
+ mLocked.orientationScale = float(M_PI_2) / mAxes.orientation.maxValue;
+ } else {
+ mLocked.orientationScale = 0.0f;
+ }
+
+ mLocked.orientedRanges.orientation.min = - M_PI_2;
+ mLocked.orientedRanges.orientation.max = M_PI_2;
+ mLocked.orientedRanges.orientation.flat = 0;
+ mLocked.orientedRanges.orientation.fuzz = mLocked.orientationScale;
+
+ // Configure surface dimensions and orientation.
+ configureSurfaceLocked();
+ } // release lock
+}
+
+void TouchInputMapper::configureAxes() {
+ mAxes.x.valid = false;
+ mAxes.y.valid = false;
+ mAxes.pressure.valid = false;
+ mAxes.size.valid = false;
+ mAxes.touchMajor.valid = false;
+ mAxes.touchMinor.valid = false;
+ mAxes.toolMajor.valid = false;
+ mAxes.toolMinor.valid = false;
+ mAxes.orientation.valid = false;
+}
+
+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 = mAxes.x.getRange();
+ height = mAxes.y.getRange();
+ }
+
+ bool orientationChanged = mLocked.surfaceOrientation != orientation;
+ if (orientationChanged) {
+ mLocked.surfaceOrientation = orientation;
+ }
+
+ bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
+ if (sizeChanged) {
+ mLocked.surfaceWidth = width;
+ mLocked.surfaceHeight = height;
+
+ // Compute size-dependent translation and scaling factors and place virtual keys.
+ if (mAxes.x.valid && mAxes.y.valid) {
+ mLocked.xOrigin = mAxes.x.minValue;
+ mLocked.yOrigin = mAxes.y.minValue;
+
+ LOGI("Device configured: id=0x%x, name=%s (display size was changed)",
+ getDeviceId(), getDeviceName().string());
+
+ mLocked.xScale = float(width) / mAxes.x.getRange();
+ mLocked.yScale = float(height) / mAxes.y.getRange();
+ mLocked.xPrecision = 1.0f / mLocked.xScale;
+ mLocked.yPrecision = 1.0f / mLocked.yScale;
+
+ configureVirtualKeysLocked();
+ } else {
+ mLocked.xOrigin = 0;
+ mLocked.yOrigin = 0;
+ mLocked.xScale = 1.0f;
+ mLocked.yScale = 1.0f;
+ mLocked.xPrecision = 1.0f;
+ mLocked.yPrecision = 1.0f;
+ }
+
+ // Configure touch and tool area ranges.
+ float diagonal = sqrt(float(width * width + height * height));
+ float diagonalFuzz = sqrt(mLocked.xScale * mLocked.xScale
+ + mLocked.yScale * mLocked.yScale);
+
+ InputDeviceInfo::MotionRange area;
+ area.min = 0.0f;
+ area.max = diagonal;
+ area.flat = 0.0f;
+ area.fuzz = diagonalFuzz;
+
+ mLocked.orientedRanges.touchMajor = area;
+ mLocked.orientedRanges.touchMinor = area;
+
+ mLocked.orientedRanges.toolMajor = area;
+ mLocked.orientedRanges.toolMinor = area;
+ }
+
+ 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::configureVirtualKeysLocked() {
+ assert(mAxes.x.valid && mAxes.y.valid);
+
+ // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
+ Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
+ getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+
+ mLocked.virtualKeys.clear();
+
+ if (virtualKeyDefinitions.size() == 0) {
+ return;
+ }
+
+ mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size());
+
+ int32_t touchScreenLeft = mAxes.x.minValue;
+ int32_t touchScreenTop = mAxes.y.minValue;
+ int32_t touchScreenWidth = mAxes.x.getRange();
+ int32_t touchScreenHeight = mAxes.y.getRange();
+
+ for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
+ const InputReaderPolicyInterface::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(" 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;
+
+ LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
+ virtualKey.scanCode, virtualKey.keyCode,
+ virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+ }
+}
+
+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) {
+ // Apply generic policy actions.
+
+ uint32_t policyFlags = 0;
+ int32_t policyActions = getPolicy()->interceptGeneric(when, policyFlags);
+
+ if (! applyStandardPolicyActions(when, policyActions)) {
+ mLastTouch.clear();
+ return; // event dropped
+ }
+
+ // 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",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.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",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+ | AKEY_EVENT_FLAG_CANCELED;
+ touchResult = DROP_STROKE;
+ goto DispatchVirtualKey;
+ } 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",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.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.
+ applyPolicyAndDispatchVirtualKey(when, policyFlags, keyEventAction, keyEventFlags,
+ keyCode, scanCode, downTime);
+ return touchResult;
+}
+
+void TouchInputMapper::applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+ int32_t keyEventAction, int32_t keyEventFlags,
+ int32_t keyCode, int32_t scanCode, nsecs_t downTime) {
+ int32_t metaState = mContext->getGlobalMetaState();
+
+ if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
+ getPolicy()->virtualKeyDownFeedback();
+ }
+
+ int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
+ keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
+
+ if (applyStandardPolicyActions(when, policyActions)) {
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ }
+}
+
+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, motionEventAction);
+ } else {
+ // There may be pointers going up and pointers going down at the same time when pointer
+ // ids are reported by the device driver.
+ BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
+ BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
+ BitSet32 activeIdBits(lastIdBits.value);
+
+ 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, & mLastTouch,
+ oldActiveIdBits, upId, motionEventAction);
+ }
+
+ 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;
+ }
+
+ dispatchTouch(when, policyFlags, & mCurrentTouch,
+ activeIdBits, downId, motionEventAction);
+ }
+ }
+}
+
+void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
+ TouchData* touch, BitSet32 idBits, uint32_t changedId,
+ int32_t motionEventAction) {
+ uint32_t pointerCount = 0;
+ 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.
+ while (! idBits.isEmpty()) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t index = touch->idToIndex[id];
+
+ float x = float(touch->pointers[index].x - mLocked.xOrigin) * mLocked.xScale;
+ float y = float(touch->pointers[index].y - mLocked.yOrigin) * mLocked.yScale;
+ float pressure = float(touch->pointers[index].pressure - mLocked.pressureOrigin)
+ * mLocked.pressureScale;
+ float size = float(touch->pointers[index].size - mLocked.sizeOrigin)
+ * mLocked.sizeScale;
+
+ float orientation = float(touch->pointers[index].orientation)
+ * mLocked.orientationScale;
+
+ float touchMajor, touchMinor, toolMajor, toolMinor;
+ if (abs(orientation) <= M_PI_4) {
+ // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X.
+ touchMajor = float(touch->pointers[index].touchMajor) * mLocked.yScale;
+ touchMinor = float(touch->pointers[index].touchMinor) * mLocked.xScale;
+ toolMajor = float(touch->pointers[index].toolMajor) * mLocked.yScale;
+ toolMinor = float(touch->pointers[index].toolMinor) * mLocked.xScale;
+ } else {
+ // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y.
+ touchMajor = float(touch->pointers[index].touchMajor) * mLocked.xScale;
+ touchMinor = float(touch->pointers[index].touchMinor) * mLocked.yScale;
+ toolMajor = float(touch->pointers[index].toolMajor) * mLocked.xScale;
+ toolMinor = float(touch->pointers[index].toolMinor) * mLocked.yScale;
+ }
+
+ 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;
+ }
+ }
+
+ pointerIds[pointerCount] = int32_t(id);
+
+ pointerCoords[pointerCount].x = x;
+ pointerCoords[pointerCount].y = y;
+ pointerCoords[pointerCount].pressure = pressure;
+ pointerCoords[pointerCount].size = size;
+ pointerCoords[pointerCount].touchMajor = touchMajor;
+ pointerCoords[pointerCount].touchMinor = touchMinor;
+ pointerCoords[pointerCount].toolMajor = toolMajor;
+ pointerCoords[pointerCount].toolMinor = toolMinor;
+ pointerCoords[pointerCount].orientation = orientation;
+
+ if (id == changedId) {
+ motionEventAction |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+
+ pointerCount += 1;
+ }
+
+ // 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(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
+ motionEventAction, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
+ pointerCount, pointerIds, pointerCoords,
+ xPrecision, yPrecision, mDownTime);
+}
+
+bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
+ if (mAxes.x.valid && mAxes.y.valid) {
+ return x >= mAxes.x.minValue && x <= mAxes.x.maxValue
+ && y >= mAxes.y.minValue && y <= mAxes.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 (! mAxes.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 = mAxes.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 (! mAxes.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 = mAxes.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 = mCurrentTouch.pointers[currentIndex].pressure;
+
+ 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;
+ }
+ }
+
+ 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;
+ mSize = 0;
+}
+
+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;
+
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ 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:
+ if (mAccumulator.isDirty()) {
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ }
+ break;
+ }
+ break;
+ }
+}
+
+void SingleTouchInputMapper::sync(nsecs_t when) {
+ /* Update device state */
+
+ uint32_t fields = mAccumulator.fields;
+
+ 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) {
+ mSize = 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].size = mSize;
+ mCurrentTouch.pointers[0].touchMajor = mPressure;
+ mCurrentTouch.pointers[0].touchMinor = mPressure;
+ mCurrentTouch.pointers[0].toolMajor = mSize;
+ mCurrentTouch.pointers[0].toolMinor = mSize;
+ mCurrentTouch.pointers[0].orientation = 0;
+ mCurrentTouch.idToIndex[0] = 0;
+ mCurrentTouch.idBits.markBit(0);
+ }
+
+ syncTouch(when, true);
+}
+
+void SingleTouchInputMapper::configureAxes() {
+ TouchInputMapper::configureAxes();
+
+ // The axes are aliased to take into account the manner in which they are presented
+ // as part of the TouchData during the sync.
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
+
+ mAxes.touchMajor = mAxes.pressure;
+ mAxes.touchMinor = mAxes.pressure;
+ mAxes.toolMajor = mAxes.size;
+ mAxes.toolMinor = mAxes.size;
+}
+
+
+// --- 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;
+ }
+ 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:
+ if (mAccumulator.isDirty()) {
+ sync(rawEvent->when);
+ mAccumulator.clear();
+ }
+ 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
+ | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
+ | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+
+ /* Update device state */
+
+ uint32_t inCount = mAccumulator.pointerCount;
+ uint32_t outCount = 0;
+ bool havePointerIds = true;
+
+ mCurrentTouch.clear();
+
+ for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
+ uint32_t fields = mAccumulator.pointers[inIndex].fields;
+
+ if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
+#if DEBUG_POINTERS
+ LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
+ inIndex, fields);
+ continue;
+#endif
+ }
+
+ if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
+ // Pointer is not down. Drop it.
+ continue;
+ }
+
+ mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
+ mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
+
+ mCurrentTouch.pointers[outCount].touchMajor =
+ mAccumulator.pointers[inIndex].absMTTouchMajor;
+ mCurrentTouch.pointers[outCount].touchMinor =
+ (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
+ ? mAccumulator.pointers[inIndex].absMTTouchMinor
+ : mAccumulator.pointers[inIndex].absMTTouchMajor;
+
+ mCurrentTouch.pointers[outCount].toolMajor =
+ mAccumulator.pointers[inIndex].absMTWidthMajor;
+ mCurrentTouch.pointers[outCount].toolMinor =
+ (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
+ ? mAccumulator.pointers[inIndex].absMTWidthMinor
+ : mAccumulator.pointers[inIndex].absMTWidthMajor;
+
+ mCurrentTouch.pointers[outCount].orientation =
+ (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
+ ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
+
+ // Derive an approximation of pressure and size.
+ // FIXME assignment of pressure may be incorrect, probably better to let
+ // pressure = touch / width. Later on we pass width to MotionEvent as a size, which
+ // isn't quite right either. Should be using touch for that.
+ mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
+ mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
+
+ if (havePointerIds) {
+ if (fields & Accumulator::
+ FIELD_ABS_MT_TRACKING_ID) {
+ uint32_t id = uint32_t(mAccumulator.pointers[inIndex].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 for optimizations",
+ id, MAX_POINTER_ID);
+#endif
+ havePointerIds = false;
+ }
+ else {
+ mCurrentTouch.pointers[outCount].id = id;
+ mCurrentTouch.idToIndex[id] = outCount;
+ mCurrentTouch.idBits.markBit(id);
+ }
+ } else {
+ havePointerIds = false;
+ }
+ }
+
+ outCount += 1;
+ }
+
+ mCurrentTouch.pointerCount = outCount;
+
+ syncTouch(when, havePointerIds);
+}
+
+void MultiTouchInputMapper::configureAxes() {
+ TouchInputMapper::configureAxes();
+
+ // The axes are aliased to take into account the manner in which they are presented
+ // as part of the TouchData during the sync.
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
+
+ if (! mAxes.touchMinor.valid) {
+ mAxes.touchMinor = mAxes.touchMajor;
+ }
+
+ if (! mAxes.toolMinor.valid) {
+ mAxes.toolMinor = mAxes.toolMajor;
+ }
+
+ mAxes.pressure = mAxes.touchMajor;
+ mAxes.size = mAxes.toolMajor;
+}
+
+
+} // namespace android
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
new file mode 100644
index 0000000..cf0f63e
--- /dev/null
+++ b/libs/ui/InputTransport.cpp
@@ -0,0 +1,692 @@
+//
+// 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 = ::write(mSendPipeFd, & signal, 1);
+
+ 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 = ::read(mReceivePipeFd, outSignal, 1);
+ 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=%d, "
+ "action=%d, flags=%d, keyCode=%d, scanCode=%d, metaState=%d, 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 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=%d, "
+ "action=%d, edgeFlags=%d, metaState=%d, xOffset=%f, yOffset=%f, "
+ "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
+ "pointerCount=%d",
+ mChannel->getName().string(),
+ deviceId, source, action, 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.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.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..edf1aed 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -59,19 +59,12 @@ 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/tests/Android.mk b/libs/ui/tests/Android.mk
index 6cc4a5a..62f824f 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -1,16 +1,50 @@
+# 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 \
+ 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..1dc6e46
--- /dev/null
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -0,0 +1,18 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputDispatcher.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class InputDispatcherTest : public testing::Test {
+public:
+};
+
+TEST_F(InputDispatcherTest, Dummy) {
+ // TODO
+}
+
+} // namespace android
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
new file mode 100644
index 0000000..3bc21fa
--- /dev/null
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -0,0 +1,469 @@
+//
+// 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 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, 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(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,
+ 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,
+ 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,
+ 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,
+ 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, 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, 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, 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/region/Android.mk b/libs/ui/tests/region/Android.mk
new file mode 100644
index 0000000..6cc4a5a
--- /dev/null
+++ b/libs/ui/tests/region/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ region.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui
+
+LOCAL_MODULE:= test-region
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region/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 afecdcb..2e20268 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -26,11 +26,14 @@ commonSources:= \
Debug.cpp \
FileMap.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 \
@@ -39,7 +42,7 @@ commonSources:= \
Threads.cpp \
Timers.cpp \
VectorImpl.cpp \
- ZipFileCRO.cpp \
+ ZipFileCRO.cpp \
ZipFileRO.cpp \
ZipUtils.cpp \
misc.cpp
@@ -64,6 +67,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)
@@ -76,8 +84,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 \
+ PollLoop.cpp
ifeq ($(TARGET_OS),linux)
LOCAL_LDLIBS += -lrt -ldl
@@ -114,3 +123,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 5a05e6a..60a0d82 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -824,7 +824,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/ObbFile.cpp b/libs/utils/ObbFile.cpp
new file mode 100644
index 0000000..fe49300
--- /dev/null
+++ b/libs/utils/ObbFile.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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 21 /* 32-bit signature version
+ * 32-bit package version
+ * 32-bit package name size
+ * 1-character package name
+ * 32-bit footer size
+ * 32-bit footer marker
+ */
+
+#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 kPackageNameLenOffset 8
+#define kPackageNameOffset 12
+
+/*
+ * 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() :
+ mVersion(-1) {
+}
+
+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) {
+ LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n",
+ footerSize, kFooterMinSize);
+ 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;
+ }
+
+ 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", 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);
+
+ 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");
+ 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", 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");
+ 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", strerror(errno));
+ return false;
+ }
+
+ if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
+ LOGW("couldn't write package name: %s", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write footer size: %s", 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", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+}
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
new file mode 100644
index 0000000..f740fa0
--- /dev/null
+++ b/libs/utils/PollLoop.cpp
@@ -0,0 +1,364 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// A select loop implementation.
+//
+#define LOG_TAG "PollLoop"
+
+//#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/PollLoop.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
+static bool gHaveTLS = false;
+static pthread_key_t gTLS = 0;
+
+PollLoop::PollLoop(bool allowNonCallbacks) :
+ mAllowNonCallbacks(allowNonCallbacks), mPolling(false),
+ mWaiters(0), mPendingFdsPos(0) {
+ openWakePipe();
+}
+
+PollLoop::~PollLoop() {
+ closeWakePipe();
+}
+
+void PollLoop::threadDestructor(void *st) {
+ PollLoop* const self = static_cast<PollLoop*>(st);
+ if (self != NULL) {
+ self->decStrong((void*)threadDestructor);
+ }
+}
+
+void PollLoop::setForThread(const sp<PollLoop>& pollLoop) {
+ sp<PollLoop> old = getForThread();
+
+ if (pollLoop != NULL) {
+ pollLoop->incStrong((void*)threadDestructor);
+ }
+
+ pthread_setspecific(gTLS, pollLoop.get());
+
+ if (old != NULL) {
+ old->decStrong((void*)threadDestructor);
+ }
+}
+
+sp<PollLoop> PollLoop::getForThread() {
+ if (!gHaveTLS) {
+ pthread_mutex_lock(&gTLSMutex);
+ if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+ pthread_mutex_unlock(&gTLSMutex);
+ return NULL;
+ }
+ gHaveTLS = true;
+ pthread_mutex_unlock(&gTLSMutex);
+ }
+
+ return (PollLoop*)pthread_getspecific(gTLS);
+}
+
+void PollLoop::openWakePipe() {
+ 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);
+
+ // 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.insertAt(requestedFd, 0);
+
+ RequestedCallback requestedCallback;
+ requestedCallback.callback = NULL;
+ requestedCallback.looperCallback = NULL;
+ requestedCallback.data = NULL;
+ mRequestedCallbacks.insertAt(requestedCallback, 0);
+}
+
+void PollLoop::closeWakePipe() {
+ close(mWakeReadPipeFd);
+ close(mWakeWritePipeFd);
+
+ // Note: We don't need to remove the poll structure or callback entry because this
+ // method is currently only called by the destructor.
+}
+
+int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) {
+ // If there are still pending fds from the last call, dispatch those
+ // first, to avoid an earlier fd from starving later ones.
+ const size_t pendingFdsCount = mPendingFds.size();
+ if (mPendingFdsPos < pendingFdsCount) {
+ const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos);
+ mPendingFdsPos++;
+ if (outEvents != NULL) *outEvents = pending.events;
+ if (outData != NULL) *outData = pending.data;
+ return pending.fd;
+ }
+
+ mLock.lock();
+ while (mWaiters != 0) {
+ mResume.wait(mLock);
+ }
+ mPolling = true;
+ mLock.unlock();
+
+ int32_t result;
+ size_t requestedCount = mRequestedFds.size();
+
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount);
+ for (size_t i = 0; i < requestedCount; i++) {
+ LOGD(" fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events);
+ }
+#endif
+
+ int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
+
+ if (respondedCount == 0) {
+ // Timeout
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - timeout", this);
+#endif
+ result = POLL_TIMEOUT;
+ goto Done;
+ }
+
+ if (respondedCount < 0) {
+ // Error
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - error, errno=%d", this, errno);
+#endif
+ if (errno != EINTR) {
+ LOGW("Poll failed with an unexpected error, errno=%d", errno);
+ }
+ result = POLL_ERROR;
+ goto Done;
+ }
+
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount);
+ for (size_t i = 0; i < requestedCount; i++) {
+ LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events,
+ mRequestedFds[i].revents);
+ }
+#endif
+
+ mPendingCallbacks.clear();
+ mPendingFds.clear();
+ mPendingFdsPos = 0;
+ if (outEvents != NULL) *outEvents = 0;
+ if (outData != NULL) *outData = NULL;
+
+ result = POLL_CALLBACK;
+ for (size_t i = 0; i < requestedCount; i++) {
+ const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
+
+ short revents = requestedFd.revents;
+ if (revents) {
+ const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i);
+ PendingCallback pending;
+ pending.fd = requestedFd.fd;
+ pending.events = revents;
+ pending.callback = requestedCallback.callback;
+ pending.looperCallback = requestedCallback.looperCallback;
+ pending.data = requestedCallback.data;
+
+ if (pending.callback || pending.looperCallback) {
+ mPendingCallbacks.push(pending);
+ } else if (pending.fd != mWakeReadPipeFd) {
+ if (result == POLL_CALLBACK) {
+ result = pending.fd;
+ if (outEvents != NULL) *outEvents = pending.events;
+ if (outData != NULL) *outData = pending.data;
+ } else {
+ mPendingFds.push(pending);
+ }
+ } else {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - awoken", this);
+#endif
+ char buffer[16];
+ ssize_t nRead;
+ do {
+ nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+ } while (nRead == sizeof(buffer));
+ }
+
+ respondedCount -= 1;
+ if (respondedCount == 0) {
+ break;
+ }
+ }
+ }
+
+Done:
+ mLock.lock();
+ mPolling = false;
+ if (mWaiters != 0) {
+ mAwake.broadcast();
+ }
+ mLock.unlock();
+
+ if (result == POLL_CALLBACK || result >= 0) {
+ size_t pendingCount = mPendingCallbacks.size();
+ for (size_t i = 0; i < pendingCount; i++) {
+ const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i);
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+ LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd);
+#endif
+
+ bool keep = true;
+ if (pendingCallback.callback != NULL) {
+ keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
+ pendingCallback.data);
+ } else {
+ keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events,
+ pendingCallback.data) != 0;
+ }
+ if (! keep) {
+ removeCallback(pendingCallback.fd);
+ }
+ }
+ }
+
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - done", this);
+#endif
+ return result;
+}
+
+void PollLoop::wake() {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ wake", this);
+#endif
+
+ ssize_t nWrite = write(mWakeWritePipeFd, "W", 1);
+ if (nWrite != 1) {
+ if (errno != EAGAIN) {
+ LOGW("Could not write wake signal, errno=%d", errno);
+ }
+ }
+}
+
+bool PollLoop::getAllowNonCallbacks() const {
+ return mAllowNonCallbacks;
+}
+
+void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
+ setCallbackCommon(fd, events, callback, NULL, data);
+}
+
+void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+ void* data) {
+ setCallbackCommon(fd, events, NULL, callback, data);
+}
+
+void PollLoop::setCallbackCommon(int fd, int events, Callback callback,
+ ALooper_callbackFunc* looperCallback, void* data) {
+
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
+#endif
+
+ if (! events) {
+ LOGE("Invalid attempt to set a callback with no selected poll events.");
+ removeCallback(fd);
+ return;
+ }
+
+ if (! callback && ! looperCallback && ! mAllowNonCallbacks) {
+ LOGE("Invalid attempt to set NULL callback but not allowed.");
+ removeCallback(fd);
+ return;
+ }
+
+ wakeAndLock();
+
+ struct pollfd requestedFd;
+ requestedFd.fd = fd;
+ requestedFd.events = events;
+
+ RequestedCallback requestedCallback;
+ requestedCallback.callback = callback;
+ requestedCallback.looperCallback = looperCallback;
+ requestedCallback.data = data;
+
+ ssize_t index = getRequestIndexLocked(fd);
+ if (index < 0) {
+ mRequestedFds.push(requestedFd);
+ mRequestedCallbacks.push(requestedCallback);
+ } else {
+ mRequestedFds.replaceAt(requestedFd, size_t(index));
+ mRequestedCallbacks.replaceAt(requestedCallback, size_t(index));
+ }
+
+ mLock.unlock();
+}
+
+bool PollLoop::removeCallback(int fd) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ removeCallback - fd=%d", this, fd);
+#endif
+
+ wakeAndLock();
+
+ ssize_t index = getRequestIndexLocked(fd);
+ if (index >= 0) {
+ mRequestedFds.removeAt(size_t(index));
+ mRequestedCallbacks.removeAt(size_t(index));
+ }
+
+ mLock.unlock();
+ return index >= 0;
+}
+
+ssize_t PollLoop::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 PollLoop::wakeAndLock() {
+ mLock.lock();
+ mWaiters += 1;
+ while (mPolling) {
+ wake();
+ mAwake.wait(mLock);
+ }
+ mWaiters -= 1;
+ if (mWaiters == 0) {
+ mResume.signal();
+ }
+}
+
+} // namespace android
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 7e0f881..a1401ad 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -4178,6 +4178,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..7ebde78
--- /dev/null
+++ b/libs/utils/StreamingZipInflater.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "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) {
+ LOGI("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/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..a4c3500 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -29,6 +29,22 @@
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
+#include <unistd.h>
+
+/*
+ * 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 +54,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
@@ -90,9 +107,8 @@ 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.
@@ -103,172 +119,238 @@ status_t ZipFileRO::open(const char* zipFileName)
return NAME_NOT_FOUND;
}
- length = lseek(fd, 0, SEEK_END);
- if (length < 0) {
+ mFileLength = lseek(fd, 0, SEEK_END);
+ if (mFileLength < kEOCDLen) {
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;
+ 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;
+ size_t readAmount = kMaxEOCDSearch;
+ if (readAmount > (size_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", val);
+ 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 failed: %s\n", 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,
+ dirOffset, 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), 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.
*/
@@ -315,7 +397,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 +436,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 +466,78 @@ 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;
+ }
- if (pCompLen != NULL) {
- *pCompLen = get4LE(ptr + kCDECompLen);
- if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) {
- LOGE("ERROR: bad compressed length in zip\n");
+ unsigned char lfhBuf[kLFHLen];
+ if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+ LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
return false;
}
- }
- if (pUncompLen != NULL) {
- *pUncompLen = get4LE(ptr + kCDEUncompLen);
- if (*pUncompLen < 0) {
- LOGE("ERROR: negative uncompressed length in zip\n");
+ 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) {
+ LOGW("didn't find signature at start of lfh, offset=%ld\n",
+ localHdrOffset);
return false;
}
+
+ 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, 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, uncompLen, (long) cdOffset);
return false;
}
- }
- if (pOffset != NULL) {
*pOffset = dataOffset;
}
+
return true;
}
@@ -457,14 +573,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 +596,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,17 +630,17 @@ 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))
+ if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
goto bail;
}
if (compLen > kSequentialMin)
- mFileMap->advise(FileMap::NORMAL);
+ file->advise(FileMap::NORMAL);
result = true;
@@ -537,29 +660,34 @@ 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;
+ const FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
+
+ ptr = (const unsigned char*) file->getDataPtr();
- actual = write(fd, basePtr + offset, uncompLen);
+ 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);
+ } else if ((size_t) actual != uncompLen) {
+ LOGE("Partial write during uncompress (%zd of %zd)\n",
+ (size_t)actual, (size_t)uncompLen);
goto bail;
} else {
LOGI("+++ successful write\n");
}
} else {
- if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen))
+ if (!inflateBuffer(fd, ptr, uncompLen, compLen))
goto bail;
}
@@ -573,7 +701,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 +710,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 +720,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,8 +747,8 @@ bail:
}
/* paranoia */
- if ((long) zstream.total_out != uncompLen) {
- LOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+ if (zstream.total_out != uncompLen) {
+ LOGW("Size mismatch on inflated file (%ld vs %zd)\n",
zstream.total_out, uncompLen);
goto z_bail;
}
@@ -638,10 +766,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 +777,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 +787,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,8 +836,8 @@ 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",
+ if (zstream.total_out != uncompLen) {
+ LOGW("Size mismatch on inflated file (%ld vs %zd)\n",
zstream.total_out, uncompLen);
goto z_bail;
}
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
new file mode 100644
index 0000000..b9f206a
--- /dev/null
+++ b/libs/utils/tests/Android.mk
@@ -0,0 +1,44 @@
+# 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 \
+ PollLoop_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 \ No newline at end of file
diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp
new file mode 100644
index 0000000..29bb70a
--- /dev/null
+++ b/libs/utils/tests/ObbFile_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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>
+
+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);
+
+ 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";
+}
+
+}
diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp
new file mode 100644
index 0000000..02f1808
--- /dev/null
+++ b/libs/utils/tests/PollLoop_test.cpp
@@ -0,0 +1,370 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <utils/PollLoop.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<PollLoop> mPollLoop;
+
+public:
+ DelayedWake(int delayMillis, const sp<PollLoop> pollLoop) :
+ DelayedTask(delayMillis), mPollLoop(pollLoop) {
+ }
+
+protected:
+ virtual void doTask() {
+ mPollLoop->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<PollLoop>& pollLoop, int fd, int events) {
+ pollLoop->setCallback(fd, events, staticHandler, this);
+ }
+
+protected:
+ virtual ~CallbackHandler() { }
+
+ virtual bool handler(int fd, int events) = 0;
+
+private:
+ static bool staticHandler(int fd, int events, void* data) {
+ return static_cast<CallbackHandler*>(data)->handler(fd, events);
+ }
+};
+
+class StubCallbackHandler : public CallbackHandler {
+public:
+ bool nextResult;
+ int callbackCount;
+
+ int fd;
+ int events;
+
+ StubCallbackHandler(bool nextResult) : nextResult(nextResult),
+ callbackCount(0), fd(-1), events(-1) {
+ }
+
+protected:
+ virtual bool handler(int fd, int events) {
+ callbackCount += 1;
+ this->fd = fd;
+ this->events = events;
+ return nextResult;
+ }
+};
+
+class PollLoopTest : public testing::Test {
+protected:
+ sp<PollLoop> mPollLoop;
+
+ virtual void SetUp() {
+ mPollLoop = new PollLoop(false);
+ }
+
+ virtual void TearDown() {
+ mPollLoop.clear();
+ }
+};
+
+
+TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) {
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) {
+ mPollLoop->wake();
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->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(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) {
+ sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop);
+ delayedWake->run();
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal wake delay";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) {
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ ASSERT_EQ(OK, pipe.writeSignal());
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be 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(POLL_IN, handler.events)
+ << "callback should have received POLL_IN as events";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout";
+ EXPECT_EQ(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ pipe.writeSignal();
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->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(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be 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(POLL_IN, handler.events)
+ << "callback should have received POLL_IN as events";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+ sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
+
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+ delayedWriteSignal->run();
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->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(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be 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(POLL_IN, handler.events)
+ << "callback should have received POLL_IN as events";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+ pipe.writeSignal(); // would cause FD to be considered signalled
+ mPollLoop->removeCallback(pipe.receiveFd);
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->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(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not be invoked";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
+ Pipe pipe;
+ StubCallbackHandler handler(false);
+
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+
+ // First loop: Callback is registered and FD is signalled.
+ pipe.writeSignal();
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->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(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be 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 = mPollLoop->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(result, PollLoop::POLL_TIMEOUT)
+ << "pollOnce result should be POLL_TIMEOUT";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should not be invoked this time";
+}
+
+TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) {
+ bool result = mPollLoop->removeCallback(1);
+
+ EXPECT_FALSE(result)
+ << "removeCallback should return false because FD not registered";
+}
+
+TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) {
+ Pipe pipe;
+ StubCallbackHandler handler(false);
+ handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+
+ // First time.
+ bool result = mPollLoop->removeCallback(pipe.receiveFd);
+
+ EXPECT_TRUE(result)
+ << "removeCallback should return true first time because FD was registered";
+
+ // Second time.
+ result = mPollLoop->removeCallback(pipe.receiveFd);
+
+ EXPECT_FALSE(result)
+ << "removeCallback should return false second time because FD was no longer registered";
+}
+
+TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler1(true);
+ StubCallbackHandler handler2(true);
+
+ handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
+ handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it
+ pipe.writeSignal(); // would cause FD to be considered signalled
+
+ StopWatch stopWatch("pollOnce");
+ int32_t result = mPollLoop->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(result, PollLoop::POLL_CALLBACK)
+ << "pollOnce result should be 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/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
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index c269976..99ea342 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -1,7 +1,7 @@
/* -*- mode: c; tab-width: 8; -*- */
/* vi: set sw=4 ts=8: */
/* Reference version of egl.h for EGL 1.4.
- * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $
+ * $Revision: 9356 $ on $Date: 2009-10-21 02:52:25 -0700 (Wed, 21 Oct 2009) $
*/
/*
@@ -109,7 +109,6 @@ typedef void *EGLClientBuffer;
#define EGL_NATIVE_RENDERABLE 0x302D
#define EGL_NATIVE_VISUAL_ID 0x302E
#define EGL_NATIVE_VISUAL_TYPE 0x302F
-#define EGL_PRESERVED_RESOURCES 0x3030
#define EGL_SAMPLES 0x3031
#define EGL_SAMPLE_BUFFERS 0x3032
#define EGL_SURFACE_TYPE 0x3033
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 545fd0e..b121158 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -6,7 +6,7 @@ extern "C" {
#endif
/*
-** Copyright (c) 2007-2009 The Khronos Group Inc.
+** Copyright (c) 2007-2010 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -34,8 +34,8 @@ extern "C" {
/* Header file version number */
/* Current version at http://www.khronos.org/registry/egl/ */
-/* $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $ */
-#define EGL_EGLEXT_VERSION 3
+/* $Revision: 11249 $ on $Date: 2010-05-05 09:54:28 -0700 (Wed, 05 May 2010) $ */
+#define EGL_EGLEXT_VERSION 5
#ifndef EGL_KHR_config_attribs
#define EGL_KHR_config_attribs 1
@@ -120,6 +120,36 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL
#define EGL_GL_RENDERBUFFER_KHR 0x30B9 /* eglCreateImageKHR target */
#endif
+#ifndef EGL_KHR_reusable_sync
+#define EGL_KHR_reusable_sync 1
+
+typedef void* EGLSyncKHR;
+typedef khronos_utime_nanoseconds_t EGLTimeKHR;
+
+#define EGL_SYNC_STATUS_KHR 0x30F1
+#define EGL_SIGNALED_KHR 0x30F2
+#define EGL_UNSIGNALED_KHR 0x30F3
+#define EGL_TIMEOUT_EXPIRED_KHR 0x30F5
+#define EGL_CONDITION_SATISFIED_KHR 0x30F6
+#define EGL_SYNC_TYPE_KHR 0x30F7
+#define EGL_SYNC_REUSABLE_KHR 0x30FA
+#define EGL_SYNC_FLUSH_COMMANDS_BIT_KHR 0x0001 /* eglClientWaitSyncKHR <flags> bitfield */
+#define EGL_FOREVER_KHR 0xFFFFFFFFFFFFFFFFull
+#define EGL_NO_SYNC_KHR ((EGLSyncKHR)0)
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync);
+EGLAPI EGLint EGLAPIENTRY eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+EGLAPI EGLBoolean EGLAPIENTRY eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode);
+EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value);
+#endif /* EGL_EGLEXT_PROTOTYPES */
+typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSIGNALSYNCKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBKHRPROC) (EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value);
+#endif
+
#ifndef EGL_KHR_image_base
#define EGL_KHR_image_base 1
/* Most interfaces defined by EGL_KHR_image_pixmap above */
@@ -131,6 +161,67 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL
/* Interfaces defined by EGL_KHR_image above */
#endif
+#ifndef EGL_IMG_context_priority
+#define EGL_IMG_context_priority 1
+#define EGL_CONTEXT_PRIORITY_LEVEL_IMG 0x3100
+#define EGL_CONTEXT_PRIORITY_HIGH_IMG 0x3101
+#define EGL_CONTEXT_PRIORITY_MEDIUM_IMG 0x3102
+#define EGL_CONTEXT_PRIORITY_LOW_IMG 0x3103
+#endif
+
+#ifndef EGL_NV_coverage_sample
+#define EGL_NV_coverage_sample 1
+#define EGL_COVERAGE_BUFFERS_NV 0x30E0
+#define EGL_COVERAGE_SAMPLES_NV 0x30E1
+#endif
+
+#ifndef EGL_NV_depth_nonlinear
+#define EGL_NV_depth_nonlinear 1
+#define EGL_DEPTH_ENCODING_NV 0x30E2
+#define EGL_DEPTH_ENCODING_NONE_NV 0
+#define EGL_DEPTH_ENCODING_NONLINEAR_NV 0x30E3
+#endif
+
+#ifndef EGL_NV_sync
+#define EGL_NV_sync 1
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_NV 0x30E6
+#define EGL_SYNC_STATUS_NV 0x30E7
+#define EGL_SIGNALED_NV 0x30E8
+#define EGL_UNSIGNALED_NV 0x30E9
+#define EGL_SYNC_FLUSH_COMMANDS_BIT_NV 0x0001
+#define EGL_FOREVER_NV 0xFFFFFFFFFFFFFFFFull
+#define EGL_ALREADY_SIGNALED_NV 0x30EA
+#define EGL_TIMEOUT_EXPIRED_NV 0x30EB
+#define EGL_CONDITION_SATISFIED_NV 0x30EC
+#define EGL_SYNC_TYPE_NV 0x30ED
+#define EGL_SYNC_CONDITION_NV 0x30EE
+#define EGL_SYNC_FENCE_NV 0x30EF
+#define EGL_NO_SYNC_NV ((EGLSyncNV)0)
+typedef void* EGLSyncNV;
+typedef unsigned long long EGLTimeNV;
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLSyncNV eglCreateFenceSyncNV (EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list);
+EGLBoolean eglDestroySyncNV (EGLSyncNV sync);
+EGLBoolean eglFenceNV (EGLSyncNV sync);
+EGLint eglClientWaitSyncNV (EGLSyncNV sync, EGLint flags, EGLTimeNV timeout);
+EGLBoolean eglSignalSyncNV (EGLSyncNV sync, EGLenum mode);
+EGLBoolean eglGetSyncAttribNV (EGLSyncNV sync, EGLint attribute, EGLint *value);
+#endif /* EGL_EGLEXT_PROTOTYPES */
+typedef EGLSyncNV (EGLAPIENTRYP PFNEGLCREATEFENCESYNCNVPROC) (EGLDisplay dpy, EGLenum condition, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCNVPROC) (EGLSyncNV sync);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLFENCENVPROC) (EGLSyncNV sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCNVPROC) (EGLSyncNV sync, EGLint flags, EGLTimeNV timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSIGNALSYNCNVPROC) (EGLSyncNV sync, EGLenum mode);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBNVPROC) (EGLSyncNV sync, EGLint attribute, EGLint *value);
+#endif
+
+#ifndef EGL_KHR_fence_sync
+#define EGL_KHR_fence_sync 1
+/* Reuses most tokens and entry points from EGL_KHR_reusable_sync */
+#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR 0x30F0
+#define EGL_SYNC_CONDITION_KHR 0x30F8
+#define EGL_SYNC_FENCE_KHR 0x30F9
+#endif
#ifndef EGL_ANDROID_image_native_buffer
#define EGL_ANDROID_image_native_buffer 1
@@ -154,7 +245,6 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSetSwapRectangleANDROID (EGLDisplay dpy, EGLSur
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSWAPRECTANGLEANDROIDPROC) (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height);
#endif
-
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 53e9e61..25d7697 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -25,7 +25,7 @@
*/
/* Platform-specific types and definitions for egl.h
- * $Revision: 7244 $ on $Date: 2009-01-20 17:06:59 -0800 (Tue, 20 Jan 2009) $
+ * $Revision: 9724 $ on $Date: 2009-12-02 02:05:33 -0800 (Wed, 02 Dec 2009) $
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
@@ -50,8 +50,10 @@
#define EGLAPI KHRONOS_APICALL
#endif
+#ifndef EGLAPIENTRY
#define EGLAPIENTRY KHRONOS_APIENTRY
-#define EGLAPIENTRYP KHRONOS_APIENTRY*
+#endif
+#define EGLAPIENTRYP EGLAPIENTRY*
/* The types NativeDisplayType, NativeWindowType, and NativePixmapType
* are aliases of window-system-dependent types, such as X Display * or
@@ -89,10 +91,11 @@ typedef Window EGLNativeWindowType;
#elif defined(ANDROID)
-struct android_native_window_t;
+#include <android/native_window.h>
+
struct egl_native_pixmap_t;
-typedef struct android_native_window_t* EGLNativeWindowType;
+typedef struct ANativeWindow* EGLNativeWindowType;
typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h
index 2e8b971..5b8d85a 100644
--- a/opengl/include/GLES/gl.h
+++ b/opengl/include/GLES/gl.h
@@ -1,7 +1,7 @@
#ifndef __gl_h_
#define __gl_h_
-/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */
+/* $Revision: 10601 $ on $Date:: 2010-03-04 22:15:27 -0800 #$ */
#include <GLES/glplatform.h>
@@ -15,6 +15,7 @@ extern "C" {
*/
typedef void GLvoid;
+typedef char GLchar;
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
@@ -678,7 +679,7 @@ GL_API void GL_APIENTRY glGetFixedv (GLenum pname, GLfixed *params);
GL_API void GL_APIENTRY glGetIntegerv (GLenum pname, GLint *params);
GL_API void GL_APIENTRY glGetLightxv (GLenum light, GLenum pname, GLfixed *params);
GL_API void GL_APIENTRY glGetMaterialxv (GLenum face, GLenum pname, GLfixed *params);
-GL_API void GL_APIENTRY glGetPointerv (GLenum pname, void **params);
+GL_API void GL_APIENTRY glGetPointerv (GLenum pname, GLvoid **params);
GL_API const GLubyte * GL_APIENTRY glGetString (GLenum name);
GL_API void GL_APIENTRY glGetTexEnviv (GLenum env, GLenum pname, GLint *params);
GL_API void GL_APIENTRY glGetTexEnvxv (GLenum env, GLenum pname, GLfixed *params);
diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h
index a8fe2e9..a5b3ead 100644
--- a/opengl/include/GLES/glext.h
+++ b/opengl/include/GLES/glext.h
@@ -1,7 +1,7 @@
#ifndef __glext_h_
#define __glext_h_
-/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */
+/* $Revision: 10965 $ on $Date:: 2010-04-09 02:11:29 -0700 #$ */
#ifdef __cplusplus
extern "C" {
@@ -68,6 +68,11 @@ extern "C" {
typedef void* GLeglImageOES;
#endif
+/* GL_OES_element_index_uint */
+#ifndef GL_OES_element_index_uint
+#define GL_UNSIGNED_INT 0x1405
+#endif
+
/* GL_OES_fixed_point */
#ifndef GL_OES_fixed_point
#define GL_FIXED_OES 0x140C
@@ -201,6 +206,16 @@ typedef void* GLeglImageOES;
#define GL_MIRRORED_REPEAT_OES 0x8370
#endif
+/* GL_OES_vertex_array_object */
+#ifndef GL_OES_vertex_array_object
+#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5
+#endif
+
+/* GL_OES_texture_external */
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
/*------------------------------------------------------------------------*
* AMD extension tokens
*------------------------------------------------------------------------*/
@@ -219,15 +234,191 @@ typedef void* GLeglImageOES;
#endif
/*------------------------------------------------------------------------*
+ * APPLE extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_APPLE_texture_2D_limited_npot */
+/* No new tokens introduced by this extension. */
+
+/*------------------------------------------------------------------------*
* EXT extension tokens
*------------------------------------------------------------------------*/
+/* GL_EXT_blend_minmax */
+#ifndef GL_EXT_blend_minmax
+#define GL_MIN_EXT 0x8007
+#define GL_MAX_EXT 0x8008
+#endif
+
+/* GL_EXT_discard_framebuffer */
+#ifndef GL_EXT_discard_framebuffer
+#define GL_COLOR_EXT 0x1800
+#define GL_DEPTH_EXT 0x1801
+#define GL_STENCIL_EXT 0x1802
+#endif
+
+/* GL_EXT_multi_draw_arrays */
+/* No new tokens introduced by this extension. */
+
+/* GL_EXT_read_format_bgra */
+#ifndef GL_EXT_read_format_bgra
+#define GL_BGRA_EXT 0x80E1
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366
+#endif
+
/* GL_EXT_texture_filter_anisotropic */
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
#endif
+/* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_BGRA8888
+#define GL_BGRA_EXT 0x80E1
+#endif
+
+/* GL_EXT_texture_lod_bias */
+#ifndef GL_EXT_texture_lod_bias
+#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD
+#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500
+#define GL_TEXTURE_LOD_BIAS_EXT 0x8501
+#endif
+
+/*------------------------------------------------------------------------*
+ * IMG extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_IMG_read_format */
+#ifndef GL_IMG_read_format
+#define GL_BGRA_IMG 0x80E1
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365
+#endif
+
+/* GL_IMG_texture_compression_pvrtc */
+#ifndef GL_IMG_texture_compression_pvrtc
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+
+/* GL_IMG_texture_env_enhanced_fixed_function */
+#ifndef GL_IMG_texture_env_enhanced_fixed_function
+#define GL_MODULATE_COLOR_IMG 0x8C04
+#define GL_RECIP_ADD_SIGNED_ALPHA_IMG 0x8C05
+#define GL_TEXTURE_ALPHA_MODULATE_IMG 0x8C06
+#define GL_FACTOR_ALPHA_MODULATE_IMG 0x8C07
+#define GL_FRAGMENT_ALPHA_MODULATE_IMG 0x8C08
+#define GL_ADD_BLEND_IMG 0x8C09
+#define GL_DOT3_RGBA_IMG 0x86AF
+#endif
+
+/* GL_IMG_user_clip_plane */
+#ifndef GL_IMG_user_clip_plane
+#define GL_CLIP_PLANE0_IMG 0x3000
+#define GL_CLIP_PLANE1_IMG 0x3001
+#define GL_CLIP_PLANE2_IMG 0x3002
+#define GL_CLIP_PLANE3_IMG 0x3003
+#define GL_CLIP_PLANE4_IMG 0x3004
+#define GL_CLIP_PLANE5_IMG 0x3005
+#define GL_MAX_CLIP_PLANES_IMG 0x0D32
+#endif
+
+/* GL_IMG_multisampled_render_to_texture */
+#ifndef GL_IMG_multisampled_render_to_texture
+#define GL_RENDERBUFFER_SAMPLES_IMG 0x9133
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134
+#define GL_MAX_SAMPLES_IMG 0x9135
+#define GL_TEXTURE_SAMPLES_IMG 0x9136
+#endif
+
+/*------------------------------------------------------------------------*
+ * NV extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_NV_fence */
+#ifndef GL_NV_fence
+#define GL_ALL_COMPLETED_NV 0x84F2
+#define GL_FENCE_STATUS_NV 0x84F3
+#define GL_FENCE_CONDITION_NV 0x84F4
+#endif
+
+/*------------------------------------------------------------------------*
+ * QCOM extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_QCOM_driver_control */
+/* No new tokens introduced by this extension. */
+
+/* GL_QCOM_extended_get */
+#ifndef GL_QCOM_extended_get
+#define GL_TEXTURE_WIDTH_QCOM 0x8BD2
+#define GL_TEXTURE_HEIGHT_QCOM 0x8BD3
+#define GL_TEXTURE_DEPTH_QCOM 0x8BD4
+#define GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5
+#define GL_TEXTURE_FORMAT_QCOM 0x8BD6
+#define GL_TEXTURE_TYPE_QCOM 0x8BD7
+#define GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8
+#define GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9
+#define GL_TEXTURE_TARGET_QCOM 0x8BDA
+#define GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB
+#define GL_STATE_RESTORE 0x8BDC
+#endif
+
+/* GL_QCOM_extended_get2 */
+/* No new tokens introduced by this extension. */
+
+/* GL_QCOM_perfmon_global_mode */
+#ifndef GL_QCOM_perfmon_global_mode
+#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0
+#endif
+
+/* GL_QCOM_writeonly_rendering */
+#ifndef GL_QCOM_writeonly_rendering
+#define GL_WRITEONLY_RENDERING_QCOM 0x8823
+#endif
+
+/* GL_QCOM_tiled_rendering */
+#ifndef GL_QCOM_tiled_rendering
+#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001
+#define GL_COLOR_BUFFER_BIT1_QCOM 0x00000002
+#define GL_COLOR_BUFFER_BIT2_QCOM 0x00000004
+#define GL_COLOR_BUFFER_BIT3_QCOM 0x00000008
+#define GL_COLOR_BUFFER_BIT4_QCOM 0x00000010
+#define GL_COLOR_BUFFER_BIT5_QCOM 0x00000020
+#define GL_COLOR_BUFFER_BIT6_QCOM 0x00000040
+#define GL_COLOR_BUFFER_BIT7_QCOM 0x00000080
+#define GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100
+#define GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200
+#define GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400
+#define GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800
+#define GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000
+#define GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000
+#define GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000
+#define GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000
+#define GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000
+#define GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000
+#define GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000
+#define GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000
+#define GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000
+#define GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000
+#define GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000
+#define GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000
+#define GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000
+#define GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000
+#define GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000
+#define GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000
+#define GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000
+#define GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000
+#define GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000
+#define GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000
+#endif
+
+/*------------------------------------------------------------------------*
+ * End of extension tokens, start of corresponding extension functions
+ *------------------------------------------------------------------------*/
+
/*------------------------------------------------------------------------*
* OES extension functions
*------------------------------------------------------------------------*/
@@ -456,11 +647,11 @@ typedef void (GL_APIENTRYP PFNGLGENERATEMIPMAPOESPROC) (GLenum target);
#ifdef GL_GLEXT_PROTOTYPES
GL_API void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access);
GL_API GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target);
-GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params);
+GL_API void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, GLvoid ** params);
#endif
typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access);
typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target);
-typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params);
+typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, GLvoid ** params);
#endif
/* GL_OES_matrix_get */
@@ -576,6 +767,26 @@ typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname,
#define GL_OES_texture_mirrored_repeat 1
#endif
+/* GL_OES_vertex_array_object */
+#ifndef GL_OES_vertex_array_object
+#define GL_OES_vertex_array_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glBindVertexArrayOES (GLuint array);
+GL_API void GL_APIENTRY glDeleteVertexArraysOES (GLsizei n, const GLuint *arrays);
+GL_API void GL_APIENTRY glGenVertexArraysOES (GLsizei n, GLuint *arrays);
+GL_API GLboolean GL_APIENTRY glIsVertexArrayOES (GLuint array);
+#endif
+typedef void (GL_APIENTRYP PFNGLBINDVERTEXARRAYOESPROC) (GLuint array);
+typedef void (GL_APIENTRYP PFNGLDELETEVERTEXARRAYSOESPROC) (GLsizei n, const GLuint *arrays);
+typedef void (GL_APIENTRYP PFNGLGENVERTEXARRAYSOESPROC) (GLsizei n, GLuint *arrays);
+typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array);
+#endif
+
+/* GL_OES_texture_external */
+#ifndef GL_OES_texture_external
+#define GL_OES_texture_external 1
+#endif
+
/*------------------------------------------------------------------------*
* AMD extension functions
*------------------------------------------------------------------------*/
@@ -591,14 +802,207 @@ typedef void (GL_APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname,
#endif
/*------------------------------------------------------------------------*
+ * APPLE extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_APPLE_texture_2D_limited_npot */
+#ifndef GL_APPLE_texture_2D_limited_npot
+#define GL_APPLE_texture_2D_limited_npot 1
+#endif
+
+/*------------------------------------------------------------------------*
* EXT extension functions
*------------------------------------------------------------------------*/
+/* GL_EXT_blend_minmax */
+#ifndef GL_EXT_blend_minmax
+#define GL_EXT_blend_minmax 1
+#endif
+
+/* GL_EXT_discard_framebuffer */
+#ifndef GL_EXT_discard_framebuffer
+#define GL_EXT_discard_framebuffer 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments);
+#endif
+typedef void (GL_APIENTRYP PFNGLDISCARDFRAMEBUFFEREXTPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments);
+#endif
+
+/* GL_EXT_multi_draw_arrays */
+#ifndef GL_EXT_multi_draw_arrays
+#define GL_EXT_multi_draw_arrays 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glMultiDrawArraysEXT (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
+GL_API void GL_APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
+typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+#endif
+
+/* GL_EXT_read_format_bgra */
+#ifndef GL_EXT_read_format_bgra
+#define GL_EXT_read_format_bgra 1
+#endif
+
/* GL_EXT_texture_filter_anisotropic */
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_EXT_texture_filter_anisotropic 1
#endif
+/* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_BGRA8888
+#define GL_EXT_texture_format_BGRA8888 1
+#endif
+
+/* GL_EXT_texture_lod_bias */
+#ifndef GL_EXT_texture_lod_bias
+#define GL_EXT_texture_lod_bias 1
+#endif
+
+/*------------------------------------------------------------------------*
+ * IMG extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_IMG_read_format */
+#ifndef GL_IMG_read_format
+#define GL_IMG_read_format 1
+#endif
+
+/* GL_IMG_texture_compression_pvrtc */
+#ifndef GL_IMG_texture_compression_pvrtc
+#define GL_IMG_texture_compression_pvrtc 1
+#endif
+
+/* GL_IMG_texture_env_enhanced_fixed_function */
+#ifndef GL_IMG_texture_env_enhanced_fixed_function
+#define GL_IMG_texture_env_enhanced_fixed_function 1
+#endif
+
+/* GL_IMG_user_clip_plane */
+#ifndef GL_IMG_user_clip_plane
+#define GL_IMG_user_clip_plane 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glClipPlanefIMG (GLenum p, const GLfloat *eqn);
+GL_API void GL_APIENTRY glClipPlanexIMG (GLenum p, const GLfixed *eqn);
+#endif
+typedef void (GL_APIENTRYP PFNGLCLIPPLANEFIMGPROC) (GLenum p, const GLfloat *eqn);
+typedef void (GL_APIENTRYP PFNGLCLIPPLANEXIMGPROC) (GLenum p, const GLfixed *eqn);
+#endif
+
+/* GL_IMG_multisampled_render_to_texture */
+#ifndef GL_IMG_multisampled_render_to_texture
+#define GL_IMG_multisampled_render_to_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glRenderbufferStorageMultisampleIMG (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+GL_API void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+#endif
+typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+#endif
+
+/*------------------------------------------------------------------------*
+ * NV extension functions
+ *------------------------------------------------------------------------*/
+
+/* NV_fence */
+#ifndef GL_NV_fence
+#define GL_NV_fence 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences);
+GL_API void GL_APIENTRY glGenFencesNV (GLsizei n, GLuint *fences);
+GL_API GLboolean GL_APIENTRY glIsFenceNV (GLuint fence);
+GL_API GLboolean GL_APIENTRY glTestFenceNV (GLuint fence);
+GL_API void GL_APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params);
+GL_API void GL_APIENTRY glFinishFenceNV (GLuint fence);
+GL_API void GL_APIENTRY glSetFenceNV (GLuint fence, GLenum condition);
+#endif
+typedef void (GL_APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences);
+typedef void (GL_APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences);
+typedef GLboolean (GL_APIENTRYP PFNGLISFENCENVPROC) (GLuint fence);
+typedef GLboolean (GL_APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence);
+typedef void (GL_APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence);
+typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition);
+#endif
+
+/*------------------------------------------------------------------------*
+ * QCOM extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_QCOM_driver_control */
+#ifndef GL_QCOM_driver_control
+#define GL_QCOM_driver_control 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls);
+GL_API void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString);
+GL_API void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl);
+GL_API void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl);
+#endif
+typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls);
+typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString);
+typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl);
+typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl);
+#endif
+
+/* GL_QCOM_extended_get */
+#ifndef GL_QCOM_extended_get
+#define GL_QCOM_extended_get 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glExtGetTexturesQCOM (GLuint *textures, GLint maxTextures, GLint *numTextures);
+GL_API void GL_APIENTRY glExtGetBuffersQCOM (GLuint *buffers, GLint maxBuffers, GLint *numBuffers);
+GL_API void GL_APIENTRY glExtGetRenderbuffersQCOM (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers);
+GL_API void GL_APIENTRY glExtGetFramebuffersQCOM (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers);
+GL_API void GL_APIENTRY glExtGetTexLevelParameterivQCOM (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params);
+GL_API void GL_APIENTRY glExtTexObjectStateOverrideiQCOM (GLenum target, GLenum pname, GLint param);
+GL_API void GL_APIENTRY glExtGetTexSubImageQCOM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels);
+GL_API void GL_APIENTRY glExtGetBufferPointervQCOM (GLenum target, GLvoid **params);
+#endif
+typedef void (GL_APIENTRYP PFNGLEXTGETTEXTURESQCOMPROC) (GLuint *textures, GLint maxTextures, GLint *numTextures);
+typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERSQCOMPROC) (GLuint *buffers, GLint maxBuffers, GLint *numBuffers);
+typedef void (GL_APIENTRYP PFNGLEXTGETRENDERBUFFERSQCOMPROC) (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers);
+typedef void (GL_APIENTRYP PFNGLEXTGETFRAMEBUFFERSQCOMPROC) (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers);
+typedef void (GL_APIENTRYP PFNGLEXTGETTEXLEVELPARAMETERIVQCOMPROC) (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLEXTTEXOBJECTSTATEOVERRIDEIQCOMPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (GL_APIENTRYP PFNGLEXTGETTEXSUBIMAGEQCOMPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels);
+typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERPOINTERVQCOMPROC) (GLenum target, GLvoid **params);
+#endif
+
+/* GL_QCOM_extended_get2 */
+#ifndef GL_QCOM_extended_get2
+#define GL_QCOM_extended_get2 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glExtGetShadersQCOM (GLuint *shaders, GLint maxShaders, GLint *numShaders);
+GL_API void GL_APIENTRY glExtGetProgramsQCOM (GLuint *programs, GLint maxPrograms, GLint *numPrograms);
+GL_API GLboolean GL_APIENTRY glExtIsProgramBinaryQCOM (GLuint program);
+GL_API void GL_APIENTRY glExtGetProgramBinarySourceQCOM (GLuint program, GLenum shadertype, GLchar *source, GLint *length);
+#endif
+typedef void (GL_APIENTRYP PFNGLEXTGETSHADERSQCOMPROC) (GLuint *shaders, GLint maxShaders, GLint *numShaders);
+typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMSQCOMPROC) (GLuint *programs, GLint maxPrograms, GLint *numPrograms);
+typedef GLboolean (GL_APIENTRYP PFNGLEXTISPROGRAMBINARYQCOMPROC) (GLuint program);
+typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMBINARYSOURCEQCOMPROC) (GLuint program, GLenum shadertype, GLchar *source, GLint *length);
+#endif
+
+/* GL_QCOM_perfmon_global_mode */
+#ifndef GL_QCOM_perfmon_global_mode
+#define GL_QCOM_perfmon_global_mode 1
+#endif
+
+/* GL_QCOM_writeonly_rendering */
+#ifndef GL_QCOM_writeonly_rendering
+#define GL_QCOM_writeonly_rendering 1
+#endif
+
+/* GL_QCOM_tiled_rendering */
+#ifndef GL_QCOM_tiled_rendering
+#define GL_QCOM_tiled_rendering 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_API void GL_APIENTRY glStartTilingQCOM (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask);
+GL_API void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask);
+#endif
+typedef void (GL_APIENTRYP PFNGLSTARTTILINGQCOMPROC) (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask);
+typedef void (GL_APIENTRYP PFNGLENDTILINGQCOMPROC) (GLbitfield preserveMask);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/GLES/glplatform.h b/opengl/include/GLES/glplatform.h
index 198e679..2db6ee2 100644
--- a/opengl/include/GLES/glplatform.h
+++ b/opengl/include/GLES/glplatform.h
@@ -1,7 +1,7 @@
#ifndef __glplatform_h_
#define __glplatform_h_
-/* $Revision: 7172 $ on $Date:: 2009-01-09 11:17:41 -0800 #$ */
+/* $Revision: 10601 $ on $Date:: 2010-03-04 22:15:27 -0800 #$ */
/*
* This document is licensed under the SGI Free Software B License Version
@@ -9,7 +9,6 @@
*/
/* Platform-specific types and definitions for OpenGL ES 1.X gl.h
- * Last modified on 2008/12/19
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
@@ -24,10 +23,8 @@
#define GL_API KHRONOS_APICALL
#endif
-#if defined(ANDROID)
-
+#ifndef GL_APIENTRY
#define GL_APIENTRY KHRONOS_APIENTRY
-
#endif
#endif /* __glplatform_h_ */
diff --git a/opengl/include/GLES2/gl2.h b/opengl/include/GLES2/gl2.h
index 0182a67..e1d3b87 100644
--- a/opengl/include/GLES2/gl2.h
+++ b/opengl/include/GLES2/gl2.h
@@ -1,7 +1,7 @@
#ifndef __gl2_h_
#define __gl2_h_
-/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */
+/* $Revision: 10602 $ on $Date:: 2010-03-04 22:35:34 -0800 #$ */
#include <GLES2/gl2platform.h>
@@ -19,6 +19,7 @@ extern "C" {
*-----------------------------------------------------------------------*/
typedef void GLvoid;
+typedef char GLchar;
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
@@ -472,7 +473,7 @@ typedef khronos_ssize_t GLsizeiptr;
GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture);
GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader);
-GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const char* name);
+GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar* name);
GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer);
GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer);
GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer);
@@ -482,8 +483,8 @@ GL_APICALL void GL_APIENTRY glBlendEquation ( GLenum mode );
GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor);
GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
-GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void* data, GLenum usage);
-GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
+GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
+GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target);
GL_APICALL void GL_APIENTRY glClear (GLbitfield mask);
GL_APICALL void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
@@ -491,8 +492,8 @@ GL_APICALL void GL_APIENTRY glClearDepthf (GLclampf depth);
GL_APICALL void GL_APIENTRY glClearStencil (GLint s);
GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader);
-GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data);
-GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data);
+GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data);
+GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data);
GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
GL_APICALL GLuint GL_APIENTRY glCreateProgram (void);
@@ -511,7 +512,7 @@ GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shade
GL_APICALL void GL_APIENTRY glDisable (GLenum cap);
GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index);
GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
-GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void* indices);
+GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);
GL_APICALL void GL_APIENTRY glEnable (GLenum cap);
GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index);
GL_APICALL void GL_APIENTRY glFinish (void);
@@ -524,10 +525,10 @@ GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target);
GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers);
GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers);
GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint* textures);
-GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
-GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
+GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders);
-GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const char* name);
+GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const GLchar* name);
GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params);
GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params);
GL_APICALL GLenum GL_APIENTRY glGetError (void);
@@ -535,21 +536,21 @@ GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat* params);
GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params);
GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint* params);
GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params);
-GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, char* infolog);
+GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog);
GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint* params);
GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params);
-GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog);
+GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog);
GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
-GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, char* source);
+GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source);
GL_APICALL const GLubyte* GL_APIENTRY glGetString (GLenum name);
GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat* params);
GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint* params);
GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat* params);
GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint* params);
-GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const char* name);
+GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const GLchar* name);
GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params);
GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params);
-GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void** pointer);
+GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid** pointer);
GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode);
GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer);
GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap);
@@ -562,25 +563,25 @@ GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width);
GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program);
GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param);
GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units);
-GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
+GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels);
GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void);
GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
GL_APICALL void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert);
GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
-GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length);
-GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const char** string, const GLint* length);
+GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length);
+GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask);
GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask);
GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask);
GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask);
GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass);
GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
-GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
+GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param);
GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat* params);
GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint* params);
-GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels);
+GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels);
GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat x);
GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat* v);
GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint x);
@@ -610,7 +611,7 @@ GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint indx, GLfloat x, GL
GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint indx, const GLfloat* values);
GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint indx, const GLfloat* values);
-GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr);
+GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr);
GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
#ifdef __cplusplus
diff --git a/opengl/include/GLES2/gl2ext.h b/opengl/include/GLES2/gl2ext.h
index 72f1ae7..de5d65a 100644
--- a/opengl/include/GLES2/gl2ext.h
+++ b/opengl/include/GLES2/gl2ext.h
@@ -1,7 +1,7 @@
#ifndef __gl2ext_h_
#define __gl2ext_h_
-/* $Revision: 8271 $ on $Date:: 2009-05-21 09:33:40 -0700 #$ */
+/* $Revision: 10969 $ on $Date:: 2010-04-09 02:27:15 -0700 #$ */
#ifdef __cplusplus
extern "C" {
@@ -57,6 +57,11 @@ extern "C" {
typedef void* GLeglImageOES;
#endif
+/* GL_OES_element_index_uint */
+#ifndef GL_OES_element_index_uint
+#define GL_UNSIGNED_INT 0x1405
+#endif
+
/* GL_OES_get_program_binary */
#ifndef GL_OES_get_program_binary
#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
@@ -100,8 +105,8 @@ typedef void* GLeglImageOES;
#define GL_STENCIL_INDEX4_OES 0x8D47
#endif
-/* GL_OES_texture3D */
-#ifndef GL_OES_texture3D
+/* GL_OES_texture_3D */
+#ifndef GL_OES_texture_3D
#define GL_TEXTURE_WRAP_R_OES 0x8072
#define GL_TEXTURE_3D_OES 0x806F
#define GL_TEXTURE_BINDING_3D_OES 0x806A
@@ -110,11 +115,28 @@ typedef void* GLeglImageOES;
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4
#endif
+/* GL_OES_texture_float */
+/* No new tokens introduced by this extension. */
+
+/* GL_OES_texture_float_linear */
+/* No new tokens introduced by this extension. */
+
/* GL_OES_texture_half_float */
#ifndef GL_OES_texture_half_float
#define GL_HALF_FLOAT_OES 0x8D61
#endif
+/* GL_OES_texture_half_float_linear */
+/* No new tokens introduced by this extension. */
+
+/* GL_OES_texture_npot */
+/* No new tokens introduced by this extension. */
+
+/* GL_OES_vertex_array_object */
+#ifndef GL_OES_vertex_array_object
+#define GL_VERTEX_ARRAY_BINDING_OES 0x85B5
+#endif
+
/* GL_OES_vertex_half_float */
/* GL_HALF_FLOAT_OES defined in GL_OES_texture_half_float already. */
@@ -124,6 +146,11 @@ typedef void* GLeglImageOES;
#define GL_INT_10_10_10_2_OES 0x8DF7
#endif
+/* GL_OES_texture_external */
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
/*------------------------------------------------------------------------*
* AMD extension tokens
*------------------------------------------------------------------------*/
@@ -141,11 +168,6 @@ typedef void* GLeglImageOES;
#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
#endif
-/* GL_AMD_program_binary_Z400 */
-#ifndef GL_AMD_program_binary_Z400
-#define GL_Z400_BINARY_AMD 0x8740
-#endif
-
/* GL_AMD_performance_monitor */
#ifndef GL_AMD_performance_monitor
#define GL_COUNTER_TYPE_AMD 0x8BC0
@@ -157,35 +179,78 @@ typedef void* GLeglImageOES;
#define GL_PERFMON_RESULT_AMD 0x8BC6
#endif
+/* GL_AMD_program_binary_Z400 */
+#ifndef GL_AMD_program_binary_Z400
+#define GL_Z400_BINARY_AMD 0x8740
+#endif
+
/*------------------------------------------------------------------------*
* EXT extension tokens
*------------------------------------------------------------------------*/
+/* GL_EXT_blend_minmax */
+#ifndef GL_EXT_blend_minmax
+#define GL_MIN_EXT 0x8007
+#define GL_MAX_EXT 0x8008
+#endif
+
+/* GL_EXT_discard_framebuffer */
+#ifndef GL_EXT_discard_framebuffer
+#define GL_COLOR_EXT 0x1800
+#define GL_DEPTH_EXT 0x1801
+#define GL_STENCIL_EXT 0x1802
+#endif
+
+/* GL_EXT_multi_draw_arrays */
+/* No new tokens introduced by this extension. */
+
+/* GL_EXT_read_format_bgra */
+#ifndef GL_EXT_read_format_bgra
+#define GL_BGRA_EXT 0x80E1
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT 0x8366
+#endif
+
/* GL_EXT_texture_filter_anisotropic */
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
#endif
+/* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_BGRA8888
+#define GL_BGRA_EXT 0x80E1
+#endif
+
/* GL_EXT_texture_type_2_10_10_10_REV */
#ifndef GL_EXT_texture_type_2_10_10_10_REV
#define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368
#endif
-/* GL_EXT_texture_format_BGRA8888 */
-#ifndef GL_EXT_texture_format_BGRA8888
-#define GL_BGRA 0x80E1
+/* GL_EXT_texture_compression_dxt1 */
+#ifndef GL_EXT_texture_compression_dxt1
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#endif
/*------------------------------------------------------------------------*
* IMG extension tokens
*------------------------------------------------------------------------*/
+/* GL_IMG_program_binary */
+#ifndef GL_IMG_program_binary
+#define GL_SGX_PROGRAM_BINARY_IMG 0x9130
+#endif
+
/* GL_IMG_read_format */
#ifndef GL_IMG_read_format
-#define GL_BGRA 0x80E1
-#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
-#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_BGRA_IMG 0x80E1
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG 0x8365
+#endif
+
+/* GL_IMG_shader_binary */
+#ifndef GL_IMG_shader_binary
+#define GL_SGX_BINARY_IMG 0x8C0A
#endif
/* GL_IMG_texture_compression_pvrtc */
@@ -196,6 +261,14 @@ typedef void* GLeglImageOES;
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
#endif
+/* GL_IMG_multisampled_render_to_texture */
+#ifndef GL_IMG_multisampled_render_to_texture
+#define GL_RENDERBUFFER_SAMPLES_IMG 0x9133
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG 0x9134
+#define GL_MAX_SAMPLES_IMG 0x9135
+#define GL_TEXTURE_SAMPLES_IMG 0x9136
+#endif
+
/*------------------------------------------------------------------------*
* NV extension tokens
*------------------------------------------------------------------------*/
@@ -207,6 +280,24 @@ typedef void* GLeglImageOES;
#define GL_FENCE_CONDITION_NV 0x84F4
#endif
+/* GL_NV_coverage_sample */
+#ifndef GL_NV_coverage_sample
+#define GL_COVERAGE_COMPONENT_NV 0x8ED0
+#define GL_COVERAGE_COMPONENT4_NV 0x8ED1
+#define GL_COVERAGE_ATTACHMENT_NV 0x8ED2
+#define GL_COVERAGE_BUFFERS_NV 0x8ED3
+#define GL_COVERAGE_SAMPLES_NV 0x8ED4
+#define GL_COVERAGE_ALL_FRAGMENTS_NV 0x8ED5
+#define GL_COVERAGE_EDGE_FRAGMENTS_NV 0x8ED6
+#define GL_COVERAGE_AUTOMATIC_NV 0x8ED7
+#define GL_COVERAGE_BUFFER_BIT_NV 0x8000
+#endif
+
+/* GL_NV_depth_nonlinear */
+#ifndef GL_NV_depth_nonlinear
+#define GL_DEPTH_COMPONENT16_NONLINEAR_NV 0x8E2C
+#endif
+
/*------------------------------------------------------------------------*
* QCOM extension tokens
*------------------------------------------------------------------------*/
@@ -214,11 +305,70 @@ typedef void* GLeglImageOES;
/* GL_QCOM_driver_control */
/* No new tokens introduced by this extension. */
+/* GL_QCOM_extended_get */
+#ifndef GL_QCOM_extended_get
+#define GL_TEXTURE_WIDTH_QCOM 0x8BD2
+#define GL_TEXTURE_HEIGHT_QCOM 0x8BD3
+#define GL_TEXTURE_DEPTH_QCOM 0x8BD4
+#define GL_TEXTURE_INTERNAL_FORMAT_QCOM 0x8BD5
+#define GL_TEXTURE_FORMAT_QCOM 0x8BD6
+#define GL_TEXTURE_TYPE_QCOM 0x8BD7
+#define GL_TEXTURE_IMAGE_VALID_QCOM 0x8BD8
+#define GL_TEXTURE_NUM_LEVELS_QCOM 0x8BD9
+#define GL_TEXTURE_TARGET_QCOM 0x8BDA
+#define GL_TEXTURE_OBJECT_VALID_QCOM 0x8BDB
+#define GL_STATE_RESTORE 0x8BDC
+#endif
+
+/* GL_QCOM_extended_get2 */
+/* No new tokens introduced by this extension. */
+
/* GL_QCOM_perfmon_global_mode */
#ifndef GL_QCOM_perfmon_global_mode
#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0
#endif
+/* GL_QCOM_writeonly_rendering */
+#ifndef GL_QCOM_writeonly_rendering
+#define GL_WRITEONLY_RENDERING_QCOM 0x8823
+#endif
+
+/* GL_QCOM_tiled_rendering */
+#ifndef GL_QCOM_tiled_rendering
+#define GL_COLOR_BUFFER_BIT0_QCOM 0x00000001
+#define GL_COLOR_BUFFER_BIT1_QCOM 0x00000002
+#define GL_COLOR_BUFFER_BIT2_QCOM 0x00000004
+#define GL_COLOR_BUFFER_BIT3_QCOM 0x00000008
+#define GL_COLOR_BUFFER_BIT4_QCOM 0x00000010
+#define GL_COLOR_BUFFER_BIT5_QCOM 0x00000020
+#define GL_COLOR_BUFFER_BIT6_QCOM 0x00000040
+#define GL_COLOR_BUFFER_BIT7_QCOM 0x00000080
+#define GL_DEPTH_BUFFER_BIT0_QCOM 0x00000100
+#define GL_DEPTH_BUFFER_BIT1_QCOM 0x00000200
+#define GL_DEPTH_BUFFER_BIT2_QCOM 0x00000400
+#define GL_DEPTH_BUFFER_BIT3_QCOM 0x00000800
+#define GL_DEPTH_BUFFER_BIT4_QCOM 0x00001000
+#define GL_DEPTH_BUFFER_BIT5_QCOM 0x00002000
+#define GL_DEPTH_BUFFER_BIT6_QCOM 0x00004000
+#define GL_DEPTH_BUFFER_BIT7_QCOM 0x00008000
+#define GL_STENCIL_BUFFER_BIT0_QCOM 0x00010000
+#define GL_STENCIL_BUFFER_BIT1_QCOM 0x00020000
+#define GL_STENCIL_BUFFER_BIT2_QCOM 0x00040000
+#define GL_STENCIL_BUFFER_BIT3_QCOM 0x00080000
+#define GL_STENCIL_BUFFER_BIT4_QCOM 0x00100000
+#define GL_STENCIL_BUFFER_BIT5_QCOM 0x00200000
+#define GL_STENCIL_BUFFER_BIT6_QCOM 0x00400000
+#define GL_STENCIL_BUFFER_BIT7_QCOM 0x00800000
+#define GL_MULTISAMPLE_BUFFER_BIT0_QCOM 0x01000000
+#define GL_MULTISAMPLE_BUFFER_BIT1_QCOM 0x02000000
+#define GL_MULTISAMPLE_BUFFER_BIT2_QCOM 0x04000000
+#define GL_MULTISAMPLE_BUFFER_BIT3_QCOM 0x08000000
+#define GL_MULTISAMPLE_BUFFER_BIT4_QCOM 0x10000000
+#define GL_MULTISAMPLE_BUFFER_BIT5_QCOM 0x20000000
+#define GL_MULTISAMPLE_BUFFER_BIT6_QCOM 0x40000000
+#define GL_MULTISAMPLE_BUFFER_BIT7_QCOM 0x80000000
+#endif
+
/*------------------------------------------------------------------------*
* End of extension tokens, start of corresponding extension functions
*------------------------------------------------------------------------*/
@@ -237,17 +387,6 @@ typedef void* GLeglImageOES;
#define GL_OES_compressed_paletted_texture 1
#endif
-/* GL_OES_EGL_image */
-#ifndef GL_OES_EGL_image
-#define GL_OES_EGL_image 1
-#ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image);
-GL_APICALL void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image);
-#endif
-typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image);
-typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image);
-#endif
-
/* GL_OES_depth24 */
#ifndef GL_OES_depth24
#define GL_OES_depth24 1
@@ -263,6 +402,17 @@ typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenu
#define GL_OES_depth_texture 1
#endif
+/* GL_OES_EGL_image */
+#ifndef GL_OES_EGL_image
+#define GL_OES_EGL_image 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image);
+GL_APICALL void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image);
+#endif
+typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image);
+typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image);
+#endif
+
/* GL_OES_element_index_uint */
#ifndef GL_OES_element_index_uint
#define GL_OES_element_index_uint 1
@@ -282,11 +432,11 @@ typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenu
#ifndef GL_OES_get_program_binary
#define GL_OES_get_program_binary 1
#ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL void GL_APIENTRY glGetProgramBinaryOES (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);
-GL_APICALL void GL_APIENTRY glProgramBinaryOES (GLuint program, GLenum binaryFormat, const void *binary, GLint length);
+GL_APICALL void GL_APIENTRY glGetProgramBinaryOES (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary);
+GL_APICALL void GL_APIENTRY glProgramBinaryOES (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length);
#endif
-typedef void (GL_APIENTRYP PFNGLGETPROGRAMBINARYOESPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);
-typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLint length);
+typedef void (GL_APIENTRYP PFNGLGETPROGRAMBINARYOESPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary);
+typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length);
#endif
/* GL_OES_mapbuffer */
@@ -295,11 +445,11 @@ typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum bi
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access);
GL_APICALL GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target);
-GL_APICALL void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params);
+GL_APICALL void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, GLvoid** params);
#endif
typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access);
typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target);
-typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params);
+typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, GLvoid** params);
#endif
/* GL_OES_packed_depth_stencil */
@@ -331,46 +481,61 @@ typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum
#ifndef GL_OES_texture_3D
#define GL_OES_texture_3D 1
#ifdef GL_GLEXT_PROTOTYPES
-GL_APICALL void GL_APIENTRY glTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels);
-GL_APICALL void GL_APIENTRY glTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
+GL_APICALL void GL_APIENTRY glTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
+GL_APICALL void GL_APIENTRY glTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels);
GL_APICALL void GL_APIENTRY glCopyTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
-GL_APICALL void GL_APIENTRY glCompressedTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data);
-GL_APICALL void GL_APIENTRY glCompressedTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data);
+GL_APICALL void GL_APIENTRY glCompressedTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data);
+GL_APICALL void GL_APIENTRY glCompressedTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data);
GL_APICALL void GL_APIENTRY glFramebufferTexture3DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
#endif
typedef void (GL_APIENTRYP PFNGLTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
-typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
+typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels);
typedef void (GL_APIENTRYP PFNGLCOPYTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
-typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data);
-typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data);
+typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data);
+typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data);
typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
#endif
-/* GL_OES_texture_float_linear */
-#ifndef GL_OES_texture_float_linear
-#define GL_OES_texture_float_linear 1
-#endif
-
-/* GL_OES_texture_half_float_linear */
-#ifndef GL_OES_texture_half_float_linear
-#define GL_OES_texture_half_float_linear 1
-#endif
-
/* GL_OES_texture_float */
#ifndef GL_OES_texture_float
#define GL_OES_texture_float 1
#endif
+/* GL_OES_texture_float_linear */
+#ifndef GL_OES_texture_float_linear
+#define GL_OES_texture_float_linear 1
+#endif
+
/* GL_OES_texture_half_float */
#ifndef GL_OES_texture_half_float
#define GL_OES_texture_half_float 1
#endif
+/* GL_OES_texture_half_float_linear */
+#ifndef GL_OES_texture_half_float_linear
+#define GL_OES_texture_half_float_linear 1
+#endif
+
/* GL_OES_texture_npot */
#ifndef GL_OES_texture_npot
#define GL_OES_texture_npot 1
#endif
+/* GL_OES_vertex_array_object */
+#ifndef GL_OES_vertex_array_object
+#define GL_OES_vertex_array_object 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glBindVertexArrayOES (GLuint array);
+GL_APICALL void GL_APIENTRY glDeleteVertexArraysOES (GLsizei n, const GLuint *arrays);
+GL_APICALL void GL_APIENTRY glGenVertexArraysOES (GLsizei n, GLuint *arrays);
+GL_APICALL GLboolean GL_APIENTRY glIsVertexArrayOES (GLuint array);
+#endif
+typedef void (GL_APIENTRYP PFNGLBINDVERTEXARRAYOESPROC) (GLuint array);
+typedef void (GL_APIENTRYP PFNGLDELETEVERTEXARRAYSOESPROC) (GLsizei n, const GLuint *arrays);
+typedef void (GL_APIENTRYP PFNGLGENVERTEXARRAYSOESPROC) (GLsizei n, GLuint *arrays);
+typedef GLboolean (GL_APIENTRYP PFNGLISVERTEXARRAYOESPROC) (GLuint array);
+#endif
+
/* GL_OES_vertex_half_float */
#ifndef GL_OES_vertex_half_float
#define GL_OES_vertex_half_float 1
@@ -381,6 +546,11 @@ typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum
#define GL_OES_vertex_type_10_10_10_2 1
#endif
+/* GL_OES_texture_external */
+#ifndef GL_OES_texture_external
+#define GL_OES_texture_external 1
+#endif
+
/*------------------------------------------------------------------------*
* AMD extension functions
*------------------------------------------------------------------------*/
@@ -395,20 +565,15 @@ typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum
#define GL_AMD_compressed_ATC_texture 1
#endif
-/* GL_AMD_program_binary_Z400 */
-#ifndef GL_AMD_program_binary_Z400
-#define GL_AMD_program_binary_Z400 1
-#endif
-
/* AMD_performance_monitor */
#ifndef GL_AMD_performance_monitor
#define GL_AMD_performance_monitor 1
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups);
GL_APICALL void GL_APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters);
-GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, char *groupString);
-GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString);
-GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, GLvoid *data);
GL_APICALL void GL_APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors);
GL_APICALL void GL_APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors);
GL_APICALL void GL_APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList);
@@ -418,9 +583,9 @@ GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLen
#endif
typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups);
typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters);
-typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, char *groupString);
-typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString);
-typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, GLvoid *data);
typedef void (GL_APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors);
typedef void (GL_APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors);
typedef void (GL_APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList);
@@ -429,39 +594,99 @@ typedef void (GL_APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor);
typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten);
#endif
+/* GL_AMD_program_binary_Z400 */
+#ifndef GL_AMD_program_binary_Z400
+#define GL_AMD_program_binary_Z400 1
+#endif
+
/*------------------------------------------------------------------------*
* EXT extension functions
*------------------------------------------------------------------------*/
+/* GL_EXT_blend_minmax */
+#ifndef GL_EXT_blend_minmax
+#define GL_EXT_blend_minmax 1
+#endif
+
+/* GL_EXT_discard_framebuffer */
+#ifndef GL_EXT_discard_framebuffer
+#define GL_EXT_discard_framebuffer 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glDiscardFramebufferEXT (GLenum target, GLsizei numAttachments, const GLenum *attachments);
+#endif
+typedef void (GL_APIENTRYP PFNGLDISCARDFRAMEBUFFEREXTPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments);
+#endif
+
+#ifndef GL_EXT_multi_draw_arrays
+#define GL_EXT_multi_draw_arrays 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glMultiDrawArraysEXT (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
+GL_APICALL void GL_APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (GL_APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
+typedef void (GL_APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+#endif
+
+/* GL_EXT_read_format_bgra */
+#ifndef GL_EXT_read_format_bgra
+#define GL_EXT_read_format_bgra 1
+#endif
+
/* GL_EXT_texture_filter_anisotropic */
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_EXT_texture_filter_anisotropic 1
#endif
+/* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_BGRA8888
+#define GL_EXT_texture_format_BGRA8888 1
+#endif
+
/* GL_EXT_texture_type_2_10_10_10_REV */
#ifndef GL_EXT_texture_type_2_10_10_10_REV
#define GL_EXT_texture_type_2_10_10_10_REV 1
#endif
-/* GL_EXT_texture_format_BGRA8888 */
-#ifndef GL_EXT_texture_format_BGRA8888
-#define GL_EXT_texture_format_BGRA8888 1
+/* GL_EXT_texture_compression_dxt1 */
+#ifndef GL_EXT_texture_compression_dxt1
+#define GL_EXT_texture_compression_dxt1 1
#endif
/*------------------------------------------------------------------------*
* IMG extension functions
*------------------------------------------------------------------------*/
+/* GL_IMG_program_binary */
+#ifndef GL_IMG_program_binary
+#define GL_IMG_program_binary 1
+#endif
+
/* GL_IMG_read_format */
#ifndef GL_IMG_read_format
#define GL_IMG_read_format 1
#endif
+/* GL_IMG_shader_binary */
+#ifndef GL_IMG_shader_binary
+#define GL_IMG_shader_binary 1
+#endif
+
/* GL_IMG_texture_compression_pvrtc */
#ifndef GL_IMG_texture_compression_pvrtc
#define GL_IMG_texture_compression_pvrtc 1
#endif
+/* GL_IMG_multisampled_render_to_texture */
+#ifndef GL_IMG_multisampled_render_to_texture
+#define GL_IMG_multisampled_render_to_texture 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleIMG (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+GL_APICALL void GL_APIENTRY glFramebufferTexture2DMultisampleIMG (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+#endif
+typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEIMG) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEIMG) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+#endif
+
/*------------------------------------------------------------------------*
* NV extension functions
*------------------------------------------------------------------------*/
@@ -487,6 +712,22 @@ typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence);
typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition);
#endif
+/* GL_NV_coverage_sample */
+#ifndef GL_NV_coverage_sample
+#define GL_NV_coverage_sample 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glCoverageMaskNV (GLboolean mask);
+GL_APICALL void GL_APIENTRY glCoverageOperationNV (GLenum operation);
+#endif
+typedef void (GL_APIENTRYP PFNGLCOVERAGEMASKNVPROC) (GLboolean mask);
+typedef void (GL_APIENTRYP PFNGLCOVERAGEOPERATIONNVPROC) (GLenum operation);
+#endif
+
+/* GL_NV_depth_nonlinear */
+#ifndef GL_NV_depth_nonlinear
+#define GL_NV_depth_nonlinear 1
+#endif
+
/*------------------------------------------------------------------------*
* QCOM extension functions
*------------------------------------------------------------------------*/
@@ -496,21 +737,75 @@ typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition)
#define GL_QCOM_driver_control 1
#ifdef GL_GLEXT_PROTOTYPES
GL_APICALL void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls);
-GL_APICALL void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString);
+GL_APICALL void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString);
GL_APICALL void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl);
GL_APICALL void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl);
#endif
typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls);
-typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString);
+typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString);
typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl);
typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl);
#endif
+/* GL_QCOM_extended_get */
+#ifndef GL_QCOM_extended_get
+#define GL_QCOM_extended_get 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glExtGetTexturesQCOM (GLuint *textures, GLint maxTextures, GLint *numTextures);
+GL_APICALL void GL_APIENTRY glExtGetBuffersQCOM (GLuint *buffers, GLint maxBuffers, GLint *numBuffers);
+GL_APICALL void GL_APIENTRY glExtGetRenderbuffersQCOM (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers);
+GL_APICALL void GL_APIENTRY glExtGetFramebuffersQCOM (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers);
+GL_APICALL void GL_APIENTRY glExtGetTexLevelParameterivQCOM (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params);
+GL_APICALL void GL_APIENTRY glExtTexObjectStateOverrideiQCOM (GLenum target, GLenum pname, GLint param);
+GL_APICALL void GL_APIENTRY glExtGetTexSubImageQCOM (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels);
+GL_APICALL void GL_APIENTRY glExtGetBufferPointervQCOM (GLenum target, GLvoid **params);
+#endif
+typedef void (GL_APIENTRYP PFNGLEXTGETTEXTURESQCOMPROC) (GLuint *textures, GLint maxTextures, GLint *numTextures);
+typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERSQCOMPROC) (GLuint *buffers, GLint maxBuffers, GLint *numBuffers);
+typedef void (GL_APIENTRYP PFNGLEXTGETRENDERBUFFERSQCOMPROC) (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers);
+typedef void (GL_APIENTRYP PFNGLEXTGETFRAMEBUFFERSQCOMPROC) (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers);
+typedef void (GL_APIENTRYP PFNGLEXTGETTEXLEVELPARAMETERIVQCOMPROC) (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLEXTTEXOBJECTSTATEOVERRIDEIQCOMPROC) (GLenum target, GLenum pname, GLint param);
+typedef void (GL_APIENTRYP PFNGLEXTGETTEXSUBIMAGEQCOMPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels);
+typedef void (GL_APIENTRYP PFNGLEXTGETBUFFERPOINTERVQCOMPROC) (GLenum target, GLvoid **params);
+#endif
+
+/* GL_QCOM_extended_get2 */
+#ifndef GL_QCOM_extended_get2
+#define GL_QCOM_extended_get2 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glExtGetShadersQCOM (GLuint *shaders, GLint maxShaders, GLint *numShaders);
+GL_APICALL void GL_APIENTRY glExtGetProgramsQCOM (GLuint *programs, GLint maxPrograms, GLint *numPrograms);
+GL_APICALL GLboolean GL_APIENTRY glExtIsProgramBinaryQCOM (GLuint program);
+GL_APICALL void GL_APIENTRY glExtGetProgramBinarySourceQCOM (GLuint program, GLenum shadertype, GLchar *source, GLint *length);
+#endif
+typedef void (GL_APIENTRYP PFNGLEXTGETSHADERSQCOMPROC) (GLuint *shaders, GLint maxShaders, GLint *numShaders);
+typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMSQCOMPROC) (GLuint *programs, GLint maxPrograms, GLint *numPrograms);
+typedef GLboolean (GL_APIENTRYP PFNGLEXTISPROGRAMBINARYQCOMPROC) (GLuint program);
+typedef void (GL_APIENTRYP PFNGLEXTGETPROGRAMBINARYSOURCEQCOMPROC) (GLuint program, GLenum shadertype, GLchar *source, GLint *length);
+#endif
+
/* GL_QCOM_perfmon_global_mode */
#ifndef GL_QCOM_perfmon_global_mode
#define GL_QCOM_perfmon_global_mode 1
#endif
+/* GL_QCOM_writeonly_rendering */
+#ifndef GL_QCOM_writeonly_rendering
+#define GL_QCOM_writeonly_rendering 1
+#endif
+
+/* GL_QCOM_tiled_rendering */
+#ifndef GL_QCOM_tiled_rendering
+#define GL_QCOM_tiled_rendering 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glStartTilingQCOM (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask);
+GL_APICALL void GL_APIENTRY glEndTilingQCOM (GLbitfield preserveMask);
+#endif
+typedef void (GL_APIENTRYP PFNGLSTARTTILINGQCOMPROC) (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask);
+typedef void (GL_APIENTRYP PFNGLENDTILINGQCOMPROC) (GLbitfield preserveMask);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/GLES2/gl2platform.h b/opengl/include/GLES2/gl2platform.h
index 3e9036c..c9fa3c4 100644
--- a/opengl/include/GLES2/gl2platform.h
+++ b/opengl/include/GLES2/gl2platform.h
@@ -1,7 +1,7 @@
#ifndef __gl2platform_h_
#define __gl2platform_h_
-/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */
+/* $Revision: 10602 $ on $Date:: 2010-03-04 22:35:34 -0800 #$ */
/*
* This document is licensed under the SGI Free Software B License Version
@@ -9,7 +9,6 @@
*/
/* Platform-specific types and definitions for OpenGL ES 2.X gl2.h
- * Last modified on 2008/12/19
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
@@ -24,6 +23,8 @@
#define GL_APICALL KHRONOS_APICALL
#endif
+#ifndef GL_APIENTRY
#define GL_APIENTRY KHRONOS_APIENTRY
+#endif
#endif /* __gl2platform_h_ */
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
index 6cb146c..b5c018f 100644
--- a/opengl/libagl/Android.mk
+++ b/opengl/libagl/Android.mk
@@ -6,9 +6,6 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-# Set to 1 to use gralloc and copybits
-LIBAGL_USE_GRALLOC_COPYBITS := 1
-
LOCAL_SRC_FILES:= \
egl.cpp \
state.cpp \
@@ -37,6 +34,10 @@ ifeq ($(TARGET_ARCH),arm)
LOCAL_CFLAGS += -fstrict-aliasing
endif
+ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+endif
+
ifneq ($(TARGET_SIMULATOR),true)
# we need to access the private Bionic header <bionic_tls.h>
# on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER
@@ -47,13 +48,6 @@ ifneq ($(TARGET_SIMULATOR),true)
LOCAL_C_INCLUDES += bionic/libc/private
endif
-ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1)
- LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS
- LOCAL_SRC_FILES += copybit.cpp
- LOCAL_SHARED_LIBRARIES += libui
-endif
-
-
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
LOCAL_MODULE:= libGLES_android
diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp
index 255ccac..bbb82fc 100644
--- a/opengl/libagl/TextureObjectManager.cpp
+++ b/opengl/libagl/TextureObjectManager.cpp
@@ -55,9 +55,6 @@ void EGLTextureObject::init()
memset(crop_rect, 0, sizeof(crop_rect));
generate_mipmap = GL_FALSE;
direct = GL_FALSE;
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- try_copybit = false;
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
buffer = 0;
}
diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h
index 279e040..70e3bef 100644
--- a/opengl/libagl/TextureObjectManager.h
+++ b/opengl/libagl/TextureObjectManager.h
@@ -80,9 +80,6 @@ public:
GLint crop_rect[4];
GLint generate_mipmap;
GLint direct;
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- bool try_copybit;
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
android_native_buffer_t* buffer;
};
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 71825c5..4997dc8 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -26,9 +26,6 @@
#include "primitives.h"
#include "texture.h"
#include "BufferObjectManager.h"
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
-#include "copybit.h"
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
// ----------------------------------------------------------------------------
@@ -707,12 +704,6 @@ void drawPrimitivesTriangleStrip(ogles_context_t* c,
void drawPrimitivesTriangleFan(ogles_context_t* c,
GLint first, GLsizei count) {
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- if (drawTriangleFanWithCopybit(c, first, count)) {
- return;
- }
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
drawPrimitivesTriangleFanOrStrip(c, first, count, 2);
}
diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp
deleted file mode 100644
index 67d1ce7..0000000
--- a/opengl/libagl/copybit.cpp
+++ /dev/null
@@ -1,618 +0,0 @@
-/*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "context.h"
-#include "fp.h"
-#include "state.h"
-#include "matrix.h"
-#include "vertex.h"
-#include "light.h"
-#include "primitives.h"
-#include "texture.h"
-#include "BufferObjectManager.h"
-#include "TextureObjectManager.h"
-
-#include <hardware/gralloc.h>
-#include <hardware/copybit.h>
-#include <private/ui/android_natives_priv.h>
-
-#include <ui/GraphicBuffer.h>
-#include <ui/Region.h>
-#include <ui/Rect.h>
-
-
-#define DEBUG_COPYBIT false
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-static void textureToCopyBitImage(
- const GGLSurface* surface, int32_t opFormat,
- android_native_buffer_t* buffer, copybit_image_t* img)
-{
- img->w = surface->stride;
- img->h = surface->height;
- img->format = opFormat;
- img->base = surface->data;
- img->handle = (native_handle_t *)buffer->handle;
-}
-
-struct clipRectRegion : public copybit_region_t {
- clipRectRegion(ogles_context_t* c)
- {
- scissor_t const* scissor = &c->rasterizer.state.scissor;
- r.l = scissor->left;
- r.t = scissor->top;
- r.r = scissor->right;
- r.b = scissor->bottom;
- next = iterate;
- }
-private:
- static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
- *rect = static_cast<clipRectRegion const*>(self)->r;
- const_cast<copybit_region_t *>(self)->next = iterate_done;
- return 1;
- }
- static int iterate_done(copybit_region_t const *, copybit_rect_t*) {
- return 0;
- }
-public:
- copybit_rect_t r;
-};
-
-static bool supportedCopybitsFormat(int format) {
- switch (format) {
- case COPYBIT_FORMAT_RGBA_8888:
- case COPYBIT_FORMAT_RGBX_8888:
- case COPYBIT_FORMAT_RGB_888:
- case COPYBIT_FORMAT_RGB_565:
- case COPYBIT_FORMAT_BGRA_8888:
- case COPYBIT_FORMAT_RGBA_5551:
- case COPYBIT_FORMAT_RGBA_4444:
- return true;
- default:
- return false;
- }
-}
-
-static bool hasAlpha(int format) {
- switch (format) {
- case COPYBIT_FORMAT_RGBA_8888:
- case COPYBIT_FORMAT_BGRA_8888:
- case COPYBIT_FORMAT_RGBA_5551:
- case COPYBIT_FORMAT_RGBA_4444:
- return true;
- default:
- return false;
- }
-}
-
-static inline int fixedToByte(GGLfixed val) {
- return (val - (val >> 8)) >> 8;
-}
-
-/**
- * Performs a quick check of the rendering state. If this function returns
- * false we cannot use the copybit driver.
- */
-
-static bool checkContext(ogles_context_t* c) {
-
- // By convention copybitQuickCheckContext() has already returned true.
- // avoid checking the same information again.
-
- if (c->copybits.blitEngine == NULL) {
- LOGD_IF(DEBUG_COPYBIT, "no copybit hal");
- return false;
- }
-
- if (c->rasterizer.state.enables
- & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
- LOGD_IF(DEBUG_COPYBIT, "depth test and/or fog");
- return false;
- }
-
- // Note: The drawSurfaceBuffer is only set for destination
- // surfaces types that are supported by the hardware and
- // do not have an alpha channel. So we don't have to re-check that here.
-
- static const int tmu = 0;
- texture_unit_t& u(c->textures.tmu[tmu]);
- EGLTextureObject* textureObject = u.texture;
-
- if (!supportedCopybitsFormat(textureObject->surface.format)) {
- LOGD_IF(DEBUG_COPYBIT, "texture format not supported");
- return false;
- }
- return true;
-}
-
-
-static bool copybit(GLint x, GLint y,
- GLint w, GLint h,
- EGLTextureObject* textureObject,
- const GLint* crop_rect,
- int transform,
- ogles_context_t* c)
-{
- status_t err = NO_ERROR;
-
- // We assume checkContext has already been called and has already
- // returned true.
-
- const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
-
- y = cbSurface.height - (y + h);
-
- const GLint Ucr = crop_rect[0];
- const GLint Vcr = crop_rect[1];
- const GLint Wcr = crop_rect[2];
- const GLint Hcr = crop_rect[3];
-
- GLint screen_w = w;
- GLint screen_h = h;
- int32_t dsdx = Wcr << 16; // dsdx = ((Wcr/screen_w)/Wt)*Wt
- int32_t dtdy = Hcr << 16; // dtdy = -((Hcr/screen_h)/Ht)*Ht
- if (transform & COPYBIT_TRANSFORM_ROT_90) {
- swap(screen_w, screen_h);
- }
- if (dsdx!=screen_w || dtdy!=screen_h) {
- // in most cases the divide is not needed
- dsdx /= screen_w;
- dtdy /= screen_h;
- }
- dtdy = -dtdy; // see equation of dtdy above
-
- // copybit doesn't say anything about filtering, so we can't
- // discriminate. On msm7k, copybit will always filter.
- // the code below handles min/mag filters, we keep it as a reference.
-
-#ifdef MIN_MAG_FILTER
- int32_t texelArea = gglMulx(dtdy, dsdx);
- if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) {
- // Non-linear filtering on a texture enlargement.
- LOGD_IF(DEBUG_COPYBIT, "mag filter is not GL_LINEAR");
- return false;
- }
- if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) {
- // Non-linear filtering on an texture shrink.
- LOGD_IF(DEBUG_COPYBIT, "min filter is not GL_LINEAR");
- return false;
- }
-#endif
-
- const uint32_t enables = c->rasterizer.state.enables;
- int planeAlpha = 255;
- bool alphaPlaneWorkaround = false;
- static const int tmu = 0;
- texture_t& tev(c->rasterizer.state.texture[tmu]);
- int32_t opFormat = textureObject->surface.format;
- const bool srcTextureHasAlpha = hasAlpha(opFormat);
- if (!srcTextureHasAlpha) {
- planeAlpha = fixedToByte(c->currentColorClamped.a);
- }
-
- const bool cbHasAlpha = hasAlpha(cbSurface.format);
- bool blending = false;
- if ((enables & GGL_ENABLE_BLENDING)
- && !(c->rasterizer.state.blend.src == GL_ONE
- && c->rasterizer.state.blend.dst == GL_ZERO)) {
- // Blending is OK if it is
- // the exact kind of blending that the copybits hardware supports.
- // Note: The hardware only supports
- // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA,
- // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA.
- // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case,
- // because the performance is worth it, even if the results are
- // not correct.
- if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA
- || c->rasterizer.state.blend.src == GL_ONE)
- && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA
- && c->rasterizer.state.blend.alpha_separate == 0)) {
- // Incompatible blend mode.
- LOGD_IF(DEBUG_COPYBIT, "incompatible blend mode");
- return false;
- }
- blending = true;
- } else {
- if (cbHasAlpha) {
- // NOTE: the result will be slightly wrong in this case because
- // the destination alpha channel will be set to 1.0 instead of
- // the iterated alpha value. *shrug*.
- }
- // disable plane blending and src blending for supported formats
- planeAlpha = 255;
- if (opFormat == COPYBIT_FORMAT_RGBA_8888) {
- opFormat = COPYBIT_FORMAT_RGBX_8888;
- } else {
- if (srcTextureHasAlpha) {
- LOGD_IF(DEBUG_COPYBIT, "texture format requires blending");
- return false;
- }
- }
- }
-
- switch (tev.env) {
- case GGL_REPLACE:
- break;
- case GGL_MODULATE:
- // only cases allowed is:
- // RGB source, color={1,1,1,a} -> can be done with GL_REPLACE
- // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE
- if (blending) {
- if (c->currentColorClamped.r == c->currentColorClamped.a &&
- c->currentColorClamped.g == c->currentColorClamped.a &&
- c->currentColorClamped.b == c->currentColorClamped.a) {
- // TODO: RGBA source, color={1,1,1,a} / regular-blending
- // is equivalent
- alphaPlaneWorkaround = true;
- break;
- }
- }
- LOGD_IF(DEBUG_COPYBIT, "GGL_MODULATE");
- return false;
- default:
- // Incompatible texture environment.
- LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment");
- return false;
- }
-
- copybit_device_t* copybit = c->copybits.blitEngine;
- copybit_image_t src;
- textureToCopyBitImage(&textureObject->surface, opFormat,
- textureObject->buffer, &src);
- copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr };
-
- /*
- * Below we perform extra passes needed to emulate things the h/w
- * cannot do.
- */
-
- const GLfixed minScaleInv = gglDivQ(0x10000, c->copybits.minScale, 16);
- const GLfixed maxScaleInv = gglDivQ(0x10000, c->copybits.maxScale, 16);
-
- sp<GraphicBuffer> tempBitmap;
-
- if (dsdx < maxScaleInv || dsdx > minScaleInv ||
- dtdy < maxScaleInv || dtdy > minScaleInv)
- {
- // The requested scale is out of the range the hardware
- // can support.
- LOGD_IF(DEBUG_COPYBIT,
- "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
- "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
- dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
-
- int32_t xscale=0x10000, yscale=0x10000;
- if (dsdx > minScaleInv) xscale = c->copybits.minScale;
- else if (dsdx < maxScaleInv) xscale = c->copybits.maxScale;
- if (dtdy > minScaleInv) yscale = c->copybits.minScale;
- else if (dtdy < maxScaleInv) yscale = c->copybits.maxScale;
- dsdx = gglMulx(dsdx, xscale);
- dtdy = gglMulx(dtdy, yscale);
-
- /* we handle only one step of resizing below. Handling an arbitrary
- * number is relatively easy (replace "if" above by "while"), but requires
- * two intermediate buffers and so far we never had the need.
- */
-
- if (dsdx < maxScaleInv || dsdx > minScaleInv ||
- dtdy < maxScaleInv || dtdy > minScaleInv) {
- LOGD_IF(DEBUG_COPYBIT,
- "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
- "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
- dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
- return false;
- }
-
- const int tmp_w = gglMulx(srect.r - srect.l, xscale, 16);
- const int tmp_h = gglMulx(srect.b - srect.t, yscale, 16);
-
- LOGD_IF(DEBUG_COPYBIT,
- "xscale=%08x, yscale=%08x, dsdx=%08x, dtdy=%08x, tmp_w=%d, tmp_h=%d",
- xscale, yscale, dsdx, dtdy, tmp_w, tmp_h);
-
- tempBitmap = new GraphicBuffer(
- tmp_w, tmp_h, src.format,
- GraphicBuffer::USAGE_HW_2D);
-
- err = tempBitmap->initCheck();
- if (err == NO_ERROR) {
- copybit_image_t tmp_dst;
- copybit_rect_t tmp_rect;
- tmp_dst.w = tmp_w;
- tmp_dst.h = tmp_h;
- tmp_dst.format = tempBitmap->format;
- tmp_dst.handle = (native_handle_t*)tempBitmap->getNativeBuffer()->handle;
- tmp_rect.l = 0;
- tmp_rect.t = 0;
- tmp_rect.r = tmp_dst.w;
- tmp_rect.b = tmp_dst.h;
- region_iterator tmp_it(Region(Rect(tmp_rect.r, tmp_rect.b)));
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
- err = copybit->stretch(copybit,
- &tmp_dst, &src, &tmp_rect, &srect, &tmp_it);
- src = tmp_dst;
- srect = tmp_rect;
- }
- }
-
- copybit_image_t dst;
- textureToCopyBitImage(&cbSurface, cbSurface.format,
- c->copybits.drawSurfaceBuffer, &dst);
- copybit_rect_t drect = {x, y, x+w, y+h};
-
-
- /* and now the alpha-plane hack. This handles the "Fade" case of a
- * texture with an alpha channel.
- */
- if (alphaPlaneWorkaround) {
- sp<GraphicBuffer> tempCb = new GraphicBuffer(
- w, h, COPYBIT_FORMAT_RGB_565,
- GraphicBuffer::USAGE_HW_2D);
-
- err = tempCb->initCheck();
-
- copybit_image_t tmpCbImg;
- copybit_rect_t tmpCbRect;
- copybit_rect_t tmpdrect = drect;
- tmpCbImg.w = w;
- tmpCbImg.h = h;
- tmpCbImg.format = tempCb->format;
- tmpCbImg.handle = (native_handle_t*)tempCb->getNativeBuffer()->handle;
- tmpCbRect.l = 0;
- tmpCbRect.t = 0;
-
- if (drect.l < 0) {
- tmpCbRect.l = -tmpdrect.l;
- tmpdrect.l = 0;
- }
- if (drect.t < 0) {
- tmpCbRect.t = -tmpdrect.t;
- tmpdrect.t = 0;
- }
- if (drect.l + tmpCbImg.w > dst.w) {
- tmpCbImg.w = dst.w - drect.l;
- tmpdrect.r = dst.w;
- }
- if (drect.t + tmpCbImg.h > dst.h) {
- tmpCbImg.h = dst.h - drect.t;
- tmpdrect.b = dst.h;
- }
-
- tmpCbRect.r = tmpCbImg.w;
- tmpCbRect.b = tmpCbImg.h;
-
- if (!err) {
- // first make a copy of the destination buffer
- region_iterator tmp_it(Region(Rect(w, h)));
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
- err = copybit->stretch(copybit,
- &tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it);
- }
- if (!err) {
- // then proceed as usual, but without the alpha plane
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
- copybit->set_parameter(copybit, COPYBIT_DITHER,
- (enables & GGL_ENABLE_DITHER) ?
- COPYBIT_ENABLE : COPYBIT_DISABLE);
- clipRectRegion it(c);
- err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
- }
- if (!err) {
- // finally copy back the destination on top with 1-alphaplane
- int invPlaneAlpha = 0xFF - fixedToByte(c->currentColorClamped.a);
- clipRectRegion it(c);
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, invPlaneAlpha);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
- err = copybit->stretch(copybit,
- &dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it);
- }
- } else {
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha);
- copybit->set_parameter(copybit, COPYBIT_DITHER,
- (enables & GGL_ENABLE_DITHER) ?
- COPYBIT_ENABLE : COPYBIT_DISABLE);
- clipRectRegion it(c);
-
- LOGD_IF(0,
- "dst={%d, %d, %d, %p, %p}, "
- "src={%d, %d, %d, %p, %p}, "
- "drect={%d,%d,%d,%d}, "
- "srect={%d,%d,%d,%d}, "
- "it={%d,%d,%d,%d}, " ,
- dst.w, dst.h, dst.format, dst.base, dst.handle,
- src.w, src.h, src.format, src.base, src.handle,
- drect.l, drect.t, drect.r, drect.b,
- srect.l, srect.t, srect.r, srect.b,
- it.r.l, it.r.t, it.r.r, it.r.b
- );
-
- err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
- }
- if (err != NO_ERROR) {
- c->textures.tmu[0].texture->try_copybit = false;
- }
- return err == NO_ERROR ? true : false;
-}
-
-/*
- * Try to draw a triangle fan with copybit, return false if we fail.
- */
-bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count)
-{
- if (!checkContext(c)) {
- return false;
- }
-
- // FIXME: we should handle culling here
- c->arrays.compileElements(c, c->vc.vBuffer, 0, 4);
-
- // we detect if we're dealing with a rectangle, by comparing the
- // rectangles {v0,v2} and {v1,v3} which should be identical.
-
- // NOTE: we should check that the rectangle is window aligned, however
- // if we do that, the optimization won't be taken in a lot of cases.
- // Since this code is intended to be used with SurfaceFlinger only,
- // so it's okay...
-
- const vec4_t& v0 = c->vc.vBuffer[0].window;
- const vec4_t& v1 = c->vc.vBuffer[1].window;
- const vec4_t& v2 = c->vc.vBuffer[2].window;
- const vec4_t& v3 = c->vc.vBuffer[3].window;
- int l = min(v0.x, v2.x);
- int b = min(v0.y, v2.y);
- int r = max(v0.x, v2.x);
- int t = max(v0.y, v2.y);
- if ((l != min(v1.x, v3.x)) || (b != min(v1.y, v3.y)) ||
- (r != max(v1.x, v3.x)) || (t != max(v1.y, v3.y))) {
- LOGD_IF(DEBUG_COPYBIT, "geometry not a rectangle");
- return false;
- }
-
- // fetch and transform texture coordinates
- // NOTE: maybe it would be better to have a "compileElementsAll" method
- // that would ensure all vertex data are fetched and transformed
- const transform_t& tr = c->transforms.texture[0].transform;
- for (size_t i=0 ; i<4 ; i++) {
- const GLubyte* tp = c->arrays.texture[0].element(i);
- vertex_t* const v = &c->vc.vBuffer[i];
- c->arrays.texture[0].fetch(c, v->texture[0].v, tp);
- // FIXME: we should bail if q!=1
- c->arrays.tex_transform[0](&tr, &v->texture[0], &v->texture[0]);
- }
-
- const vec4_t& t0 = c->vc.vBuffer[0].texture[0];
- const vec4_t& t1 = c->vc.vBuffer[1].texture[0];
- const vec4_t& t2 = c->vc.vBuffer[2].texture[0];
- const vec4_t& t3 = c->vc.vBuffer[3].texture[0];
- int txl = min(t0.x, t2.x);
- int txb = min(t0.y, t2.y);
- int txr = max(t0.x, t2.x);
- int txt = max(t0.y, t2.y);
- if ((txl != min(t1.x, t3.x)) || (txb != min(t1.y, t3.y)) ||
- (txr != max(t1.x, t3.x)) || (txt != max(t1.y, t3.y))) {
- LOGD_IF(DEBUG_COPYBIT, "texcoord not a rectangle");
- return false;
- }
- if ((txl != 0) || (txb != 0) ||
- (txr != FIXED_ONE) || (txt != FIXED_ONE)) {
- // we could probably handle this case, if we wanted to
- LOGD_IF(DEBUG_COPYBIT, "texture is cropped: %08x,%08x,%08x,%08x",
- txl, txb, txr, txt);
- return false;
- }
-
- // at this point, we know we are dealing with a rectangle, so we
- // only need to consider 3 vertices for computing the jacobians
-
- const int dx01 = v1.x - v0.x;
- const int dx02 = v2.x - v0.x;
- const int dy01 = v1.y - v0.y;
- const int dy02 = v2.y - v0.y;
- const int ds01 = t1.S - t0.S;
- const int ds02 = t2.S - t0.S;
- const int dt01 = t1.T - t0.T;
- const int dt02 = t2.T - t0.T;
- const int area = dx01*dy02 - dy01*dx02;
- int dsdx, dsdy, dtdx, dtdy;
- if (area >= 0) {
- dsdx = ds01*dy02 - ds02*dy01;
- dtdx = dt01*dy02 - dt02*dy01;
- dsdy = ds02*dx01 - ds01*dx02;
- dtdy = dt02*dx01 - dt01*dx02;
- } else {
- dsdx = ds02*dy01 - ds01*dy02;
- dtdx = dt02*dy01 - dt01*dy02;
- dsdy = ds01*dx02 - ds02*dx01;
- dtdy = dt01*dx02 - dt02*dx01;
- }
-
- // here we rely on the fact that we know the transform is
- // a rigid-body transform AND that it can only rotate in 90 degrees
- // increments
-
- int transform = 0;
- if (dsdx == 0) {
- // 90 deg rotation case
- // [ 0 dtdx ]
- // [ dsdx 0 ]
- transform |= COPYBIT_TRANSFORM_ROT_90;
- // FIXME: not sure if FLIP_H and FLIP_V shouldn't be inverted
- if (dtdx > 0)
- transform |= COPYBIT_TRANSFORM_FLIP_H;
- if (dsdy < 0)
- transform |= COPYBIT_TRANSFORM_FLIP_V;
- } else {
- // [ dsdx 0 ]
- // [ 0 dtdy ]
- if (dsdx < 0)
- transform |= COPYBIT_TRANSFORM_FLIP_H;
- if (dtdy < 0)
- transform |= COPYBIT_TRANSFORM_FLIP_V;
- }
-
- //LOGD("l=%d, b=%d, w=%d, h=%d, tr=%d", x, y, w, h, transform);
- //LOGD("A=%f\tB=%f\nC=%f\tD=%f",
- // dsdx/65536.0, dtdx/65536.0, dsdy/65536.0, dtdy/65536.0);
-
- int x = l >> 4;
- int y = b >> 4;
- int w = (r-l) >> 4;
- int h = (t-b) >> 4;
- texture_unit_t& u(c->textures.tmu[0]);
- EGLTextureObject* textureObject = u.texture;
- GLint tWidth = textureObject->surface.width;
- GLint tHeight = textureObject->surface.height;
- GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight};
- const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
- y = cbSurface.height - (y + h);
- return copybit(x, y, w, h, textureObject, crop_rect, transform, c);
-}
-
-/*
- * Try to drawTexiOESWithCopybit, return false if we fail.
- */
-
-bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z,
- GLint w, GLint h, ogles_context_t* c)
-{
- // quickly process empty rects
- if ((w|h) <= 0) {
- return true;
- }
- if (!checkContext(c)) {
- return false;
- }
- texture_unit_t& u(c->textures.tmu[0]);
- EGLTextureObject* textureObject = u.texture;
- return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c);
-}
-
-} // namespace android
-
diff --git a/opengl/libagl/copybit.h b/opengl/libagl/copybit.h
deleted file mode 100644
index b8b5afd..0000000
--- a/opengl/libagl/copybit.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef ANDROID_OPENGLES_COPYBIT_H
-#define ANDROID_OPENGLES_COPYBIT_H
-
-#include <stdlib.h>
-
-#include <GLES/gl.h>
-
-#include "TextureObjectManager.h"
-namespace android {
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
-
-bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z,
- GLint w, GLint h, ogles_context_t* c);
-
-bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first,
- GLsizei count);
-
-inline bool copybitQuickCheckContext(ogles_context_t* c) {
- return c->copybits.drawSurfaceBuffer != 0
- && c->rasterizer.state.enabled_tmu == 1
- && c->textures.tmu[0].texture->try_copybit;
-}
-
-/*
- * Tries to draw a drawTexiOES using copybit hardware.
- * Returns true if successful.
- */
-inline bool drawTexiOESWithCopybit(GLint x, GLint y, GLint z,
- GLint w, GLint h, ogles_context_t* c) {
- if (!copybitQuickCheckContext(c)) {
- return false;
- }
-
- return drawTexiOESWithCopybit_impl(x, y, z, w, h, c);
-}
-
-/*
- * Tries to draw a triangle fan using copybit hardware.
- * Returns true if successful.
- */
-inline bool drawTriangleFanWithCopybit(ogles_context_t* c, GLint first,
- GLsizei count) {
- /*
- * We are looking for the glDrawArrays call made by SurfaceFlinger.
- */
-
- if ((count!=4) || first || !copybitQuickCheckContext(c))
- return false;
-
- return drawTriangleFanWithCopybit_impl(c, first, count);
-}
-
-
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
-} // namespace android
-
-#endif // ANDROID_OPENGLES_COPYBIT_H
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index b6e0aae..5bbe441 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -213,7 +213,7 @@ struct egl_window_surface_v2_t : public egl_surface_t
egl_window_surface_v2_t(
EGLDisplay dpy, EGLConfig config,
int32_t depthFormat,
- android_native_window_t* window);
+ ANativeWindow* window);
~egl_window_surface_v2_t();
@@ -235,7 +235,7 @@ struct egl_window_surface_v2_t : public egl_surface_t
private:
status_t lock(android_native_buffer_t* buf, int usage, void** vaddr);
status_t unlock(android_native_buffer_t* buf);
- android_native_window_t* nativeWindow;
+ ANativeWindow* nativeWindow;
android_native_buffer_t* buffer;
android_native_buffer_t* previousBuffer;
gralloc_module_t const* module;
@@ -355,7 +355,7 @@ private:
egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
EGLConfig config,
int32_t depthFormat,
- android_native_window_t* window)
+ ANativeWindow* window)
: egl_surface_t(dpy, config, depthFormat),
nativeWindow(window), buffer(0), previousBuffer(0), module(0),
blitengine(0), bits(NULL)
@@ -628,23 +628,6 @@ EGLClientBuffer egl_window_surface_v2_t::getRenderBuffer() const
return buffer;
}
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
-
-static bool supportedCopybitsDestinationFormat(int format) {
- // Hardware supported
- switch (format) {
- case HAL_PIXEL_FORMAT_RGB_565:
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- case HAL_PIXEL_FORMAT_RGBA_4444:
- case HAL_PIXEL_FORMAT_RGBA_5551:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- return true;
- }
- return false;
-}
-#endif
-
EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
{
GGLSurface buffer;
@@ -658,18 +641,6 @@ EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
if (depth.data != gl->rasterizer.state.buffers.depth.data)
gl->rasterizer.procs.depthBuffer(gl, &depth);
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- gl->copybits.drawSurfaceBuffer = 0;
- if (gl->copybits.blitEngine != NULL) {
- if (supportedCopybitsDestinationFormat(buffer.format)) {
- buffer_handle_t handle = this->buffer->handle;
- if (handle != NULL) {
- gl->copybits.drawSurfaceBuffer = this->buffer;
- }
- }
- }
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
return EGL_TRUE;
}
EGLBoolean egl_window_surface_v2_t::bindReadSurface(ogles_context_t* gl)
@@ -1300,7 +1271,7 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
if (!(surfaceType & EGL_WINDOW_BIT))
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
- if (static_cast<android_native_window_t*>(window)->common.magic !=
+ if (static_cast<ANativeWindow*>(window)->common.magic !=
ANDROID_NATIVE_WINDOW_MAGIC) {
return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
}
@@ -1323,7 +1294,7 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
egl_surface_t* surface;
surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
- static_cast<android_native_window_t*>(window));
+ static_cast<ANativeWindow*>(window));
if (!surface->initCheck()) {
// there was a problem in the ctor, the error
@@ -1525,8 +1496,13 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
}
if (ggl_unlikely(attrib_list==0)) {
- *num_config = 0;
- return EGL_TRUE;
+ /*
+ * A NULL attrib_list should be treated as though it was an empty
+ * one (terminated with EGL_NONE) as defined in
+ * section 3.4.1 "Querying Configurations" in the EGL specification.
+ */
+ static const EGLint dummy = EGL_NONE;
+ attrib_list = &dummy;
}
int numAttributes = 0;
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index 27bb545..a0f720a 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -28,10 +28,6 @@
#include "BufferObjectManager.h"
#include "TextureObjectManager.h"
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
-#include <hardware/copybit.h>
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
namespace android {
// ----------------------------------------------------------------------------
@@ -101,35 +97,6 @@ ogles_context_t *ogles_init(size_t extra)
// OpenGL enables dithering by default
c->rasterizer.procs.enable(c, GL_DITHER);
- c->copybits.blitEngine = NULL;
- c->copybits.minScale = 0;
- c->copybits.maxScale = 0;
- c->copybits.drawSurfaceBuffer = 0;
-
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- hw_module_t const* module;
- if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
- struct copybit_device_t* copyBits;
- if (copybit_open(module, &copyBits) == 0) {
- c->copybits.blitEngine = copyBits;
- {
- int minLim = copyBits->get(copyBits,
- COPYBIT_MINIFICATION_LIMIT);
- if (minLim != -EINVAL && minLim > 0) {
- c->copybits.minScale = (1 << 16) / minLim;
- }
- }
- {
- int magLim = copyBits->get(copyBits,
- COPYBIT_MAGNIFICATION_LIMIT);
- if (magLim != -EINVAL && magLim > 0) {
- c->copybits.maxScale = min(32*1024-1, magLim) << 16;
- }
- }
- }
- }
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
return c;
}
@@ -144,11 +111,6 @@ void ogles_uninit(ogles_context_t* c)
c->bufferObjectManager->decStrong(c);
ggl_uninit_context(&(c->rasterizer));
free(c->rasterizer.base);
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- if (c->copybits.blitEngine != NULL) {
- copybit_close((struct copybit_device_t*) c->copybits.blitEngine);
- }
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
}
void _ogles_error(ogles_context_t* c, GLenum error)
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 9407bd5..eb96895 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -26,10 +26,6 @@
#include <private/ui/android_natives_priv.h>
#include <ETC1/etc1.h>
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
-#include "copybit.h"
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
namespace android {
// ----------------------------------------------------------------------------
@@ -763,17 +759,10 @@ static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h
static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
ogles_context_t* c)
{
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- if (drawTexiOESWithCopybit(gglFixedToIntRound(x),
- gglFixedToIntRound(y), gglFixedToIntRound(z),
- gglFixedToIntRound(w), gglFixedToIntRound(h), c)) {
- return;
- }
-#else
// quickly reject empty rects
if ((w|h) <= 0)
return;
-#endif
+
drawTexxOESImp(x, y, z, w, h, c);
}
@@ -785,11 +774,6 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte
// which is a lot faster.
if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) {
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- if (drawTexiOESWithCopybit(x, y, z, w, h, c)) {
- return;
- }
-#endif
const int tmu = 0;
texture_unit_t& u(c->textures.tmu[tmu]);
EGLTextureObject* textureObject = u.texture;
@@ -797,9 +781,7 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte
const GLint Hcr = textureObject->crop_rect[3];
if ((w == Wcr) && (h == -Hcr)) {
-#ifndef LIBAGL_USE_GRALLOC_COPYBITS
if ((w|h) <= 0) return; // quickly reject empty rects
-#endif
if (u.dirty) {
c->rasterizer.procs.activeTexture(c, tmu);
@@ -1515,7 +1497,7 @@ void glReadPixels(
ogles_error(c, GL_INVALID_VALUE);
return;
}
- if (x<0 || x<0) {
+ if (x<0 || y<0) {
ogles_error(c, GL_INVALID_VALUE);
return;
}
@@ -1646,13 +1628,6 @@ void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
// bind it to the texture unit
sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
tex->setImage(native_buffer);
-
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
- tex->try_copybit = false;
- if (c->copybits.blitEngine != NULL) {
- tex->try_copybit = true;
- }
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
}
void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 6b7020f..ae924cd 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -6,10 +6,11 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- EGL/egl.cpp \
- EGL/hooks.cpp \
- EGL/Loader.cpp \
+LOCAL_SRC_FILES:= \
+ EGL/egl.cpp \
+ EGL/getProcAddress.cpp.arm \
+ EGL/hooks.cpp \
+ EGL/Loader.cpp \
#
LOCAL_SHARED_LIBRARIES += libcutils libutils
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 89b3e1f..315a2a3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -37,12 +37,13 @@
#include <cutils/memory.h>
#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
#include "hooks.h"
#include "egl_impl.h"
#include "Loader.h"
-#define MAKE_CONFIG(_impl, _index) ((EGLConfig)(((_impl)<<24) | (_index)))
#define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
// ----------------------------------------------------------------------------
@@ -143,6 +144,22 @@ public:
SortedVector<egl_object_t*> egl_object_t::sObjects;
Mutex egl_object_t::sLock;
+
+struct egl_config_t {
+ egl_config_t() {}
+ egl_config_t(int impl, EGLConfig config)
+ : impl(impl), config(config), configId(0), implConfigId(0) { }
+ int impl; // the implementation this config is for
+ EGLConfig config; // the implementation's EGLConfig
+ EGLint configId; // our CONFIG_ID
+ EGLint implConfigId; // the implementation's CONFIG_ID
+ inline bool operator < (const egl_config_t& rhs) const {
+ if (impl < rhs.impl) return true;
+ if (impl > rhs.impl) return false;
+ return config < rhs.config;
+ }
+};
+
struct egl_display_t {
enum { NOT_INITIALIZED, INITIALIZED, TERMINATED };
@@ -163,13 +180,14 @@ struct egl_display_t {
strings_t queryString;
};
- uint32_t magic;
- DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS];
- EGLint numTotalConfigs;
- uint32_t refs;
- Mutex lock;
+ uint32_t magic;
+ DisplayImpl disp[IMPL_NUM_IMPLEMENTATIONS];
+ EGLint numTotalConfigs;
+ egl_config_t* configs;
+ uint32_t refs;
+ Mutex lock;
- egl_display_t() : magic('_dpy'), numTotalConfigs(0) { }
+ egl_display_t() : magic('_dpy'), numTotalConfigs(0), configs(0) { }
~egl_display_t() { magic = 0; }
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
@@ -179,14 +197,15 @@ struct egl_surface_t : public egl_object_t
{
typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
- egl_surface_t(EGLDisplay dpy, EGLSurface surface,
+ egl_surface_t(EGLDisplay dpy, EGLSurface surface, EGLConfig config,
int impl, egl_connection_t const* cnx)
- : dpy(dpy), surface(surface), impl(impl), cnx(cnx) {
+ : dpy(dpy), surface(surface), config(config), impl(impl), cnx(cnx) {
}
~egl_surface_t() {
}
EGLDisplay dpy;
EGLSurface surface;
+ EGLConfig config;
int impl;
egl_connection_t const* cnx;
};
@@ -195,7 +214,7 @@ struct egl_context_t : public egl_object_t
{
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
- egl_context_t(EGLDisplay dpy, EGLContext context,
+ egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
int impl, egl_connection_t const* cnx, int version)
: dpy(dpy), context(context), read(0), draw(0), impl(impl), cnx(cnx),
version(version)
@@ -203,6 +222,7 @@ struct egl_context_t : public egl_object_t
}
EGLDisplay dpy;
EGLContext context;
+ EGLConfig config;
EGLSurface read;
EGLSurface draw;
int impl;
@@ -239,7 +259,7 @@ struct tls_t
// ----------------------------------------------------------------------------
-egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
+static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
static egl_display_t gDisplay[NUM_DISPLAYS];
static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t gEGLThreadLocalStorageKey = -1;
@@ -354,7 +374,7 @@ int binarySearch(
{
while (first <= last) {
int mid = (first + last) / 2;
- if (key > sortedArray[mid]) {
+ if (sortedArray[mid] < key) {
first = mid + 1;
} else if (key < sortedArray[mid]) {
last = mid - 1;
@@ -365,26 +385,11 @@ int binarySearch(
return -1;
}
-static EGLint configToUniqueId(egl_display_t const* dp, int i, int index)
-{
- // NOTE: this mapping works only if we have no more than two EGLimpl
- return (i>0 ? dp->disp[0].numConfigs : 0) + index;
-}
-
-static void uniqueIdToConfig(egl_display_t const* dp, EGLint configId,
- int& i, int& index)
-{
- // NOTE: this mapping works only if we have no more than two EGLimpl
- size_t numConfigs = dp->disp[0].numConfigs;
- i = configId / numConfigs;
- index = configId % numConfigs;
-}
-
static int cmp_configs(const void* a, const void *b)
{
- EGLConfig c0 = *(EGLConfig const *)a;
- EGLConfig c1 = *(EGLConfig const *)b;
- return c0<c1 ? -1 : (c0>c1 ? 1 : 0);
+ const egl_config_t& c0 = *(egl_config_t const *)a;
+ const egl_config_t& c1 = *(egl_config_t const *)b;
+ return c0<c1 ? -1 : (c1<c0 ? 1 : 0);
}
struct extention_map_t {
@@ -407,7 +412,11 @@ static const extention_map_t gExtentionMap[] = {
(__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
-static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+
+// accesses protected by gInitDriverMutex
+static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> gGLExtentionMap;
+static int gGLExtentionSlot = 0;
static void(*findProcAddress(const char* name,
const extention_map_t* map, size_t n))()
@@ -477,20 +486,15 @@ egl_image_t* get_image(EGLImageKHR image) {
static egl_connection_t* validate_display_config(
EGLDisplay dpy, EGLConfig config,
- egl_display_t const*& dp, int& impl, int& index)
+ egl_display_t const*& dp)
{
dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, (egl_connection_t*)NULL);
- impl = uintptr_t(config)>>24;
- if (uint32_t(impl) >= IMPL_NUM_IMPLEMENTATIONS) {
- return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
- }
- index = uintptr_t(config) & 0xFFFFFF;
- if (index >= dp->disp[impl].numConfigs) {
+ if (intptr_t(config) >= dp->numTotalConfigs) {
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
- egl_connection_t* const cnx = &gEGLImpl[impl];
+ egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(config)].impl];
if (cnx->dso == 0) {
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
@@ -718,11 +722,6 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
dp->disp[i].dpy, dp->disp[i].config, n,
&dp->disp[i].numConfigs))
{
- // sort the configurations so we can do binary searches
- qsort( dp->disp[i].config,
- dp->disp[i].numConfigs,
- sizeof(EGLConfig), cmp_configs);
-
dp->numTotalConfigs += n;
res = EGL_TRUE;
}
@@ -732,6 +731,30 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
}
if (res == EGL_TRUE) {
+ dp->configs = new egl_config_t[ dp->numTotalConfigs ];
+ for (int i=0, k=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
+ egl_connection_t* const cnx = &gEGLImpl[i];
+ if (cnx->dso && cnx->major>=0 && cnx->minor>=0) {
+ for (int j=0 ; j<dp->disp[i].numConfigs ; j++) {
+ dp->configs[k].impl = i;
+ dp->configs[k].config = dp->disp[i].config[j];
+ dp->configs[k].configId = k + 1; // CONFIG_ID start at 1
+ // store the implementation's CONFIG_ID
+ cnx->egl.eglGetConfigAttrib(
+ dp->disp[i].dpy,
+ dp->disp[i].config[j],
+ EGL_CONFIG_ID,
+ &dp->configs[k].implConfigId);
+ k++;
+ }
+ }
+ }
+
+ // sort our configurations so we can do binary-searches
+ qsort( dp->configs,
+ dp->numTotalConfigs,
+ sizeof(egl_config_t), cmp_configs);
+
dp->refs++;
if (major != NULL) *major = VERSION_MAJOR;
if (minor != NULL) *minor = VERSION_MINOR;
@@ -784,6 +807,7 @@ EGLBoolean eglTerminate(EGLDisplay dpy)
dp->refs--;
dp->numTotalConfigs = 0;
+ delete [] dp->configs;
clearTLS();
return res;
}
@@ -804,14 +828,13 @@ EGLBoolean eglGetConfigs( EGLDisplay dpy,
*num_config = numConfigs;
return EGL_TRUE;
}
+
GLint n = 0;
- for (int j=0 ; j<IMPL_NUM_IMPLEMENTATIONS ; j++) {
- for (int i=0 ; i<dp->disp[j].numConfigs && config_size ; i++) {
- *configs++ = MAKE_CONFIG(j, i);
- config_size--;
- n++;
- }
- }
+ for (intptr_t i=0 ; i<dp->numTotalConfigs && config_size ; i++) {
+ *configs++ = EGLConfig(i);
+ config_size--;
+ n++;
+ }
*num_config = n;
return EGL_TRUE;
@@ -834,7 +857,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
// It is unfortunate, but we need to remap the EGL_CONFIG_IDs,
- // to do this, we have to go through the attrib_list array once
+ // to do this, we have to go through the attrib_list array once
// to figure out both its size and if it contains an EGL_CONFIG_ID
// key. If so, the full array is copied and patched.
// NOTE: we assume that there can be only one occurrence
@@ -843,10 +866,12 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
EGLint patch_index = -1;
GLint attr;
size_t size = 0;
- while ((attr=attrib_list[size]) != EGL_NONE) {
- if (attr == EGL_CONFIG_ID)
- patch_index = size;
- size += 2;
+ if (attrib_list) {
+ while ((attr=attrib_list[size]) != EGL_NONE) {
+ if (attr == EGL_CONFIG_ID)
+ patch_index = size;
+ size += 2;
+ }
}
if (patch_index >= 0) {
size += 2; // we need copy the sentinel as well
@@ -856,16 +881,20 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
memcpy(new_list, attrib_list, size*sizeof(EGLint));
// patch the requested EGL_CONFIG_ID
- int i, index;
+ bool found = false;
+ EGLConfig ourConfig(0);
EGLint& configId(new_list[patch_index+1]);
- uniqueIdToConfig(dp, configId, i, index);
-
- egl_connection_t* const cnx = &gEGLImpl[i];
- if (cnx->dso) {
- cnx->egl.eglGetConfigAttrib(
- dp->disp[i].dpy, dp->disp[i].config[index],
- EGL_CONFIG_ID, &configId);
+ for (intptr_t i=0 ; i<dp->numTotalConfigs ; i++) {
+ if (dp->configs[i].configId == configId) {
+ ourConfig = EGLConfig(i);
+ configId = dp->configs[i].implConfigId;
+ found = true;
+ break;
+ }
+ }
+ egl_connection_t* const cnx = &gEGLImpl[dp->configs[intptr_t(ourConfig)].impl];
+ if (found && cnx->dso) {
// and switch to the new list
attrib_list = const_cast<const EGLint *>(new_list);
@@ -878,12 +907,13 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
// which one.
res = cnx->egl.eglChooseConfig(
- dp->disp[i].dpy, attrib_list, configs, config_size, &n);
+ dp->disp[ dp->configs[intptr_t(ourConfig)].impl ].dpy,
+ attrib_list, configs, config_size, &n);
if (res && n>0) {
// n has to be 0 or 1, by construction, and we already know
// which config it will return (since there can be only one).
if (configs) {
- configs[0] = MAKE_CONFIG(i, index);
+ configs[0] = ourConfig;
}
*num_config = 1;
}
@@ -893,6 +923,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
return res;
}
+
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
@@ -900,15 +931,14 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
dp->disp[i].dpy, attrib_list, configs, config_size, &n)) {
if (configs) {
// now we need to convert these client EGLConfig to our
- // internal EGLConfig format. This is done in O(n log n).
+ // internal EGLConfig format.
+ // This is done in O(n Log(n)) time.
for (int j=0 ; j<n ; j++) {
- int index = binarySearch<EGLConfig>(
- dp->disp[i].config, 0,
- dp->disp[i].numConfigs-1, configs[j]);
+ egl_config_t key(i, configs[j]);
+ intptr_t index = binarySearch<egl_config_t>(
+ dp->configs, 0, dp->numTotalConfigs, key);
if (index >= 0) {
- if (configs) {
- configs[j] = MAKE_CONFIG(i, index);
- }
+ configs[j] = EGLConfig(index);
} else {
return setError(EGL_BAD_CONFIG, EGL_FALSE);
}
@@ -928,18 +958,16 @@ EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
EGLint attribute, EGLint *value)
{
egl_display_t const* dp = 0;
- int i=0, index=0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+ egl_connection_t* cnx = validate_display_config(dpy, config, dp);
if (!cnx) return EGL_FALSE;
if (attribute == EGL_CONFIG_ID) {
- // EGL_CONFIG_IDs must be unique, just use the order of the selected
- // EGLConfig.
- *value = configToUniqueId(dp, i, index);
+ *value = dp->configs[intptr_t(config)].configId;
return EGL_TRUE;
}
return cnx->egl.eglGetConfigAttrib(
- dp->disp[i].dpy, dp->disp[i].config[index], attribute, value);
+ dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
+ dp->configs[intptr_t(config)].config, attribute, value);
}
// ----------------------------------------------------------------------------
@@ -951,13 +979,14 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
- int i=0, index=0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+ egl_connection_t* cnx = validate_display_config(dpy, config, dp);
if (cnx) {
EGLSurface surface = cnx->egl.eglCreateWindowSurface(
- dp->disp[i].dpy, dp->disp[i].config[index], window, attrib_list);
+ dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
+ dp->configs[intptr_t(config)].config, window, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
+ egl_surface_t* s = new egl_surface_t(dpy, surface, config,
+ dp->configs[intptr_t(config)].impl, cnx);
return s;
}
}
@@ -969,13 +998,14 @@ EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config,
const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
- int i=0, index=0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+ egl_connection_t* cnx = validate_display_config(dpy, config, dp);
if (cnx) {
EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
- dp->disp[i].dpy, dp->disp[i].config[index], pixmap, attrib_list);
+ dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
+ dp->configs[intptr_t(config)].config, pixmap, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
+ egl_surface_t* s = new egl_surface_t(dpy, surface, config,
+ dp->configs[intptr_t(config)].impl, cnx);
return s;
}
}
@@ -986,13 +1016,14 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
- int i=0, index=0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+ egl_connection_t* cnx = validate_display_config(dpy, config, dp);
if (cnx) {
EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
- dp->disp[i].dpy, dp->disp[i].config[index], attrib_list);
+ dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
+ dp->configs[intptr_t(config)].config, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
+ egl_surface_t* s = new egl_surface_t(dpy, surface, config,
+ dp->configs[intptr_t(config)].impl, cnx);
return s;
}
}
@@ -1028,23 +1059,35 @@ EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
- return s->cnx->egl.eglQuerySurface(
- dp->disp[s->impl].dpy, s->surface, attribute, value);
+ EGLBoolean result(EGL_TRUE);
+ if (attribute == EGL_CONFIG_ID) {
+ // We need to remap EGL_CONFIG_IDs
+ *value = dp->configs[intptr_t(s->config)].configId;
+ } else {
+ result = s->cnx->egl.eglQuerySurface(
+ dp->disp[s->impl].dpy, s->surface, attribute, value);
+ }
+
+ return result;
}
// ----------------------------------------------------------------------------
-// contextes
+// Contexts
// ----------------------------------------------------------------------------
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
EGLContext share_list, const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
- int i=0, index=0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+ egl_connection_t* cnx = validate_display_config(dpy, config, dp);
if (cnx) {
+ if (share_list != EGL_NO_CONTEXT) {
+ egl_context_t* const c = get_context(share_list);
+ share_list = c->context;
+ }
EGLContext context = cnx->egl.eglCreateContext(
- dp->disp[i].dpy, dp->disp[i].config[index],
+ dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
+ dp->configs[intptr_t(config)].config,
share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
@@ -1062,7 +1105,8 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
}
};
}
- egl_context_t* c = new egl_context_t(dpy, context, i, cnx, version);
+ egl_context_t* c = new egl_context_t(dpy, context, config,
+ dp->configs[intptr_t(config)].impl, cnx, version);
return c;
}
}
@@ -1207,8 +1251,16 @@ EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
egl_display_t const * const dp = get_display(dpy);
egl_context_t * const c = get_context(ctx);
- return c->cnx->egl.eglQueryContext(
- dp->disp[c->impl].dpy, c->context, attribute, value);
+ EGLBoolean result(EGL_TRUE);
+ if (attribute == EGL_CONFIG_ID) {
+ *value = dp->configs[intptr_t(c->config)].configId;
+ } else {
+ // We need to remap EGL_CONFIG_IDs
+ result = c->cnx->egl.eglQueryContext(
+ dp->disp[c->impl].dpy, c->context, attribute, value);
+ }
+
+ return result;
}
EGLContext eglGetCurrentContext(void)
@@ -1323,55 +1375,54 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap));
if (addr) return addr;
- return NULL; // TODO: finish implementation below
+ // this protects accesses to gGLExtentionMap and gGLExtentionSlot
+ pthread_mutex_lock(&gInitDriverMutex);
- addr = findProcAddress(procname, gGLExtentionMap, NELEM(gGLExtentionMap));
- if (addr) return addr;
-
- addr = 0;
- int slot = -1;
- for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
- egl_connection_t* const cnx = &gEGLImpl[i];
- if (cnx->dso) {
- if (cnx->egl.eglGetProcAddress) {
- addr = cnx->egl.eglGetProcAddress(procname);
- if (addr) {
- if (slot == -1) {
- slot = 0; // XXX: find free slot
- if (slot == -1) {
- addr = 0;
- break;
- }
- }
- //cnx->hooks->ext.extensions[slot] = addr;
+ /*
+ * Since eglGetProcAddress() is not associated to anything, it needs
+ * to return a function pointer that "works" regardless of what
+ * the current context is.
+ *
+ * For this reason, we return a "forwarder", a small stub that takes
+ * care of calling the function associated with the context
+ * currently bound.
+ *
+ * We first look for extensions we've already resolved, if we're seeing
+ * this extension for the first time, we go through all our
+ * implementations and call eglGetProcAddress() and record the
+ * result in the appropriate implementation hooks and return the
+ * address of the forwarder corresponding to that hook set.
+ *
+ */
+
+ const String8 name(procname);
+ addr = gGLExtentionMap.valueFor(name);
+ const int slot = gGLExtentionSlot;
+
+ LOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
+ "no more slots for eglGetProcAddress(\"%s\")",
+ procname);
+
+ if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+ bool found = false;
+ for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
+ egl_connection_t* const cnx = &gEGLImpl[i];
+ if (cnx->dso && cnx->egl.eglGetProcAddress) {
+ found = true;
+ cnx->hooks[i]->ext.extensions[slot] =
+ cnx->egl.eglGetProcAddress(procname);
}
}
+ if (found) {
+ addr = gExtensionForwarders[slot];
+ gGLExtentionMap.add(name, addr);
+ gGLExtentionSlot++;
+ }
}
- }
-
- if (slot >= 0) {
- addr = 0; // XXX: address of stub 'slot'
- gGLExtentionMap[slot].name = strdup(procname);
- gGLExtentionMap[slot].address = addr;
- }
-
- return addr;
-
- /*
- * TODO: For OpenGL ES extensions, we must generate a stub
- * that looks like
- * mov r12, #0xFFFF0FFF
- * ldr r12, [r12, #-15]
- * ldr r12, [r12, #TLS_SLOT_OPENGL_API*4]
- * mov r12, [r12, #api_offset]
- * ldrne pc, r12
- * mov pc, #unsupported_extension
- *
- * and write the address of the extension in *all*
- * gl_hooks_t::gl_ext_t at offset "api_offset" from gl_hooks_t
- *
- */
+ pthread_mutex_unlock(&gInitDriverMutex);
+
+ return addr;
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
@@ -1580,13 +1631,13 @@ EGLSurface eglCreatePbufferFromClientBuffer(
EGLConfig config, const EGLint *attrib_list)
{
egl_display_t const* dp = 0;
- int i=0, index=0;
- egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
+ egl_connection_t* cnx = validate_display_config(dpy, config, dp);
if (!cnx) return EGL_FALSE;
if (cnx->egl.eglCreatePbufferFromClientBuffer) {
return cnx->egl.eglCreatePbufferFromClientBuffer(
- dp->disp[i].dpy, buftype, buffer,
- dp->disp[i].config[index], attrib_list);
+ dp->disp[ dp->configs[intptr_t(config)].impl ].dpy,
+ buftype, buffer,
+ dp->configs[intptr_t(config)].config, attrib_list);
}
return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
}
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
new file mode 100644
index 0000000..23837ef
--- /dev/null
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -0,0 +1,176 @@
+/*
+ ** Copyright 2009, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef GL_EXTENSION
+#undef GL_EXTENSION_NAME
+
+#if defined(__arm__)
+
+ #ifdef HAVE_ARM_TLS_REGISTER
+ #define GET_TLS(reg) \
+ "mrc p15, 0, " #reg ", c13, c0, 3 \n"
+ #else
+ #define GET_TLS(reg) \
+ "mov " #reg ", #0xFFFF0FFF \n" \
+ "ldr " #reg ", [" #reg ", #-15] \n"
+ #endif
+
+ #define API_ENTRY(_api) __attribute__((naked)) _api
+
+ #define CALL_GL_EXTENSION_API(_api) \
+ asm volatile( \
+ GET_TLS(r12) \
+ "ldr r12, [r12, %[tls]] \n" \
+ "cmp r12, #0 \n" \
+ "ldrne r12, [r12, %[api]] \n" \
+ "cmpne r12, #0 \n" \
+ "bxne r12 \n" \
+ "bx lr \n" \
+ : \
+ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \
+ [api] "J"(__builtin_offsetof(gl_hooks_t, \
+ ext.extensions[_api])) \
+ : \
+ );
+
+ #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
+
+ #define GL_EXTENSION(_n) \
+ void API_ENTRY(GL_EXTENSION_NAME(_n))() { \
+ CALL_GL_EXTENSION_API(_n); \
+ }
+
+
+#else
+
+ #define GL_EXTENSION_NAME(_n) NULL
+
+ #define GL_EXTENSION(_n)
+
+ #warning "eglGetProcAddress() partially supported on this architecture"
+
+#endif
+
+GL_EXTENSION(0)
+GL_EXTENSION(1)
+GL_EXTENSION(2)
+GL_EXTENSION(3)
+GL_EXTENSION(4)
+GL_EXTENSION(5)
+GL_EXTENSION(6)
+GL_EXTENSION(7)
+GL_EXTENSION(8)
+GL_EXTENSION(9)
+GL_EXTENSION(10)
+GL_EXTENSION(11)
+GL_EXTENSION(12)
+GL_EXTENSION(13)
+GL_EXTENSION(14)
+GL_EXTENSION(15)
+
+GL_EXTENSION(16)
+GL_EXTENSION(17)
+GL_EXTENSION(18)
+GL_EXTENSION(19)
+GL_EXTENSION(20)
+GL_EXTENSION(21)
+GL_EXTENSION(22)
+GL_EXTENSION(23)
+GL_EXTENSION(24)
+GL_EXTENSION(25)
+GL_EXTENSION(26)
+GL_EXTENSION(27)
+GL_EXTENSION(28)
+GL_EXTENSION(29)
+GL_EXTENSION(30)
+GL_EXTENSION(31)
+
+GL_EXTENSION(32)
+GL_EXTENSION(33)
+GL_EXTENSION(34)
+GL_EXTENSION(35)
+GL_EXTENSION(36)
+GL_EXTENSION(37)
+GL_EXTENSION(38)
+GL_EXTENSION(39)
+GL_EXTENSION(40)
+GL_EXTENSION(41)
+GL_EXTENSION(42)
+GL_EXTENSION(43)
+GL_EXTENSION(44)
+GL_EXTENSION(45)
+GL_EXTENSION(46)
+GL_EXTENSION(47)
+
+GL_EXTENSION(48)
+GL_EXTENSION(49)
+GL_EXTENSION(50)
+GL_EXTENSION(51)
+GL_EXTENSION(52)
+GL_EXTENSION(53)
+GL_EXTENSION(54)
+GL_EXTENSION(55)
+GL_EXTENSION(56)
+GL_EXTENSION(57)
+GL_EXTENSION(58)
+GL_EXTENSION(59)
+GL_EXTENSION(60)
+GL_EXTENSION(61)
+GL_EXTENSION(62)
+GL_EXTENSION(63)
+
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
+ GL_EXTENSION_NAME(0), GL_EXTENSION_NAME(1), GL_EXTENSION_NAME(2), GL_EXTENSION_NAME(3),
+ GL_EXTENSION_NAME(4), GL_EXTENSION_NAME(5), GL_EXTENSION_NAME(6), GL_EXTENSION_NAME(7),
+ GL_EXTENSION_NAME(8), GL_EXTENSION_NAME(9), GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11),
+ GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15),
+ GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19),
+ GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23),
+ GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27),
+ GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31),
+ GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35),
+ GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39),
+ GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43),
+ GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47),
+ GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51),
+ GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55),
+ GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59),
+ GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63)
+ };
+
+#undef GL_EXTENSION_NAME
+#undef GL_EXTENSION
+#undef API_ENTRY
+#undef CALL_GL_API
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in
index 9c2e69a..5164450 100644
--- a/opengl/libs/GLES2/gl2_api.in
+++ b/opengl/libs/GLES2/gl2_api.in
@@ -4,7 +4,7 @@ void API_ENTRY(glActiveTexture)(GLenum texture) {
void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) {
CALL_GL_API(glAttachShader, program, shader);
}
-void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const char* name) {
+void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const GLchar* name) {
CALL_GL_API(glBindAttribLocation, program, index, name);
}
void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) {
@@ -34,10 +34,10 @@ void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) {
void API_ENTRY(glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
CALL_GL_API(glBlendFuncSeparate, srcRGB, dstRGB, srcAlpha, dstAlpha);
}
-void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
+void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) {
CALL_GL_API(glBufferData, target, size, data, usage);
}
-void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
+void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) {
CALL_GL_API(glBufferSubData, target, offset, size, data);
}
GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) {
@@ -61,10 +61,10 @@ void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLbo
void API_ENTRY(glCompileShader)(GLuint shader) {
CALL_GL_API(glCompileShader, shader);
}
-void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) {
+void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data) {
CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data);
}
-void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
+void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) {
CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data);
}
void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
@@ -121,7 +121,7 @@ void API_ENTRY(glDisableVertexAttribArray)(GLuint index) {
void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) {
CALL_GL_API(glDrawArrays, mode, first, count);
}
-void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices) {
+void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) {
CALL_GL_API(glDrawElements, mode, count, type, indices);
}
void API_ENTRY(glEnable)(GLenum cap) {
@@ -160,16 +160,16 @@ void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint* renderbuffers) {
void API_ENTRY(glGenTextures)(GLsizei n, GLuint* textures) {
CALL_GL_API(glGenTextures, n, textures);
}
-void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) {
+void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
CALL_GL_API(glGetActiveAttrib, program, index, bufsize, length, size, type, name);
}
-void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) {
+void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
CALL_GL_API(glGetActiveUniform, program, index, bufsize, length, size, type, name);
}
void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) {
CALL_GL_API(glGetAttachedShaders, program, maxcount, count, shaders);
}
-int API_ENTRY(glGetAttribLocation)(GLuint program, const char* name) {
+int API_ENTRY(glGetAttribLocation)(GLuint program, const GLchar* name) {
CALL_GL_API_RETURN(glGetAttribLocation, program, name);
}
void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean* params) {
@@ -193,7 +193,7 @@ void API_ENTRY(glGetIntegerv)(GLenum pname, GLint* params) {
void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint* params) {
CALL_GL_API(glGetProgramiv, program, pname, params);
}
-void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) {
+void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) {
CALL_GL_API(glGetProgramInfoLog, program, bufsize, length, infolog);
}
void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params) {
@@ -202,13 +202,13 @@ void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint*
void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint* params) {
CALL_GL_API(glGetShaderiv, shader, pname, params);
}
-void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) {
+void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) {
CALL_GL_API(glGetShaderInfoLog, shader, bufsize, length, infolog);
}
void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision);
}
-void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) {
+void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) {
CALL_GL_API(glGetShaderSource, shader, bufsize, length, source);
}
const GLubyte* API_ENTRY(glGetString)(GLenum name) {
@@ -226,7 +226,7 @@ void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat* params)
void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint* params) {
CALL_GL_API(glGetUniformiv, program, location, params);
}
-int API_ENTRY(glGetUniformLocation)(GLuint program, const char* name) {
+int API_ENTRY(glGetUniformLocation)(GLuint program, const GLchar* name) {
CALL_GL_API_RETURN(glGetUniformLocation, program, name);
}
void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params) {
@@ -235,7 +235,7 @@ void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params)
void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint* params) {
CALL_GL_API(glGetVertexAttribiv, index, pname, params);
}
-void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer) {
+void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, GLvoid** pointer) {
CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer);
}
void API_ENTRY(glHint)(GLenum target, GLenum mode) {
@@ -274,7 +274,7 @@ void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) {
void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) {
CALL_GL_API(glPolygonOffset, factor, units);
}
-void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
+void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) {
CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels);
}
void API_ENTRY(glReleaseShaderCompiler)(void) {
@@ -289,10 +289,10 @@ void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) {
void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) {
CALL_GL_API(glScissor, x, y, width, height);
}
-void API_ENTRY(glShaderBinary)(GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length) {
+void API_ENTRY(glShaderBinary)(GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) {
CALL_GL_API(glShaderBinary, n, shaders, binaryformat, binary, length);
}
-void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length) {
+void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar** string, const GLint* length) {
CALL_GL_API(glShaderSource, shader, count, string, length);
}
void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
@@ -313,7 +313,7 @@ void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) {
void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) {
CALL_GL_API(glStencilOpSeparate, face, fail, zfail, zpass);
}
-void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
+void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels);
}
void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
@@ -328,7 +328,7 @@ void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) {
void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint* params) {
CALL_GL_API(glTexParameteriv, target, pname, params);
}
-void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) {
+void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) {
CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels);
}
void API_ENTRY(glUniform1f)(GLint location, GLfloat x) {
@@ -418,7 +418,7 @@ void API_ENTRY(glVertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, G
void API_ENTRY(glVertexAttrib4fv)(GLuint indx, const GLfloat* values) {
CALL_GL_API(glVertexAttrib4fv, indx, values);
}
-void API_ENTRY(glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) {
+void API_ENTRY(glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) {
CALL_GL_API(glVertexAttribPointer, indx, size, type, normalized, stride, ptr);
}
void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) {
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
index 6eeecb3..e965625 100644
--- a/opengl/libs/GLES2/gl2ext_api.in
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -4,10 +4,10 @@ void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES imag
void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
}
-void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) {
+void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary) {
CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary);
}
-void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) {
+void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length) {
CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length);
}
void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) {
@@ -16,40 +16,52 @@ void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) {
GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) {
CALL_GL_API_RETURN(glUnmapBufferOES, target);
}
-void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void** params) {
+void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, GLvoid** params) {
CALL_GL_API(glGetBufferPointervOES, target, pname, params);
}
-void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) {
+void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels);
}
-void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
+void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels) {
CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
}
void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height);
}
-void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data) {
+void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data) {
CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data);
}
-void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) {
+void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data) {
CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
}
void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) {
CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset);
}
+void API_ENTRY(glBindVertexArrayOES)(GLuint array) {
+ CALL_GL_API(glBindVertexArrayOES, array);
+}
+void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) {
+ CALL_GL_API(glDeleteVertexArraysOES, n, arrays);
+}
+void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) {
+ CALL_GL_API(glGenVertexArraysOES, n, arrays);
+}
+GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) {
+ CALL_GL_API_RETURN(glIsVertexArrayOES, array);
+}
void API_ENTRY(glGetPerfMonitorGroupsAMD)(GLint *numGroups, GLsizei groupsSize, GLuint *groups) {
CALL_GL_API(glGetPerfMonitorGroupsAMD, numGroups, groupsSize, groups);
}
void API_ENTRY(glGetPerfMonitorCountersAMD)(GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters) {
CALL_GL_API(glGetPerfMonitorCountersAMD, group, numCounters, maxActiveCounters, counterSize, counters);
}
-void API_ENTRY(glGetPerfMonitorGroupStringAMD)(GLuint group, GLsizei bufSize, GLsizei *length, char *groupString) {
+void API_ENTRY(glGetPerfMonitorGroupStringAMD)(GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString) {
CALL_GL_API(glGetPerfMonitorGroupStringAMD, group, bufSize, length, groupString);
}
-void API_ENTRY(glGetPerfMonitorCounterStringAMD)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString) {
+void API_ENTRY(glGetPerfMonitorCounterStringAMD)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString) {
CALL_GL_API(glGetPerfMonitorCounterStringAMD, group, counter, bufSize, length, counterString);
}
-void API_ENTRY(glGetPerfMonitorCounterInfoAMD)(GLuint group, GLuint counter, GLenum pname, void *data) {
+void API_ENTRY(glGetPerfMonitorCounterInfoAMD)(GLuint group, GLuint counter, GLenum pname, GLvoid *data) {
CALL_GL_API(glGetPerfMonitorCounterInfoAMD, group, counter, pname, data);
}
void API_ENTRY(glGenPerfMonitorsAMD)(GLsizei n, GLuint *monitors) {
@@ -70,6 +82,21 @@ void API_ENTRY(glEndPerfMonitorAMD)(GLuint monitor) {
void API_ENTRY(glGetPerfMonitorCounterDataAMD)(GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten) {
CALL_GL_API(glGetPerfMonitorCounterDataAMD, monitor, pname, dataSize, data, bytesWritten);
}
+void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) {
+ CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments);
+}
+void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) {
+ CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount);
+}
+void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount) {
+ CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount);
+}
+void API_ENTRY(glRenderbufferStorageMultisampleIMG)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glRenderbufferStorageMultisampleIMG, target, samples, internalformat, width, height);
+}
+void API_ENTRY(glFramebufferTexture2DMultisampleIMG)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) {
+ CALL_GL_API(glFramebufferTexture2DMultisampleIMG, target, attachment, textarget, texture, level, samples);
+}
void API_ENTRY(glDeleteFencesNV)(GLsizei n, const GLuint *fences) {
CALL_GL_API(glDeleteFencesNV, n, fences);
}
@@ -91,10 +118,16 @@ void API_ENTRY(glFinishFenceNV)(GLuint fence) {
void API_ENTRY(glSetFenceNV)(GLuint fence, GLenum condition) {
CALL_GL_API(glSetFenceNV, fence, condition);
}
+void API_ENTRY(glCoverageMaskNV)(GLboolean mask) {
+ CALL_GL_API(glCoverageMaskNV, mask);
+}
+void API_ENTRY(glCoverageOperationNV)(GLenum operation) {
+ CALL_GL_API(glCoverageOperationNV, operation);
+}
void API_ENTRY(glGetDriverControlsQCOM)(GLint *num, GLsizei size, GLuint *driverControls) {
CALL_GL_API(glGetDriverControlsQCOM, num, size, driverControls);
}
-void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString) {
+void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) {
CALL_GL_API(glGetDriverControlStringQCOM, driverControl, bufSize, length, driverControlString);
}
void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) {
@@ -103,3 +136,45 @@ void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) {
void API_ENTRY(glDisableDriverControlQCOM)(GLuint driverControl) {
CALL_GL_API(glDisableDriverControlQCOM, driverControl);
}
+void API_ENTRY(glExtGetTexturesQCOM)(GLuint *textures, GLint maxTextures, GLint *numTextures) {
+ CALL_GL_API(glExtGetTexturesQCOM, textures, maxTextures, numTextures);
+}
+void API_ENTRY(glExtGetBuffersQCOM)(GLuint *buffers, GLint maxBuffers, GLint *numBuffers) {
+ CALL_GL_API(glExtGetBuffersQCOM, buffers, maxBuffers, numBuffers);
+}
+void API_ENTRY(glExtGetRenderbuffersQCOM)(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) {
+ CALL_GL_API(glExtGetRenderbuffersQCOM, renderbuffers, maxRenderbuffers, numRenderbuffers);
+}
+void API_ENTRY(glExtGetFramebuffersQCOM)(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) {
+ CALL_GL_API(glExtGetFramebuffersQCOM, framebuffers, maxFramebuffers, numFramebuffers);
+}
+void API_ENTRY(glExtGetTexLevelParameterivQCOM)(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) {
+ CALL_GL_API(glExtGetTexLevelParameterivQCOM, texture, face, level, pname, params);
+}
+void API_ENTRY(glExtTexObjectStateOverrideiQCOM)(GLenum target, GLenum pname, GLint param) {
+ CALL_GL_API(glExtTexObjectStateOverrideiQCOM, target, pname, param);
+}
+void API_ENTRY(glExtGetTexSubImageQCOM)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) {
+ CALL_GL_API(glExtGetTexSubImageQCOM, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels);
+}
+void API_ENTRY(glExtGetBufferPointervQCOM)(GLenum target, GLvoid **params) {
+ CALL_GL_API(glExtGetBufferPointervQCOM, target, params);
+}
+void API_ENTRY(glExtGetShadersQCOM)(GLuint *shaders, GLint maxShaders, GLint *numShaders) {
+ CALL_GL_API(glExtGetShadersQCOM, shaders, maxShaders, numShaders);
+}
+void API_ENTRY(glExtGetProgramsQCOM)(GLuint *programs, GLint maxPrograms, GLint *numPrograms) {
+ CALL_GL_API(glExtGetProgramsQCOM, programs, maxPrograms, numPrograms);
+}
+GLboolean API_ENTRY(glExtIsProgramBinaryQCOM)(GLuint program) {
+ CALL_GL_API_RETURN(glExtIsProgramBinaryQCOM, program);
+}
+void API_ENTRY(glExtGetProgramBinarySourceQCOM)(GLuint program, GLenum shadertype, GLchar *source, GLint *length) {
+ CALL_GL_API(glExtGetProgramBinarySourceQCOM, program, shadertype, source, length);
+}
+void API_ENTRY(glStartTilingQCOM)(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) {
+ CALL_GL_API(glStartTilingQCOM, x, y, width, height, preserveMask);
+}
+void API_ENTRY(glEndTilingQCOM)(GLbitfield preserveMask) {
+ CALL_GL_API(glEndTilingQCOM, preserveMask);
+}
diff --git a/opengl/libs/GLES_CM/gl_api.in b/opengl/libs/GLES_CM/gl_api.in
index 5437d47..7f20c4f 100644
--- a/opengl/libs/GLES_CM/gl_api.in
+++ b/opengl/libs/GLES_CM/gl_api.in
@@ -259,7 +259,7 @@ void API_ENTRY(glGetLightxv)(GLenum light, GLenum pname, GLfixed *params) {
void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) {
CALL_GL_API(glGetMaterialxv, face, pname, params);
}
-void API_ENTRY(glGetPointerv)(GLenum pname, void **params) {
+void API_ENTRY(glGetPointerv)(GLenum pname, GLvoid **params) {
CALL_GL_API(glGetPointerv, pname, params);
}
const GLubyte * API_ENTRY(glGetString)(GLenum name) {
diff --git a/opengl/libs/GLES_CM/glext_api.in b/opengl/libs/GLES_CM/glext_api.in
index 2c8648e..5393fa6 100644
--- a/opengl/libs/GLES_CM/glext_api.in
+++ b/opengl/libs/GLES_CM/glext_api.in
@@ -205,7 +205,7 @@ void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) {
GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) {
CALL_GL_API_RETURN(glUnmapBufferOES, target);
}
-void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void** params) {
+void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, GLvoid ** params) {
CALL_GL_API(glGetBufferPointervOES, target, pname, params);
}
void API_ENTRY(glCurrentPaletteMatrixOES)(GLuint matrixpaletteindex) {
@@ -268,3 +268,111 @@ void API_ENTRY(glGetTexGenivOES)(GLenum coord, GLenum pname, GLint *params) {
void API_ENTRY(glGetTexGenxvOES)(GLenum coord, GLenum pname, GLfixed *params) {
CALL_GL_API(glGetTexGenxvOES, coord, pname, params);
}
+void API_ENTRY(glBindVertexArrayOES)(GLuint array) {
+ CALL_GL_API(glBindVertexArrayOES, array);
+}
+void API_ENTRY(glDeleteVertexArraysOES)(GLsizei n, const GLuint *arrays) {
+ CALL_GL_API(glDeleteVertexArraysOES, n, arrays);
+}
+void API_ENTRY(glGenVertexArraysOES)(GLsizei n, GLuint *arrays) {
+ CALL_GL_API(glGenVertexArraysOES, n, arrays);
+}
+GLboolean API_ENTRY(glIsVertexArrayOES)(GLuint array) {
+ CALL_GL_API_RETURN(glIsVertexArrayOES, array);
+}
+void API_ENTRY(glDiscardFramebufferEXT)(GLenum target, GLsizei numAttachments, const GLenum *attachments) {
+ CALL_GL_API(glDiscardFramebufferEXT, target, numAttachments, attachments);
+}
+void API_ENTRY(glMultiDrawArraysEXT)(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount) {
+ CALL_GL_API(glMultiDrawArraysEXT, mode, first, count, primcount);
+}
+void API_ENTRY(glMultiDrawElementsEXT)(GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount) {
+ CALL_GL_API(glMultiDrawElementsEXT, mode, count, type, indices, primcount);
+}
+void API_ENTRY(glClipPlanefIMG)(GLenum p, const GLfloat *eqn) {
+ CALL_GL_API(glClipPlanefIMG, p, eqn);
+}
+void API_ENTRY(glClipPlanexIMG)(GLenum p, const GLfixed *eqn) {
+ CALL_GL_API(glClipPlanexIMG, p, eqn);
+}
+void API_ENTRY(glRenderbufferStorageMultisampleIMG)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glRenderbufferStorageMultisampleIMG, target, samples, internalformat, width, height);
+}
+void API_ENTRY(glFramebufferTexture2DMultisampleIMG)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) {
+ CALL_GL_API(glFramebufferTexture2DMultisampleIMG, target, attachment, textarget, texture, level, samples);
+}
+void API_ENTRY(glDeleteFencesNV)(GLsizei n, const GLuint *fences) {
+ CALL_GL_API(glDeleteFencesNV, n, fences);
+}
+void API_ENTRY(glGenFencesNV)(GLsizei n, GLuint *fences) {
+ CALL_GL_API(glGenFencesNV, n, fences);
+}
+GLboolean API_ENTRY(glIsFenceNV)(GLuint fence) {
+ CALL_GL_API_RETURN(glIsFenceNV, fence);
+}
+GLboolean API_ENTRY(glTestFenceNV)(GLuint fence) {
+ CALL_GL_API_RETURN(glTestFenceNV, fence);
+}
+void API_ENTRY(glGetFenceivNV)(GLuint fence, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetFenceivNV, fence, pname, params);
+}
+void API_ENTRY(glFinishFenceNV)(GLuint fence) {
+ CALL_GL_API(glFinishFenceNV, fence);
+}
+void API_ENTRY(glSetFenceNV)(GLuint fence, GLenum condition) {
+ CALL_GL_API(glSetFenceNV, fence, condition);
+}
+void API_ENTRY(glGetDriverControlsQCOM)(GLint *num, GLsizei size, GLuint *driverControls) {
+ CALL_GL_API(glGetDriverControlsQCOM, num, size, driverControls);
+}
+void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) {
+ CALL_GL_API(glGetDriverControlStringQCOM, driverControl, bufSize, length, driverControlString);
+}
+void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) {
+ CALL_GL_API(glEnableDriverControlQCOM, driverControl);
+}
+void API_ENTRY(glDisableDriverControlQCOM)(GLuint driverControl) {
+ CALL_GL_API(glDisableDriverControlQCOM, driverControl);
+}
+void API_ENTRY(glExtGetTexturesQCOM)(GLuint *textures, GLint maxTextures, GLint *numTextures) {
+ CALL_GL_API(glExtGetTexturesQCOM, textures, maxTextures, numTextures);
+}
+void API_ENTRY(glExtGetBuffersQCOM)(GLuint *buffers, GLint maxBuffers, GLint *numBuffers) {
+ CALL_GL_API(glExtGetBuffersQCOM, buffers, maxBuffers, numBuffers);
+}
+void API_ENTRY(glExtGetRenderbuffersQCOM)(GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) {
+ CALL_GL_API(glExtGetRenderbuffersQCOM, renderbuffers, maxRenderbuffers, numRenderbuffers);
+}
+void API_ENTRY(glExtGetFramebuffersQCOM)(GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) {
+ CALL_GL_API(glExtGetFramebuffersQCOM, framebuffers, maxFramebuffers, numFramebuffers);
+}
+void API_ENTRY(glExtGetTexLevelParameterivQCOM)(GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) {
+ CALL_GL_API(glExtGetTexLevelParameterivQCOM, texture, face, level, pname, params);
+}
+void API_ENTRY(glExtTexObjectStateOverrideiQCOM)(GLenum target, GLenum pname, GLint param) {
+ CALL_GL_API(glExtTexObjectStateOverrideiQCOM, target, pname, param);
+}
+void API_ENTRY(glExtGetTexSubImageQCOM)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels) {
+ CALL_GL_API(glExtGetTexSubImageQCOM, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels);
+}
+void API_ENTRY(glExtGetBufferPointervQCOM)(GLenum target, GLvoid **params) {
+ CALL_GL_API(glExtGetBufferPointervQCOM, target, params);
+}
+void API_ENTRY(glExtGetShadersQCOM)(GLuint *shaders, GLint maxShaders, GLint *numShaders) {
+ CALL_GL_API(glExtGetShadersQCOM, shaders, maxShaders, numShaders);
+}
+void API_ENTRY(glExtGetProgramsQCOM)(GLuint *programs, GLint maxPrograms, GLint *numPrograms) {
+ CALL_GL_API(glExtGetProgramsQCOM, programs, maxPrograms, numPrograms);
+}
+GLboolean API_ENTRY(glExtIsProgramBinaryQCOM)(GLuint program) {
+ CALL_GL_API_RETURN(glExtIsProgramBinaryQCOM, program);
+}
+void API_ENTRY(glExtGetProgramBinarySourceQCOM)(GLuint program, GLenum shadertype, GLchar *source, GLint *length) {
+ CALL_GL_API(glExtGetProgramBinarySourceQCOM, program, shadertype, source, length);
+}
+void API_ENTRY(glStartTilingQCOM)(GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) {
+ CALL_GL_API(glStartTilingQCOM, x, y, width, height, preserveMask);
+}
+void API_ENTRY(glEndTilingQCOM)(GLbitfield preserveMask) {
+ CALL_GL_API(glEndTilingQCOM, preserveMask);
+}
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index 1fba209..c8f529a 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -31,6 +31,7 @@ namespace android {
struct egl_connection_t
{
+ inline egl_connection_t() : dso(0) { }
void * dso;
gl_hooks_t * hooks[2];
EGLint major;
diff --git a/opengl/libs/entries.in b/opengl/libs/entries.in
index bbe3e23..61acb5f 100644
--- a/opengl/libs/entries.in
+++ b/opengl/libs/entries.in
@@ -4,13 +4,14 @@ GL_ENTRY(void, glAlphaFuncx, GLenum func, GLclampx ref)
GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLclampx ref)
GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader)
GL_ENTRY(void, glBeginPerfMonitorAMD, GLuint monitor)
-GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const char* name)
+GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const GLchar* name)
GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer)
GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer)
GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer)
GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer)
GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer)
GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture)
+GL_ENTRY(void, glBindVertexArrayOES, GLuint array)
GL_ENTRY(void, glBlendColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
GL_ENTRY(void, glBlendEquation, GLenum mode )
GL_ENTRY(void, glBlendEquationOES, GLenum mode)
@@ -34,8 +35,10 @@ GL_ENTRY(void, glClearDepthxOES, GLclampx depth)
GL_ENTRY(void, glClearStencil, GLint s)
GL_ENTRY(void, glClientActiveTexture, GLenum texture)
GL_ENTRY(void, glClipPlanef, GLenum plane, const GLfloat *equation)
+GL_ENTRY(void, glClipPlanefIMG, GLenum p, const GLfloat *eqn)
GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation)
GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed *equation)
+GL_ENTRY(void, glClipPlanexIMG, GLenum p, const GLfixed *eqn)
GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed *equation)
GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)
@@ -45,12 +48,14 @@ GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLbo
GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
GL_ENTRY(void, glCompileShader, GLuint shader)
GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data)
-GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data)
+GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data)
GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data)
-GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data)
+GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data)
GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glCoverageMaskNV, GLboolean mask)
+GL_ENTRY(void, glCoverageOperationNV, GLenum operation)
GL_ENTRY(GLuint, glCreateProgram, void)
GL_ENTRY(GLuint, glCreateShader, GLenum type)
GL_ENTRY(void, glCullFace, GLenum mode)
@@ -65,6 +70,7 @@ GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint* renderbuffers)
GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint* renderbuffers)
GL_ENTRY(void, glDeleteShader, GLuint shader)
GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures)
+GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays)
GL_ENTRY(void, glDepthFunc, GLenum func)
GL_ENTRY(void, glDepthMask, GLboolean flag)
GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar)
@@ -76,6 +82,7 @@ GL_ENTRY(void, glDisable, GLenum cap)
GL_ENTRY(void, glDisableClientState, GLenum array)
GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl)
GL_ENTRY(void, glDisableVertexAttribArray, GLuint index)
+GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments)
GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count)
GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height)
@@ -93,6 +100,19 @@ GL_ENTRY(void, glEnableClientState, GLenum array)
GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl)
GL_ENTRY(void, glEnableVertexAttribArray, GLuint index)
GL_ENTRY(void, glEndPerfMonitorAMD, GLuint monitor)
+GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask)
+GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, GLvoid **params)
+GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers)
+GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers)
+GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length)
+GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms)
+GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers)
+GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders)
+GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params)
+GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels)
+GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures)
+GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program)
+GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param)
GL_ENTRY(void, glFinish, void)
GL_ENTRY(void, glFinishFenceNV, GLuint fence)
GL_ENTRY(void, glFlush, void)
@@ -105,6 +125,7 @@ GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *params)
GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
GL_ENTRY(void, glFrontFace, GLenum mode)
@@ -120,20 +141,21 @@ GL_ENTRY(void, glGenPerfMonitorsAMD, GLsizei n, GLuint *monitors)
GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint* renderbuffers)
GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint* renderbuffers)
GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures)
+GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays)
GL_ENTRY(void, glGenerateMipmap, GLenum target)
GL_ENTRY(void, glGenerateMipmapOES, GLenum target)
-GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
-GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
+GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name)
+GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name)
GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders)
-GL_ENTRY(int, glGetAttribLocation, GLuint program, const char* name)
+GL_ENTRY(int, glGetAttribLocation, GLuint program, const GLchar* name)
GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *params)
GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params)
-GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void** params)
+GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, GLvoid ** params)
GL_ENTRY(void, glGetClipPlanef, GLenum pname, GLfloat eqn[4])
GL_ENTRY(void, glGetClipPlanefOES, GLenum pname, GLfloat eqn[4])
GL_ENTRY(void, glGetClipPlanex, GLenum pname, GLfixed eqn[4])
GL_ENTRY(void, glGetClipPlanexOES, GLenum pname, GLfixed eqn[4])
-GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString)
+GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString)
GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls)
GL_ENTRY(GLenum, glGetError, void)
GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params)
@@ -150,20 +172,20 @@ GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params)
GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params)
GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params)
GL_ENTRY(void, glGetPerfMonitorCounterDataAMD, GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten)
-GL_ENTRY(void, glGetPerfMonitorCounterInfoAMD, GLuint group, GLuint counter, GLenum pname, void *data)
-GL_ENTRY(void, glGetPerfMonitorCounterStringAMD, GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString)
+GL_ENTRY(void, glGetPerfMonitorCounterInfoAMD, GLuint group, GLuint counter, GLenum pname, GLvoid *data)
+GL_ENTRY(void, glGetPerfMonitorCounterStringAMD, GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString)
GL_ENTRY(void, glGetPerfMonitorCountersAMD, GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters)
-GL_ENTRY(void, glGetPerfMonitorGroupStringAMD, GLuint group, GLsizei bufSize, GLsizei *length, char *groupString)
+GL_ENTRY(void, glGetPerfMonitorGroupStringAMD, GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString)
GL_ENTRY(void, glGetPerfMonitorGroupsAMD, GLint *numGroups, GLsizei groupsSize, GLuint *groups)
-GL_ENTRY(void, glGetPointerv, GLenum pname, void **params)
-GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
-GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufsize, GLsizei* length, char* infolog)
+GL_ENTRY(void, glGetPointerv, GLenum pname, GLvoid **params)
+GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary)
+GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog)
GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint* params)
GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint* params)
GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint* params)
-GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog)
+GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog)
GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision)
-GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufsize, GLsizei* length, char* source)
+GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source)
GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint* params)
GL_ENTRY(const GLubyte *, glGetString, GLenum name)
GL_ENTRY(void, glGetTexEnvfv, GLenum env, GLenum pname, GLfloat *params)
@@ -177,10 +199,10 @@ GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params
GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params)
GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params)
GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params)
-GL_ENTRY(int, glGetUniformLocation, GLuint program, const char* name)
+GL_ENTRY(int, glGetUniformLocation, GLuint program, const GLchar* name)
GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat* params)
GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint* params)
-GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void** pointer)
+GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, GLvoid** pointer)
GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat* params)
GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint* params)
GL_ENTRY(void, glHint, GLenum target, GLenum mode)
@@ -194,6 +216,7 @@ GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer)
GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer)
GL_ENTRY(GLboolean, glIsShader, GLuint shader)
GL_ENTRY(GLboolean, glIsTexture, GLuint texture)
+GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array)
GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param)
GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params)
GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param)
@@ -228,6 +251,8 @@ GL_ENTRY(void, glMatrixMode, GLenum mode)
GL_ENTRY(void, glMultMatrixf, const GLfloat *m)
GL_ENTRY(void, glMultMatrixx, const GLfixed *m)
GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m)
+GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, GLint *first, GLsizei *count, GLsizei primcount)
+GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount)
GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
GL_ENTRY(void, glMultiTexCoord4x, GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
GL_ENTRY(void, glMultiTexCoord4xOES, GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
@@ -254,12 +279,13 @@ GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units)
GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units)
GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units)
GL_ENTRY(void, glPopMatrix, void)
-GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length)
+GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length)
GL_ENTRY(void, glPushMatrix, void)
GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed mantissa[16], GLint exponent[16])
GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
GL_ENTRY(void, glReleaseShaderCompiler, void)
GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
@@ -274,8 +300,9 @@ GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height)
GL_ENTRY(void, glSelectPerfMonitorCountersAMD, GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList)
GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition)
GL_ENTRY(void, glShadeModel, GLenum mode)
-GL_ENTRY(void, glShaderBinary, GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length)
-GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const char** string, const GLint* length)
+GL_ENTRY(void, glShaderBinary, GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length)
+GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const GLchar** string, const GLint* length)
+GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask)
GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask)
GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask)
GL_ENTRY(void, glStencilMask, GLuint mask)
@@ -298,8 +325,8 @@ GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param)
GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params)
GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param)
GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params)
-GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
-GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels)
+GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)
+GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param)
GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params)
GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param)
@@ -309,7 +336,7 @@ GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param)
GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params)
GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params)
GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)
-GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels)
+GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels)
GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z)
GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z)
GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z)
@@ -343,7 +370,7 @@ GL_ENTRY(void, glVertexAttrib3f, GLuint indx, GLfloat x, GLfloat y, GLfloat z)
GL_ENTRY(void, glVertexAttrib3fv, GLuint indx, const GLfloat* values)
GL_ENTRY(void, glVertexAttrib4f, GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
GL_ENTRY(void, glVertexAttrib4fv, GLuint indx, const GLfloat* values)
-GL_ENTRY(void, glVertexAttribPointer, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr)
+GL_ENTRY(void, glVertexAttribPointer, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height)
GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index f47f093..1ab58cc 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -37,7 +37,7 @@
#endif
#undef NELEM
#define NELEM(x) (sizeof(x)/sizeof(*(x)))
-#define MAX_NUMBER_OF_GL_EXTENSIONS 32
+#define MAX_NUMBER_OF_GL_EXTENSIONS 64
#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && __OPTIMIZE__
@@ -86,7 +86,7 @@ struct gl_hooks_t {
#include "entries.in"
} gl;
struct gl_ext_t {
- void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void);
+ __eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS];
} ext;
};
#undef GL_ENTRY
diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp
index 2361db5..f274c7c 100644
--- a/opengl/tests/gl2_basic/gl2_basic.cpp
+++ b/opengl/tests/gl2_basic/gl2_basic.cpp
@@ -195,7 +195,6 @@ void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
X(EGL_NATIVE_RENDERABLE),
X(EGL_NATIVE_VISUAL_ID),
X(EGL_NATIVE_VISUAL_TYPE),
- X(EGL_PRESERVED_RESOURCES),
X(EGL_SAMPLES),
X(EGL_SAMPLE_BUFFERS),
X(EGL_SURFACE_TYPE),
diff --git a/opengl/tests/gl_basic/gl_basic.cpp b/opengl/tests/gl_basic/gl_basic.cpp
index feb964a..0cc8398 100644
--- a/opengl/tests/gl_basic/gl_basic.cpp
+++ b/opengl/tests/gl_basic/gl_basic.cpp
@@ -114,7 +114,6 @@ void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
X(EGL_NATIVE_RENDERABLE),
X(EGL_NATIVE_VISUAL_ID),
X(EGL_NATIVE_VISUAL_TYPE),
- X(EGL_PRESERVED_RESOURCES),
X(EGL_SAMPLES),
X(EGL_SAMPLE_BUFFERS),
X(EGL_SURFACE_TYPE),
diff --git a/opengl/tests/gl_jni/jni/gl_code.cpp b/opengl/tests/gl_jni/jni/gl_code.cpp
index 33b25ab..f031c79 100644
--- a/opengl/tests/gl_jni/jni/gl_code.cpp
+++ b/opengl/tests/gl_jni/jni/gl_code.cpp
@@ -180,4 +180,5 @@ JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_step(JNIEnv * env, jobjec
JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * env, jobject obj)
{
background = 1.0f - background;
-} \ No newline at end of file
+}
+
diff --git a/opengl/tools/glgen/specs/gles11/GLES20.spec b/opengl/tools/glgen/specs/gles11/GLES20.spec
index 61094d1..ee88f59 100644
--- a/opengl/tools/glgen/specs/gles11/GLES20.spec
+++ b/opengl/tools/glgen/specs/gles11/GLES20.spec
@@ -39,6 +39,7 @@ void glDetachShader ( GLuint program, GLuint shader )
void glDisable ( GLenum cap )
void glDisableVertexAttribArray ( GLuint index )
void glDrawArrays ( GLenum mode, GLint first, GLsizei count )
+void glDrawElements ( GLenum mode, GLsizei count, GLenum type, GLint offset )
void glDrawElements ( GLenum mode, GLsizei count, GLenum type, const GLvoid *indices )
void glEnable ( GLenum cap )
void glEnableVertexAttribArray ( GLuint index )
@@ -138,5 +139,6 @@ void glVertexAttrib3f ( GLuint indx, GLfloat x, GLfloat y, GLfloat z )
void glVertexAttrib3fv ( GLuint indx, const GLfloat *values )
void glVertexAttrib4f ( GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w )
void glVertexAttrib4fv ( GLuint indx, const GLfloat *values )
+void glVertexAttribPointer ( GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLint offset )
void glVertexAttribPointer ( GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr )
-void glViewport ( GLint x, GLint y, GLsizei width, GLsizei height ) \ No newline at end of file
+void glViewport ( GLint x, GLint y, GLsizei width, GLsizei height )
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index ebaca90..9d8c5a0 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -695,7 +695,7 @@ public class JniCodeEmitter {
boolean isPointerFunc = isPointerFunc(jfunc);
boolean isVBOPointerFunc = (outName.endsWith("Pointer") ||
outName.endsWith("PointerOES") ||
- outName.endsWith("DrawElements")) &&
+ outName.endsWith("DrawElements") || outName.endsWith("VertexAttribPointer")) &&
!jfunc.getCFunc().hasPointerArg();
if (isPointerFunc) {
outName += "Bounds";
diff --git a/libs/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 86eb78d..a14bfb5 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
BlurFilter.cpp.arm \
+ GLExtensions.cpp \
Layer.cpp \
LayerBase.cpp \
LayerBuffer.cpp \
@@ -13,14 +14,14 @@ LOCAL_SRC_FILES:= \
LayerDim.cpp \
MessageQueue.cpp \
SurfaceFlinger.cpp \
- Tokenizer.cpp \
+ TextureManager.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
+ifeq ($(TARGET_BOARD_PLATFORM), omap3)
+ LOCAL_CFLAGS += -DNO_RGBX_8888
endif
# need "-lrt" on Linux simulator to pick up clock_gettime
diff --git a/libs/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
index e2bcf6a..6f8507e 100644
--- a/libs/surfaceflinger/Barrier.h
+++ b/services/surfaceflinger/Barrier.h
@@ -29,10 +29,6 @@ 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();
diff --git a/libs/surfaceflinger/BlurFilter.cpp b/services/surfaceflinger/BlurFilter.cpp
index 1ffbd5b..1ffbd5b 100644
--- a/libs/surfaceflinger/BlurFilter.cpp
+++ b/services/surfaceflinger/BlurFilter.cpp
diff --git a/libs/surfaceflinger/BlurFilter.h b/services/surfaceflinger/BlurFilter.h
index 294db43..294db43 100644
--- a/libs/surfaceflinger/BlurFilter.h
+++ b/services/surfaceflinger/BlurFilter.h
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index ea68352..2eac0a8 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -40,6 +40,8 @@
#include <hardware/overlay.h>
#include <hardware/gralloc.h>
+#include "GLExtensions.h"
+
using namespace android;
@@ -73,7 +75,8 @@ void checkEGLErrors(const char* token)
DisplayHardware::DisplayHardware(
const sp<SurfaceFlinger>& flinger,
uint32_t dpy)
- : DisplayHardwareBase(flinger, dpy)
+ : DisplayHardwareBase(flinger, dpy),
+ mFlags(0)
{
init(dpy);
}
@@ -97,6 +100,9 @@ void DisplayHardware::init(uint32_t dpy)
{
mNativeWindow = new FramebufferNativeWindow();
framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+ mDpiX = mNativeWindow->xdpi;
+ mDpiY = mNativeWindow->ydpi;
+ mRefreshRate = fbDev->fps;
mOverlayEngine = NULL;
hw_module_t const* module;
@@ -104,6 +110,11 @@ void DisplayHardware::init(uint32_t dpy)
overlay_control_open(module, &mOverlayEngine);
}
+ EGLint w, h, dummy;
+ EGLint numConfigs=0;
+ EGLSurface surface;
+ EGLContext context;
+
// initialize EGL
EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
@@ -121,12 +132,6 @@ void DisplayHardware::init(uint32_t dpy)
}
}
- EGLint w, h, dummy;
- EGLint numConfigs=0;
- EGLSurface surface;
- EGLContext context;
- mFlags = CACHED_BUFFERS;
-
// TODO: all the extensions below should be queried through
// eglGetProcAddress().
@@ -145,22 +150,6 @@ void DisplayHardware::init(uint32_t dpy)
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;
}
@@ -175,6 +164,8 @@ void DisplayHardware::init(uint32_t dpy)
*/
surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
+ eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
+ eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
if (mFlags & PARTIAL_UPDATES) {
// if we have partial updates, we definitely don't need to
@@ -188,31 +179,6 @@ void DisplayHardware::init(uint32_t dpy)
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.
@@ -235,61 +201,67 @@ void DisplayHardware::init(uint32_t dpy)
context = eglCreateContext(display, config, NULL, NULL);
+ mDisplay = display;
+ mConfig = config;
+ mSurface = surface;
+ mContext = context;
+ mFormat = fbDev->format;
+ mPageFlipCount = 0;
+
/*
* 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);
+
+ GLExtensions& extensions(GLExtensions::getInstance());
+ extensions.initWithGLStrings(
+ glGetString(GL_VENDOR),
+ glGetString(GL_RENDERER),
+ glGetString(GL_VERSION),
+ glGetString(GL_EXTENSIONS),
+ eglQueryString(display, EGL_VENDOR),
+ eglQueryString(display, EGL_VERSION),
+ eglQueryString(display, EGL_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;
+#ifdef EGL_ANDROID_swap_rectangle
+ if (extensions.hasExtension("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;
+ }
}
-#else
-#warning "EGL_ANDROID_image_native_buffer not supported"
+ // 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("EGL informations:");
+ LOGI("# of configs : %d", numConfigs);
+ LOGI("vendor : %s", extensions.getEglVendor());
+ LOGI("version : %s", extensions.getEglVersion());
+ LOGI("extensions: %s", extensions.getEglExtension());
+ LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
+ LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+
+ LOGI("OpenGL informations:");
+ LOGI("vendor : %s", extensions.getVendor());
+ LOGI("renderer : %s", extensions.getRenderer());
+ LOGI("version : %s", extensions.getVersion());
+ LOGI("extensions: %s", extensions.getExtension());
+ LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
+ LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
+ LOGI("flags = %08x", mFlags);
// 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;
}
/*
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index df046af..66bf521 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -29,6 +29,8 @@
#include <pixelflinger/pixelflinger.h>
+#include "GLExtensions.h"
+
#include "DisplayHardware/DisplayHardwareBase.h"
struct overlay_control_device_t;
@@ -43,15 +45,11 @@ 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
+ COPY_BITS_EXTENSION = 0x00000008,
+ BUFFER_PRESERVED = 0x00010000,
+ PARTIAL_UPDATES = 0x00020000, // video driver feature
+ SLOW_CONFIG = 0x00040000, // software
+ SWAP_RECTANGLE = 0x00080000,
};
DisplayHardware(
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index 1d09f84..1d09f84 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
index 8369bb8..8369bb8 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
new file mode 100644
index 0000000..7f4f9fc
--- /dev/null
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 <stdio.h>
+#include <stdint.h>
+
+#include "GLExtensions.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions )
+
+GLExtensions::GLExtensions()
+ : mHaveTextureExternal(false),
+ mHaveNpot(false),
+ mHaveDirectTexture(false)
+{
+}
+
+void GLExtensions::initWithGLStrings(
+ GLubyte const* vendor,
+ GLubyte const* renderer,
+ GLubyte const* version,
+ GLubyte const* extensions,
+ char const* egl_vendor,
+ char const* egl_version,
+ char const* egl_extensions)
+{
+ mVendor = (char const*)vendor;
+ mRenderer = (char const*)renderer;
+ mVersion = (char const*)version;
+ mExtensions = (char const*)extensions;
+ mEglVendor = egl_vendor;
+ mEglVersion = egl_version;
+ mEglExtensions = egl_extensions;
+
+ char const* curr = (char const*)extensions;
+ char const* head = curr;
+ do {
+ head = strchr(curr, ' ');
+ String8 s(curr, head ? head-curr : strlen(curr));
+ if (s.length()) {
+ mExtensionList.add(s);
+ }
+ curr = head+1;
+ } while (head);
+
+ curr = egl_extensions;
+ head = curr;
+ do {
+ head = strchr(curr, ' ');
+ String8 s(curr, head ? head-curr : strlen(curr));
+ if (s.length()) {
+ mExtensionList.add(s);
+ }
+ curr = head+1;
+ } while (head);
+
+#ifdef EGL_ANDROID_image_native_buffer
+ if (hasExtension("GL_OES_EGL_image") &&
+ (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) &&
+ hasExtension("EGL_ANDROID_image_native_buffer"))
+ {
+ mHaveDirectTexture = true;
+ }
+#else
+#warning "EGL_ANDROID_image_native_buffer not supported"
+#endif
+
+ if (hasExtension("GL_ARB_texture_non_power_of_two")) {
+ mHaveNpot = true;
+ }
+
+ if (hasExtension("GL_OES_texture_external")) {
+ mHaveTextureExternal = true;
+ } else if (strstr(mRenderer.string(), "Adreno")) {
+ // hack for Adreno 200
+ mHaveTextureExternal = true;
+ }
+}
+
+bool GLExtensions::hasExtension(char const* extension) const
+{
+ const String8 s(extension);
+ return mExtensionList.indexOf(s) >= 0;
+}
+
+char const* GLExtensions::getVendor() const {
+ return mVendor.string();
+}
+
+char const* GLExtensions::getRenderer() const {
+ return mRenderer.string();
+}
+
+char const* GLExtensions::getVersion() const {
+ return mVersion.string();
+}
+
+char const* GLExtensions::getExtension() const {
+ return mExtensions.string();
+}
+
+char const* GLExtensions::getEglVendor() const {
+ return mEglVendor.string();
+}
+
+char const* GLExtensions::getEglVersion() const {
+ return mEglVersion.string();
+}
+
+char const* GLExtensions::getEglExtension() const {
+ return mEglExtensions.string();
+}
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
new file mode 100644
index 0000000..bbb284e
--- /dev/null
+++ b/services/surfaceflinger/GLExtensions.h
@@ -0,0 +1,94 @@
+/*
+ * 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 ANDROID_SF_GLEXTENSION_H
+#define ANDROID_SF_GLEXTENSION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+#include <utils/Singleton.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class GLExtensions : public Singleton<GLExtensions>
+{
+ friend class Singleton<GLExtensions>;
+
+ bool mHaveTextureExternal : 1;
+ bool mHaveNpot : 1;
+ bool mHaveDirectTexture : 1;
+
+ String8 mVendor;
+ String8 mRenderer;
+ String8 mVersion;
+ String8 mExtensions;
+ String8 mEglVendor;
+ String8 mEglVersion;
+ String8 mEglExtensions;
+ SortedVector<String8> mExtensionList;
+
+ GLExtensions(const GLExtensions&);
+ GLExtensions& operator = (const GLExtensions&);
+
+protected:
+ GLExtensions();
+
+public:
+ inline bool haveTextureExternal() const {
+ return mHaveTextureExternal;
+ }
+ inline bool haveNpot() const {
+ return mHaveNpot;
+ }
+ inline bool haveDirectTexture() const {
+ return mHaveDirectTexture;
+ }
+
+ void initWithGLStrings(
+ GLubyte const* vendor,
+ GLubyte const* renderer,
+ GLubyte const* version,
+ GLubyte const* extensions,
+ char const* egl_vendor,
+ char const* egl_version,
+ char const* egl_extensions);
+
+ char const* getVendor() const;
+ char const* getRenderer() const;
+ char const* getVersion() const;
+ char const* getExtension() const;
+
+ char const* getEglVendor() const;
+ char const* getEglVersion() const;
+ char const* getEglExtension() const;
+
+ bool hasExtension(char const* extension) const;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
new file mode 100644
index 0000000..629d993
--- /dev/null
+++ b/services/surfaceflinger/Layer.cpp
@@ -0,0 +1,859 @@
+/*
+ * 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 "GLExtensions.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;
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::Layer(SurfaceFlinger* flinger,
+ DisplayID display, const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client),
+ mGLExtensions(GLExtensions::getInstance()),
+ mNeedsBlending(true),
+ mNeedsDithering(false),
+ mSecure(false),
+ mTextureManager(),
+ mBufferManager(mTextureManager),
+ mWidth(0), mHeight(0), mFixedSize(false)
+{
+}
+
+Layer::~Layer()
+{
+ // FIXME: must be called from the main UI thread
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ mBufferManager.destroy(dpy);
+
+ // we can use getUserClientUnsafe here because we know we're
+ // single-threaded at that point.
+ sp<UserClient> ourClient(mUserClientRef.getUserClientUnsafe());
+ if (ourClient != 0) {
+ ourClient->detachLayer(this);
+ }
+}
+
+status_t Layer::setToken(const sp<UserClient>& userClient,
+ SharedClient* sharedClient, int32_t token)
+{
+ sp<SharedBufferServer> lcblk = new SharedBufferServer(
+ sharedClient, token, mBufferManager.getDefaultBufferCount(),
+ getIdentity());
+
+ status_t err = mUserClientRef.setToken(userClient, lcblk, token);
+
+ LOGE_IF(err != NO_ERROR,
+ "ClientRef::setToken(%p, %p, %u) failed",
+ userClient.get(), lcblk.get(), token);
+
+ if (err == NO_ERROR) {
+ // we need to free the buffers associated with this surface
+ }
+
+ return err;
+}
+
+int32_t Layer::getToken() const
+{
+ return mUserClientRef.getToken();
+}
+
+sp<UserClient> Layer::getClient() const
+{
+ return mUserClientRef.getClient();
+}
+
+// called with SurfaceFlinger::mStateLock as soon as the layer is entered
+// in the purgatory list
+void Layer::onRemoved()
+{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ // wake up the condition
+ lcblk->setStatus(NO_INIT);
+ }
+}
+
+sp<LayerBaseClient::Surface> Layer::createSurface() const
+{
+ return mSurface;
+}
+
+status_t Layer::ditch()
+{
+ // NOTE: Called from the main UI thread
+
+ // the layer is not on screen anymore. free as much resources as possible
+ mFreezeLock.clear();
+
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ mBufferManager.destroy(dpy);
+ mSurface.clear();
+
+ Mutex::Autolock _l(mLock);
+ mWidth = mHeight = 0;
+ 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;
+ mReqFormat = format;
+ mWidth = w;
+ mHeight = h;
+ mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
+ mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
+
+ // we use the red index
+ int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED);
+ int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);
+ mNeedsDithering = layerRedsize > displayRedSize;
+
+ mSurface = new SurfaceLayer(mFlinger, this);
+ return NO_ERROR;
+}
+
+void Layer::reloadTexture(const Region& dirty)
+{
+ sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
+ 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;
+ }
+
+ if (mGLExtensions.haveDirectTexture()) {
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) {
+ // not sure what we can do here...
+ goto slowpath;
+ }
+ } else {
+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) {
+ mBufferManager.loadTexture(dirty, t);
+ buffer->unlock();
+ }
+ }
+}
+
+void Layer::onDraw(const Region& clip) const
+{
+ Texture tex(mBufferManager.getActiveTexture());
+ if (tex.name == -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, 0, 0, 0, 1);
+ }
+ return;
+ }
+ drawWithOpenGL(clip, tex);
+}
+
+bool Layer::needsFiltering() const
+{
+ if (!(mFlags & DisplayHardware::SLOW_CONFIG)) {
+ // NOTE: there is a race here, because mFixedSize is updated in a
+ // binder transaction. however, it doesn't really matter since it is
+ // evaluated each time we draw. To be perfectly correct, this flag
+ // would have to be associated with a buffer.
+ if (mFixedSize)
+ return true;
+ }
+ return LayerBase::needsFiltering();
+}
+
+
+status_t Layer::setBufferCount(int bufferCount)
+{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
+ // oops, the client is already gone
+ return DEAD_OBJECT;
+ }
+
+ // NOTE: lcblk->resize() is protected by an internal lock
+ status_t err = lcblk->resize(bufferCount);
+ if (err == NO_ERROR)
+ mBufferManager.resize(bufferCount);
+
+ return err;
+}
+
+sp<GraphicBuffer> Layer::requestBuffer(int index,
+ uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat,
+ uint32_t usage)
+{
+ sp<GraphicBuffer> buffer;
+
+ if (int32_t(reqWidth | reqHeight | reqFormat) < 0)
+ return buffer;
+
+ if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight))
+ return buffer;
+
+ // this ensures our client doesn't go away while we're accessing
+ // the shared area.
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
+ // 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.
+ */
+
+ status_t err = NO_ERROR;
+ uint32_t w, h, f;
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight);
+ const bool formatChanged = mReqFormat != reqFormat;
+ mReqWidth = reqWidth;
+ mReqHeight = reqHeight;
+ mReqFormat = reqFormat;
+ mFixedSize = reqWidth && reqHeight;
+ w = reqWidth ? reqWidth : mWidth;
+ h = reqHeight ? reqHeight : mHeight;
+ f = reqFormat ? reqFormat : mFormat;
+ if (fixedSizeChanged || formatChanged) {
+ lcblk->reallocateAllExcept(index);
+ }
+ }
+
+ // here we have to reallocate a new buffer because the buffer could be
+ // used as the front buffer, or by a client in our process
+ // (eg: status bar), and we can't release the handle under its feet.
+ const uint32_t effectiveUsage = getEffectiveUsage(usage);
+ buffer = new GraphicBuffer(w, h, f, 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);
+ mBufferManager.attachBuffer(index, buffer);
+ }
+ 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.
+ // 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());
+
+ const bool sizeChanged = (front.requested_w != temp.requested_w) ||
+ (front.requested_h != temp.requested_h);
+
+ if (sizeChanged) {
+ // 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)",
+ this,
+ int(temp.requested_w), int(temp.requested_h),
+ int(front.requested_w), int(front.requested_h));
+
+ if (!isFixedSize()) {
+ // 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.
+ setBufferSize(temp.requested_w, temp.requested_h);
+
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ // all buffers need reallocation
+ lcblk->reallocateAll();
+ }
+ } else {
+ // record the new size
+ setBufferSize(temp.requested_w, temp.requested_h);
+ }
+ }
+
+ 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::setBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mLock);
+ mWidth = w;
+ mHeight = h;
+}
+
+bool Layer::isFixedSize() const {
+ Mutex::Autolock _l(mLock);
+ return mFixedSize;
+}
+
+// ----------------------------------------------------------------------------
+// pageflip handling...
+// ----------------------------------------------------------------------------
+
+void Layer::lockPageFlip(bool& recomputeVisibleRegions)
+{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (!lcblk) {
+ // client died
+ recomputeVisibleRegions = true;
+ return;
+ }
+
+ ssize_t buf = lcblk->retireAndLock();
+ if (buf == NOT_ENOUGH_DATA) {
+ // NOTE: This is not an error, it simply means there is nothing to
+ // retire. The buffer is locked because we will use it
+ // for composition later in the loop
+ return;
+ }
+
+ if (buf < NO_ERROR) {
+ LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
+ mPostedDirtyRegion.clear();
+ return;
+ }
+
+ // we retired a buffer, which becomes the new front buffer
+ if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) {
+ LOGE("retireAndLock() buffer index (%d) out of range", int(buf));
+ mPostedDirtyRegion.clear();
+ return;
+ }
+
+ // 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();
+ }
+
+ /* a buffer was posted, so we need to call reloadTexture(), which
+ * will update our internal data structures (eg: EGLImageKHR or
+ * texture names). we need to do this even if mPostedDirtyRegion is
+ * empty -- it's orthogonal to the fact that a new buffer was posted,
+ * for instance, a degenerate case could be that the user did an empty
+ * update but repainted the buffer with appropriate content (after a
+ * resize for instance).
+ */
+ 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 therefore never release it)
+ mFreezeLock.clear();
+ }
+}
+
+void Layer::finishPageFlip()
+{
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ if (lcblk) {
+ int buf = mBufferManager.getActiveBufferIndex();
+ if (buf >= 0) {
+ status_t err = lcblk->unlock( buf );
+ LOGE_IF(err!=NO_ERROR,
+ "layer %p, buffer=%d wasn't locked!",
+ this, buf);
+ }
+ }
+}
+
+
+void Layer::dump(String8& result, char* buffer, size_t SIZE) const
+{
+ LayerBaseClient::dump(result, buffer, SIZE);
+
+ ClientRef::Access sharedClient(mUserClientRef);
+ SharedBufferServer* lcblk(sharedClient.get());
+ uint32_t totalTime = 0;
+ if (lcblk) {
+ SharedBufferStack::Statistics stats = lcblk->getStats();
+ totalTime= stats.totalTime;
+ result.append( lcblk->dump(" ") );
+ }
+
+ sp<const GraphicBuffer> buf0(getBuffer(0));
+ sp<const GraphicBuffer> buf1(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",
+ mFormat, w0, h0, s0, w1, h1, s1,
+ getFreezeLock().get(), totalTime);
+
+ result.append(buffer);
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::ClientRef::ClientRef()
+ : mControlBlock(0), mToken(-1) {
+}
+
+Layer::ClientRef::~ClientRef() {
+}
+
+int32_t Layer::ClientRef::getToken() const {
+ Mutex::Autolock _l(mLock);
+ return mToken;
+}
+
+sp<UserClient> Layer::ClientRef::getClient() const {
+ Mutex::Autolock _l(mLock);
+ return mUserClient.promote();
+}
+
+status_t Layer::ClientRef::setToken(const sp<UserClient>& uc,
+ const sp<SharedBufferServer>& sharedClient, int32_t token) {
+ Mutex::Autolock _l(mLock);
+
+ { // scope for strong mUserClient reference
+ sp<UserClient> userClient(mUserClient.promote());
+ if (mUserClient != 0 && mControlBlock != 0) {
+ mControlBlock->setStatus(NO_INIT);
+ }
+ }
+
+ mUserClient = uc;
+ mToken = token;
+ mControlBlock = sharedClient;
+ return NO_ERROR;
+}
+
+sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const {
+ return mUserClient.promote();
+}
+
+// this class gives us access to SharedBufferServer safely
+// it makes sure the UserClient (and its associated shared memory)
+// won't go away while we're accessing it.
+Layer::ClientRef::Access::Access(const ClientRef& ref)
+ : mControlBlock(0)
+{
+ Mutex::Autolock _l(ref.mLock);
+ mUserClientStrongRef = ref.mUserClient.promote();
+ if (mUserClientStrongRef != 0)
+ mControlBlock = ref.mControlBlock;
+}
+
+Layer::ClientRef::Access::~Access()
+{
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::BufferManager::BufferManager(TextureManager& tm)
+ : mNumBuffers(NUM_BUFFERS), mTextureManager(tm),
+ mActiveBuffer(-1), mFailover(false)
+{
+}
+
+Layer::BufferManager::~BufferManager()
+{
+}
+
+status_t Layer::BufferManager::resize(size_t size)
+{
+ Mutex::Autolock _l(mLock);
+ mNumBuffers = size;
+ return NO_ERROR;
+}
+
+// only for debugging
+sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const {
+ return mBufferData[index].buffer;
+}
+
+status_t Layer::BufferManager::setActiveBufferIndex(size_t index) {
+ mActiveBuffer = index;
+ return NO_ERROR;
+}
+
+size_t Layer::BufferManager::getActiveBufferIndex() const {
+ return mActiveBuffer;
+}
+
+Texture Layer::BufferManager::getActiveTexture() const {
+ Texture res;
+ if (mFailover || mActiveBuffer<0) {
+ res = mFailoverTexture;
+ } else {
+ static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture;
+ }
+ return res;
+}
+
+sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const {
+ sp<GraphicBuffer> result;
+ const ssize_t activeBuffer = mActiveBuffer;
+ if (activeBuffer >= 0) {
+ BufferData const * const buffers = mBufferData;
+ Mutex::Autolock _l(mLock);
+ result = buffers[activeBuffer].buffer;
+ }
+ return result;
+}
+
+sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index)
+{
+ BufferData* const buffers = mBufferData;
+ sp<GraphicBuffer> buffer;
+ Mutex::Autolock _l(mLock);
+ buffer = buffers[index].buffer;
+ buffers[index].buffer = 0;
+ return buffer;
+}
+
+status_t Layer::BufferManager::attachBuffer(size_t index,
+ const sp<GraphicBuffer>& buffer)
+{
+ BufferData* const buffers = mBufferData;
+ Mutex::Autolock _l(mLock);
+ buffers[index].buffer = buffer;
+ buffers[index].texture.dirty = true;
+ return NO_ERROR;
+}
+
+status_t Layer::BufferManager::destroy(EGLDisplay dpy)
+{
+ BufferData* const buffers = mBufferData;
+ size_t num;
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ num = mNumBuffers;
+ for (size_t i=0 ; i<num ; i++) {
+ buffers[i].buffer = 0;
+ }
+ }
+ for (size_t i=0 ; i<num ; i++) {
+ destroyTexture(&buffers[i].texture, dpy);
+ }
+ destroyTexture(&mFailoverTexture, dpy);
+ return NO_ERROR;
+}
+
+status_t Layer::BufferManager::initEglImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& buffer)
+{
+ status_t err = NO_INIT;
+ ssize_t index = mActiveBuffer;
+ if (index >= 0) {
+ if (!mFailover) {
+ Image& texture(mBufferData[index].texture);
+ err = mTextureManager.initEglImage(&texture, dpy, buffer);
+ // if EGLImage fails, we switch to regular texture mode, and we
+ // free all resources associated with using EGLImages.
+ if (err == NO_ERROR) {
+ mFailover = false;
+ destroyTexture(&mFailoverTexture, dpy);
+ } else {
+ mFailover = true;
+ const size_t num = mNumBuffers;
+ for (size_t i=0 ; i<num ; i++) {
+ destroyTexture(&mBufferData[i].texture, dpy);
+ }
+ }
+ } else {
+ // we failed once, don't try again
+ err = BAD_VALUE;
+ }
+ }
+ return err;
+}
+
+status_t Layer::BufferManager::loadTexture(
+ const Region& dirty, const GGLSurface& t)
+{
+ return mTextureManager.loadTexture(&mFailoverTexture, dirty, t);
+}
+
+status_t Layer::BufferManager::destroyTexture(Image* tex, EGLDisplay dpy)
+{
+ if (tex->name != -1U) {
+ glDeleteTextures(1, &tex->name);
+ tex->name = -1U;
+ }
+ if (tex->image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, tex->image);
+ tex->image = EGL_NO_IMAGE_KHR;
+ }
+ return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
+ const sp<Layer>& owner)
+ : Surface(flinger, owner->getIdentity(), owner)
+{
+}
+
+Layer::SurfaceLayer::~SurfaceLayer()
+{
+}
+
+sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
+{
+ sp<GraphicBuffer> buffer;
+ sp<Layer> owner(getOwner());
+ if (owner != 0) {
+ /*
+ * requestBuffer() cannot be called from the main thread
+ * as it could cause a dead-lock, since it may have to wait
+ * on conditions updated my the main thread.
+ */
+ buffer = owner->requestBuffer(index, w, h, format, usage);
+ }
+ return buffer;
+}
+
+status_t Layer::SurfaceLayer::setBufferCount(int bufferCount)
+{
+ status_t err = DEAD_OBJECT;
+ sp<Layer> owner(getOwner());
+ if (owner != 0) {
+ /*
+ * setBufferCount() cannot be called from the main thread
+ * as it could cause a dead-lock, since it may have to wait
+ * on conditions updated my the main thread.
+ */
+ err = owner->setBufferCount(bufferCount);
+ }
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+
+
+}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
new file mode 100644
index 0000000..e1d283b
--- /dev/null
+++ b/services/surfaceflinger/Layer.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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"
+#include "TextureManager.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class FreezeLock;
+class Client;
+class GLExtensions;
+class UserClient;
+
+// ---------------------------------------------------------------------------
+
+class Layer : public LayerBaseClient
+{
+public:
+ Layer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client);
+
+ virtual ~Layer();
+
+ virtual const char* getTypeId() const { return "Layer"; }
+
+ // the this layer's size and format
+ status_t setBuffers(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags=0);
+
+ // associate a UserClient to this Layer
+ status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx);
+ int32_t getToken() const;
+ sp<UserClient> getClient() const;
+
+ // Set this Layer's buffers size
+ void setBufferSize(uint32_t w, uint32_t h);
+ bool isFixedSize() const;
+
+ // LayerBase interface
+ 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 needsFiltering() const;
+ virtual bool isSecure() const { return mSecure; }
+ virtual sp<Surface> createSurface() const;
+ virtual status_t ditch();
+ virtual void onRemoved();
+
+ // only for debugging
+ inline sp<GraphicBuffer> getBuffer(int i) const {
+ return mBufferManager.getBuffer(i); }
+ // only for debugging
+ inline const sp<FreezeLock>& getFreezeLock() const {
+ return mFreezeLock; }
+
+protected:
+ virtual void dump(String8& result, char* scratch, size_t size) const;
+
+private:
+ void reloadTexture(const Region& dirty);
+ uint32_t getEffectiveUsage(uint32_t usage) const;
+ sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ status_t setBufferCount(int bufferCount);
+
+ // -----------------------------------------------------------------------
+
+ class SurfaceLayer : public LayerBaseClient::Surface {
+ public:
+ SurfaceLayer(const sp<SurfaceFlinger>& flinger, const sp<Layer>& owner);
+ ~SurfaceLayer();
+ private:
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ virtual status_t setBufferCount(int bufferCount);
+ sp<Layer> getOwner() const {
+ return static_cast<Layer*>(Surface::getOwner().get());
+ }
+ };
+ friend class SurfaceLayer;
+
+ // -----------------------------------------------------------------------
+
+ class ClientRef {
+ ClientRef(const ClientRef& rhs);
+ ClientRef& operator = (const ClientRef& rhs);
+ mutable Mutex mLock;
+ // binder thread, page-flip thread
+ sp<SharedBufferServer> mControlBlock;
+ wp<UserClient> mUserClient;
+ int32_t mToken;
+ public:
+ ClientRef();
+ ~ClientRef();
+ int32_t getToken() const;
+ sp<UserClient> getClient() const;
+ status_t setToken(const sp<UserClient>& uc,
+ const sp<SharedBufferServer>& sharedClient, int32_t token);
+ sp<UserClient> getUserClientUnsafe() const;
+ class Access {
+ Access(const Access& rhs);
+ Access& operator = (const Access& rhs);
+ sp<UserClient> mUserClientStrongRef;
+ sp<SharedBufferServer> mControlBlock;
+ public:
+ Access(const ClientRef& ref);
+ ~Access();
+ inline SharedBufferServer* get() const { return mControlBlock.get(); }
+ };
+ friend class Access;
+ };
+
+ // -----------------------------------------------------------------------
+
+ class BufferManager {
+ static const size_t NUM_BUFFERS = 2;
+ struct BufferData {
+ sp<GraphicBuffer> buffer;
+ Image texture;
+ };
+ // this lock protect mBufferData[].buffer but since there
+ // is very little contention, we have only one like for
+ // the whole array, we also use it to protect mNumBuffers.
+ mutable Mutex mLock;
+ BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX];
+ size_t mNumBuffers;
+ Texture mFailoverTexture;
+ TextureManager& mTextureManager;
+ ssize_t mActiveBuffer;
+ bool mFailover;
+ static status_t destroyTexture(Image* tex, EGLDisplay dpy);
+
+ public:
+ static size_t getDefaultBufferCount() { return NUM_BUFFERS; }
+ BufferManager(TextureManager& tm);
+ ~BufferManager();
+
+ // detach/attach buffer from/to given index
+ sp<GraphicBuffer> detachBuffer(size_t index);
+ status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
+ // resize the number of active buffers
+ status_t resize(size_t size);
+
+ // ----------------------------------------------
+ // must be called from GL thread
+
+ // set/get active buffer index
+ status_t setActiveBufferIndex(size_t index);
+ size_t getActiveBufferIndex() const;
+ // return the active buffer
+ sp<GraphicBuffer> getActiveBuffer() const;
+ // return the active texture (or fail-over)
+ Texture getActiveTexture() const;
+ // frees resources associated with all buffers
+ status_t destroy(EGLDisplay dpy);
+ // load bitmap data into the active buffer
+ status_t loadTexture(const Region& dirty, const GGLSurface& t);
+ // make active buffer an EGLImage if needed
+ status_t initEglImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& buffer);
+
+ // ----------------------------------------------
+ // only for debugging
+ sp<GraphicBuffer> getBuffer(size_t index) const;
+ };
+
+ // -----------------------------------------------------------------------
+
+ // thread-safe
+ ClientRef mUserClientRef;
+
+ // constants
+ sp<Surface> mSurface;
+ PixelFormat mFormat;
+ const GLExtensions& mGLExtensions;
+ bool mNeedsBlending;
+ bool mNeedsDithering;
+
+ // page-flip thread (currently main thread)
+ bool mSecure;
+ Region mPostedDirtyRegion;
+
+ // page-flip thread and transaction thread (currently main thread)
+ sp<FreezeLock> mFreezeLock;
+
+ // see threading usage in declaration
+ TextureManager mTextureManager;
+ BufferManager mBufferManager;
+
+ // binder thread, transaction thread
+ mutable Mutex mLock;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mReqWidth;
+ uint32_t mReqHeight;
+ uint32_t mReqFormat;
+ bool mFixedSize;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_H
diff --git a/libs/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index a8b735e..d5aa53f 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -32,29 +32,21 @@
#include "LayerBase.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "TextureManager.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),
+ mNeedsFiltering(false),
mOrientation(0),
mLeft(0), mTop(0),
mTransactionFlags(0),
- mPremultipliedAlpha(true), mDebug(false),
+ mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
mInvalidate(0)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
@@ -159,7 +151,6 @@ bool LayerBase::setAlpha(uint8_t alpha) {
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);
@@ -167,7 +158,6 @@ bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) {
return true;
}
bool LayerBase::setTransparentRegionHint(const Region& transparent) {
- // TODO: check the region has changed
mCurrentState.sequence++;
mCurrentState.transparentRegion = transparent;
requestTransaction();
@@ -221,13 +211,12 @@ uint32_t LayerBase::doTransaction(uint32_t flags)
flags |= eVisibleRegion;
this->contentDirty = true;
- const bool linearFiltering = mUseLinearFiltering;
- mUseLinearFiltering = false;
+ mNeedsFiltering = 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;
+ mNeedsFiltering = true;
}
}
}
@@ -267,7 +256,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform)
// cache a few things...
mOrientation = tr.getOrientation();
mTransformedBounds = tr.makeBounds(w, h);
- mTransformed = transformed;
mLeft = tr.tx();
mTop = tr.ty();
}
@@ -336,45 +324,25 @@ void LayerBase::draw(const Region& inClip) const
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
+void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
+ GLclampf green, GLclampf blue,
+ GLclampf alpha) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t fbHeight = hw.getHeight();
- glColor4x(red,green,blue,alpha);
- glDisable(GL_TEXTURE_2D);
+ glColor4f(red,green,blue,alpha);
+
+ TextureManager::deactivateTextures();
+
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);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
while (it != end) {
const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
@@ -395,39 +363,25 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
const State& s(drawingState());
// bind our texture
- validateTexture(texture.name);
+ TextureManager::activateTexture(texture, needsFiltering());
uint32_t width = texture.width;
uint32_t height = texture.height;
-
- glEnable(GL_TEXTURE_2D);
+ GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
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;
+ const GLfloat alpha = s.alpha * (1.0f/255.0f);
+ if (mPremultipliedAlpha) {
+ glColor4f(alpha, alpha, alpha, alpha);
} else {
- env = GL_REPLACE;
- src = GL_SRC_ALPHA;
+ glColor4f(1, 1, 1, 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);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
} else {
+ glColor4f(1, 1, 1, 1);
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 {
@@ -437,13 +391,11 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
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 }
+ const GLfloat texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 1, 1 },
+ { 1, 0 }
};
glMatrixMode(GL_TEXTURE);
@@ -469,9 +421,15 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
glScalef(texture.wScale, texture.hScale, 1.0f);
}
+ if (needsDithering()) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
+ }
+
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glVertexPointer(2, GL_FIXED, 0, mVertices);
- glTexCoordPointer(2, GL_FIXED, 0, texCoords);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
while (it != end) {
const Rect& r = *it++;
@@ -482,246 +440,40 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
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
+void LayerBase::dump(String8& result, char* buffer, size_t SIZE) 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;
+ const Layer::State& s(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",
+ getTypeId(), this, s.z, tx(), ty(), s.w, s.h,
+ needsBlending(), needsDithering(), contentDirty,
+ s.alpha, s.flags,
+ s.transform[0][0], s.transform[0][1],
+ s.transform[1][0], s.transform[1][1]);
+ result.append(buffer);
}
-
// ---------------------------------------------------------------------------
-int32_t LayerBaseClient::sIdentity = 0;
+int32_t LayerBaseClient::sIdentity = 1;
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- const sp<Client>& client, int32_t i)
- : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),
+ const sp<Client>& client)
+ : LayerBase(flinger, display), mClientRef(client),
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;
+ sp<Client> c(mClientRef.promote());
+ if (c != 0) {
+ c->detachLayer(this);
}
- return 0xFFFF0000 | mIndex;
}
sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
@@ -738,25 +490,31 @@ sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
{
- return new Surface(mFlinger, clientIndex(), mIdentity,
+ return new Surface(mFlinger, mIdentity,
const_cast<LayerBaseClient *>(this));
}
-// called with SurfaceFlinger::mStateLock as soon as the layer is entered
-// in the purgatory list
-void LayerBaseClient::onRemoved()
+void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const
{
- // wake up the condition
- lcblk->setStatus(NO_INIT);
+ LayerBase::dump(result, buffer, SIZE);
+
+ sp<Client> client(mClientRef.promote());
+ snprintf(buffer, SIZE,
+ " name=%s\n"
+ " client=%p, identity=%u\n",
+ getName().string(),
+ client.get(), getIdentity());
+
+ result.append(buffer);
}
// ---------------------------------------------------------------------------
LayerBaseClient::Surface::Surface(
const sp<SurfaceFlinger>& flinger,
- SurfaceID id, int identity,
+ int identity,
const sp<LayerBaseClient>& owner)
- : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner)
+ : mFlinger(flinger), mIdentity(identity), mOwner(owner)
{
}
@@ -799,11 +557,17 @@ status_t LayerBaseClient::Surface::onTransact(
return BnSurface::onTransact(code, data, reply, flags);
}
-sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage)
+sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
{
return NULL;
}
+status_t LayerBaseClient::Surface::setBufferCount(int bufferCount)
+{
+ return INVALID_OPERATION;
+}
+
status_t LayerBaseClient::Surface::registerBuffers(
const ISurface::BufferHeap& buffers)
{
diff --git a/libs/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 62ec839..4288cf7 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -29,7 +29,7 @@
#include <ui/Region.h>
#include <ui/Overlay.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
#include <private/surfaceflinger/SharedBufferStack.h>
#include <private/surfaceflinger/LayerState.h>
@@ -45,41 +45,17 @@ class DisplayHardware;
class Client;
class GraphicBuffer;
class GraphicPlane;
+class LayerBaseClient;
class SurfaceFlinger;
+class Texture;
// ---------------------------------------------------------------------------
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);
-
- LayerBase(SurfaceFlinger* flinger, DisplayID display);
-
DisplayID dpy;
mutable bool contentDirty;
Region visibleRegionScreen;
@@ -125,6 +101,10 @@ public:
void invalidate();
+ virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
+
+ virtual const char* getTypeId() const { return "LayerBase"; }
+
/**
* draw - performs some global clipping optimizations
* and calls onDraw().
@@ -199,9 +179,9 @@ public:
virtual bool needsDithering() const { return false; }
/**
- * transformed -- true is this surface needs a to be transformed
+ * needsLinearFiltering - true if this surface needs filtering
*/
- virtual bool transformed() const { return mTransformed; }
+ virtual bool needsFiltering() const { return mNeedsFiltering; }
/**
* isSecure - true if this surface is secure, that is if it prevents
@@ -217,7 +197,10 @@ public:
* current list */
virtual void onRemoved() { };
-
+ /** always call base class first */
+ virtual void dump(String8& result, char* scratch, size_t size) const;
+
+
enum { // flags for doTransaction()
eVisibleRegion = 0x00000002,
};
@@ -241,44 +224,18 @@ 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, GLclampf r, GLclampf g,
+ GLclampf b, GLclampf 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;
+ bool mNeedsFiltering;
int32_t mOrientation;
- GLfixed mVertices[4][2];
+ GLfloat mVertices[4][2];
Rect mTransformedBounds;
int mLeft;
int mTop;
@@ -303,7 +260,6 @@ protected:
private:
LayerBase(const LayerBase& rhs);
- void validateTexture(GLint textureName) const;
};
@@ -313,42 +269,25 @@ 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);
+ LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client);
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();
+ virtual sp<LayerBaseClient> getLayerBaseClient() const {
+ return const_cast<LayerBaseClient*>(this); }
+ virtual const char* getTypeId() const { return "LayerBaseClient"; }
+ uint32_t getIdentity() const { return mIdentity; }
- class Surface : public BnSurface
- {
+ 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,
+ Surface(const sp<SurfaceFlinger>& flinger, int identity,
const sp<LayerBaseClient>& owner);
virtual ~Surface();
virtual status_t onTransact(uint32_t code, const Parcel& data,
@@ -356,7 +295,10 @@ public:
sp<LayerBaseClient> getOwner() const;
private:
- virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage);
+ virtual status_t setBufferCount(int bufferCount);
+
virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
@@ -366,20 +308,22 @@ public:
protected:
friend class LayerBaseClient;
sp<SurfaceFlinger> mFlinger;
- int32_t mToken;
int32_t mIdentity;
wp<LayerBaseClient> mOwner;
};
friend class Surface;
+protected:
+ virtual void dump(String8& result, char* scratch, size_t size) const;
+
private:
- int32_t mIndex;
- mutable Mutex mLock;
- mutable wp<Surface> mClientSurface;
+ mutable Mutex mLock;
+ mutable wp<Surface> mClientSurface;
+ const wp<Client> mClientRef;
// only read
- const uint32_t mIdentity;
- static int32_t sIdentity;
+ const uint32_t mIdentity;
+ static int32_t sIdentity;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 5fd7904..64a43c7 100644
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -33,14 +33,9 @@
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),
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client), mCacheDirty(true),
mRefreshCache(true), mCacheAge(0), mTextureName(-1U),
mWidthScale(1.0f), mHeightScale(1.0f),
mBlurFormat(GGL_PIXEL_FORMAT_RGB_565)
@@ -100,7 +95,9 @@ void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirty
mCacheDirty = false;
} else {
if (!mAutoRefreshPending) {
- mFlinger->signalDelayedEvent(ms2ns(500));
+ mFlinger->postMessageAsync(
+ new MessageBase(MessageQueue::INVALIDATE),
+ ms2ns(500));
mAutoRefreshPending = true;
}
}
@@ -149,6 +146,11 @@ void LayerBlur::onDraw(const Region& clip) const
Region::const_iterator it = clip.begin();
Region::const_iterator const end = clip.end();
if (it != end) {
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#endif
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mTextureName);
@@ -181,7 +183,7 @@ void LayerBlur::onDraw(const Region& clip) const
bl.data = (GGLubyte*)pixels;
blurFilter(&bl, 8, 2);
- if (mFlags & (DisplayHardware::NPOT_EXTENSION)) {
+ if (GLExtensions::getInstance().haveNpot()) {
glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0,
mReadFormat, mReadType, pixels);
mWidthScale = 1.0f / w;
@@ -206,8 +208,8 @@ void LayerBlur::onDraw(const Region& clip) const
const State& s = drawingState();
if (UNLIKELY(s.alpha < 0xFF)) {
- const GGLfixed alpha = (s.alpha << 16)/255;
- glColor4x(0, 0, 0, alpha);
+ const GLfloat alpha = s.alpha * (1.0f/255.0f);
+ glColor4f(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);
@@ -225,38 +227,20 @@ void LayerBlur::onDraw(const Region& clip) const
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);
- }
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glScalef(mWidthScale, mHeightScale, 1);
+ glTranslatef(-x, mYOffset - y, 0);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
+ glTexCoordPointer(2, GL_FLOAT, 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);
}
}
diff --git a/libs/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h
index 5b63dec..4c9ec64 100644
--- a/libs/surfaceflinger/LayerBlur.h
+++ b/services/surfaceflinger/LayerBlur.h
@@ -31,18 +31,14 @@ 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);
+ const sp<Client>& client);
virtual ~LayerBlur();
virtual void onDraw(const Region& clip) const;
virtual bool needsBlending() const { return true; }
virtual bool isSecure() const { return false; }
+ virtual const char* getTypeId() const { return "LayerBlur"; }
virtual uint32_t doTransaction(uint32_t flags);
virtual void setVisibleRegion(const Region& visibleRegion);
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5c21593..5f83636 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -39,15 +39,13 @@ 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),
+ const sp<Client>& client)
+ : LayerBaseClient(flinger, display, client),
mNeedsBlending(false), mBlitEngine(0)
{
}
@@ -62,8 +60,7 @@ LayerBuffer::~LayerBuffer()
void LayerBuffer::onFirstRef()
{
LayerBaseClient::onFirstRef();
- mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(),
- const_cast<LayerBuffer *>(this));
+ mSurface = new SurfaceLayerBuffer(mFlinger, this);
hw_module_t const* module = (hw_module_t const*)sGrallocModule;
if (!module) {
@@ -120,7 +117,7 @@ uint32_t LayerBuffer::doTransaction(uint32_t flags)
source->onTransaction(flags);
uint32_t res = LayerBase::doTransaction(flags);
// we always want filtering for these surfaces
- mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG);
+ mNeedsFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG);
return res;
}
@@ -145,14 +142,6 @@ void LayerBuffer::onDraw(const Region& clip) const
}
}
-bool LayerBuffer::transformed() const
-{
- sp<Source> source(getSource());
- if (LIKELY(source != 0))
- return source->transformed();
- return false;
-}
-
void LayerBuffer::serverDestroy()
{
sp<Source> source(clearSource());
@@ -214,9 +203,9 @@ sp<LayerBuffer::Source> LayerBuffer::clearSource() {
// 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(
+ const sp<SurfaceFlinger>& flinger, const sp<LayerBuffer>& owner)
+ : LayerBaseClient::Surface(flinger, owner->getIdentity(), owner)
{
}
@@ -321,16 +310,12 @@ 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)
+ : Source(layer), mStatus(NO_ERROR), mBufferSize(0)
{
if (buffers.heap == NULL) {
// this is allowed, but in this case, it is illegal to receive
@@ -388,11 +373,11 @@ LayerBuffer::BufferSource::~BufferSource()
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) );
+ getFlinger()->mEventQueue.postMessage(
+ new MessageDestroyTexture(getFlinger(), mTexture.name) );
}
if (mTexture.image != EGL_NO_IMAGE_KHR) {
- EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
eglDestroyImageKHR(dpy, mTexture.image);
}
}
@@ -444,11 +429,6 @@ void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer)
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());
@@ -462,35 +442,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
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) {
+ if (GLExtensions::getInstance().haveDirectTexture()) {
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
@@ -527,7 +482,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
t.format = src.img.format;
t.data = (GGLubyte*)src.img.base;
const Region dirty(Rect(t.width, t.height));
- mLayer.loadTexture(&mTexture, dirty, t);
+ mTextureManager.loadTexture(&mTexture, dirty, t);
}
mTexture.transform = mBufferHeap.transform;
@@ -569,7 +524,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
// figure out if we need linear filtering
if (buffers.w * h == buffers.h * w) {
// same pixel area, don't use filtering
- mLayer.mUseLinearFiltering = false;
+ mLayer.mNeedsFiltering = false;
}
// Allocate a temporary buffer and create the corresponding EGLImageKHR
@@ -593,7 +548,8 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
dst.crop.r = w;
dst.crop.b = h;
- err = mLayer.initializeEglImage(buffer, &mTexture);
+ EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay());
+ err = mTextureManager.initEglImage(&mTexture, dpy, buffer);
}
return err;
@@ -602,14 +558,13 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
void LayerBuffer::BufferSource::clearTempBufferImage() const
{
// delete the image
- EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ EGLDisplay dpy(getFlinger()->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();
}
// ---------------------------------------------------------------------------
@@ -620,7 +575,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
: Source(layer), mVisibilityChanged(false),
mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation)
{
- overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine();
+ overlay_control_device_t* overlay_dev = getFlinger()->getOverlayEngine();
if (overlay_dev == NULL) {
// overlays not supported
return;
@@ -651,7 +606,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
*overlayRef = new OverlayRef(mOverlayHandle, channel,
mWidth, mHeight, mFormat, mWidthStride, mHeightStride);
- mLayer.mFlinger->signalEvent();
+ getFlinger()->signalEvent();
}
LayerBuffer::OverlaySource::~OverlaySource()
@@ -665,9 +620,9 @@ LayerBuffer::OverlaySource::~OverlaySource()
void LayerBuffer::OverlaySource::onDraw(const Region& clip) const
{
// this would be where the color-key would be set, should we need it.
- GLclampx red = 0;
- GLclampx green = 0;
- GLclampx blue = 0;
+ GLclampf red = 0;
+ GLclampf green = 0;
+ GLclampf blue = 0;
mLayer.clearWithOpenGL(clip, red, green, blue, 0);
}
diff --git a/libs/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
index b176623..1c0bf83 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include "LayerBase.h"
+#include "TextureManager.h"
struct copybit_device_t;
@@ -45,31 +46,26 @@ class LayerBuffer : public LayerBaseClient
virtual void onVisibilityResolved(const Transform& planeTransform);
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
- virtual bool transformed() const;
virtual void destroy() { }
+ SurfaceFlinger* getFlinger() const { return mLayer.mFlinger.get(); }
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);
+ const sp<Client>& client);
virtual ~LayerBuffer();
virtual void onFirstRef();
virtual bool needsBlending() const;
+ virtual const char* getTypeId() const { return "LayerBuffer"; }
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);
@@ -133,7 +129,6 @@ private:
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;
@@ -143,9 +138,9 @@ private:
status_t mStatus;
ISurface::BufferHeap mBufferHeap;
size_t mBufferSize;
- mutable LayerBase::Texture mTexture;
+ mutable Texture mTexture;
mutable NativeBuffer mTempBuffer;
- mutable bool mUseEGLImageDirectly;
+ mutable TextureManager mTextureManager;
};
class OverlaySource : public Source {
@@ -195,7 +190,7 @@ private:
{
public:
SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
- SurfaceID id, const sp<LayerBuffer>& owner);
+ const sp<LayerBuffer>& owner);
virtual ~SurfaceLayerBuffer();
virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
new file mode 100644
index 0000000..a1f339e
--- /dev/null
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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 {
+// ---------------------------------------------------------------------------
+
+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)
+ : LayerBaseClient(flinger, display, client)
+{
+}
+
+void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h)
+{
+ sTexId = -1;
+ sImage = EGL_NO_IMAGE_KHR;
+ sWidth = w;
+ sHeight = h;
+ sUseTexture = false;
+}
+
+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 GLfloat alpha = s.alpha/255.0f;
+ const uint32_t fbHeight = hw.getHeight();
+ glDisable(GL_DITHER);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4f(0, 0, 0, alpha);
+
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#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/services/surfaceflinger/LayerDim.h
index d4672a1..f032314 100644
--- a/libs/surfaceflinger/LayerDim.h
+++ b/services/surfaceflinger/LayerDim.h
@@ -37,18 +37,14 @@ class LayerDim : public LayerBaseClient
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);
+ const sp<Client>& client);
virtual ~LayerDim();
virtual void onDraw(const Region& clip) const;
virtual bool needsBlending() const { return true; }
virtual bool isSecure() const { return false; }
+ virtual const char* getTypeId() const { return "LayerDim"; }
static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h);
};
diff --git a/libs/surfaceflinger/MODULE_LICENSE_APACHE2 b/services/surfaceflinger/MODULE_LICENSE_APACHE2
index e69de29..e69de29 100644
--- a/libs/surfaceflinger/MODULE_LICENSE_APACHE2
+++ b/services/surfaceflinger/MODULE_LICENSE_APACHE2
diff --git a/libs/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index b43d801..d668e88 100644
--- a/libs/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -60,9 +60,9 @@ MessageQueue::~MessageQueue()
{
}
-MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
+sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout)
{
- MessageList::value_type result;
+ sp<MessageBase> result;
bool again;
do {
@@ -132,6 +132,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
if (again) {
// the message has been processed. release our reference to it
// without holding the lock.
+ result->notify();
result = 0;
}
@@ -141,7 +142,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
}
status_t MessageQueue::postMessage(
- const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+ const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
{
return queueMessage(message, relTime, flags);
}
@@ -154,7 +155,7 @@ status_t MessageQueue::invalidate() {
}
status_t MessageQueue::queueMessage(
- const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+ const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags)
{
Mutex::Autolock _l(mLock);
message->when = systemTime() + relTime;
@@ -167,13 +168,13 @@ status_t MessageQueue::queueMessage(
return NO_ERROR;
}
-void MessageQueue::dump(const MessageList::value_type& message)
+void MessageQueue::dump(const sp<MessageBase>& message)
{
Mutex::Autolock _l(mLock);
dumpLocked(message);
}
-void MessageQueue::dumpLocked(const MessageList::value_type& message)
+void MessageQueue::dumpLocked(const sp<MessageBase>& message)
{
LIST::const_iterator cur(mMessages.begin());
LIST::const_iterator end(mMessages.end());
diff --git a/libs/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index dc8138d..890f809 100644
--- a/libs/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -25,6 +25,7 @@
#include <utils/Timers.h>
#include <utils/List.h>
+#include "Barrier.h"
namespace android {
@@ -37,7 +38,6 @@ 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(); }
@@ -63,11 +63,19 @@ public:
// return true if message has a handler
virtual bool handler() { return false; }
+
+ // waits for the handler to be processed
+ void wait() const { barrier.wait(); }
+ // releases all waiters. this is done automatically if
+ // handler returns true
+ void notify() const { barrier.open(); }
+
protected:
virtual ~MessageBase() { }
private:
+ mutable Barrier barrier;
friend class LightRefBase<MessageBase>;
};
@@ -82,42 +90,33 @@ 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
+ INVALIDATE = '_upd'
};
- MessageList::value_type waitMessage(nsecs_t timeout = -1);
+ sp<MessageBase> waitMessage(nsecs_t timeout = -1);
- status_t postMessage(const MessageList::value_type& message,
+ status_t postMessage(const sp<MessageBase>& message,
nsecs_t reltime=0, uint32_t flags = 0);
-
+
status_t invalidate();
- void dump(const MessageList::value_type& message);
+ void dump(const sp<MessageBase>& message);
private:
- status_t queueMessage(const MessageList::value_type& message,
+ status_t queueMessage(const sp<MessageBase>& message,
nsecs_t reltime, uint32_t flags);
- void dumpLocked(const MessageList::value_type& message);
+ void dumpLocked(const sp<MessageBase>& message);
Mutex mLock;
Condition mCondition;
MessageList mMessages;
bool mInvalidate;
- MessageList::value_type mInvalidateMessage;
+ sp<MessageBase> mInvalidateMessage;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0722fda..3167c4c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -44,6 +44,7 @@
#include <GLES/gl.h>
#include "clz.h"
+#include "GLExtensions.h"
#include "Layer.h"
#include "LayerBlur.h"
#include "LayerBuffer.h"
@@ -62,20 +63,6 @@
#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)
@@ -206,8 +193,8 @@ void SurfaceFlinger::init()
property_get("debug.sf.showbackground", value, "0");
mDebugBackground = atoi(value);
- LOGI_IF(mDebugRegion, "showupdates enabled");
- LOGI_IF(mDebugBackground, "showbackground enabled");
+ LOGI_IF(mDebugRegion, "showupdates enabled");
+ LOGI_IF(mDebugBackground, "showbackground enabled");
}
SurfaceFlinger::~SurfaceFlinger()
@@ -225,56 +212,29 @@ sp<IMemoryHeap> SurfaceFlinger::getCblk() const
return mServerHeap;
}
-sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()
+sp<ISurfaceComposerClient> 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<ISurfaceComposerClient> bclient;
+ sp<Client> client(new Client(this));
+ status_t err = client->initCheck();
+ if (err == NO_ERROR) {
+ bclient = client;
}
- sp<BClient> bclient =
- new BClient(this, token, client->getControlBlockMemory());
return bclient;
}
-void SurfaceFlinger::destroyConnection(ClientID cid)
+sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection()
{
- 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);
+ sp<ISurfaceComposerClient> bclient;
+ sp<UserClient> client(new UserClient(this));
+ status_t err = client->initCheck();
+ if (err == NO_ERROR) {
+ bclient = client;
}
+ return bclient;
}
+
const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const
{
LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy);
@@ -357,16 +317,8 @@ status_t SurfaceFlinger::readyToRun()
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);
@@ -427,7 +379,7 @@ void SurfaceFlinger::waitForEvent()
timeout = waitTime>0 ? waitTime : 0;
}
- MessageList::value_type msg = mEventQueue.waitMessage(timeout);
+ sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
// see if we timed out
if (isFrozen()) {
@@ -462,9 +414,20 @@ void SurfaceFlinger::signal() const {
const_cast<SurfaceFlinger*>(this)->signalEvent();
}
-void SurfaceFlinger::signalDelayedEvent(nsecs_t delay)
+status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags)
+{
+ return mEventQueue.postMessage(msg, reltime, flags);
+}
+
+status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
+ nsecs_t reltime, uint32_t flags)
{
- mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay);
+ status_t res = mEventQueue.postMessage(msg, reltime, flags);
+ if (res == NO_ERROR) {
+ msg->wait();
+ }
+ return res;
}
// ----------------------------------------------------------------------------
@@ -655,10 +618,6 @@ void SurfaceFlinger::handleTransactionLocked(
}
}
}
-
- // get rid of all resources we don't need anymore
- // (layers and clients)
- free_resources_l();
}
commitTransaction();
@@ -805,7 +764,8 @@ void SurfaceFlinger::commitTransaction()
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ);
+ LayerVector& currentLayers = const_cast<LayerVector&>(
+ mDrawingState.layersSortedByZ);
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -827,7 +787,7 @@ bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
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];
+ const sp<LayerBase>& layer(layers[i]);
layer->lockPageFlip(recomputeVisibleRegions);
}
return recomputeVisibleRegions;
@@ -840,7 +800,7 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
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];
+ const sp<LayerBase>& layer(layers[i]);
layer->unlockPageFlip(planeTransform, mDirtyRegion);
}
}
@@ -872,7 +832,7 @@ void SurfaceFlinger::handleRepaint()
// 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
+ // TODO: 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 {
@@ -938,17 +898,18 @@ void SurfaceFlinger::unlockClients()
void SurfaceFlinger::debugFlashRegions()
{
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- const uint32_t flags = hw.getFlags();
+ 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);
+ }
+
+ TextureManager::deactivateTextures();
- 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);
@@ -956,9 +917,9 @@ void SurfaceFlinger::debugFlashRegions()
static int toggle = 0;
toggle = 1 - toggle;
if (toggle) {
- glColor4x(0x10000, 0, 0x10000, 0x10000);
+ glColor4f(1, 0, 1, 1);
} else {
- glColor4x(0x10000, 0x10000, 0, 0x10000);
+ glColor4f(1, 1, 0, 1);
}
Region::const_iterator it = mDirtyRegion.begin();
@@ -974,7 +935,7 @@ void SurfaceFlinger::debugFlashRegions()
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
-
+
if (mInvalidRegion.isEmpty()) {
mDirtyRegion.dump("mDirtyRegion");
mInvalidRegion.dump("mInvalidRegion");
@@ -982,7 +943,7 @@ void SurfaceFlinger::debugFlashRegions()
hw.flip(mInvalidRegion);
if (mDebugRegion > 1)
- usleep(mDebugRegion * 1000);
+ usleep(mDebugRegion * 1000);
glEnable(GL_SCISSOR_TEST);
//mDirtyRegion.dump("mDirtyRegion");
@@ -1002,7 +963,7 @@ void SurfaceFlinger::drawWormhole() const
glDisable(GL_DITHER);
if (LIKELY(!mDebugBackground)) {
- glClearColorx(0,0,0,0);
+ glClearColor(0,0,0,0);
Region::const_iterator it = region.begin();
Region::const_iterator const end = region.end();
while (it != end) {
@@ -1018,6 +979,11 @@ void SurfaceFlinger::drawWormhole() const
glVertexPointer(2, GL_SHORT, 0, vertices);
glTexCoordPointer(2, GL_SHORT, 0, tcoords);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#endif
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@@ -1061,6 +1027,27 @@ status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer)
return NO_ERROR;
}
+status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
+{
+ ssize_t i = mCurrentState.layersSortedByZ.add(
+ layer, &LayerBase::compareCurrentStateZ);
+ return (i < 0) ? status_t(i) : status_t(NO_ERROR);
+}
+
+ssize_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
+ const sp<LayerBaseClient>& lbc)
+{
+ Mutex::Autolock _l(mStateLock);
+
+ // attach this layer to the client
+ ssize_t name = client->attachLayer(lbc);
+
+ // add this layer to the current state list
+ addLayer_l(lbc);
+
+ return name;
+}
+
status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
{
Mutex::Autolock _l(mStateLock);
@@ -1070,36 +1057,15 @@ status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
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)
+status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
{
- if (layer == 0)
- return BAD_VALUE;
- ssize_t i = mCurrentState.layersSortedByZ.add(
- layer, &LayerBase::compareCurrentStateZ);
- sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
+ sp<LayerBaseClient> lbc(layerBase->getLayerBaseClient());
if (lbc != 0) {
- mLayerMap.add(lbc->serverIndex(), lbc);
+ mLayerMap.removeItem( lbc->getSurface()->asBinder() );
}
- 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);
@@ -1114,22 +1080,16 @@ status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase)
// 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().
+ // from the user because there is a race between Client::destroySurface(),
+ // ~Client() and ~ISurface().
return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err;
}
-
-void SurfaceFlinger::free_resources_l()
+status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
{
- // 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();
+ layer->forceVisibilityTransaction();
+ setTransactionFlags(eTraversalNeeded);
+ return NO_ERROR;
}
uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
@@ -1137,15 +1097,11 @@ 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 SurfaceFlinger::setTransactionFlags(uint32_t flags)
{
uint32_t old = android_atomic_or(flags, &mTransactionFlags);
if ((old & flags)==0) { // wake the server up
- if (delay > 0) {
- signalDelayedEvent(delay);
- } else {
- signalEvent();
- }
+ signalEvent();
}
return old;
}
@@ -1224,8 +1180,8 @@ int SurfaceFlinger::setOrientation(DisplayID dpy,
return orientation;
}
-sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
- const String8& name, ISurfaceFlingerClient::surface_data_t* params,
+sp<ISurface> SurfaceFlinger::createSurface(const sp<Client>& client, int pid,
+ const String8& name, ISurfaceComposerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
@@ -1238,57 +1194,52 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
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;
- }
-
+ sp<Layer> normalLayer;
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
- layer = createPushBuffersSurfaceLocked(client, d, id,
- w, h, flags);
+ layer = createPushBuffersSurface(client, d, w, h, flags);
} else {
- layer = createNormalSurfaceLocked(client, d, id,
- w, h, flags, format);
+ normalLayer = createNormalSurface(client, d, w, h, flags, format);
+ layer = normalLayer;
}
break;
case eFXSurfaceBlur:
- layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
+ layer = createBlurSurface(client, d, w, h, flags);
break;
case eFXSurfaceDim:
- layer = createDimSurfaceLocked(client, d, id, w, h, flags);
+ layer = createDimSurface(client, d, w, h, flags);
break;
}
if (layer != 0) {
+ layer->initStates(w, h, flags);
layer->setName(name);
- setTransactionFlags(eTransactionNeeded);
+ ssize_t token = addClientLayer(client, layer);
+
surfaceHandle = layer->getSurface();
if (surfaceHandle != 0) {
- params->token = surfaceHandle->getToken();
+ params->token = token;
params->identity = surfaceHandle->getIdentity();
params->width = w;
params->height = h;
params->format = format;
+ if (normalLayer != 0) {
+ Mutex::Autolock _l(mStateLock);
+ mLayerMap.add(surfaceHandle->asBinder(), normalLayer);
+ }
}
+
+ setTransactionFlags(eTransactionNeeded);
}
return surfaceHandle;
}
-sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
+sp<Layer> SurfaceFlinger::createNormalSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format)
{
// initialize the surfaces
@@ -1298,53 +1249,56 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
format = PIXEL_FORMAT_RGBA_8888;
break;
case PIXEL_FORMAT_OPAQUE:
+#ifdef NO_RGBX_8888
format = PIXEL_FORMAT_RGB_565;
+#else
+ format = PIXEL_FORMAT_RGBX_8888;
+#endif
break;
}
- sp<Layer> layer = new Layer(this, display, client, id);
+#ifdef NO_RGBX_8888
+ if (format == PIXEL_FORMAT_RGBX_8888)
+ format = PIXEL_FORMAT_RGBA_8888;
+#endif
+
+ sp<Layer> layer = new Layer(this, display, client);
status_t err = layer->setBuffers(w, h, format, flags);
- if (LIKELY(err == NO_ERROR)) {
- layer->initStates(w, h, flags);
- addLayer_l(layer);
- } else {
+ if (LIKELY(err != NO_ERROR)) {
LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
layer.clear();
}
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked(
+sp<LayerBlur> SurfaceFlinger::createBlurSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerBlur> layer = new LayerBlur(this, display, client, id);
+ sp<LayerBlur> layer = new LayerBlur(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked(
+sp<LayerDim> SurfaceFlinger::createDimSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerDim> layer = new LayerDim(this, display, client, id);
+ sp<LayerDim> layer = new LayerDim(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
return layer;
}
-sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(
+sp<LayerBuffer> SurfaceFlinger::createPushBuffersSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+ uint32_t w, uint32_t h, uint32_t flags)
{
- sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id);
+ sp<LayerBuffer> layer = new LayerBuffer(this, display, client);
layer->initStates(w, h, flags);
- addLayer_l(layer);
return layer;
}
-status_t SurfaceFlinger::removeSurface(SurfaceID index)
+status_t SurfaceFlinger::removeSurface(const sp<Client>& client, SurfaceID sid)
{
/*
* called by the window manager, when a surface should be marked for
@@ -1357,7 +1311,7 @@ status_t SurfaceFlinger::removeSurface(SurfaceID index)
status_t err = NAME_NOT_FOUND;
Mutex::Autolock _l(mStateLock);
- sp<LayerBaseClient> layer = getLayerUser_l(index);
+ sp<LayerBaseClient> layer = client->getLayerUser(sid);
if (layer != 0) {
err = purgatorizeLayer_l(layer);
if (err == NO_ERROR) {
@@ -1397,21 +1351,20 @@ status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
}
};
- mEventQueue.postMessage( new MessageDestroySurface(this, layer) );
+ postMessageAsync( new MessageDestroySurface(this, layer) );
return NO_ERROR;
}
status_t SurfaceFlinger::setClientState(
- ClientID cid,
+ const sp<Client>& client,
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));
+ const layer_state_t& s(states[i]);
+ sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
if (what & ePositionChanged) {
@@ -1457,12 +1410,6 @@ status_t SurfaceFlinger::setClientState(
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
@@ -1512,83 +1459,17 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
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;
- }
+ const sp<LayerBase>& layer(currentLayers[i]);
+ layer->dump(result, buffer, SIZE);
+ const Layer::State& s(layer->drawingState());
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,
@@ -1601,18 +1482,19 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
" 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);
@@ -1705,116 +1587,189 @@ status_t SurfaceFlinger::onTransact(
}
// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)
- : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)
+sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
{
- const int pgsize = getpagesize();
- const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
+ sp<Layer> result;
+ Mutex::Autolock _l(mStateLock);
+ result = mLayerMap.valueFor( sur->asBinder() ).promote();
+ return result;
+}
- 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(const sp<SurfaceFlinger>& flinger)
+ : mFlinger(flinger), mNameGenerator(1)
+{
}
-Client::~Client() {
- if (ctrlblk) {
- ctrlblk->~SharedClient(); // destroy our shared-structure.
+Client::~Client()
+{
+ const size_t count = mLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<LayerBaseClient> layer(mLayers.valueAt(i).promote());
+ if (layer != 0) {
+ mFlinger->removeLayer(layer);
+ }
}
}
-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::initCheck() const {
+ return NO_ERROR;
}
-status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id)
+ssize_t Client::attachLayer(const sp<LayerBaseClient>& layer)
{
- ssize_t idx = mInUse.indexOf(id);
- if (idx < 0)
- return NAME_NOT_FOUND;
- return mLayers.insertAt(layer, idx);
+ int32_t name = android_atomic_inc(&mNameGenerator);
+ mLayers.add(name, layer);
+ return name;
}
-void Client::free(int32_t id)
+void Client::detachLayer(const LayerBaseClient* layer)
{
- ssize_t idx = mInUse.remove(uint8_t(id));
- if (idx >= 0) {
- mBitmap &= ~(1<<(31-id));
- mLayers.removeItemsAt(idx);
+ // we do a linear search here, because this doesn't happen often
+ const size_t count = mLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (mLayers.valueAt(i) == layer) {
+ mLayers.removeItemsAt(i, 1);
+ break;
+ }
}
}
-
-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));
+ const wp<LayerBaseClient>& layer(mLayers.valueFor(i));
+ if (layer != 0) {
+ lbc = layer.promote();
+ LOGE_IF(lbc==0, "getLayerUser(name=%d) is dead", int(i));
}
return lbc;
}
-void Client::dump(const char* what)
+sp<IMemoryHeap> Client::getControlBlock() const {
+ return 0;
+}
+ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const {
+ return -1;
+}
+sp<ISurface> Client::createSurface(
+ ISurfaceComposerClient::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(this, pid, name, params,
+ display, w, h, format, flags);
+}
+status_t Client::destroySurface(SurfaceID sid) {
+ return mFlinger->removeSurface(this, sid);
+}
+status_t Client::setState(int32_t count, const layer_state_t* states) {
+ return mFlinger->setClientState(this, count, states);
}
// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk)
- : mId(cid), mFlinger(flinger), mCblk(cblk)
+UserClient::UserClient(const sp<SurfaceFlinger>& flinger)
+ : ctrlblk(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;
+ }
}
-BClient::~BClient() {
- // destroy all resources attached to this client
- mFlinger->destroyConnection(mId);
+UserClient::~UserClient()
+{
+ if (ctrlblk) {
+ ctrlblk->~SharedClient(); // destroy our shared-structure.
+ }
+
+ /*
+ * When a UserClient dies, it's unclear what to do exactly.
+ * We could go ahead and destroy all surfaces linked to that client
+ * however, it wouldn't be fair to the main Client
+ * (usually the the window-manager), which might want to re-target
+ * the layer to another UserClient.
+ * I think the best is to do nothing, or not much; in most cases the
+ * WM itself will go ahead and clean things up when it detects a client of
+ * his has died.
+ * The remaining question is what to display? currently we keep
+ * just keep the current buffer.
+ */
}
-sp<IMemoryHeap> BClient::getControlBlock() const {
- return mCblk;
+status_t UserClient::initCheck() const {
+ return ctrlblk == 0 ? NO_INIT : NO_ERROR;
}
-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)
+void UserClient::detachLayer(const Layer* layer)
{
- return mFlinger->createSurface(mId, pid, name, params, display, w, h,
- format, flags);
+ int32_t name = layer->getToken();
+ if (name >= 0) {
+ int32_t mask = 1LU<<name;
+ if ((android_atomic_and(~mask, &mBitmap) & mask) == 0) {
+ LOGW("token %d wasn't marked as used %08x", name, int(mBitmap));
+ }
+ }
}
-status_t BClient::destroySurface(SurfaceID sid)
-{
- sid |= (mId << 16); // add the client-part to id
- return mFlinger->removeSurface(sid);
+sp<IMemoryHeap> UserClient::getControlBlock() const {
+ return mCblkHeap;
}
-status_t BClient::setState(int32_t count, const layer_state_t* states)
+ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const
{
- return mFlinger->setClientState(mId, count, states);
+ int32_t name = NAME_NOT_FOUND;
+ sp<Layer> layer(mFlinger->getLayer(sur));
+ if (layer == 0) return name;
+
+ // if this layer already has a token, just return it
+ name = layer->getToken();
+ if ((name >= 0) && (layer->getClient() == this))
+ return name;
+
+ name = 0;
+ do {
+ int32_t mask = 1LU<<name;
+ if ((android_atomic_or(mask, &mBitmap) & mask) == 0) {
+ // we found and locked that name
+ status_t err = layer->setToken(
+ const_cast<UserClient*>(this), ctrlblk, name);
+ if (err != NO_ERROR) {
+ // free the name
+ android_atomic_and(~mask, &mBitmap);
+ name = err;
+ }
+ break;
+ }
+ if (++name > 31)
+ name = NO_MEMORY;
+ } while(name >= 0);
+
+ //LOGD("getTokenForSurface(%p) => %d (client=%p, bitmap=%08lx)",
+ // sur->asBinder().get(), name, this, mBitmap);
+ return name;
+}
+
+sp<ISurface> UserClient::createSurface(
+ ISurfaceComposerClient::surface_data_t* params, int pid,
+ const String8& name,
+ DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags) {
+ return 0;
+}
+status_t UserClient::destroySurface(SurfaceID sid) {
+ return INVALID_OPERATION;
+}
+status_t UserClient::setState(int32_t count, const layer_state_t* states) {
+ return INVALID_OPERATION;
}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d75dc15..8821e5c 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -29,14 +29,14 @@
#include <binder/IMemory.h>
#include <binder/Permission.h>
+#include <binder/BinderService.h>
#include <ui/PixelFormat.h>
#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
#include "Barrier.h"
#include "Layer.h"
-#include "Tokenizer.h"
#include "MessageQueue.h"
@@ -48,55 +48,80 @@ namespace android {
// ---------------------------------------------------------------------------
class Client;
-class BClient;
class DisplayHardware;
class FreezeLock;
class Layer;
+class LayerBlur;
+class LayerDim;
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
+class Client : public BnSurfaceComposerClient
+{
+public:
+ Client(const sp<SurfaceFlinger>& flinger);
+ ~Client();
+
+ status_t initCheck() const;
+
+ // protected by SurfaceFlinger::mStateLock
+ ssize_t attachLayer(const sp<LayerBaseClient>& layer);
+ void detachLayer(const LayerBaseClient* layer);
+ sp<LayerBaseClient> getLayerUser(int32_t i) const;
+
+private:
+
+ // ISurfaceComposerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) 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);
+
+ DefaultKeyedVector< size_t, wp<LayerBaseClient> > mLayers;
+ sp<SurfaceFlinger> mFlinger;
+ int32_t mNameGenerator;
+};
+
+class UserClient : public BnSurfaceComposerClient
{
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;
+ SharedClient* ctrlblk;
+
+public:
+ UserClient(const sp<SurfaceFlinger>& flinger);
+ ~UserClient();
+
+ status_t initCheck() const;
+
+ // protected by SurfaceFlinger::mStateLock
+ void detachLayer(const Layer* layer);
-
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;
+
+ // ISurfaceComposerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) 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);
+
+ // atomic-ops
+ mutable volatile int32_t mBitmap;
+
+ sp<IMemoryHeap> mCblkHeap;
+ sp<SurfaceFlinger> mFlinger;
};
// ---------------------------------------------------------------------------
@@ -143,11 +168,13 @@ enum {
eTraversalNeeded = 0x02
};
-class SurfaceFlinger : public BnSurfaceComposer, protected Thread
+class SurfaceFlinger :
+ public BinderService<SurfaceFlinger>,
+ public BnSurfaceComposer,
+ protected Thread
{
public:
- static void instantiate();
- static void shutdown();
+ static char const* getServiceName() { return "SurfaceFlinger"; }
SurfaceFlinger();
virtual ~SurfaceFlinger();
@@ -159,7 +186,8 @@ public:
virtual status_t dump(int fd, const Vector<String16>& args);
// ISurfaceComposer interface
- virtual sp<ISurfaceFlingerClient> createConnection();
+ virtual sp<ISurfaceComposerClient> createConnection();
+ virtual sp<ISurfaceComposerClient> createClientConnection();
virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
virtual void openGlobalTransaction();
@@ -174,13 +202,14 @@ public:
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);
-
+
+ sp<Layer> getLayer(const sp<ISurface>& sur) const;
+
private:
- friend class BClient;
+ friend class Client;
friend class LayerBase;
friend class LayerBuffer;
friend class LayerBaseClient;
@@ -189,31 +218,33 @@ private:
friend class LayerBlur;
friend class LayerDim;
- sp<ISurface> createSurface(ClientID client, int pid, const String8& name,
- ISurfaceFlingerClient::surface_data_t* params,
+ sp<ISurface> createSurface(const sp<Client>& client,
+ int pid, const String8& name,
+ ISurfaceComposerClient::surface_data_t* params,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags);
- sp<LayerBaseClient> createNormalSurfaceLocked(
+ sp<Layer> createNormalSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format);
- sp<LayerBaseClient> createBlurSurfaceLocked(
+ sp<LayerBlur> createBlurSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- sp<LayerBaseClient> createDimSurfaceLocked(
+ sp<LayerDim> createDimSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- sp<LayerBaseClient> createPushBuffersSurfaceLocked(
+ sp<LayerBuffer> createPushBuffersSurface(
const sp<Client>& client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+ uint32_t w, uint32_t h, uint32_t flags);
- status_t removeSurface(SurfaceID surface_id);
+ status_t removeSurface(const sp<Client>& client, SurfaceID sid);
status_t destroySurface(const sp<LayerBaseClient>& layer);
- status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states);
+ status_t setClientState(const sp<Client>& client,
+ int32_t count, const layer_state_t* states);
class LayerVector {
@@ -256,8 +287,6 @@ private:
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(
@@ -278,15 +307,14 @@ private:
void unlockClients();
- void destroyConnection(ClientID cid);
- sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const;
+ ssize_t addClientLayer(const sp<Client>& client,
+ const sp<LayerBaseClient>& lbc);
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);
+ uint32_t setTransactionFlags(uint32_t flags);
void commitTransaction();
@@ -310,9 +338,13 @@ private:
mutable MessageQueue mEventQueue;
-
-
-
+
+ status_t postMessageAsync(const sp<MessageBase>& msg,
+ nsecs_t reltime=0, uint32_t flags = 0);
+
+ status_t postMessageSync(const sp<MessageBase>& msg,
+ nsecs_t reltime=0, uint32_t flags = 0);
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState;
@@ -321,14 +353,11 @@ private:
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;
+ GraphicPlane mGraphicPlanes[1];
+ bool mLayersRemoved;
+ DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayerMap;
// constant members (no synchronization needed for access)
sp<IMemoryHeap> mServerHeap;
@@ -389,32 +418,6 @@ public:
};
// ---------------------------------------------------------------------------
-
-class BClient : public BnSurfaceFlingerClient
-{
-public:
- BClient(SurfaceFlinger *flinger, ClientID cid,
- const sp<IMemoryHeap>& cblk);
- ~BClient();
-
- // ISurfaceFlingerClient interface
- virtual sp<IMemoryHeap> getControlBlock() const;
-
- virtual sp<ISurface> createSurface(
- surface_data_t* params, int pid, const String8& name,
- DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
- uint32_t flags);
-
- virtual status_t destroySurface(SurfaceID surfaceId);
- virtual status_t setState(int32_t count, const layer_state_t* states);
-
-private:
- ClientID mId;
- SurfaceFlinger* mFlinger;
- sp<IMemoryHeap> mCblk;
-};
-
-// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
new file mode 100644
index 0000000..3b326df
--- /dev/null
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <hardware/hardware.h>
+
+#include "clz.h"
+#include "DisplayHardware/DisplayHardware.h"
+#include "GLExtensions.h"
+#include "TextureManager.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+TextureManager::TextureManager()
+ : mGLExtensions(GLExtensions::getInstance())
+{
+}
+
+GLenum TextureManager::getTextureTarget(const Image* image) {
+#if defined(GL_OES_texture_external)
+ switch (image->target) {
+ case Texture::TEXTURE_EXTERNAL:
+ return GL_TEXTURE_EXTERNAL_OES;
+ }
+#endif
+ return GL_TEXTURE_2D;
+}
+
+status_t TextureManager::initTexture(Texture* texture)
+{
+ if (texture->name != -1UL)
+ return INVALID_OPERATION;
+
+ GLuint textureName = -1;
+ glGenTextures(1, &textureName);
+ texture->name = textureName;
+ texture->width = 0;
+ texture->height = 0;
+
+ const GLenum target = GL_TEXTURE_2D;
+ glBindTexture(target, textureName);
+ glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ return NO_ERROR;
+}
+
+status_t TextureManager::initTexture(Image* pImage, int32_t format)
+{
+ if (pImage->name != -1UL)
+ return INVALID_OPERATION;
+
+ GLuint textureName = -1;
+ glGenTextures(1, &textureName);
+ pImage->name = textureName;
+ pImage->width = 0;
+ pImage->height = 0;
+
+ GLenum target = GL_TEXTURE_2D;
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ if (format && isYuvFormat(format)) {
+ target = GL_TEXTURE_EXTERNAL_OES;
+ pImage->target = Texture::TEXTURE_EXTERNAL;
+ }
+ }
+#endif
+
+ glBindTexture(target, textureName);
+ glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ return NO_ERROR;
+}
+
+bool TextureManager::isSupportedYuvFormat(int format)
+{
+ switch (format) {
+ case HAL_PIXEL_FORMAT_YV12:
+ return true;
+ }
+ return false;
+}
+
+bool TextureManager::isYuvFormat(int format)
+{
+ switch (format) {
+ // supported YUV formats
+ case HAL_PIXEL_FORMAT_YV12:
+ // Legacy/deprecated YUV formats
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+ return true;
+ }
+
+ // Any OEM format needs to be considered
+ if (format>=0x100 && format<=0x1FF)
+ return true;
+
+ return false;
+}
+
+status_t TextureManager::initEglImage(Image* pImage,
+ EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
+{
+ status_t err = NO_ERROR;
+ if (!pImage->dirty) return err;
+
+ // free the previous image
+ if (pImage->image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, pImage->image);
+ pImage->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
+ };
+ pImage->image = eglCreateImageKHR(
+ dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ (EGLClientBuffer)clientBuf, attrs);
+
+ if (pImage->image != EGL_NO_IMAGE_KHR) {
+ if (pImage->name == -1UL) {
+ initTexture(pImage, buffer->format);
+ }
+ const GLenum target = getTextureTarget(pImage);
+ glBindTexture(target, pImage->name);
+ glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
+ GLint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
+ pImage->image, error);
+ err = INVALID_OPERATION;
+ } else {
+ // Everything went okay!
+ pImage->dirty = false;
+ pImage->width = clientBuf->width;
+ pImage->height = clientBuf->height;
+ }
+ } else {
+ LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
+ err = INVALID_OPERATION;
+ }
+ return err;
+}
+
+status_t TextureManager::loadTexture(Texture* texture,
+ const Region& dirty, const GGLSurface& t)
+{
+ if (texture->name == -1UL) {
+ status_t err = initTexture(texture);
+ LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
+ return err;
+ }
+
+ if (texture->target != GL_TEXTURE_2D)
+ return INVALID_OPERATION;
+
+ 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 (!mGLExtensions.haveNpot()) {
+ 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("texture=%d, using format %d, which is not "
+ "supported by the GL", 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);
+ }
+ }
+ return NO_ERROR;
+}
+
+void TextureManager::activateTexture(const Texture& texture, bool filter)
+{
+ const GLenum target = getTextureTarget(&texture);
+ if (target == GL_TEXTURE_2D) {
+ glBindTexture(GL_TEXTURE_2D, texture.name);
+ glEnable(GL_TEXTURE_2D);
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+ } else {
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
+ glEnable(GL_TEXTURE_EXTERNAL_OES);
+ glDisable(GL_TEXTURE_2D);
+#endif
+ }
+
+ if (filter) {
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+}
+
+void TextureManager::deactivateTextures()
+{
+ glDisable(GL_TEXTURE_2D);
+#if defined(GL_OES_texture_external)
+ if (GLExtensions::getInstance().haveTextureExternal()) {
+ glDisable(GL_TEXTURE_EXTERNAL_OES);
+ }
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
new file mode 100644
index 0000000..c7c14e7
--- /dev/null
+++ b/services/surfaceflinger/TextureManager.h
@@ -0,0 +1,94 @@
+/*
+ * 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 ANDROID_TEXTURE_MANAGER_H
+#define ANDROID_TEXTURE_MANAGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+#include <ui/Region.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class GLExtensions;
+class GraphicBuffer;
+
+// ---------------------------------------------------------------------------
+
+struct Image {
+ enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
+ Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
+ transform(0), dirty(1), target(TEXTURE_2D) { }
+ GLuint name;
+ EGLImageKHR image;
+ GLuint width;
+ GLuint height;
+ uint32_t transform;
+ unsigned dirty : 1;
+ unsigned target : 1;
+};
+
+struct Texture : public Image {
+ Texture() : Image(), NPOTAdjust(0) { }
+ GLuint potWidth;
+ GLuint potHeight;
+ GLfloat wScale;
+ GLfloat hScale;
+ unsigned NPOTAdjust : 1;
+};
+
+// ---------------------------------------------------------------------------
+
+class TextureManager {
+ const GLExtensions& mGLExtensions;
+ static status_t initTexture(Image* texture, int32_t format);
+ static status_t initTexture(Texture* texture);
+ static bool isSupportedYuvFormat(int format);
+ static bool isYuvFormat(int format);
+ static GLenum getTextureTarget(const Image* pImage);
+public:
+
+ TextureManager();
+
+ // load bitmap data into the active buffer
+ status_t loadTexture(Texture* texture,
+ const Region& dirty, const GGLSurface& t);
+
+ // make active buffer an EGLImage if needed
+ status_t initEglImage(Image* texture,
+ EGLDisplay dpy, const sp<GraphicBuffer>& buffer);
+
+ // activate a texture
+ static void activateTexture(const Texture& texture, bool filter);
+
+ // deactivate a texture
+ static void deactivateTextures();
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_TEXTURE_MANAGER_H
diff --git a/libs/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index 175f989..5e27cc9 100644
--- a/libs/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -229,14 +229,13 @@ Transform::vec3 Transform::transform(const vec3& v) const {
return r;
}
-void Transform::transform(fixed1616* point, int x, int y) const
+void Transform::transform(float* 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;
+ point[0] = v[0];
+ point[1] = v[1];
}
Rect Transform::makeBounds(int w, int h) const
diff --git a/libs/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 2e5b893..20fa11a 100644
--- a/libs/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -37,8 +37,6 @@ public:
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,
@@ -76,7 +74,7 @@ public:
// transform data
Rect makeBounds(int w, int h) const;
- void transform(fixed1616* point, int x, int y) const;
+ void transform(float* point, int x, int y) const;
Region transform(const Region& reg) const;
Transform operator * (const Transform& rhs) const;
diff --git a/libs/surfaceflinger/clz.cpp b/services/surfaceflinger/clz.cpp
index 2456b86..2456b86 100644
--- a/libs/surfaceflinger/clz.cpp
+++ b/services/surfaceflinger/clz.cpp
diff --git a/libs/surfaceflinger/clz.h b/services/surfaceflinger/clz.h
index 0ddf986..0ddf986 100644
--- a/libs/surfaceflinger/clz.h
+++ b/services/surfaceflinger/clz.h
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/services/surfaceflinger/tests/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/surfaceflinger/tests/overlays/Android.mk b/services/surfaceflinger/tests/overlays/Android.mk
index 592b601..592b601 100644
--- a/libs/surfaceflinger/tests/overlays/Android.mk
+++ b/services/surfaceflinger/tests/overlays/Android.mk
diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/services/surfaceflinger/tests/overlays/overlays.cpp
index c248a615..c248a615 100644
--- a/libs/surfaceflinger/tests/overlays/overlays.cpp
+++ b/services/surfaceflinger/tests/overlays/overlays.cpp
diff --git a/libs/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk
index 24c2d01..24c2d01 100644
--- a/libs/surfaceflinger/tests/resize/Android.mk
+++ b/services/surfaceflinger/tests/resize/Android.mk
diff --git a/libs/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 127cca3..127cca3 100644
--- a/libs/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
diff --git a/services/surfaceflinger/tests/surface/Android.mk b/services/surfaceflinger/tests/surface/Android.mk
new file mode 100644
index 0000000..ce0e807
--- /dev/null
+++ b/services/surfaceflinger/tests/surface/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ surface.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-surface
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
new file mode 100644
index 0000000..b4de4b4
--- /dev/null
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -0,0 +1,54 @@
+#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;
+
+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<SurfaceControl> surfaceControl = client->createSurface(
+ getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
+ client->openTransaction();
+ surfaceControl->setLayer(100000);
+ client->closeTransaction();
+
+ // pretend it went cross-process
+ Parcel parcel;
+ SurfaceControl::writeSurfaceToParcel(surfaceControl, &parcel);
+ parcel.setDataPosition(0);
+ sp<Surface> surface = Surface::readFromParcel(parcel);
+ ANativeWindow* window = surface.get();
+
+ printf("window=%p\n", window);
+
+ int err = native_window_set_buffer_count(window, 8);
+ android_native_buffer_t* buffer;
+
+ for (int i=0 ; i<8 ; i++) {
+ window->dequeueBuffer(window, &buffer);
+ printf("buffer %d: %p\n", i, buffer);
+ }
+
+ printf("test complete. CTRL+C to finish.\n");
+
+ IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index ce522c8..ce40b5d 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -85,7 +85,8 @@ public class VpnManager {
// TODO(oam): Test VPN when EFS is enabled (will do later)...
public static String getProfilePath() {
- return Environment.getDataDirectory().getPath() + PROFILES_PATH;
+ // This call will return the correct path if Encrypted FS is enabled or not.
+ return Environment.getSecureDataDirectory().getPath() + PROFILES_PATH;
}
/**