diff options
Diffstat (limited to 'libs/ui')
29 files changed, 7072 insertions, 0 deletions
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk new file mode 100644 index 0000000..f944357 --- /dev/null +++ b/libs/ui/Android.mk @@ -0,0 +1,41 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + Camera.cpp \ + CameraParameters.cpp \ + EGLDisplaySurface.cpp \ + EGLNativeWindowSurface.cpp \ + EventHub.cpp \ + EventRecurrence.cpp \ + KeyLayoutMap.cpp \ + KeyCharacterMap.cpp \ + ICamera.cpp \ + ICameraClient.cpp \ + ICameraService.cpp \ + IOverlay.cpp \ + ISurfaceComposer.cpp \ + ISurface.cpp \ + ISurfaceFlingerClient.cpp \ + LayerState.cpp \ + Overlay.cpp \ + PixelFormat.cpp \ + Point.cpp \ + Rect.cpp \ + Region.cpp \ + Surface.cpp \ + SurfaceComposerClient.cpp \ + SurfaceFlingerSynchro.cpp \ + Time.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcorecg \ + libcutils \ + libutils \ + libpixelflinger \ + libhardware \ + libhardware_legacy + +LOCAL_MODULE:= libui + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp new file mode 100644 index 0000000..b3cbda1 --- /dev/null +++ b/libs/ui/Camera.cpp @@ -0,0 +1,408 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Camera" +#include <utils/Log.h> +#include <utils/IServiceManager.h> +#include <utils/threads.h> +#include <utils/IMemory.h> +#include <ui/Surface.h> +#include <ui/Camera.h> +#include <ui/ICameraService.h> + +namespace android { + +// client singleton for camera service binder interface +Mutex Camera::mLock; +sp<ICameraService> Camera::mCameraService; +sp<Camera::DeathNotifier> Camera::mDeathNotifier; + +// establish binder interface to camera service +const sp<ICameraService>& Camera::getCameraService() +{ + Mutex::Autolock _l(mLock); + if (mCameraService.get() == 0) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + do { + binder = sm->getService(String16("media.camera")); + if (binder != 0) + break; + LOGW("CameraService not published, waiting..."); + usleep(500000); // 0.5 s + } while(true); + if (mDeathNotifier == NULL) { + mDeathNotifier = new DeathNotifier(); + } + binder->linkToDeath(mDeathNotifier); + mCameraService = interface_cast<ICameraService>(binder); + } + LOGE_IF(mCameraService==0, "no CameraService!?"); + return mCameraService; +} + +// --------------------------------------------------------------------------- + +Camera::Camera() +{ + init(); +} + +Camera::Camera(const sp<ICamera>& camera) +{ + init(); + // connect this client to existing camera remote + if (camera->connect(this) == NO_ERROR) { + mStatus = NO_ERROR; + mCamera = camera; + camera->asBinder()->linkToDeath(this); + } +} + +void Camera::init() +{ + mStatus = UNKNOWN_ERROR; + mShutterCallback = 0; + mShutterCallbackCookie = 0; + mRawCallback = 0; + mRawCallbackCookie = 0; + mJpegCallback = 0; + mJpegCallbackCookie = 0; + mPreviewCallback = 0; + mPreviewCallbackCookie = 0; + mRecordingCallback = 0; + mRecordingCallbackCookie = 0; + mErrorCallback = 0; + mErrorCallbackCookie = 0; + mAutoFocusCallback = 0; + mAutoFocusCallbackCookie = 0; +} + +Camera::~Camera() +{ + disconnect(); +} + +sp<Camera> Camera::connect() +{ + LOGV("connect"); + sp<Camera> c = new Camera(); + const sp<ICameraService>& cs = getCameraService(); + if (cs != 0) { + c->mCamera = cs->connect(c); + } + if (c->mCamera != 0) { + c->mCamera->asBinder()->linkToDeath(c); + c->mStatus = NO_ERROR; + } else { + c.clear(); + } + return c; +} + +void Camera::disconnect() +{ + LOGV("disconnect"); + if (mCamera != 0) { + mErrorCallback = 0; + mCamera->disconnect(); + mCamera = 0; + } +} + +status_t Camera::reconnect() +{ + LOGV("reconnect"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->connect(this); +} + +sp<ICamera> Camera::remote() +{ + return mCamera; +} + +status_t Camera::lock() +{ + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->lock(); +} + +status_t Camera::unlock() +{ + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->unlock(); +} + +// pass the buffered ISurface to the camera service +status_t Camera::setPreviewDisplay(const sp<Surface>& surface) +{ + LOGV("setPreviewDisplay"); + if (surface == 0) { + LOGE("app passed NULL surface"); + return NO_INIT; + } + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->setPreviewDisplay(surface->getISurface()); +} + +status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) +{ + LOGV("setPreviewDisplay"); + if (surface == 0) { + LOGE("app passed NULL surface"); + return NO_INIT; + } + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->setPreviewDisplay(surface); +} + + +// start preview mode, must call setPreviewDisplay first +status_t Camera::startPreview() +{ + LOGV("startPreview"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->startPreview(); +} + +// start recording mode, must call setPreviewDisplay first +status_t Camera::startRecording() +{ + LOGV("startRecording"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->startRecording(); +} + +// stop preview mode +void Camera::stopPreview() +{ + LOGV("stopPreview"); + sp <ICamera> c = mCamera; + if (c == 0) return; + c->stopPreview(); +} + +// stop recording mode +void Camera::stopRecording() +{ + LOGV("stopRecording"); + sp <ICamera> c = mCamera; + if (c == 0) return; + c->stopRecording(); +} + +// release a recording frame +void Camera::releaseRecordingFrame(const sp<IMemory>& mem) +{ + LOGV("releaseRecordingFrame"); + sp <ICamera> c = mCamera; + if (c == 0) return; + c->releaseRecordingFrame(mem); +} + +// get preview state +bool Camera::previewEnabled() +{ + LOGV("previewEnabled"); + sp <ICamera> c = mCamera; + if (c == 0) return false; + return c->previewEnabled(); +} + +// get recording state +bool Camera::recordingEnabled() +{ + LOGV("recordingEnabled"); + sp <ICamera> c = mCamera; + if (c == 0) return false; + return c->recordingEnabled(); +} + +status_t Camera::autoFocus() +{ + LOGV("autoFocus"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->autoFocus(); +} + +// take a picture +status_t Camera::takePicture() +{ + LOGV("takePicture"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->takePicture(); +} + +// set preview/capture parameters - key/value pairs +status_t Camera::setParameters(const String8& params) +{ + LOGV("setParameters"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->setParameters(params); +} + +// get preview/capture parameters - key/value pairs +String8 Camera::getParameters() const +{ + LOGV("getParameters"); + String8 params; + sp <ICamera> c = mCamera; + if (c != 0) params = mCamera->getParameters(); + return params; +} + +void Camera::setAutoFocusCallback(autofocus_callback cb, void *cookie) +{ + LOGV("setAutoFocusCallback"); + mAutoFocusCallback = cb; + mAutoFocusCallbackCookie = cookie; +} + +void Camera::setShutterCallback(shutter_callback cb, void *cookie) +{ + LOGV("setShutterCallback"); + mShutterCallback = cb; + mShutterCallbackCookie = cookie; +} + +void Camera::setRawCallback(frame_callback cb, void *cookie) +{ + LOGV("setRawCallback"); + mRawCallback = cb; + mRawCallbackCookie = cookie; +} + +void Camera::setJpegCallback(frame_callback cb, void *cookie) +{ + LOGV("setJpegCallback"); + mJpegCallback = cb; + mJpegCallbackCookie = cookie; +} + +void Camera::setPreviewCallback(frame_callback cb, void *cookie, int flag) +{ + LOGV("setPreviewCallback"); + mPreviewCallback = cb; + mPreviewCallbackCookie = cookie; + sp <ICamera> c = mCamera; + if (c == 0) return; + mCamera->setPreviewCallbackFlag(flag); +} + +void Camera::setRecordingCallback(frame_callback cb, void *cookie) +{ + LOGV("setRecordingCallback"); + mRecordingCallback = cb; + mRecordingCallbackCookie = cookie; +} + +void Camera::setErrorCallback(error_callback cb, void *cookie) +{ + LOGV("setErrorCallback"); + mErrorCallback = cb; + mErrorCallbackCookie = cookie; +} + +void Camera::autoFocusCallback(bool focused) +{ + LOGV("autoFocusCallback"); + if (mAutoFocusCallback) { + mAutoFocusCallback(focused, mAutoFocusCallbackCookie); + } +} + +void Camera::shutterCallback() +{ + LOGV("shutterCallback"); + if (mShutterCallback) { + mShutterCallback(mShutterCallbackCookie); + } +} + +void Camera::rawCallback(const sp<IMemory>& picture) +{ + LOGV("rawCallback"); + if (mRawCallback) { + mRawCallback(picture, mRawCallbackCookie); + } +} + +// callback from camera service when image is ready +void Camera::jpegCallback(const sp<IMemory>& picture) +{ + LOGV("jpegCallback"); + if (mJpegCallback) { + mJpegCallback(picture, mJpegCallbackCookie); + } +} + +// callback from camera service when preview frame is ready +void Camera::previewCallback(const sp<IMemory>& frame) +{ + LOGV("frameCallback"); + if (mPreviewCallback) { + mPreviewCallback(frame, mPreviewCallbackCookie); + } +} + +// callback from camera service when a recording frame is ready +void Camera::recordingCallback(const sp<IMemory>& frame) +{ + LOGV("recordingCallback"); + if (mRecordingCallback) { + mRecordingCallback(frame, mRecordingCallbackCookie); + } +} + +// callback from camera service when an error occurs in preview or takePicture +void Camera::errorCallback(status_t error) +{ + LOGV("errorCallback"); + if (mErrorCallback) { + mErrorCallback(error, mErrorCallbackCookie); + } +} + +void Camera::binderDied(const wp<IBinder>& who) { + LOGW("ICamera died"); + if (mErrorCallback) { + mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie); + } +} + +void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { + LOGV("binderDied"); + Mutex::Autolock _l(Camera::mLock); + Camera::mCameraService.clear(); + LOGW("Camera server died!"); +} + +}; // namespace android + diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp new file mode 100644 index 0000000..6c25836 --- /dev/null +++ b/libs/ui/CameraParameters.cpp @@ -0,0 +1,273 @@ +/* +** +** 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 "CameraParams" +#include <utils/Log.h> + +#include <string.h> +#include <stdlib.h> +#include <ui/CameraParameters.h> + +namespace android { + +static const char* portrait = "portrait"; +static const char* landscape = "landscape"; + +CameraParameters::CameraParameters() + : mMap() +{ +} + +CameraParameters::~CameraParameters() +{ +} + +String8 CameraParameters::flatten() const +{ + String8 flattened(""); + size_t size = mMap.size(); + + for (size_t i = 0; i < size; i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + + flattened += k; + flattened += "="; + flattened += v; + if (i != size-1) + flattened += ";"; + } + + return flattened; +} + +void CameraParameters::unflatten(const String8 ¶ms) +{ + const char *a = params.string(); + const char *b; + + mMap.clear(); + + for (;;) { + // Find the bounds of the key name. + b = strchr(a, '='); + if (b == 0) + break; + + // Create the key string. + String8 k(a, (size_t)(b-a)); + + // Find the value. + a = b+1; + b = strchr(a, ';'); + if (b == 0) { + // If there's no semicolon, this is the last item. + String8 v(a); + mMap.add(k, v); + break; + } + + String8 v(a, (size_t)(b-a)); + mMap.add(k, v); + a = b+1; + } +} + + +void CameraParameters::set(const char *key, const char *value) +{ + // XXX i think i can do this with strspn() + if (strchr(key, '=') || strchr(key, ';')) { + //XXX LOGE("Key \"%s\"contains invalid character (= or ;)", key); + return; + } + + if (strchr(value, '=') || strchr(key, ';')) { + //XXX LOGE("Value \"%s\"contains invalid character (= or ;)", value); + return; + } + + mMap.replaceValueFor(String8(key), String8(value)); +} + +void CameraParameters::set(const char *key, int value) +{ + char str[16]; + sprintf(str, "%d", value); + set(key, str); +} + +const char *CameraParameters::get(const char *key) const +{ + String8 v = mMap.valueFor(String8(key)); + if (v.length() == 0) + return 0; + return v.string(); +} + +int CameraParameters::getInt(const char *key) const +{ + const char *v = get(key); + if (v == 0) + return -1; + return strtol(v, 0, 0); +} + +static int parse_size(const char *str, int &width, int &height) +{ + // Find the width. + char *end; + int w = (int)strtol(str, &end, 10); + // If an 'x' does not immediately follow, give up. + if (*end != 'x') + return -1; + + // Find the height, immediately after the 'x'. + int h = (int)strtol(end+1, 0, 10); + + width = w; + height = h; + + return 0; +} + +void CameraParameters::setPreviewSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set("preview-size", str); +} + +void CameraParameters::getPreviewSize(int *width, int *height) const +{ + *width = -1; + *height = -1; + + // Get the current string, if it doesn't exist, leave the -1x-1 + const char *p = get("preview-size"); + if (p == 0) + return; + + int w, h; + if (parse_size(p, w, h) == 0) { + *width = w; + *height = h; + } +} + +void CameraParameters::setPreviewFrameRate(int fps) +{ + set("preview-frame-rate", fps); +} + +int CameraParameters::getPreviewFrameRate() const +{ + return getInt("preview-frame-rate"); +} + +void CameraParameters::setPreviewFormat(const char *format) +{ + set("preview-format", format); +} + +int CameraParameters::getOrientation() const +{ + const char* orientation = get("orientation"); + if (orientation && !strcmp(orientation, portrait)) + return CAMERA_ORIENTATION_PORTRAIT; + return CAMERA_ORIENTATION_LANDSCAPE; +} + +void CameraParameters::setOrientation(int orientation) +{ + if (orientation == CAMERA_ORIENTATION_PORTRAIT) { + set("preview-format", portrait); + } else { + set("preview-format", landscape); + } +} + +const char *CameraParameters::getPreviewFormat() const +{ + return get("preview-format"); +} + +void CameraParameters::setPictureSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set("picture-size", str); +} + +void CameraParameters::getPictureSize(int *width, int *height) const +{ + *width = -1; + *height = -1; + + // Get the current string, if it doesn't exist, leave the -1x-1 + const char *p = get("picture-size"); + if (p == 0) + return; + + int w, h; + if (parse_size(p, w, h) == 0) { + *width = w; + *height = h; + } +} + +void CameraParameters::setPictureFormat(const char *format) +{ + set("picture-format", format); +} + +const char *CameraParameters::getPictureFormat() const +{ + return get("picture-format"); +} + +void CameraParameters::dump() const +{ + LOGD("dump: mMap.size = %d", mMap.size()); + for (size_t i = 0; i < mMap.size(); i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + LOGD("%s: %s\n", k.string(), v.string()); + } +} + +status_t CameraParameters::dump(int fd, const Vector<String16>& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, 255, "CameraParameters::dump: mMap.size = %d\n", mMap.size()); + result.append(buffer); + for (size_t i = 0; i < mMap.size(); i++) { + String8 k, v; + k = mMap.keyAt(i); + v = mMap.valueAt(i); + snprintf(buffer, 255, "\t%s: %s\n", k.string(), v.string()); + result.append(buffer); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp new file mode 100644 index 0000000..d06c98b --- /dev/null +++ b/libs/ui/EGLDisplaySurface.cpp @@ -0,0 +1,519 @@ +/* + ** + ** Copyright 2007 The Android Open Source Project + ** + ** Licensed under the Apache License Version 2.0(the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing software + ** distributed under the License is distributed on an "AS IS" BASIS + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#define LOG_TAG "EGLDisplaySurface" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> + +#include <hardware/copybit.h> + +#include <ui/SurfaceComposerClient.h> +#include <ui/DisplayInfo.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/EGLDisplaySurface.h> + +#if HAVE_ANDROID_OS +#include <linux/msm_mdp.h> +#endif + +#include <EGL/egl.h> + +#include <pixelflinger/format.h> + + +// ---------------------------------------------------------------------------- + +egl_native_window_t* android_createDisplaySurface() +{ + egl_native_window_t* s = new android::EGLDisplaySurface(); + s->memory_type = NATIVE_MEMORY_TYPE_GPU; + return s; +} + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +EGLDisplaySurface::EGLDisplaySurface() + : EGLNativeSurface<EGLDisplaySurface>() +{ + egl_native_window_t::version = sizeof(egl_native_window_t); + egl_native_window_t::ident = 0; + egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef; + egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef; + egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers; + egl_native_window_t::connect = 0; + egl_native_window_t::disconnect = 0; + + mFb[0].data = 0; + mFb[1].data = 0; + mBlitEngine = 0; + egl_native_window_t::fd = mapFrameBuffer(); + if (egl_native_window_t::fd >= 0) { + + hw_module_t const* module; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } + + const float in2mm = 25.4f; + float refreshRate = 1000000000000000LLU / ( + float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres ) + * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres ) + * mInfo.pixclock); + + const GGLSurface& buffer = mFb[1 - mIndex]; + egl_native_window_t::width = buffer.width; + egl_native_window_t::height = buffer.height; + egl_native_window_t::stride = buffer.stride; + egl_native_window_t::format = buffer.format; + egl_native_window_t::base = intptr_t(mFb[0].data); + egl_native_window_t::offset = + intptr_t(buffer.data) - egl_native_window_t::base; + egl_native_window_t::flags = 0; + egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width; + egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height; + egl_native_window_t::fps = refreshRate; + egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB; + // no error, set the magic word + egl_native_window_t::magic = 0x600913; + } + mSwapCount = -1; + mPageFlipCount = 0; +} + +EGLDisplaySurface::~EGLDisplaySurface() +{ + magic = 0; + copybit_close(mBlitEngine); + mBlitEngine = 0; + close(egl_native_window_t::fd); + munmap(mFb[0].data, mSize); + if (!(mFlags & PAGE_FLIP)) + free((void*)mFb[1].data); +} + +void EGLDisplaySurface::hook_incRef(NativeWindowType window) { + EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); + that->incStrong(that); +} +void EGLDisplaySurface::hook_decRef(NativeWindowType window) { + EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); + that->decStrong(that); +} +uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) { + EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); + return that->swapBuffers(); +} + +void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) +{ + mInfo.reserved[0] = 0x54445055; // "UPDT"; + mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); + mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); +} + +uint32_t EGLDisplaySurface::swapBuffers() +{ +#define SHOW_FPS 0 +#if SHOW_FPS + nsecs_t now = systemTime(); + if (mSwapCount == -1) { + mTime = now; + mSwapCount = 0; + mSleep = 0; + } else { + nsecs_t d = now-mTime; + if (d >= seconds(1)) { + double fps = (mSwapCount * double(seconds(1))) / double(d); + LOGD("%f fps, sleep=%d / frame", + fps, (int)ns2us(mSleep / mSwapCount)); + mSwapCount = 0; + mTime = now; + mSleep = 0; + } else { + mSwapCount++; + } + } +#endif + /* If we can't do the page_flip, just copy the back buffer to the front */ + if (!(mFlags & PAGE_FLIP)) { + memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2); + return 0; + } + + // do the actual flip + mIndex = 1 - mIndex; + mInfo.activate = FB_ACTIVATE_VBL; + mInfo.yoffset = mIndex ? mInfo.yres : 0; + if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) { + LOGE("FBIOPUT_VSCREENINFO failed"); + return 0; + } + + /* + * this is a monstrous hack: Because the h/w accelerator is not able + * to render directly into the framebuffer, we need to copy its + * internal framebuffer out to the fb. + * oem[0] is used to access the fd of internal fb. + * All this is needed only in standalone mode, in SurfaceFlinger mode + * we control where the GPU renders. + * We do this only if we have copybit, since this hack is needed only + * with msm7k. + */ + if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) { + copybit_device_t *copybit = mBlitEngine; + copybit_rect_t sdrect = { 0, 0, + egl_native_window_t::width, egl_native_window_t::height }; + copybit_image_t dst = { + egl_native_window_t::width, + egl_native_window_t::height, + egl_native_window_t::format, + egl_native_window_t::offset, + (void*)egl_native_window_t::base, + egl_native_window_t::fd + }; + copybit_image_t src = { + egl_native_window_t::width, + egl_native_window_t::height, + egl_native_window_t::format, // XXX: use proper format + egl_native_window_t::offset, + (void*)egl_native_window_t::base, // XXX: use proper base + egl_native_window_t::oem[0] + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); + copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it); + } + + // update the address of the buffer to draw to next + const GGLSurface& buffer = mFb[1 - mIndex]; + egl_native_window_t::offset = + intptr_t(buffer.data) - egl_native_window_t::base; + +#if SHOW_FPS + mSleep += systemTime()-now; +#endif + + mPageFlipCount++; + + // We don't support screen-size changes for now + return 0; +} + +int32_t EGLDisplaySurface::getPageFlipCount() const +{ + return mPageFlipCount; +} + +void EGLDisplaySurface::copyFrontToBack(const Region& copyback) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t dst = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[1-mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(copyback); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + /* no extra copy needed since we copied back to front instead of + * flipping */ + if (!(mFlags & PAGE_FLIP)) { + return; + } + + Region::iterator iterator(copyback); + if (iterator) { + Rect r; + uint8_t* const screen_src = mFb[ mIndex].data; + uint8_t* const screen_dst = mFb[1-mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + while (iterator.iterate(&r)) { + ssize_t h = r.bottom - r.top; + if (h) { + size_t size = (r.right - r.left) * bpp; + size_t o = (r.left + egl_native_window_t::stride * r.top) * bpp; + uint8_t* s = screen_src + o; + uint8_t* d = screen_dst + o; + if (size == bpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += bpr; + s += bpr; + } while (--h > 0); + } + } + } + } +} + +void EGLDisplaySurface::copyFrontToImage(const copybit_image_t& dst) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + uint8_t* const screen_src = mFb[ mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + memcpy((char*)dst.base + dst.offset, screen_src, + bpr*egl_native_window_t::height); + } +} + +void EGLDisplaySurface::copyBackToImage(const copybit_image_t& dst) +{ +#if HAVE_ANDROID_OS + if (mBlitEngine) { + copybit_image_t src = { + w: egl_native_window_t::stride, + h: egl_native_window_t::height, + format: egl_native_window_t::format, + offset: mFb[1-mIndex].data - mFb[0].data, + base: (void*)egl_native_window_t::base, + fd: egl_native_window_t::fd + }; + region_iterator it(Region(Rect( + egl_native_window_t::width, egl_native_window_t::height))); + mBlitEngine->blit(mBlitEngine, &dst, &src, &it); + } else +#endif + { + uint8_t* const screen_src = mFb[1-mIndex].data; + const size_t bpp = bytesPerPixel(egl_native_window_t::format); + const size_t bpr = egl_native_window_t::stride * bpp; + memcpy((char*)dst.base + dst.offset, screen_src, + bpr*egl_native_window_t::height); + } +} + + +status_t EGLDisplaySurface::mapFrameBuffer() +{ + char const * const device_template[] = { + "/dev/graphics/fb%u", + "/dev/fb%u", + 0 }; + int fd = -1; + int i=0; + char name[64]; + while ((fd==-1) && device_template[i]) { + snprintf(name, 64, device_template[i], 0); + fd = open(name, O_RDWR, 0); + i++; + } + if (fd < 0) + return -errno; + + struct fb_fix_screeninfo finfo; + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + struct fb_var_screeninfo info; + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + info.reserved[0] = 0; + info.reserved[1] = 0; + info.reserved[2] = 0; + info.xoffset = 0; + info.yoffset = 0; + info.yres_virtual = info.yres * 2; + info.bits_per_pixel = 16; + /* Explicitly request 5/6/5 */ + info.red.offset = 11; + info.red.length = 5; + info.green.offset = 5; + info.green.length = 6; + info.blue.offset = 0; + info.blue.length = 5; + info.transp.offset = 0; + info.transp.length = 0; + info.activate = FB_ACTIVATE_NOW; + + uint32_t flags = PAGE_FLIP; + if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); + } + + if (info.yres_virtual < info.yres * 2) { + info.yres_virtual = info.yres; + flags &= ~PAGE_FLIP; + LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", + info.yres_virtual, info.yres*2); + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) + return -errno; + + int refreshRate = 1000000000000000LLU / + ( + uint64_t( info.upper_margin + info.lower_margin + info.yres ) + * ( info.left_margin + info.right_margin + info.xres ) + * info.pixclock + ); + + if (refreshRate == 0) { + // bleagh, bad info from the driver + refreshRate = 60*1000; // 60 Hz + } + if (int(info.width) <= 0 || int(info.height) <= 0) { + // the driver doesn't return that information + // default to 160 dpi + info.width = ((info.xres * 25.4f)/160.0f + 0.5f); + info.height = ((info.yres * 25.4f)/160.0f + 0.5f); + } + + float xdpi = (info.xres * 25.4f) / info.width; + float ydpi = (info.yres * 25.4f) / info.height; + float fps = refreshRate / 1000.0f; + + LOGI( "using (fd=%d)\n" + "id = %s\n" + "xres = %d px\n" + "yres = %d px\n" + "xres_virtual = %d px\n" + "yres_virtual = %d px\n" + "bpp = %d\n" + "r = %2u:%u\n" + "g = %2u:%u\n" + "b = %2u:%u\n", + fd, + finfo.id, + info.xres, + info.yres, + info.xres_virtual, + info.yres_virtual, + info.bits_per_pixel, + info.red.offset, info.red.length, + info.green.offset, info.green.length, + info.blue.offset, info.blue.length + ); + + LOGI( "width = %d mm (%f dpi)\n" + "height = %d mm (%f dpi)\n" + "refresh rate = %.2f Hz\n", + info.width, xdpi, + info.height, ydpi, + fps + ); + + + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) + return -errno; + + if (finfo.smem_len <= 0) + return -errno; + + /* + * Open and map the display. + */ + + void* buffer = (uint16_t*) mmap( + 0, finfo.smem_len, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, 0); + + if (buffer == MAP_FAILED) + return -errno; + + // at least for now, always clear the fb + memset(buffer, 0, finfo.smem_len); + + uint8_t* offscreen[2]; + offscreen[0] = (uint8_t*)buffer; + if (flags & PAGE_FLIP) { + offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres; + } else { + offscreen[1] = (uint8_t*)malloc(finfo.smem_len); + if (offscreen[1] == 0) { + munmap(buffer, finfo.smem_len); + return NO_MEMORY; + } + } + + mFlags = flags; + mInfo = info; + mFinfo = finfo; + mSize = finfo.smem_len; + mIndex = 0; + for (int i=0 ; i<2 ; i++) { + mFb[i].version = sizeof(GGLSurface); + mFb[i].width = info.xres; + mFb[i].height = info.yres; + mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3); + mFb[i].data = (GGLubyte*)(offscreen[i]); + mFb[i].format = GGL_PIXEL_FORMAT_RGB_565; + } + return fd; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp new file mode 100644 index 0000000..f1071cf --- /dev/null +++ b/libs/ui/EGLNativeWindowSurface.cpp @@ -0,0 +1,161 @@ +/* +** +** Copyright 2007 The Android Open Source Project +** +** Licensed under the Apache License Version 2.0(the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing software +** distributed under the License is distributed on an "AS IS" BASIS +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "EGLNativeWindowSurface" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> + +#include <ui/SurfaceComposerClient.h> +#include <ui/DisplayInfo.h> +#include <ui/Rect.h> + +#include <EGL/egl.h> + +#include <pixelflinger/format.h> + +#include <ui/EGLNativeWindowSurface.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +EGLNativeWindowSurface::EGLNativeWindowSurface(const sp<Surface>& surface) + : EGLNativeSurface<EGLNativeWindowSurface>(), + mSurface(surface), mConnected(false) +{ + egl_native_window_t::magic = 0x600913; + egl_native_window_t::version = sizeof(egl_native_window_t); + egl_native_window_t::ident = 0; + egl_native_window_t::incRef = &EGLNativeWindowSurface::hook_incRef; + egl_native_window_t::decRef = &EGLNativeWindowSurface::hook_decRef; + egl_native_window_t::swapBuffers = &EGLNativeWindowSurface::hook_swapBuffers; + egl_native_window_t::connect = &EGLNativeWindowSurface::hook_connect; + egl_native_window_t::disconnect = &EGLNativeWindowSurface::hook_disconnect; + + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + egl_native_window_t::xdpi = dinfo.xdpi; + egl_native_window_t::ydpi = dinfo.ydpi; + egl_native_window_t::fps = dinfo.fps; + egl_native_window_t::flags= EGL_NATIVES_FLAG_DESTROY_BACKBUFFER; +} + +EGLNativeWindowSurface::~EGLNativeWindowSurface() +{ + disconnect(); + mSurface.clear(); + magic = 0; +} + +void EGLNativeWindowSurface::hook_incRef(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); + that->incStrong(that); +} + +void EGLNativeWindowSurface::hook_decRef(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); + that->decStrong(that); +} + +void EGLNativeWindowSurface::hook_connect(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); + that->connect(); +} + +void EGLNativeWindowSurface::hook_disconnect(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); + that->disconnect(); +} + +uint32_t EGLNativeWindowSurface::hook_swapBuffers(NativeWindowType window) +{ + EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); + return that->swapBuffers(); +} + +void EGLNativeWindowSurface::setSwapRectangle(int l, int t, int w, int h) +{ + mSurface->setSwapRectangle(Rect(l, t, l+w, t+h)); +} + +uint32_t EGLNativeWindowSurface::swapBuffers() +{ + const int w = egl_native_window_t::width; + const int h = egl_native_window_t::height; + const sp<Surface>& surface(mSurface); + Surface::SurfaceInfo info; + surface->unlockAndPost(); + surface->lock(&info); + // update the address of the buffer to draw to next + egl_native_window_t::base = intptr_t(info.base); + egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); + + // update size if it changed + if (w != int(info.w) || h != int(info.h)) { + egl_native_window_t::width = info.w; + egl_native_window_t::height = info.h; + egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); + egl_native_window_t::format = info.format; + return EGL_NATIVES_FLAG_SIZE_CHANGED; + } + return 0; +} + +void EGLNativeWindowSurface::connect() +{ + if (!mConnected) { + Surface::SurfaceInfo info; + mSurface->lock(&info); + mSurface->setSwapRectangle(Rect(info.w, info.h)); + mConnected = true; + + egl_native_window_t::width = info.w; + egl_native_window_t::height = info.h; + egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); + egl_native_window_t::format = info.format; + egl_native_window_t::base = intptr_t(info.base); + egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); + // FIXME: egl_native_window_t::memory_type used to be set from + // mSurface, but we wanted to break this dependency. We set it to + // GPU because the software rendered doesn't care, but the h/w + // accelerator needs it. Eventually, this value should go away + // completely, since memory will be managed by OpenGL. + egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_GPU; + egl_native_window_t::fd = 0; + } +} + +void EGLNativeWindowSurface::disconnect() +{ + if (mConnected) { + mSurface->unlock(); + mConnected = false; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp new file mode 100644 index 0000000..3b29b09 --- /dev/null +++ b/libs/ui/EventHub.cpp @@ -0,0 +1,793 @@ +// +// Copyright 2005 The Android Open Source Project +// +// Handle events, like key input and vsync. +// +// The goal is to provide an optimized solution for Linux, not an +// implementation that works well across all platforms. We expect +// events to arrive on file descriptors, so that we can use a select() +// select() call to sleep. +// +// We can't select() on anything but network sockets in Windows, so we +// provide an alternative implementation of waitEvent for that platform. +// +#define LOG_TAG "EventHub" + +//#define LOG_NDEBUG 0 + +#include <ui/EventHub.h> +#include <hardware_legacy/power.h> + +#include <cutils/properties.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/Timers.h> +#include <utils.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <memory.h> +#include <errno.h> +#include <assert.h> + +#include "KeyLayoutMap.h" + +#include <string.h> +#include <stdint.h> +#include <dirent.h> +#ifdef HAVE_INOTIFY +# include <sys/inotify.h> +#endif +#ifdef HAVE_ANDROID_OS +# include <sys/limits.h> /* not part of Linux */ +#endif +#include <sys/poll.h> +#include <sys/ioctl.h> + +/* this macro is used to tell if "bit" is set in "array" + * it selects a byte from the array, and does a boolean AND + * operation with a byte that only has the relevant bit set. + * eg. to check for the 12th bit, we do (array[1] & 1<<4) + */ +#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) + +#define ID_MASK 0x0000ffff +#define SEQ_MASK 0x7fff0000 +#define SEQ_SHIFT 16 +#define id_to_index(id) ((id&ID_MASK)+1) + +namespace android { + +static const char *WAKE_LOCK_ID = "KeyEvents"; +static const char *device_path = "/dev/input"; + +/* return the larger integer */ +static inline int max(int v1, int v2) +{ + return (v1 > v2) ? v1 : v2; +} + +EventHub::device_t::device_t(int32_t _id, const char* _path) + : id(_id), path(_path), classes(0) + , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) { +} + +EventHub::device_t::~device_t() { + delete [] keyBitmask; + delete layoutMap; +} + +EventHub::EventHub(void) + : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) + , mDevicesById(0), mNumDevicesById(0) + , mOpeningDevices(0), mClosingDevices(0) + , mDevices(0), mFDs(0), mFDCount(0) +{ + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); +#ifdef EV_SW + memset(mSwitches, 0, sizeof(mSwitches)); +#endif +} + +/* + * Clean up. + */ +EventHub::~EventHub(void) +{ + release_wake_lock(WAKE_LOCK_ID); + // we should free stuff here... +} + +void EventHub::onFirstRef() +{ + mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; +} + +status_t EventHub::errorCheck() const +{ + return mError; +} + +String8 EventHub::getDeviceName(int32_t deviceId) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return String8(); + return device->name; +} + +uint32_t EventHub::getDeviceClasses(int32_t deviceId) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return 0; + return device->classes; +} + +int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue, + int* outMaxValue, int* outFlat, int* outFuzz) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + if (device == NULL) return -1; + + struct input_absinfo info; + + if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) { + LOGE("Error reading absolute controller %d for device %s fd %d\n", + axis, device->name.string(), mFDs[id_to_index(device->id)].fd); + return -1; + } + *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); + } + } +#endif + return -1; +} + +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+1)/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; + } + } +#endif + + return -1; +} + +int EventHub::getScancodeState(int code) const +{ + return getScancodeState(mFirstKeyboardId, code); +} + +int EventHub::getScancodeState(int32_t deviceId, int code) 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+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + return test_bit(code, key_bitmask) ? 1 : 0; + } + } + + return -1; +} + +int EventHub::getKeycodeState(int code) const +{ + return getKeycodeState(mFirstKeyboardId, code); +} + +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; + + Vector<int32_t> scanCodes; + device->layoutMap->findScancodes(code, &scanCodes); + + uint8_t key_bitmask[(KEY_MAX+1)/8]; + memset(key_bitmask, 0, sizeof(key_bitmask)); + if (ioctl(mFDs[id_to_index(device->id)].fd, + EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { + #if 0 + for (size_t i=0; i<=KEY_MAX; i++) { + LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask)); + } + #endif + const size_t N = scanCodes.size(); + for (size_t i=0; i<N && i<=KEY_MAX; i++) { + 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 0; +} + +EventHub::device_t* EventHub::getDevice(int32_t deviceId) const +{ + if (deviceId == 0) deviceId = mFirstKeyboardId; + int32_t id = deviceId & ID_MASK; + if (id >= mNumDevicesById || id < 0) return NULL; + device_t* dev = mDevicesById[id].device; + if (dev->id == deviceId) { + return dev; + } + 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) +{ + *outDeviceId = 0; + *outType = 0; + *outScancode = 0; + *outKeycode = 0; + *outFlags = 0; + *outValue = 0; + *outWhen = 0; + + status_t err; + + fd_set readfds; + int maxFd = -1; + int cc; + int i; + int res; + int pollres; + struct input_event iev; + + // Note that we only allow one caller to getEvent(), so don't need + // to do locking here... only when adding/removing devices. + + while(1) { + + // First, report any devices that had last been added/removed. + if (mClosingDevices != NULL) { + device_t* device = mClosingDevices; + LOGV("Reporting device closed: id=0x%x, name=%s\n", + device->id, device->path.string()); + mClosingDevices = device->next; + *outDeviceId = device->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = DEVICE_REMOVED; + 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; + return true; + } + + release_wake_lock(WAKE_LOCK_ID); + + pollres = poll(mFDs, mFDCount, -1); + + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + + if (pollres <= 0) { + if (errno != EINTR) { + LOGW("select failed (errno=%d)\n", errno); + usleep(100000); + } + continue; + } + + //printf("poll %d, returned %d\n", mFDCount, pollres); + + // mFDs[0] is used for inotify, so process regular events starting at mFDs[1] + for(i = 1; i < mFDCount; i++) { + if(mFDs[i].revents) { + LOGV("revents for %d = 0x%08x", i, mFDs[i].revents); + if(mFDs[i].revents & POLLIN) { + res = read(mFDs[i].fd, &iev, sizeof(iev)); + if (res == sizeof(iev)) { + LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", + mDevices[i]->path.string(), + (int) iev.time.tv_sec, (int) iev.time.tv_usec, + iev.type, iev.code, iev.value); + *outDeviceId = mDevices[i]->id; + if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0; + *outType = iev.type; + *outScancode = iev.code; + if (iev.type == EV_KEY) { + err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags); + LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n", + iev.code, *outKeycode, *outFlags, err); + if (err != 0) { + *outKeycode = 0; + *outFlags = 0; + } + } else { + *outKeycode = iev.code; + } + *outValue = iev.value; + *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec); + return true; + } else { + if (res<0) { + LOGW("could not get event (errno=%d)", errno); + } else { + LOGE("could not get event (wrong size: %d)", res); + } + continue; + } + } + } + } + + // read_notify() will modify mFDs and mFDCount, so this must be done after + // processing all other events. + if(mFDs[0].revents & POLLIN) { + read_notify(mFDs[0].fd); + } + } +} + +/* + * Open the platform-specific input device. + */ +bool EventHub::openPlatformInput(void) +{ + /* + * Open platform-specific input device(s). + */ + int res; + + mFDCount = 1; + mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); + mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); + mFDs[0].events = POLLIN; + mDevices[0] = NULL; +#ifdef HAVE_INOTIFY + mFDs[0].fd = inotify_init(); + res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE); + if(res < 0) { + LOGE("could not add watch for %s, %s\n", device_path, strerror(errno)); + } +#else + /* + * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. + * We allocate space for it and set it to something invalid. + */ + mFDs[0].fd = -1; +#endif + + res = scan_dir(device_path); + if(res < 0) { + LOGE("scan dir failed for %s\n", device_path); + //open_device("/dev/input/event0"); + } + + return true; +} + +/* + * Inspect the known devices to determine whether physical keys exist for the given + * framework-domain key codes. + */ +bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) { + for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { + outFlags[codeIndex] = 0; + + // check each available hardware device for support for this keycode + Vector<int32_t> scanCodes; + for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) { + if (mDevices[n]) { + status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); + if (!err) { + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) { + outFlags[codeIndex] = 1; + break; + } + } + } + } + } + } + + return true; +} + +// ---------------------------------------------------------------------------- + +int EventHub::open_device(const char *deviceName) +{ + int version; + int fd; + struct pollfd *new_mFDs; + device_t **new_devices; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + LOGV("Opening device: %s", deviceName); + + AutoMutex _l(mLock); + + fd = open(deviceName, O_RDWR); + if(fd < 0) { + LOGE("could not open %s, %s\n", deviceName, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGVERSION, &version)) { + LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); + return -1; + } + if(ioctl(fd, EVIOCGID, &id)) { + LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); + return -1; + } + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); + name[0] = '\0'; + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); + location[0] = '\0'; + } + if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); + idstr[0] = '\0'; + } + + int devid = 0; + while (devid < mNumDevicesById) { + if (mDevicesById[devid].device == NULL) { + break; + } + devid++; + } + if (devid >= mNumDevicesById) { + device_ent* new_devids = (device_ent*)realloc(mDevicesById, + sizeof(mDevicesById[0]) * (devid + 1)); + if (new_devids == NULL) { + LOGE("out of memory"); + return -1; + } + mDevicesById = new_devids; + mNumDevicesById = devid+1; + mDevicesById[devid].device = NULL; + mDevicesById[devid].seq = 0; + } + + mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; + if (mDevicesById[devid].seq == 0) { + mDevicesById[devid].seq = 1<<SEQ_SHIFT; + } + + new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); + new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); + if (new_mFDs == NULL || new_devices == NULL) { + LOGE("out of memory"); + return -1; + } + mFDs = new_mFDs; + mDevices = new_devices; + +#if 0 + LOGI("add device %d: %s\n", mFDCount, deviceName); + LOGI(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + id.bustype, id.vendor, id.product, id.version); + LOGI(" name: \"%s\"\n", name); + LOGI(" location: \"%s\"\n" + " id: \"%s\"\n", location, idstr); + LOGI(" version: %d.%d.%d\n", + version >> 16, (version >> 8) & 0xff, version & 0xff); +#endif + + device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName); + if (device == NULL) { + LOGE("out of memory"); + return -1; + } + + mFDs[mFDCount].fd = fd; + mFDs[mFDCount].events = POLLIN; + + // figure out the kinds of events the device reports + uint8_t key_bitmask[(KEY_MAX+1)/8]; + 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+1)/8); 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; + // 'Q' key support = cheap test of whether this is an alpha-capable kbd + if (test_bit(KEY_Q, key_bitmask)) { + device->classes |= CLASS_ALPHAKEY; + } + break; + } + } + if ((device->classes & CLASS_KEYBOARD) != 0) { + device->keyBitmask = new uint8_t[(KEY_MAX+1)/8]; + if (device->keyBitmask != NULL) { + memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); + } else { + delete device; + LOGE("out of memory allocating key bitmask"); + return -1; + } + } + } + if (test_bit(BTN_MOUSE, key_bitmask)) { + uint8_t rel_bitmask[(REL_MAX+1)/8]; + memset(rel_bitmask, 0, sizeof(rel_bitmask)); + LOGV("Getting relative controllers..."); + 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; + } + } + } + if (test_bit(BTN_TOUCH, key_bitmask)) { + uint8_t abs_bitmask[(ABS_MAX+1)/8]; + memset(abs_bitmask, 0, sizeof(abs_bitmask)); + LOGV("Getting absolute controllers..."); + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) + { + if (test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { + device->classes |= CLASS_TOUCHSCREEN; + } + } + } + +#ifdef EV_SW + // figure out the switches this device reports + uint8_t sw_bitmask[(SW_MAX+1)/8]; + memset(sw_bitmask, 0, sizeof(sw_bitmask)); + 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)) { + if (mSwitches[i] == 0) { + mSwitches[i] = device->id; + } + } + } + } +#endif + + LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", + deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); + + if ((device->classes&CLASS_KEYBOARD) != 0) { + char devname[101]; + char tmpfn[101]; + char keylayoutFilename[300]; + + // a more descriptive name + ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname); + devname[sizeof(devname)-1] = 0; + device->name = devname; + + // replace all the spaces with underscores + strcpy(tmpfn, devname); + for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) + *p = '_'; + + // find the .kl file we need for this device + const char* root = getenv("ANDROID_ROOT"); + snprintf(keylayoutFilename, sizeof(keylayoutFilename), + "%s/usr/keylayout/%s.kl", root, tmpfn); + bool defaultKeymap = false; + if (access(keylayoutFilename, R_OK)) { + snprintf(keylayoutFilename, sizeof(keylayoutFilename), + "%s/usr/keylayout/%s", root, "qwerty.kl"); + defaultKeymap = true; + } + device->layoutMap->load(keylayoutFilename); + + // tell the world about the devname (the descriptive name) + int32_t publicID; + if (!mHaveFirstKeyboard && !defaultKeymap) { + publicID = 0; + // the built-in keyboard has a well-known device ID of 0, + // this device better not go away. + mHaveFirstKeyboard = true; + mFirstKeyboardId = device->id; + } else { + publicID = device->id; + // ensure mFirstKeyboardId is set to -something-. + if (mFirstKeyboardId == 0) { + mFirstKeyboardId = device->id; + } + } + char propName[100]; + sprintf(propName, "hw.keyboards.%u.devname", publicID); + property_set(propName, devname); + + LOGI("New keyboard: publicID=%d device->id=%d devname='%s' propName='%s' keylayout='%s'\n", + publicID, device->id, devname, propName, keylayoutFilename); + } + + LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", + deviceName, device, mFDCount, devid, device->classes); + + mDevicesById[devid].device = device; + device->next = mOpeningDevices; + mOpeningDevices = device; + mDevices[mFDCount] = device; + + mFDCount++; + return 0; +} + +int EventHub::close_device(const char *deviceName) +{ + AutoMutex _l(mLock); + + int i; + for(i = 1; i < mFDCount; i++) { + if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { + //LOGD("remove device %d: %s\n", i, deviceName); + device_t* device = mDevices[i]; + int count = mFDCount - i - 1; + int index = (device->id&ID_MASK); + mDevicesById[index].device = NULL; + memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); + memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); + +#ifdef EV_SW + for (int j=0; j<EV_SW; j++) { + if (mSwitches[j] == device->id) { + mSwitches[j] = 0; + } + } +#endif + + device->next = mClosingDevices; + mClosingDevices = device; + + mFDCount--; + + uint32_t publicID; + if (device->id == mFirstKeyboardId) { + LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", + device->path.string(), mFirstKeyboardId); + mFirstKeyboardId = 0; + publicID = 0; + } else { + publicID = device->id; + } + // clear the property + char propName[100]; + sprintf(propName, "hw.keyboards.%u.devname", publicID); + property_set(propName, NULL); + return 0; + } + } + LOGE("remote device: %s not found\n", deviceName); + return -1; +} + +int EventHub::read_notify(int nfd) +{ +#ifdef HAVE_INOTIFY + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if(res < (int)sizeof(*event)) { + if(errno == EINTR) + return 0; + LOGW("could not get event, %s\n", strerror(errno)); + return 1; + } + //printf("got %d bytes of event information\n", res); + + strcpy(devname, device_path); + filename = devname + strlen(devname); + *filename++ = '/'; + + while(res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); + if(event->len) { + strcpy(filename, event->name); + if(event->mask & IN_CREATE) { + open_device(devname); + } + else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } +#endif + return 0; +} + + +int EventHub::scan_dir(const char *dirname) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + dir = opendir(dirname); + if(dir == NULL) + return -1; + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + while((de = readdir(dir))) { + if(de->d_name[0] == '.' && + (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + continue; + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + +}; // namespace android diff --git a/libs/ui/EventRecurrence.cpp b/libs/ui/EventRecurrence.cpp new file mode 100644 index 0000000..b436b50 --- /dev/null +++ b/libs/ui/EventRecurrence.cpp @@ -0,0 +1,484 @@ +/* + * Copyright 2006 The Android Open Source Project + */ + +#include <pim/EventRecurrence.h> +#include <utils/String8.h> +#include <stdio.h> +#include <limits.h> + +namespace android { + +#define FAIL_HERE() do { \ + printf("Parsing failed at line %d\n", __LINE__); \ + return UNKNOWN_ERROR; \ + } while(0) + +EventRecurrence::EventRecurrence() + :freq((freq_t)0), + until(), + count(0), + interval(0), + bysecond(0), + bysecondCount(0), + byminute(0), + byminuteCount(0), + byhour(0), + byhourCount(0), + byday(0), + bydayNum(0), + bydayCount(0), + bymonthday(0), + bymonthdayCount(0), + byyearday(0), + byyeardayCount(0), + byweekno(0), + byweeknoCount(0), + bymonth(0), + bymonthCount(0), + bysetpos(0), + bysetposCount(0), + wkst(0) +{ +} + +EventRecurrence::~EventRecurrence() +{ + delete[] bysecond; + delete[] byminute; + delete[] byhour; + delete[] byday; + delete[] bydayNum; + delete[] byyearday; + delete[] bymonthday; + delete[] byweekno; + delete[] bymonth; + delete[] bysetpos; +} + +enum LHS { + NONE_LHS = 0, + FREQ, + UNTIL, + COUNT, + INTERVAL, + BYSECOND, + BYMINUTE, + BYHOUR, + BYDAY, + BYMONTHDAY, + BYYEARDAY, + BYWEEKNO, + BYMONTH, + BYSETPOS, + WKST +}; + +struct LHSProc +{ + const char16_t* text; + size_t textSize; + uint32_t value; +}; + +const char16_t FREQ_text[] = { 'F', 'R', 'E', 'Q' }; +const char16_t UNTIL_text[] = { 'U', 'N', 'T', 'I', 'L' }; +const char16_t COUNT_text[] = { 'C', 'O', 'U', 'N', 'T' }; +const char16_t INTERVAL_text[] = { 'I', 'N', 'T', 'E', 'R', 'V', 'A', 'L'}; +const char16_t BYSECOND_text[] = { 'B', 'Y', 'S', 'E', 'C', 'O', 'N', 'D' }; +const char16_t BYMINUTE_text[] = { 'B', 'Y', 'M', 'I', 'N', 'U', 'T', 'E' }; +const char16_t BYHOUR_text[] = { 'B', 'Y', 'H', 'O', 'U', 'R' }; +const char16_t BYDAY_text[] = { 'B', 'Y', 'D', 'A', 'Y' }; +const char16_t BYMONTHDAY_text[] = { 'B','Y','M','O','N','T','H','D','A','Y' }; +const char16_t BYYEARDAY_text[] = { 'B','Y','Y','E','A','R','D','A','Y' }; +const char16_t BYWEEKNO_text[] = { 'B', 'Y', 'W', 'E', 'E', 'K', 'N', 'O' }; +const char16_t BYMONTH_text[] = { 'B', 'Y', 'M', 'O', 'N', 'T', 'H' }; +const char16_t BYSETPOS_text[] = { 'B', 'Y', 'S', 'E', 'T', 'P', 'O', 'S' }; +const char16_t WKST_text[] = { 'W', 'K', 'S', 'T' }; + +#define SIZ(x) (sizeof(x)/sizeof(x[0])) + +const LHSProc LHSPROC[] = { + { FREQ_text, SIZ(FREQ_text), FREQ }, + { UNTIL_text, SIZ(UNTIL_text), UNTIL }, + { COUNT_text, SIZ(COUNT_text), COUNT }, + { INTERVAL_text, SIZ(INTERVAL_text), INTERVAL }, + { BYSECOND_text, SIZ(BYSECOND_text), BYSECOND }, + { BYMINUTE_text, SIZ(BYMINUTE_text), BYMINUTE }, + { BYHOUR_text, SIZ(BYHOUR_text), BYHOUR }, + { BYDAY_text, SIZ(BYDAY_text), BYDAY }, + { BYMONTHDAY_text, SIZ(BYMONTHDAY_text), BYMONTHDAY }, + { BYYEARDAY_text, SIZ(BYYEARDAY_text), BYYEARDAY }, + { BYWEEKNO_text, SIZ(BYWEEKNO_text), BYWEEKNO }, + { BYMONTH_text, SIZ(BYMONTH_text), BYMONTH }, + { BYSETPOS_text, SIZ(BYSETPOS_text), BYSETPOS }, + { WKST_text, SIZ(WKST_text), WKST }, + { NULL, 0, NONE_LHS }, +}; + +const char16_t SECONDLY_text[] = { 'S','E','C','O','N','D','L','Y' }; +const char16_t MINUTELY_text[] = { 'M','I','N','U','T','E','L','Y' }; +const char16_t HOURLY_text[] = { 'H','O','U','R','L','Y' }; +const char16_t DAILY_text[] = { 'D','A','I','L','Y' }; +const char16_t WEEKLY_text[] = { 'W','E','E','K','L','Y' }; +const char16_t MONTHLY_text[] = { 'M','O','N','T','H','L','Y' }; +const char16_t YEARLY_text[] = { 'Y','E','A','R','L','Y' }; + +typedef LHSProc FreqProc; + +const FreqProc FREQPROC[] = { + { SECONDLY_text, SIZ(SECONDLY_text), EventRecurrence::SECONDLY }, + { MINUTELY_text, SIZ(MINUTELY_text), EventRecurrence::MINUTELY }, + { HOURLY_text, SIZ(HOURLY_text), EventRecurrence::HOURLY }, + { DAILY_text, SIZ(DAILY_text), EventRecurrence::DAILY }, + { WEEKLY_text, SIZ(WEEKLY_text), EventRecurrence::WEEKLY }, + { MONTHLY_text, SIZ(MONTHLY_text), EventRecurrence::MONTHLY }, + { YEARLY_text, SIZ(YEARLY_text), EventRecurrence::YEARLY }, + { NULL, 0, NONE_LHS }, +}; + +const char16_t SU_text[] = { 'S','U' }; +const char16_t MO_text[] = { 'M','O' }; +const char16_t TU_text[] = { 'T','U' }; +const char16_t WE_text[] = { 'W','E' }; +const char16_t TH_text[] = { 'T','H' }; +const char16_t FR_text[] = { 'F','R' }; +const char16_t SA_text[] = { 'S','A' }; + +const FreqProc WEEKDAYPROC[] = { + { SU_text, SIZ(SU_text), EventRecurrence::SU }, + { MO_text, SIZ(MO_text), EventRecurrence::MO }, + { TU_text, SIZ(TU_text), EventRecurrence::TU }, + { WE_text, SIZ(WE_text), EventRecurrence::WE }, + { TH_text, SIZ(TH_text), EventRecurrence::TH }, + { FR_text, SIZ(FR_text), EventRecurrence::FR }, + { SA_text, SIZ(SA_text), EventRecurrence::SA }, + { NULL, 0, NONE_LHS }, +}; + +// returns the index into LHSPROC for the match or -1 if not found +inline static int +match_proc(const LHSProc* p, const char16_t* str, size_t len) +{ + int i = 0; + while (p->text != NULL) { + if (p->textSize == len) { + if (0 == memcmp(p->text, str, len*sizeof(char16_t))) { + return i; + } + } + p++; + i++; + } + return -1; +} + +// rangeMin and rangeMax are inclusive +static status_t +parse_int(const char16_t* str, size_t len, int* out, + int rangeMin, int rangeMax, bool zeroOK) +{ + char16_t c; + size_t i=0; + + if (len == 0) { + FAIL_HERE(); + } + bool negative = false; + c = str[0]; + if (c == '-' ) { + negative = true; + i++; + } + else if (c == '+') { + i++; + } + int n = 0; + for (; i<len; i++) { + c = str[i]; + if (c < '0' || c > '9') { + FAIL_HERE(); + } + int prev = n; + n *= 10; + // the spec doesn't address how big these numbers can be, + // so we're not going to worry about not being able to represent + // INT_MIN, and if we're going to wrap, we'll just clamp to + // INT_MAX instead + if (n < prev) { + n = INT_MAX; + } else { + n += c - '0'; + } + } + if (negative) { + n = -n; + } + if (n < rangeMin || n > rangeMax) { + FAIL_HERE(); + } + if (!zeroOK && n == 0) { + FAIL_HERE(); + } + *out = n; + return NO_ERROR; +} + +static status_t +parse_int_list(const char16_t* str, size_t len, int* countOut, int** listOut, + int rangeMin, int rangeMax, bool zeroOK, + status_t (*func)(const char16_t*,size_t,int*,int,int,bool)=parse_int) +{ + status_t err; + + if (len == 0) { + *countOut = 0; + *listOut = NULL; + return NO_ERROR; + } + + // make one pass through looking for commas so we know how big to make our + // out array. + int count = 1; + for (size_t i=0; i<len; i++) { + if (str[i] == ',') { + count++; + } + } + + int* list = new int[count]; + const char16_t* p = str; + int commaIndex = 0; + size_t i; + + for (i=0; i<len; i++) { + if (str[i] == ',') { + err = func(p, (str+i-p), list+commaIndex, rangeMin, + rangeMax, zeroOK); + if (err != NO_ERROR) { + goto bail; + } + commaIndex++; + p = str+i+1; + } + } + + err = func(p, (str+i-p), list+commaIndex, rangeMin, rangeMax, zeroOK); + if (err != NO_ERROR) { + goto bail; + } + commaIndex++; + + *countOut = count; + *listOut = list; + + return NO_ERROR; + +bail: + delete[] list; + FAIL_HERE(); +} + +// the numbers here are small, so we pack them both into one value, and then +// split it out later. it lets us reuse all the comma separated list code. +static status_t +parse_byday(const char16_t* s, size_t len, int* out, + int rangeMin, int rangeMax, bool zeroOK) +{ + status_t err; + int n = 0; + const char16_t* p = s; + size_t plen = len; + + if (len > 0) { + char16_t c = s[0]; + if (c == '-' || c == '+' || (c >= '0' && c <= '9')) { + if (len > 1) { + size_t nlen = 0; + c = s[nlen]; + while (nlen < len + && (c == '-' || c == '+' || (c >= '0' && c <= '9'))) { + c = s[nlen]; + nlen++; + } + if (nlen > 0) { + nlen--; + err = parse_int(s, nlen, &n, rangeMin, rangeMax, zeroOK); + if (err != NO_ERROR) { + FAIL_HERE(); + } + p += nlen; + plen -= nlen; + } + } + } + + int index = match_proc(WEEKDAYPROC, p, plen); + if (index >= 0) { + *out = (0xffff0000 & WEEKDAYPROC[index].value) + | (0x0000ffff & n); + return NO_ERROR; + } + } + return UNKNOWN_ERROR; +} + +static void +postprocess_byday(int count, int* byday, int** bydayNum) +{ + int* bdn = new int[count]; + *bydayNum = bdn; + for (int i=0; i<count; i++) { + uint32_t v = byday[i]; + int16_t num = v & 0x0000ffff; + byday[i] = v & 0xffff0000; + // will sign extend: + bdn[i] = num; + } +} + +#define PARSE_INT_LIST_CHECKED(name, rangeMin, rangeMax, zeroOK) \ + if (name##Count != 0 || NO_ERROR != parse_int_list(s, slen, \ + &name##Count, &name, rangeMin, rangeMax, zeroOK)) { \ + FAIL_HERE(); \ + } +status_t +EventRecurrence::parse(const String16& str) +{ + char16_t const* work = str.string(); + size_t len = str.size(); + + int lhsIndex = NONE_LHS; + int index; + + size_t start = 0; + for (size_t i=0; i<len; i++) { + char16_t c = work[i]; + if (c != ';' && i == len-1) { + c = ';'; + i++; + } + if (c == ';' || c == '=') { + if (i != start) { + const char16_t* s = work+start; + const size_t slen = i-start; + + String8 thestring(String16(s, slen)); + + switch (c) + { + case '=': + if (lhsIndex == NONE_LHS) { + lhsIndex = match_proc(LHSPROC, s, slen); + if (lhsIndex >= 0) { + break; + } + } + FAIL_HERE(); + case ';': + { + switch (LHSPROC[lhsIndex].value) + { + case FREQ: + if (this->freq != 0) { + FAIL_HERE(); + } + index = match_proc(FREQPROC, s, slen); + if (index >= 0) { + this->freq = (freq_t)FREQPROC[index].value; + } + break; + case UNTIL: + // XXX should check that this is a valid time + until.setTo(String16(s, slen)); + break; + case COUNT: + if (count != 0 + || NO_ERROR != parse_int(s, slen, + &count, INT_MIN, INT_MAX, true)) { + FAIL_HERE(); + } + break; + case INTERVAL: + if (interval != 0 + || NO_ERROR != parse_int(s, slen, + &interval, INT_MIN, INT_MAX, false)) { + FAIL_HERE(); + } + break; + case BYSECOND: + PARSE_INT_LIST_CHECKED(bysecond, 0, 59, true) + break; + case BYMINUTE: + PARSE_INT_LIST_CHECKED(byminute, 0, 59, true) + break; + case BYHOUR: + PARSE_INT_LIST_CHECKED(byhour, 0, 23, true) + break; + case BYDAY: + if (bydayCount != 0 || NO_ERROR != + parse_int_list(s, slen, &bydayCount, + &byday, -53, 53, false, + parse_byday)) { + FAIL_HERE(); + } + postprocess_byday(bydayCount, byday, &bydayNum); + break; + case BYMONTHDAY: + PARSE_INT_LIST_CHECKED(bymonthday, -31, 31, + false) + break; + case BYYEARDAY: + PARSE_INT_LIST_CHECKED(byyearday, -366, 366, + false) + break; + case BYWEEKNO: + PARSE_INT_LIST_CHECKED(byweekno, -53, 53, + false) + break; + case BYMONTH: + PARSE_INT_LIST_CHECKED(bymonth, 1, 12, false) + break; + case BYSETPOS: + PARSE_INT_LIST_CHECKED(bysetpos, + INT_MIN, INT_MAX, true) + break; + case WKST: + if (this->wkst != 0) { + FAIL_HERE(); + } + index = match_proc(WEEKDAYPROC, s, slen); + if (index >= 0) { + this->wkst = (int)WEEKDAYPROC[index].value; + } + break; + default: + FAIL_HERE(); + } + lhsIndex = NONE_LHS; + break; + } + } + + start = i+1; + } + } + } + + // enforce that there was a FREQ + if (freq == 0) { + FAIL_HERE(); + } + + // default wkst to MO if it wasn't specified + if (wkst == 0) { + wkst = MO; + } + + return NO_ERROR; +} + + +}; // namespace android + + diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp new file mode 100644 index 0000000..ab0fef1 --- /dev/null +++ b/libs/ui/ICamera.cpp @@ -0,0 +1,346 @@ +/* +** +** 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_NDEBUG 0 +#define LOG_TAG "ICamera" +#include <utils/Log.h> +#include <stdint.h> +#include <sys/types.h> +#include <utils/Parcel.h> +#include <ui/ICamera.h> + +namespace android { + +enum { + DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, + SET_PREVIEW_DISPLAY, + SET_PREVIEW_CALLBACK_FLAG, + START_PREVIEW, + STOP_PREVIEW, + AUTO_FOCUS, + TAKE_PICTURE, + SET_PARAMETERS, + GET_PARAMETERS, + CONNECT, + LOCK, + UNLOCK, + PREVIEW_ENABLED, + START_RECORDING, + STOP_RECORDING, + RECORDING_ENABLED, + RELEASE_RECORDING_FRAME, +}; + +class BpCamera: public BpInterface<ICamera> +{ +public: + BpCamera(const sp<IBinder>& impl) + : BpInterface<ICamera>(impl) + { + } + + // disconnect from camera service + void disconnect() + { + LOGV("disconnect"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(DISCONNECT, data, &reply); + } + + // pass the buffered ISurface to the camera service + status_t setPreviewDisplay(const sp<ISurface>& surface) + { + LOGV("setPreviewDisplay"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(surface->asBinder()); + remote()->transact(SET_PREVIEW_DISPLAY, data, &reply); + return reply.readInt32(); + } + + // set the preview callback flag to affect how the received frames from + // preview are handled. See Camera.h for details. + void setPreviewCallbackFlag(int flag) + { + LOGV("setPreviewCallbackFlag(%d)", flag); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(flag); + remote()->transact(SET_PREVIEW_CALLBACK_FLAG, data, &reply); + } + + // start preview mode, must call setPreviewDisplay first + status_t startPreview() + { + LOGV("startPreview"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(START_PREVIEW, data, &reply); + return reply.readInt32(); + } + + // start recording mode, must call setPreviewDisplay first + status_t startRecording() + { + LOGV("startRecording"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(START_RECORDING, data, &reply); + return reply.readInt32(); + } + + // stop preview mode + void stopPreview() + { + LOGV("stopPreview"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(STOP_PREVIEW, data, &reply); + } + + // stop recording mode + void stopRecording() + { + LOGV("stopRecording"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(STOP_RECORDING, data, &reply); + } + + void releaseRecordingFrame(const sp<IMemory>& mem) + { + LOGV("releaseRecordingFrame"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(mem->asBinder()); + remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); + } + + // check preview state + bool previewEnabled() + { + LOGV("previewEnabled"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(PREVIEW_ENABLED, data, &reply); + return reply.readInt32(); + } + + // check recording state + bool recordingEnabled() + { + LOGV("recordingEnabled"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(RECORDING_ENABLED, data, &reply); + return reply.readInt32(); + } + + // auto focus + status_t autoFocus() + { + LOGV("autoFocus"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(AUTO_FOCUS, data, &reply); + status_t ret = reply.readInt32(); + return ret; + } + + // take a picture - returns an IMemory (ref-counted mmap) + status_t takePicture() + { + LOGV("takePicture"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(TAKE_PICTURE, data, &reply); + status_t ret = reply.readInt32(); + return ret; + } + + // set preview/capture parameters - key/value pairs + status_t setParameters(const String8& params) + { + LOGV("setParameters"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeString8(params); + remote()->transact(SET_PARAMETERS, data, &reply); + return reply.readInt32(); + } + + // get preview/capture parameters - key/value pairs + String8 getParameters() const + { + LOGV("getParameters"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(GET_PARAMETERS, data, &reply); + return reply.readString8(); + } + virtual status_t connect(const sp<ICameraClient>& cameraClient) + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeStrongBinder(cameraClient->asBinder()); + remote()->transact(CONNECT, data, &reply); + return reply.readInt32(); + } + virtual status_t lock() + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(LOCK, data, &reply); + return reply.readInt32(); + } + virtual status_t unlock() + { + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(UNLOCK, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnCamera::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DISCONNECT: { + LOGV("DISCONNECT"); + CHECK_INTERFACE(ICamera, data, reply); + disconnect(); + return NO_ERROR; + } break; + case SET_PREVIEW_DISPLAY: { + LOGV("SET_PREVIEW_DISPLAY"); + CHECK_INTERFACE(ICamera, data, reply); + sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); + reply->writeInt32(setPreviewDisplay(surface)); + return NO_ERROR; + } break; + case SET_PREVIEW_CALLBACK_FLAG: { + LOGV("SET_PREVIEW_CALLBACK_TYPE"); + CHECK_INTERFACE(ICamera, data, reply); + int callback_flag = data.readInt32(); + setPreviewCallbackFlag(callback_flag); + return NO_ERROR; + } break; + case START_PREVIEW: { + LOGV("START_PREVIEW"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(startPreview()); + return NO_ERROR; + } break; + case START_RECORDING: { + LOGV("START_RECORDING"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(startRecording()); + return NO_ERROR; + } break; + case STOP_PREVIEW: { + LOGV("STOP_PREVIEW"); + CHECK_INTERFACE(ICamera, data, reply); + stopPreview(); + return NO_ERROR; + } break; + case STOP_RECORDING: { + LOGV("STOP_RECORDING"); + CHECK_INTERFACE(ICamera, data, reply); + stopRecording(); + return NO_ERROR; + } break; + case RELEASE_RECORDING_FRAME: { + LOGV("RELEASE_RECORDING_FRAME"); + CHECK_INTERFACE(ICamera, data, reply); + sp<IMemory> mem = interface_cast<IMemory>(data.readStrongBinder()); + releaseRecordingFrame(mem); + return NO_ERROR; + } break; + case PREVIEW_ENABLED: { + LOGV("PREVIEW_ENABLED"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(previewEnabled()); + return NO_ERROR; + } break; + case RECORDING_ENABLED: { + LOGV("RECORDING_ENABLED"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(recordingEnabled()); + return NO_ERROR; + } break; + case AUTO_FOCUS: { + LOGV("AUTO_FOCUS"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(autoFocus()); + return NO_ERROR; + } break; + case TAKE_PICTURE: { + LOGV("TAKE_PICTURE"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(takePicture()); + return NO_ERROR; + } break; + case SET_PARAMETERS: { + LOGV("SET_PARAMETERS"); + CHECK_INTERFACE(ICamera, data, reply); + String8 params(data.readString8()); + reply->writeInt32(setParameters(params)); + return NO_ERROR; + } break; + case GET_PARAMETERS: { + LOGV("GET_PARAMETERS"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeString8(getParameters()); + return NO_ERROR; + } break; + case CONNECT: { + CHECK_INTERFACE(ICamera, data, reply); + sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); + reply->writeInt32(connect(cameraClient)); + return NO_ERROR; + } break; + case LOCK: { + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(lock()); + return NO_ERROR; + } break; + case UNLOCK: { + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(unlock()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp new file mode 100644 index 0000000..4bec9d2 --- /dev/null +++ b/libs/ui/ICameraClient.cpp @@ -0,0 +1,185 @@ +/* +** +** 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_NDEBUG 0 +#define LOG_TAG "ICameraClient" +#include <utils/Log.h> +#include <stdint.h> +#include <sys/types.h> +#include <ui/ICameraClient.h> + +namespace android { + +enum { + SHUTTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, + RAW_CALLBACK, + JPEG_CALLBACK, + PREVIEW_CALLBACK, + ERROR_CALLBACK, + AUTOFOCUS_CALLBACK, + RECORDING_CALLBACK, +}; + +class BpCameraClient: public BpInterface<ICameraClient> +{ +public: + BpCameraClient(const sp<IBinder>& impl) + : BpInterface<ICameraClient>(impl) + { + } + + // callback to let the app know the shutter has closed, ideal for playing the shutter sound + void shutterCallback() + { + LOGV("shutterCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + remote()->transact(SHUTTER_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with picture data + void rawCallback(const sp<IMemory>& picture) + { + LOGV("rawCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(picture->asBinder()); + remote()->transact(RAW_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with picture data + void jpegCallback(const sp<IMemory>& picture) + { + LOGV("jpegCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(picture->asBinder()); + remote()->transact(JPEG_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with preview frame data + void previewCallback(const sp<IMemory>& frame) + { + LOGV("previewCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(frame->asBinder()); + remote()->transact(PREVIEW_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app with recording frame data + void recordingCallback(const sp<IMemory>& frame) + { + LOGV("recordingCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeStrongBinder(frame->asBinder()); + remote()->transact(RECORDING_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app to report error + void errorCallback(status_t error) + { + LOGV("errorCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeInt32(error); + remote()->transact(ERROR_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } + + // callback from camera service to app to report autofocus completion + void autoFocusCallback(bool focused) + { + LOGV("autoFocusCallback"); + Parcel data, reply; + data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor()); + data.writeInt32(focused); + remote()->transact(AUTOFOCUS_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(CameraClient, "android.hardware.ICameraClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnCameraClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case SHUTTER_CALLBACK: { + LOGV("SHUTTER_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + shutterCallback(); + return NO_ERROR; + } break; + case RAW_CALLBACK: { + LOGV("RAW_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp<IMemory> picture = interface_cast<IMemory>(data.readStrongBinder()); + rawCallback(picture); + return NO_ERROR; + } break; + case JPEG_CALLBACK: { + LOGV("JPEG_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp<IMemory> picture = interface_cast<IMemory>(data.readStrongBinder()); + jpegCallback(picture); + return NO_ERROR; + } break; + case PREVIEW_CALLBACK: { + LOGV("PREVIEW_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp<IMemory> frame = interface_cast<IMemory>(data.readStrongBinder()); + previewCallback(frame); + return NO_ERROR; + } break; + case RECORDING_CALLBACK: { + LOGV("RECORDING_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + sp<IMemory> frame = interface_cast<IMemory>(data.readStrongBinder()); + recordingCallback(frame); + return NO_ERROR; + } break; + case ERROR_CALLBACK: { + LOGV("ERROR_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + status_t error = data.readInt32(); + errorCallback(error); + return NO_ERROR; + } break; + case AUTOFOCUS_CALLBACK: { + LOGV("AUTOFOCUS_CALLBACK"); + CHECK_INTERFACE(ICameraClient, data, reply); + bool focused = (bool)data.readInt32(); + autoFocusCallback(focused); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/ICameraService.cpp b/libs/ui/ICameraService.cpp new file mode 100644 index 0000000..e5687fe --- /dev/null +++ b/libs/ui/ICameraService.cpp @@ -0,0 +1,77 @@ +/* +** +** 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. +*/ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> + +#include <ui/ICameraService.h> + +namespace android { + +class BpCameraService: public BpInterface<ICameraService> +{ +public: + BpCameraService(const sp<IBinder>& impl) + : BpInterface<ICameraService>(impl) + { + } + + // connect to camera service + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient) + { + Parcel data, reply; + data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); + data.writeStrongBinder(cameraClient->asBinder()); + remote()->transact(BnCameraService::CONNECT, data, &reply); + return interface_cast<ICamera>(reply.readStrongBinder()); + } +}; + +IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnCameraService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CONNECT: { + CHECK_INTERFACE(ICameraService, data, reply); + sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); + sp<ICamera> camera = connect(cameraClient); + reply->writeStrongBinder(camera->asBinder()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp new file mode 100644 index 0000000..fed47c2 --- /dev/null +++ b/libs/ui/IOverlay.cpp @@ -0,0 +1,72 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> +#include <utils/IInterface.h> + +#include <ui/IOverlay.h> + +namespace android { + +enum { + DESTROY = IBinder::FIRST_CALL_TRANSACTION, // one-way transaction +}; + +class BpOverlay : public BpInterface<IOverlay> +{ +public: + BpOverlay(const sp<IBinder>& impl) + : BpInterface<IOverlay>(impl) + { + } + + virtual void destroy() + { + Parcel data, reply; + data.writeInterfaceToken(IOverlay::getInterfaceDescriptor()); + remote()->transact(DESTROY, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnOverlay::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case DESTROY: { + CHECK_INTERFACE(IOverlay, data, reply); + destroy(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp new file mode 100644 index 0000000..d5e9f81 --- /dev/null +++ b/libs/ui/ISurface.cpp @@ -0,0 +1,164 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> +#include <utils/IMemory.h> + +#include <ui/ISurface.h> +#include <ui/Overlay.h> + + +namespace android { + +ISurface::BufferHeap::BufferHeap() + : w(0), h(0), hor_stride(0), ver_stride(0), format(0), + transform(0), flags(0) +{ +} + +ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, const sp<IMemoryHeap>& heap) + : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), + format(format), transform(0), flags(0), heap(heap) +{ +} + +ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, + int32_t hor_stride, int32_t ver_stride, + PixelFormat format, uint32_t transform, uint32_t flags, + const sp<IMemoryHeap>& heap) + : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), + format(format), transform(transform), flags(flags), heap(heap) +{ +} + + +ISurface::BufferHeap::~BufferHeap() +{ +} + +class BpSurface : public BpInterface<ISurface> +{ +public: + BpSurface(const sp<IBinder>& impl) + : BpInterface<ISurface>(impl) + { + } + + virtual status_t registerBuffers(const BufferHeap& buffers) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(buffers.w); + data.writeInt32(buffers.h); + data.writeInt32(buffers.hor_stride); + data.writeInt32(buffers.ver_stride); + data.writeInt32(buffers.format); + data.writeInt32(buffers.transform); + data.writeInt32(buffers.flags); + data.writeStrongBinder(buffers.heap->asBinder()); + remote()->transact(REGISTER_BUFFERS, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual void postBuffer(ssize_t offset) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(offset); + remote()->transact(POST_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual void unregisterBuffers() + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + remote()->transact(UNREGISTER_BUFFERS, data, &reply); + } + + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + remote()->transact(CREATE_OVERLAY, data, &reply); + return OverlayRef::readFromParcel(reply); + } +}; + +IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnSurface::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case REGISTER_BUFFERS: { + CHECK_INTERFACE(ISurface, data, reply); + BufferHeap buffer; + buffer.w = data.readInt32(); + buffer.h = data.readInt32(); + buffer.hor_stride = data.readInt32(); + buffer.ver_stride= data.readInt32(); + buffer.format = data.readInt32(); + buffer.transform = data.readInt32(); + buffer.flags = data.readInt32(); + buffer.heap = interface_cast<IMemoryHeap>(data.readStrongBinder()); + status_t err = registerBuffers(buffer); + reply->writeInt32(err); + return NO_ERROR; + } break; + case UNREGISTER_BUFFERS: { + CHECK_INTERFACE(ISurface, data, reply); + unregisterBuffers(); + return NO_ERROR; + } break; + case POST_BUFFER: { + CHECK_INTERFACE(ISurface, data, reply); + ssize_t offset = data.readInt32(); + postBuffer(offset); + return NO_ERROR; + } break; + case CREATE_OVERLAY: { + CHECK_INTERFACE(ISurface, data, reply); + int w = data.readInt32(); + int h = data.readInt32(); + int f = data.readInt32(); + sp<OverlayRef> o = createOverlay(w, h, f); + return OverlayRef::writeToParcel(reply, o); + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/ui/ISurfaceComposer.cpp b/libs/ui/ISurfaceComposer.cpp new file mode 100644 index 0000000..0fea6f9 --- /dev/null +++ b/libs/ui/ISurfaceComposer.cpp @@ -0,0 +1,277 @@ +/* + * 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. + */ + +// tag as surfaceflinger +#define LOG_TAG "SurfaceFlinger" + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> +#include <utils/IMemory.h> +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> + +#include <ui/ISurfaceComposer.h> +#include <ui/DisplayInfo.h> + +// --------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +class BpSurfaceComposer : public BpInterface<ISurfaceComposer> +{ +public: + BpSurfaceComposer(const sp<IBinder>& impl) + : BpInterface<ISurfaceComposer>(impl) + { + } + + virtual sp<ISurfaceFlingerClient> createConnection() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); + return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder()); + } + + virtual sp<IMemory> getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply); + return interface_cast<IMemory>(reply.readStrongBinder()); + } + + virtual void openGlobalTransaction() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::OPEN_GLOBAL_TRANSACTION, data, &reply); + } + + virtual void closeGlobalTransaction() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CLOSE_GLOBAL_TRANSACTION, data, &reply); + } + + virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(flags); + remote()->transact(BnSurfaceComposer::FREEZE_DISPLAY, data, &reply); + return reply.readInt32(); + } + + virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(flags); + remote()->transact(BnSurfaceComposer::UNFREEZE_DISPLAY, data, &reply); + return reply.readInt32(); + } + + virtual int setOrientation(DisplayID dpy, int orientation) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(orientation); + remote()->transact(BnSurfaceComposer::SET_ORIENTATION, data, &reply); + return reply.readInt32(); + } + + virtual void bootFinished() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); + } + + virtual status_t requestGPU( + const sp<IGPUCallback>& callback, gpu_info_t* gpu) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(callback->asBinder()); + remote()->transact(BnSurfaceComposer::REQUEST_GPU, data, &reply); + gpu->regs = interface_cast<IMemory>(reply.readStrongBinder()); + gpu->count = reply.readInt32(); + + // FIXME: for now, we don't dynamically allocate the regions array + size_t maxCount = sizeof(gpu->regions)/sizeof(*gpu->regions); + if (gpu->count > maxCount) + return BAD_VALUE; + + for (size_t i=0 ; i<gpu->count ; i++) { + gpu->regions[i].region = interface_cast<IMemory>(reply.readStrongBinder()); + gpu->regions[i].reserved = reply.readInt32(); + } + return reply.readInt32(); + } + + virtual status_t revokeGPU() + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::REVOKE_GPU, data, &reply); + return reply.readInt32(); + } + + virtual void signal() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnSurfaceComposer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + status_t err = BnInterface<ISurfaceComposer>::onTransact(code, data, reply, flags); + if (err == NO_ERROR) + return err; + + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + switch(code) { + case CREATE_CONNECTION: { + sp<IBinder> b = createConnection()->asBinder(); + reply->writeStrongBinder(b); + } break; + case OPEN_GLOBAL_TRANSACTION: { + openGlobalTransaction(); + } break; + case CLOSE_GLOBAL_TRANSACTION: { + closeGlobalTransaction(); + } break; + case SET_ORIENTATION: { + DisplayID dpy = data.readInt32(); + int orientation = data.readInt32(); + reply->writeInt32( setOrientation(dpy, orientation) ); + } break; + case FREEZE_DISPLAY: { + DisplayID dpy = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( freezeDisplay(dpy, flags) ); + } break; + case UNFREEZE_DISPLAY: { + DisplayID dpy = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( unfreezeDisplay(dpy, flags) ); + } break; + case BOOT_FINISHED: { + bootFinished(); + } break; + case REVOKE_GPU: { + reply->writeInt32( revokeGPU() ); + } break; + case SIGNAL: { + signal(); + } break; + case GET_CBLK: { + sp<IBinder> b = getCblk()->asBinder(); + reply->writeStrongBinder(b); + } break; + case REQUEST_GPU: { + // TODO: this should be protected by a permission + gpu_info_t info; + sp<IGPUCallback> callback + = interface_cast<IGPUCallback>(data.readStrongBinder()); + status_t res = requestGPU(callback, &info); + + // FIXME: for now, we don't dynamically allocate the regions array + size_t maxCount = sizeof(info.regions)/sizeof(*info.regions); + if (info.count > maxCount) + return BAD_VALUE; + + reply->writeStrongBinder(info.regs->asBinder()); + reply->writeInt32(info.count); + for (size_t i=0 ; i<info.count ; i++) { + reply->writeStrongBinder(info.regions[i].region->asBinder()); + reply->writeInt32(info.regions[i].reserved); + } + reply->writeInt32(res); + } break; + default: + return UNKNOWN_TRANSACTION; + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +enum { + // Note: BOOT_FINISHED must remain this value, it is called by ActivityManagerService. + GPU_LOST = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpGPUCallback : public BpInterface<IGPUCallback> +{ +public: + BpGPUCallback(const sp<IBinder>& impl) + : BpInterface<IGPUCallback>(impl) + { + } + + virtual void gpuLost() + { + Parcel data, reply; + data.writeInterfaceToken(IGPUCallback::getInterfaceDescriptor()); + remote()->transact(GPU_LOST, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(GPUCallback, "android.ui.IGPUCallback"); + +status_t BnGPUCallback::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GPU_LOST: { + CHECK_INTERFACE(IGPUCallback, data, reply); + gpuLost(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp new file mode 100644 index 0000000..dd6a798 --- /dev/null +++ b/libs/ui/ISurfaceFlingerClient.cpp @@ -0,0 +1,208 @@ +/* + * 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. + */ + +// tag as surfaceflinger +#define LOG_TAG "SurfaceFlinger" + +#include <stdio.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Parcel.h> +#include <utils/IMemory.h> +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> + +#include <ui/ISurface.h> +#include <ui/ISurfaceFlingerClient.h> +#include <ui/Point.h> +#include <ui/Rect.h> + +#include <private/ui/LayerState.h> + +// --------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + CREATE_SURFACE, + DESTROY_SURFACE, + SET_STATE +}; + +class BpSurfaceFlingerClient : public BpInterface<ISurfaceFlingerClient> +{ +public: + BpSurfaceFlingerClient(const sp<IBinder>& impl) + : BpInterface<ISurfaceFlingerClient>(impl) + { + } + + virtual void getControlBlocks(sp<IMemory>* ctl) const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + *ctl = interface_cast<IMemory>(reply.readStrongBinder()); + } + + virtual sp<ISurface> createSurface( surface_data_t* params, + int pid, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeInt32(display); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(flags); + remote()->transact(CREATE_SURFACE, data, &reply); + params->readFromParcel(reply); + return interface_cast<ISurface>(reply.readStrongBinder()); + } + + virtual status_t destroySurface(SurfaceID sid) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInt32(sid); + remote()->transact(DESTROY_SURFACE, data, &reply); + return reply.readInt32(); + } + + virtual status_t setState(int32_t count, const layer_state_t* states) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInt32(count); + for (int i=0 ; i<count ; i++) + states[i].write(data); + remote()->transact(SET_STATE, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnSurfaceFlingerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // codes that don't require permission check + + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + sp<IMemory> ctl; + getControlBlocks(&ctl); + reply->writeStrongBinder(ctl->asBinder()); + return NO_ERROR; + } break; + } + + // these must be checked + + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + + switch(code) { + case CREATE_SURFACE: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + surface_data_t params; + int32_t pid = data.readInt32(); + DisplayID display = data.readInt32(); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + PixelFormat format = data.readInt32(); + uint32_t flags = data.readInt32(); + sp<ISurface> s = createSurface(¶ms, pid, display, w, h, format, flags); + params.writeToParcel(reply); + reply->writeStrongBinder(s->asBinder()); + return NO_ERROR; + } break; + case DESTROY_SURFACE: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + reply->writeInt32( destroySurface( data.readInt32() ) ); + return NO_ERROR; + } break; + case SET_STATE: { + CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + int32_t count = data.readInt32(); + layer_state_t* states = new layer_state_t[count]; + for (int i=0 ; i<count ; i++) + states[i].read(data); + status_t err = setState(count, states); + delete [] states; + reply->writeInt32(err); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------- + +status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel) +{ + token = parcel.readInt32(); + identity = parcel.readInt32(); + heap[0] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); + heap[1] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); + return NO_ERROR; +} + +status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) const +{ + parcel->writeInt32(token); + parcel->writeInt32(identity); + parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); + parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp new file mode 100644 index 0000000..e891181 --- /dev/null +++ b/libs/ui/KeyCharacterMap.cpp @@ -0,0 +1,263 @@ +#define LOG_TAG "KeyCharacterMap" + +#include <ui/KeyCharacterMap.h> +#include <cutils/properties.h> + +#include <utils/Log.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <limits.h> +#include <string.h> + +struct Header +{ + char magic[8]; + unsigned int endian; + unsigned int version; + unsigned int keycount; + unsigned char kbdtype; + char padding[11]; +}; + +KeyCharacterMap::KeyCharacterMap() +{ +} + +KeyCharacterMap::~KeyCharacterMap() +{ + free(m_keys); +} + +unsigned short +KeyCharacterMap::get(int keycode, int meta) +{ + Key* k = find_key(keycode); + if (k != NULL) { + return k->data[meta & META_MASK]; + } + return 0; +} + +unsigned short +KeyCharacterMap::getNumber(int keycode) +{ + Key* k = find_key(keycode); + if (k != NULL) { + return k->number; + } + return 0; +} + +unsigned short +KeyCharacterMap::getMatch(int keycode, const unsigned short* chars, + int charsize, uint32_t modifiers) +{ + Key* k = find_key(keycode); + modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it + if (k != NULL) { + const uint16_t* data = k->data; + for (int j=0; j<charsize; j++) { + uint16_t c = chars[j]; + for (int i=0; i<(META_MASK + 1); i++) { + if ((modifiers == 0) || ((modifiers & i) != 0)) { + if (c == data[i]) { + return c; + } + } + } + } + } + return 0; +} + +unsigned short +KeyCharacterMap::getDisplayLabel(int keycode) +{ + Key* k = find_key(keycode); + if (k != NULL) { + return k->display_label; + } + return 0; +} + +bool +KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel, + unsigned short *number, unsigned short* results) +{ + Key* k = find_key(keycode); + if (k != NULL) { + memcpy(results, k->data, sizeof(short)*(META_MASK + 1)); + *number = k->number; + *displayLabel = k->display_label; + return true; + } else { + return false; + } +} + +bool +KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods) +{ + uint32_t N = m_keyCount; + for (int j=0; j<(META_MASK + 1); j++) { + Key const* keys = m_keys; + for (uint32_t i=0; i<N; i++) { + if (keys->data[j] == c) { + *key = keys->keycode; + *mods = j; + return true; + } + keys++; + } + } + return false; +} + +bool +KeyCharacterMap::getEvents(uint16_t* chars, size_t len, + Vector<int32_t>* keys, Vector<uint32_t>* modifiers) +{ + for (size_t i=0; i<len; i++) { + uint32_t k, mods; + if (find_char(chars[i], &k, &mods)) { + keys->add(k); + modifiers->add(mods); + } else { + return false; + } + } + return true; +} + +KeyCharacterMap::Key* +KeyCharacterMap::find_key(int keycode) +{ + Key* keys = m_keys; + int low = 0; + int high = m_keyCount - 1; + int mid; + int n; + while (low <= high) { + mid = (low + high) / 2; + n = keys[mid].keycode; + if (keycode < n) { + high = mid - 1; + } else if (keycode > n) { + low = mid + 1; + } else { + return keys + mid; + } + } + return NULL; +} + +KeyCharacterMap* +KeyCharacterMap::load(int id) +{ + KeyCharacterMap* rv = NULL; + char path[PATH_MAX]; + char propName[100]; + char dev[PROPERTY_VALUE_MAX]; + char tmpfn[PROPERTY_VALUE_MAX]; + int err; + const char* root = getenv("ANDROID_ROOT"); + + sprintf(propName, "hw.keyboards.%u.devname", id); + err = property_get(propName, dev, ""); + if (err > 0) { + // replace all the spaces with underscores + strcpy(tmpfn, dev); + for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) + *p = '_'; + snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn); + //LOGD("load: dev='%s' path='%s'\n", dev, path); + rv = try_file(path); + if (rv != NULL) { + return rv; + } + LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev); + } else { + LOGW("No keyboard for id %d", id); + } + + snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root); + rv = try_file(path); + if (rv == NULL) { + LOGE("Can't find any keycharmaps (also tried %s)", path); + return NULL; + } + LOGW("Using default keymap: %s", path); + + return rv; +} + +KeyCharacterMap* +KeyCharacterMap::try_file(const char* filename) +{ + KeyCharacterMap* rv = NULL; + Key* keys; + int fd; + off_t filesize; + Header header; + int err; + + fd = open(filename, O_RDONLY); + if (fd == -1) { + LOGW("Can't open keycharmap file"); + return NULL; + } + + filesize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + // validate the header + if (filesize <= (off_t)sizeof(header)) { + LOGW("Bad keycharmap - filesize=%d\n", (int)filesize); + goto cleanup1; + } + + err = read(fd, &header, sizeof(header)); + if (err == -1) { + LOGW("Error reading keycharmap file"); + goto cleanup1; + } + + if (0 != memcmp(header.magic, "keychar", 8)) { + LOGW("Bad keycharmap magic token"); + goto cleanup1; + } + if (header.endian != 0x12345678) { + LOGW("Bad keycharmap endians"); + goto cleanup1; + } + if ((header.version & 0xff) != 2) { + LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version); + goto cleanup1; + } + if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) { + LOGW("Bad keycharmap file size\n"); + goto cleanup1; + } + + // read the key data + keys = (Key*)malloc(sizeof(Key)*header.keycount); + err = read(fd, keys, sizeof(Key)*header.keycount); + if (err == -1) { + LOGW("Error reading keycharmap file"); + free(keys); + goto cleanup1; + } + + // return the object + rv = new KeyCharacterMap; + rv->m_keyCount = header.keycount; + rv->m_keys = keys; + rv->m_type = header.kbdtype; + +cleanup1: + close(fd); + + return rv; +} diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp new file mode 100644 index 0000000..15ae54c --- /dev/null +++ b/libs/ui/KeyLayoutMap.cpp @@ -0,0 +1,235 @@ +#define LOG_TAG "KeyLayoutMap" + +#include "KeyLayoutMap.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <utils/String8.h> +#include <stdlib.h> +#include <ui/KeycodeLabels.h> +#include <utils/Log.h> + +namespace android { + +KeyLayoutMap::KeyLayoutMap() + :m_status(NO_INIT), + m_keys() +{ +} + +KeyLayoutMap::~KeyLayoutMap() +{ +} + +static String8 +next_token(char const** p, int *line) +{ + bool begun = false; + const char* begin = *p; + const char* end = *p; + while (true) { + if (*end == '\n') { + (*line)++; + } + switch (*end) + { + case '#': + if (begun) { + *p = end; + return String8(begin, end-begin); + } else { + do { + begin++; + end++; + } while (*begin != '\0' && *begin != '\n'); + } + case '\0': + case ' ': + case '\n': + case '\r': + case '\t': + if (begun || (*end == '\0')) { + *p = end; + return String8(begin, end-begin); + } else { + begin++; + end++; + break; + } + default: + end++; + begun = true; + } + } +} + +static int32_t +token_to_value(const char *literal, const KeycodeLabel *list) +{ + while (list->literal) { + if (0 == strcmp(literal, list->literal)) { + return list->value; + } + list++; + } + return list->value; +} + +status_t +KeyLayoutMap::load(const char* filename) +{ + int fd = open(filename, O_RDONLY); + if (fd < 0) { + LOGE("error opening file=%s err=%s\n", filename, strerror(errno)); + m_status = errno; + return errno; + } + + off_t len = lseek(fd, 0, SEEK_END); + off_t errlen = lseek(fd, 0, SEEK_SET); + if (len < 0 || errlen < 0) { + close(fd); + LOGE("error seeking file=%s err=%s\n", filename, strerror(errno)); + m_status = errno; + return errno; + } + + char* buf = (char*)malloc(len+1); + if (read(fd, buf, len) != len) { + LOGE("error reading file=%s err=%s\n", filename, strerror(errno)); + m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); + return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); + } + errno = 0; + buf[len] = '\0'; + + int32_t scancode = -1; + int32_t keycode = -1; + uint32_t flags = 0; + uint32_t tmp; + char* end; + status_t err = NO_ERROR; + int line = 1; + char const* p = buf; + enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN; + while (true) { + String8 token = next_token(&p, &line); + if (*p == '\0') { + break; + } + switch (state) + { + case BEGIN: + if (token == "key") { + state = SCANCODE; + } else { + LOGE("%s:%d: expected key, got '%s'\n", filename, line, + token.string()); + err = BAD_VALUE; + goto done; + } + break; + case SCANCODE: + scancode = strtol(token.string(), &end, 0); + if (*end != '\0') { + LOGE("%s:%d: expected scancode (a number), got '%s'\n", + filename, line, token.string()); + goto done; + } + //LOGI("%s:%d: got scancode %d\n", filename, line, scancode ); + state = KEYCODE; + break; + case KEYCODE: + keycode = token_to_value(token.string(), KEYCODES); + //LOGI("%s:%d: got keycode %d for %s\n", filename, line, keycode, token.string() ); + if (keycode == 0) { + LOGE("%s:%d: expected keycode, got '%s'\n", + filename, line, token.string()); + goto done; + } + state = FLAG; + break; + case FLAG: + if (token == "key") { + if (scancode != -1) { + //LOGI("got key decl scancode=%d keycode=%d" + // " flags=0x%08x\n", scancode, keycode, flags); + Key k = { keycode, flags }; + m_keys.add(scancode, k); + state = SCANCODE; + scancode = -1; + keycode = -1; + flags = 0; + break; + } + } + tmp = token_to_value(token.string(), FLAGS); + //LOGI("%s:%d: got flags %x for %s\n", filename, line, tmp, token.string() ); + if (tmp == 0) { + LOGE("%s:%d: expected flag, got '%s'\n", + filename, line, token.string()); + goto done; + } + flags |= tmp; + break; + } + } + if (state == FLAG && scancode != -1 ) { + //LOGI("got key decl scancode=%d keycode=%d" + // " flags=0x%08x\n", scancode, keycode, flags); + Key k = { keycode, flags }; + m_keys.add(scancode, k); + } + +done: + free(buf); + close(fd); + + m_status = err; + return err; +} + +status_t +KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const +{ + if (m_status != NO_ERROR) { + return m_status; + } + + ssize_t index = m_keys.indexOfKey(scancode); + if (index < 0) { + //LOGW("couldn't map scancode=%d\n", scancode); + return NAME_NOT_FOUND; + } + + const Key& k = m_keys.valueAt(index); + + *keycode = k.keycode; + *flags = k.flags; + + //LOGD("mapped scancode=%d to keycode=%d flags=0x%08x\n", scancode, + // keycode, flags); + + return NO_ERROR; +} + +status_t +KeyLayoutMap::findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const +{ + if (m_status != NO_ERROR) { + return m_status; + } + + const size_t N = m_keys.size(); + for (size_t i=0; i<N; i++) { + if (m_keys.valueAt(i).keycode == keycode) { + outScancodes->add(m_keys.keyAt(i)); + } + } + + return NO_ERROR; +} + +}; diff --git a/libs/ui/KeyLayoutMap.h b/libs/ui/KeyLayoutMap.h new file mode 100644 index 0000000..43f84ce --- /dev/null +++ b/libs/ui/KeyLayoutMap.h @@ -0,0 +1,31 @@ +#ifndef KEYLAYOUTMAP_H +#define KEYLAYOUTMAP_H + +#include <utils/KeyedVector.h> + +namespace android { + +class KeyLayoutMap +{ +public: + KeyLayoutMap(); + ~KeyLayoutMap(); + + status_t load(const char* filename); + + status_t map(int32_t scancode, int32_t *keycode, uint32_t *flags) const; + status_t findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const; + +private: + struct Key { + int32_t keycode; + uint32_t flags; + }; + + status_t m_status; + KeyedVector<int32_t,Key> m_keys; +}; + +}; + +#endif // KEYLAYOUTMAP_H diff --git a/libs/ui/LayerState.cpp b/libs/ui/LayerState.cpp new file mode 100644 index 0000000..0b6374b --- /dev/null +++ b/libs/ui/LayerState.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utils/Errors.h> +#include <utils/Parcel.h> +#include <private/ui/LayerState.h> + +namespace android { + +status_t layer_state_t::write(Parcel& output) const +{ + size_t size = sizeof(layer_state_t); + + //output.writeStrongBinder(surface->asBinder()); + //size -= sizeof(surface); + + transparentRegion.write(output); + size -= sizeof(transparentRegion); + + output.write(this, size); + + return NO_ERROR; +} + +status_t layer_state_t::read(const Parcel& input) +{ + size_t size = sizeof(layer_state_t); + + //surface = interface_cast<ISurface>(input.readStrongBinder()); + //size -= sizeof(surface); + + transparentRegion.read(input); + size -= sizeof(transparentRegion); + + input.read(this, size); + + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/ui/MODULE_LICENSE_APACHE2 b/libs/ui/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libs/ui/MODULE_LICENSE_APACHE2 diff --git a/libs/ui/NOTICE b/libs/ui/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/libs/ui/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp new file mode 100644 index 0000000..b236edc --- /dev/null +++ b/libs/ui/Overlay.cpp @@ -0,0 +1,181 @@ +/* + * 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 <utils/IMemory.h> +#include <utils/Parcel.h> +#include <utils/Errors.h> +#include <utils/MemoryHeapBase.h> + +#include <ui/IOverlay.h> +#include <ui/Overlay.h> + +#include <hardware/overlay.h> + +namespace android { + +Overlay::Overlay(const sp<OverlayRef>& overlayRef) + : mOverlayRef(overlayRef), mOverlayData(0), mStatus(NO_INIT) +{ + mOverlayData = NULL; + hw_module_t const* module; + if (overlayRef != 0) { + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + if (overlay_data_open(module, &mOverlayData) == NO_ERROR) { + mStatus = mOverlayData->initialize(mOverlayData, + overlayRef->mOverlayHandle); + } + } + } +} + +Overlay::~Overlay() { + if (mOverlayData) { + overlay_data_close(mOverlayData); + } +} + +status_t Overlay::dequeueBuffer(overlay_buffer_t* buffer) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->dequeueBuffer(mOverlayData, buffer); +} + +status_t Overlay::queueBuffer(overlay_buffer_t buffer) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->queueBuffer(mOverlayData, buffer); +} + +int32_t Overlay::getBufferCount() const +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->getBufferCount(mOverlayData); +} + +void* Overlay::getBufferAddress(overlay_buffer_t buffer) +{ + if (mStatus != NO_ERROR) return NULL; + return mOverlayData->getBufferAddress(mOverlayData, buffer); +} + +void Overlay::destroy() { + if (mStatus != NO_ERROR) return; + mOverlayRef->mOverlayChannel->destroy(); +} + +status_t Overlay::getStatus() const { + return mStatus; +} + +overlay_handle_t Overlay::getHandleRef() const { + if (mStatus != NO_ERROR) return NULL; + return mOverlayRef->mOverlayHandle; +} + +uint32_t Overlay::getWidth() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mWidth; +} + +uint32_t Overlay::getHeight() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mHeight; +} + +int32_t Overlay::getFormat() const { + if (mStatus != NO_ERROR) return -1; + return mOverlayRef->mFormat; +} + +int32_t Overlay::getWidthStride() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mWidthStride; +} + +int32_t Overlay::getHeightStride() const { + if (mStatus != NO_ERROR) return 0; + return mOverlayRef->mHeightStride; +} +// ---------------------------------------------------------------------------- + +OverlayRef::OverlayRef() + : mOverlayHandle(0), + mWidth(0), mHeight(0), mFormat(0), mWidthStride(0), mHeightStride(0), + mOwnHandle(true) +{ +} + +OverlayRef::OverlayRef(overlay_handle_t handle, const sp<IOverlay>& channel, + uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs) + : mOverlayHandle(handle), mOverlayChannel(channel), + mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs), + mOwnHandle(false) +{ +} + +OverlayRef::~OverlayRef() +{ + if (mOwnHandle) { + /* FIXME: handles should be promoted to "real" API and be handled by + * the framework */ + for (int i=0 ; i<mOverlayHandle->numFds ; i++) { + close(mOverlayHandle->data[i]); + } + free((void*)mOverlayHandle); + } +} + +sp<OverlayRef> OverlayRef::readFromParcel(const Parcel& data) { + sp<OverlayRef> result; + sp<IOverlay> overlay = IOverlay::asInterface(data.readStrongBinder()); + if (overlay != NULL) { + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t f = data.readInt32(); + uint32_t ws = data.readInt32(); + uint32_t hs = data.readInt32(); + native_handle* handle = data.readNativeHandle(NULL, NULL); + + result = new OverlayRef(); + result->mOverlayHandle = handle; + result->mOverlayChannel = overlay; + result->mWidth = w; + result->mHeight = h; + result->mFormat = f; + result->mWidthStride = ws; + result->mHeightStride = hs; + } + return result; +} + +status_t OverlayRef::writeToParcel(Parcel* reply, const sp<OverlayRef>& o) { + if (o != NULL) { + reply->writeStrongBinder(o->mOverlayChannel->asBinder()); + reply->writeInt32(o->mWidth); + reply->writeInt32(o->mHeight); + reply->writeInt32(o->mFormat); + reply->writeInt32(o->mWidthStride); + reply->writeInt32(o->mHeightStride); + reply->writeNativeHandle(*(o->mOverlayHandle)); + } else { + reply->writeStrongBinder(NULL); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp new file mode 100644 index 0000000..b65ed97 --- /dev/null +++ b/libs/ui/PixelFormat.cpp @@ -0,0 +1,97 @@ +/* + * 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 <ui/PixelFormat.h> +#include <pixelflinger/format.h> + +namespace android { + +size_t PixelFormatInfo::getScanlineSize(unsigned int width) const +{ + size_t size; + if ((components >= 6) && (components <= 8)) { + // YCbCr formats are differents. + size = (width * bitsPerPixel)>>3; + } else { + size = width * bytesPerPixel; + } + return size; +} + +ssize_t bytesPerPixel(PixelFormat format) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + return (err < 0) ? err : info.bytesPerPixel; +} + +ssize_t bitsPerPixel(PixelFormat format) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + return (err < 0) ? err : info.bitsPerPixel; +} + +status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) +{ + if (format < 0) + return BAD_VALUE; + + if (info->version != sizeof(PixelFormatInfo)) + return INVALID_OPERATION; + + size_t numEntries; + const GGLFormat *i = gglGetPixelFormatTable(&numEntries) + format; + bool valid = uint32_t(format) < numEntries; + if (!valid) { + return BAD_INDEX; + } + + #define COMPONENT(name) \ + case GGL_##name: info->components = PixelFormatInfo::name; break; + + switch (i->components) { + COMPONENT(ALPHA) + COMPONENT(RGB) + COMPONENT(RGBA) + COMPONENT(LUMINANCE) + COMPONENT(LUMINANCE_ALPHA) + COMPONENT(Y_CB_CR_SP) + COMPONENT(Y_CB_CR_P) + COMPONENT(Y_CB_CR_I) + default: + return BAD_INDEX; + } + + #undef COMPONENT + + info->format = format; + info->bytesPerPixel = i->size; + info->bitsPerPixel = i->bitsPerPixel; + info->h_alpha = i->ah; + info->l_alpha = i->al; + info->h_red = i->rh; + info->l_red = i->rl; + info->h_green = i->gh; + info->l_green = i->gl; + info->h_blue = i->bh; + info->l_blue = i->bl; + + return NO_ERROR; +} + +}; // namespace android + diff --git a/libs/ui/Point.cpp b/libs/ui/Point.cpp new file mode 100644 index 0000000..438d49f --- /dev/null +++ b/libs/ui/Point.cpp @@ -0,0 +1,11 @@ +/* + * Point.cpp + * Android + * + * Created on 11/16/2006. + * Copyright 2005 The Android Open Source Project + * + */ + +#include <ui/Point.h> + diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp new file mode 100644 index 0000000..99e68bb --- /dev/null +++ b/libs/ui/Rect.cpp @@ -0,0 +1,86 @@ +/* + * Rect.cpp + * Android + * + * Created on 10/14/05. + * Copyright 2005 The Android Open Source Project + * + */ + +#include <ui/Rect.h> + +namespace android { + +inline int min(int a, int b) { + return (a<b) ? a : b; +} + +inline int max(int a, int b) { + return (a>b) ? a : b; +} + +void Rect::makeInvalid() { + left = 0; + top = 0; + right = -1; + bottom = -1; +} + +bool Rect::operator < (const Rect& rhs) const +{ + if (top<rhs.top) { + return true; + } else if (top == rhs.top) { + if (left < rhs.left) { + return true; + } else if (left == rhs.left) { + if (bottom<rhs.bottom) { + return true; + } else if (bottom == rhs.bottom) { + if (right<rhs.right) { + return true; + } + } + } + } + return false; +} + +Rect& Rect::offsetTo(int x, int y) +{ + right -= left - x; + bottom -= top - y; + left = x; + top = y; + return *this; +} + +Rect& Rect::offsetBy(int x, int y) +{ + left += x; + top += y; + right+= x; + bottom+=y; + return *this; +} + +Rect Rect::operator + (const Point& rhs) const +{ + return Rect(left+rhs.x, top+rhs.y, right+rhs.x, bottom+rhs.y); +} + +Rect Rect::operator - (const Point& rhs) const +{ + return Rect(left-rhs.x, top-rhs.y, right-rhs.x, bottom-rhs.y); +} + +bool Rect::intersect(const Rect& with, Rect* result) const +{ + result->left = max(left, with.left); + result->top = max(top, with.top); + result->right = min(right, with.right); + result->bottom = min(bottom, with.bottom); + return !(result->isEmpty()); +} + +}; // namespace android diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp new file mode 100644 index 0000000..26e694a --- /dev/null +++ b/libs/ui/Region.cpp @@ -0,0 +1,313 @@ +/* + * 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 "Region" + +#include <stdio.h> +#include <utils/Atomic.h> +#include <utils/Debug.h> +#include <utils/String8.h> +#include <ui/Region.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +Region::Region() +{ +} + +Region::Region(const Region& rhs) + : mRegion(rhs.mRegion) +{ +} + +Region::Region(const SkRegion& rhs) + : mRegion(rhs) +{ +} + +Region::~Region() +{ +} + +Region::Region(const Rect& rhs) +{ + set(rhs); +} + +Region::Region(const Parcel& parcel) +{ + read(parcel); +} + +Region::Region(const void* buffer) +{ + read(buffer); +} + +Region& Region::operator = (const Region& rhs) +{ + mRegion = rhs.mRegion; + return *this; +} + +const SkRegion& Region::toSkRegion() const +{ + return mRegion; +} + +Rect Region::bounds() const +{ + const SkIRect& b(mRegion.getBounds()); + return Rect(b.fLeft, b.fTop, b.fRight, b.fBottom); +} + +void Region::clear() +{ + mRegion.setEmpty(); +} + +void Region::set(const Rect& r) +{ + SkIRect ir; + ir.set(r.left, r.top, r.right, r.bottom); + mRegion.setRect(ir); +} + +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Rect& r) +{ + SkIRect ir; + ir.set(r.left, r.top, r.right, r.bottom); + mRegion.op(ir, SkRegion::kUnion_Op); + return *this; +} + +Region& Region::andSelf(const Rect& r) +{ + SkIRect ir; + ir.set(r.left, r.top, r.right, r.bottom); + mRegion.op(ir, SkRegion::kIntersect_Op); + return *this; +} + +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Region& rhs) { + mRegion.op(rhs.mRegion, SkRegion::kUnion_Op); + return *this; +} + +Region& Region::andSelf(const Region& rhs) { + mRegion.op(rhs.mRegion, SkRegion::kIntersect_Op); + return *this; +} + +Region& Region::subtractSelf(const Region& rhs) { + mRegion.op(rhs.mRegion, SkRegion::kDifference_Op); + return *this; +} + +Region& Region::translateSelf(int x, int y) { + if (x|y) mRegion.translate(x, y); + return *this; +} + +Region Region::merge(const Region& rhs) const { + Region result; + result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kUnion_Op); + return result; +} + +Region Region::intersect(const Region& rhs) const { + Region result; + result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kIntersect_Op); + return result; +} + +Region Region::subtract(const Region& rhs) const { + Region result; + result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kDifference_Op); + return result; +} + +Region Region::translate(int x, int y) const { + Region result; + mRegion.translate(x, y, &result.mRegion); + return result; +} + +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Region& rhs, int dx, int dy) { + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + mRegion.op(r, SkRegion::kUnion_Op); + return *this; +} + +Region& Region::andSelf(const Region& rhs, int dx, int dy) { + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + mRegion.op(r, SkRegion::kIntersect_Op); + return *this; +} + +Region& Region::subtractSelf(const Region& rhs, int dx, int dy) { + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + mRegion.op(r, SkRegion::kDifference_Op); + return *this; +} + +Region Region::merge(const Region& rhs, int dx, int dy) const { + Region result; + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + result.mRegion.op(mRegion, r, SkRegion::kUnion_Op); + return result; +} + +Region Region::intersect(const Region& rhs, int dx, int dy) const { + Region result; + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + result.mRegion.op(mRegion, r, SkRegion::kIntersect_Op); + return result; +} + +Region Region::subtract(const Region& rhs, int dx, int dy) const { + Region result; + SkRegion r(rhs.mRegion); + r.translate(dx, dy); + result.mRegion.op(mRegion, r, SkRegion::kDifference_Op); + return result; +} + +// ---------------------------------------------------------------------------- + +Region::iterator::iterator(const Region& r) + : mIt(r.mRegion) +{ +} + +int Region::iterator::iterate(Rect* rect) +{ + if (mIt.done()) + return 0; + const SkIRect& r(mIt.rect()); + rect->left = r.fLeft; + rect->top = r.fTop; + rect->right = r.fRight; + rect->bottom= r.fBottom; + mIt.next(); + return 1; +} + +// ---------------------------------------------------------------------------- + +// we write a 4byte size ahead of the actual region, so we know how much we'll need for reading + +status_t Region::write(Parcel& parcel) const +{ + int32_t size = mRegion.flatten(NULL); + parcel.writeInt32(size); + mRegion.flatten(parcel.writeInplace(size)); + return NO_ERROR; +} + +status_t Region::read(const Parcel& parcel) +{ + size_t size = parcel.readInt32(); + mRegion.unflatten(parcel.readInplace(size)); + return NO_ERROR; +} + +ssize_t Region::write(void* buffer, size_t size) const +{ + size_t sizeNeeded = mRegion.flatten(NULL); + if (sizeNeeded > size) return NO_MEMORY; + return mRegion.flatten(buffer); +} + +ssize_t Region::read(const void* buffer) +{ + return mRegion.unflatten(buffer); +} + +ssize_t Region::writeEmpty(void* buffer, size_t size) +{ + if (size < 4) return NO_MEMORY; + // this needs to stay in sync with SkRegion + *static_cast<int32_t*>(buffer) = -1; + return 4; +} + +bool Region::isEmpty(void* buffer) +{ + // this needs to stay in sync with SkRegion + return *static_cast<int32_t*>(buffer) == -1; +} + +size_t Region::rects(Vector<Rect>& rectList) const +{ + rectList.clear(); + if (!isEmpty()) { + SkRegion::Iterator iterator(mRegion); + while( !iterator.done() ) { + const SkIRect& ir(iterator.rect()); + rectList.push(Rect(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom)); + iterator.next(); + } + } + return rectList.size(); +} + +void Region::dump(String8& out, const char* what, uint32_t flags) const +{ + (void)flags; + Vector<Rect> r; + rects(r); + + size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", what, this, r.size()); + out.append(buffer); + for (size_t i=0 ; i<r.size() ; i++) { + snprintf(buffer, SIZE, " [%3d, %3d, %3d, %3d]\n", + r[i].left, r[i].top,r[i].right,r[i].bottom); + out.append(buffer); + } +} + +void Region::dump(const char* what, uint32_t flags) const +{ + (void)flags; + Vector<Rect> r; + rects(r); + LOGD(" Region %s (this=%p, count=%d)\n", what, this, r.size()); + for (size_t i=0 ; i<r.size() ; i++) { + LOGD(" [%3d, %3d, %3d, %3d]\n", + r[i].left, r[i].top,r[i].right,r[i].bottom); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp new file mode 100644 index 0000000..4ea9ae2 --- /dev/null +++ b/libs/ui/Surface.cpp @@ -0,0 +1,256 @@ +/* + * 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 "Surface" + +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/threads.h> +#include <utils/IPCThreadState.h> +#include <utils/IMemory.h> +#include <utils/Log.h> + +#include <ui/ISurface.h> +#include <ui/Surface.h> +#include <ui/SurfaceComposerClient.h> +#include <ui/Rect.h> + +#include <private/ui/SharedState.h> +#include <private/ui/LayerState.h> + +namespace android { + +// --------------------------------------------------------------------------- + +Surface::Surface(const sp<SurfaceComposerClient>& client, + const sp<ISurface>& surface, + const ISurfaceFlingerClient::surface_data_t& data, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, + bool owner) + : mClient(client), mSurface(surface), + mToken(data.token), mIdentity(data.identity), + mFormat(format), mFlags(flags), mOwner(owner) +{ + mSwapRectangle.makeInvalid(); + mSurfaceHeapBase[0] = 0; + mSurfaceHeapBase[1] = 0; + mHeap[0] = data.heap[0]; + mHeap[1] = data.heap[1]; +} + +Surface::Surface(Surface const* rhs) + : mOwner(false) +{ + mToken = rhs->mToken; + mIdentity= rhs->mIdentity; + mClient = rhs->mClient; + mSurface = rhs->mSurface; + mHeap[0] = rhs->mHeap[0]; + mHeap[1] = rhs->mHeap[1]; + mFormat = rhs->mFormat; + mFlags = rhs->mFlags; + mSurfaceHeapBase[0] = rhs->mSurfaceHeapBase[0]; + mSurfaceHeapBase[1] = rhs->mSurfaceHeapBase[1]; + mSwapRectangle.makeInvalid(); +} + +Surface::~Surface() +{ + if (mOwner && mToken>=0 && mClient!=0) { + mClient->destroySurface(mToken); + } + mClient.clear(); + mSurface.clear(); + mHeap[0].clear(); + mHeap[1].clear(); + IPCThreadState::self()->flushCommands(); +} + +sp<Surface> Surface::dup() const +{ + Surface const * r = this; + if (this && mOwner) { + // the only reason we need to do this is because of Java's garbage + // collector: because we're creating a copy of the Surface + // instead of a reference, we can garantee that when our last + // reference goes away, the real surface will be deleted. + // Without this hack (the code is correct too), we'd have to + // wait for a GC for the surface to go away. + r = new Surface(this); + } + return const_cast<Surface*>(r); +} + +status_t Surface::nextBuffer(SurfaceInfo* info) { + return mClient->nextBuffer(this, info); +} + +status_t Surface::lock(SurfaceInfo* info, bool blocking) { + return Surface::lock(info, NULL, blocking); +} + +status_t Surface::lock(SurfaceInfo* info, Region* dirty, bool blocking) { + if (heapBase(0) == 0) return INVALID_OPERATION; + if (heapBase(1) == 0) return INVALID_OPERATION; + return mClient->lockSurface(this, info, dirty, blocking); +} + +status_t Surface::unlockAndPost() { + if (heapBase(0) == 0) return INVALID_OPERATION; + if (heapBase(1) == 0) return INVALID_OPERATION; + return mClient->unlockAndPostSurface(this); +} + +status_t Surface::unlock() { + if (heapBase(0) == 0) return INVALID_OPERATION; + if (heapBase(1) == 0) return INVALID_OPERATION; + return mClient->unlockSurface(this); +} + +status_t Surface::setLayer(int32_t layer) { + return mClient->setLayer(this, layer); +} +status_t Surface::setPosition(int32_t x, int32_t y) { + return mClient->setPosition(this, x, y); +} +status_t Surface::setSize(uint32_t w, uint32_t h) { + return mClient->setSize(this, w, h); +} +status_t Surface::hide() { + return mClient->hide(this); +} +status_t Surface::show(int32_t layer) { + return mClient->show(this, layer); +} +status_t Surface::freeze() { + return mClient->freeze(this); +} +status_t Surface::unfreeze() { + return mClient->unfreeze(this); +} +status_t Surface::setFlags(uint32_t flags, uint32_t mask) { + return mClient->setFlags(this, flags, mask); +} +status_t Surface::setTransparentRegionHint(const Region& transparent) { + return mClient->setTransparentRegionHint(this, transparent); +} +status_t Surface::setAlpha(float alpha) { + return mClient->setAlpha(this, alpha); +} +status_t Surface::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + return mClient->setMatrix(this, dsdx, dtdx, dsdy, dtdy); +} +status_t Surface::setFreezeTint(uint32_t tint) { + return mClient->setFreezeTint(this, tint); +} + +Region Surface::dirtyRegion() const { + return mDirtyRegion; +} +void Surface::setDirtyRegion(const Region& region) const { + mDirtyRegion = region; +} +const Rect& Surface::swapRectangle() const { + return mSwapRectangle; +} +void Surface::setSwapRectangle(const Rect& r) { + mSwapRectangle = r; +} + +sp<Surface> Surface::readFromParcel(Parcel* parcel) +{ + sp<SurfaceComposerClient> client; + ISurfaceFlingerClient::surface_data_t data; + sp<IBinder> clientBinder= parcel->readStrongBinder(); + sp<ISurface> surface = interface_cast<ISurface>(parcel->readStrongBinder()); + data.heap[0] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); + data.heap[1] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); + data.token = parcel->readInt32(); + data.identity = parcel->readInt32(); + PixelFormat format = parcel->readInt32(); + uint32_t flags = parcel->readInt32(); + + if (clientBinder != NULL) + client = SurfaceComposerClient::clientForConnection(clientBinder); + + return new Surface(client, surface, data, 0, 0, format, flags, false); +} + +status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) +{ + uint32_t flags=0; + uint32_t format=0; + SurfaceID token = -1; + uint32_t identity = 0; + sp<SurfaceComposerClient> client; + sp<ISurface> sur; + sp<IMemoryHeap> heap[2]; + if (surface->isValid()) { + token = surface->mToken; + identity = surface->mIdentity; + client = surface->mClient; + sur = surface->mSurface; + heap[0] = surface->mHeap[0]; + heap[1] = surface->mHeap[1]; + format = surface->mFormat; + flags = surface->mFlags; + } + parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); + parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); + parcel->writeInt32(token); + parcel->writeInt32(identity); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; +} + +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(); +} + +void* Surface::heapBase(int i) const +{ + void* heapBase = mSurfaceHeapBase[i]; + // map lazily so it doesn't get mapped in clients that don't need it + if (heapBase == 0) { + const sp<IMemoryHeap>& heap(mHeap[i]); + if (heap != 0) { + heapBase = static_cast<uint8_t*>(heap->base()); + if (heapBase == MAP_FAILED) { + heapBase = NULL; + LOGE("Couldn't map Surface's heap (binder=%p, heap=%p)", + heap->asBinder().get(), heap.get()); + } + mSurfaceHeapBase[i] = heapBase; + } + } + return heapBase; +} + +}; // namespace android + diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp new file mode 100644 index 0000000..9354a7a --- /dev/null +++ b/libs/ui/SurfaceComposerClient.cpp @@ -0,0 +1,1026 @@ +/* + * 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 "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/IPCThreadState.h> +#include <utils/IServiceManager.h> +#include <utils/IMemory.h> +#include <utils/Log.h> + +#include <ui/ISurfaceComposer.h> +#include <ui/ISurfaceFlingerClient.h> +#include <ui/ISurface.h> +#include <ui/SurfaceComposerClient.h> +#include <ui/DisplayInfo.h> +#include <ui/Rect.h> +#include <ui/Point.h> + +#include <private/ui/SharedState.h> +#include <private/ui/LayerState.h> +#include <private/ui/SurfaceFlingerSynchro.h> + +#include <pixelflinger/pixelflinger.h> + +#include <utils/BpBinder.h> + +#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 { + +// --------------------------------------------------------------------------- + +// 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<IMemory> gServerCblkMemory; +static volatile surface_flinger_cblk_t* gServerCblk; + +const sp<ISurfaceComposer>& _get_surface_manager() +{ + if (gSurfaceManager != 0) { + return gSurfaceManager; + } + + sp<IBinder> binder; + sp<IServiceManager> sm = defaultServiceManager(); + do { + binder = sm->getService(String16("SurfaceFlinger")); + if (binder == 0) { + LOGW("SurfaceFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } + } while(binder == 0); + sp<ISurfaceComposer> sc(interface_cast<ISurfaceComposer>(binder)); + + Mutex::Autolock _l(gLock); + if (gSurfaceManager == 0) { + gSurfaceManager = sc; + } + return gSurfaceManager; +} + +static volatile surface_flinger_cblk_t const * get_cblk() +{ + if (gServerCblk == 0) { + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + Mutex::Autolock _l(gLock); + if (gServerCblk == 0) { + gServerCblkMemory = sm->getCblk(); + LOGE_IF(gServerCblkMemory==0, "Can't get server control block"); + gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->pointer(); + LOGE_IF(gServerCblk==0, "Can't get server control block address"); + } + } + return gServerCblk; +} + +// --------------------------------------------------------------------------- + +static void copyBlt(const GGLSurface& dst, + const GGLSurface& src, const Region& reg) +{ + Region::iterator iterator(reg); + if (iterator) { + // NOTE: dst and src must be the same format + Rect r; + const size_t bpp = bytesPerPixel(src.format); + const size_t dbpr = dst.stride * bpp; + const size_t sbpr = src.stride * bpp; + while (iterator.iterate(&r)) { + ssize_t h = r.bottom - r.top; + if (h) { + size_t size = (r.right - r.left) * bpp; + uint8_t* s = src.data + (r.left + src.stride * r.top) * bpp; + uint8_t* d = dst.data + (r.left + dst.stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + } +} + +// --------------------------------------------------------------------------- + +surface_flinger_cblk_t::surface_flinger_cblk_t() +{ +} + +// --------------------------------------------------------------------------- + +per_client_cblk_t::per_client_cblk_t() +{ +} + +// these functions are used by the clients +inline status_t per_client_cblk_t::validate(size_t i) const { + if (uint32_t(i) >= NUM_LAYERS_MAX) + return BAD_INDEX; + if (layers[i].swapState & eInvalidSurface) + return NO_MEMORY; + return NO_ERROR; +} + +int32_t per_client_cblk_t::lock_layer(size_t i, uint32_t flags) +{ + int32_t index; + uint32_t state; + int timeout = 0; + status_t result; + layer_cblk_t * const layer = layers + i; + const bool blocking = flags & BLOCKING; + const bool inspect = flags & INSPECT; + + do { + state = layer->swapState; + + if (UNLIKELY((state&(eFlipRequested|eNextFlipPending)) == eNextFlipPending)) { + LOGE("eNextFlipPending set but eFlipRequested not set, " + "layer=%d (lcblk=%p), state=%08x", + int(i), layer, int(state)); + return INVALID_OPERATION; + } + + if (UNLIKELY(state&eLocked)) { + LOGE("eLocked set when entering lock_layer(), " + "layer=%d (lcblk=%p), state=%08x", + int(i), layer, int(state)); + return WOULD_BLOCK; + } + + + if (state & (eFlipRequested | eNextFlipPending | eResizeRequested + | eInvalidSurface)) + { + int32_t resizeIndex; + Mutex::Autolock _l(lock); + // might block for a very short amount of time + // will never cause the server to block (trylock()) + + goto start_loop_here; + + // We block the client if: + // eNextFlipPending: we've used both buffers already, so we need to + // wait for one to become availlable. + // eResizeRequested: the buffer we're going to acquire is being + // resized. Block until it is done. + // eFlipRequested && eBusy: the buffer we're going to acquire is + // currently in use by the server. + // eInvalidSurface: this is a special case, we don't block in this + // case, we just return an error. + + while((state & (eNextFlipPending|eInvalidSurface)) || + (state & ((resizeIndex) ? eResizeBuffer1 : eResizeBuffer0)) || + ((state & (eFlipRequested|eBusy)) == (eFlipRequested|eBusy)) ) + { + if (state & eInvalidSurface) + return NO_MEMORY; + + if (!blocking) + return WOULD_BLOCK; + + timeout = 0; + result = cv.waitRelative(lock, seconds(1)); + if (__builtin_expect(result!=NO_ERROR, false)) { + const int newState = layer->swapState; + LOGW( "lock_layer timed out (is the CPU pegged?) " + "layer=%d, lcblk=%p, state=%08x (was %08x)", + int(i), layer, newState, int(state)); + timeout = newState != int(state); + } + + start_loop_here: + state = layer->swapState; + resizeIndex = (state&eIndex) ^ ((state&eFlipRequested)>>1); + } + + LOGW_IF(timeout, + "lock_layer() timed out but didn't appear to need " + "to be locked and we recovered " + "(layer=%d, lcblk=%p, state=%08x)", + int(i), layer, int(state)); + } + + // eFlipRequested is not set and cannot be set by another thread: it's + // safe to use the first buffer without synchronization. + + // Choose the index depending on eFlipRequested. + // When it's set, choose the 'other' buffer. + index = (state&eIndex) ^ ((state&eFlipRequested)>>1); + + // make sure this buffer is valid + if (layer->surface[index].bits_offset < 0) { + return status_t(layer->surface[index].bits_offset); + } + + if (inspect) { + // we just want to inspect this layer. don't lock it. + goto done; + } + + // last thing before we're done, we need to atomically lock the state + } while (android_atomic_cmpxchg(state, state|eLocked, &(layer->swapState))); + + VERBOSE("locked layer=%d (lcblk=%p), buffer=%d, state=0x%08x", + int(i), layer, int(index), int(state)); + + // store the index of the locked buffer (for client use only) + layer->flags &= ~eBufferIndex; + layer->flags |= ((index << eBufferIndexShift) & eBufferIndex); + +done: + return index; +} + +uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i) +{ + // atomically set eFlipRequested and clear eLocked and optionnaly + // set eNextFlipPending if eFlipRequested was already set + + layer_cblk_t * const layer = layers + i; + int32_t oldvalue, newvalue; + do { + oldvalue = layer->swapState; + // get current value + + newvalue = oldvalue & ~eLocked; + // clear eLocked + + newvalue |= eFlipRequested; + // set eFlipRequested + + if (oldvalue & eFlipRequested) + newvalue |= eNextFlipPending; + // if eFlipRequested was alread set, set eNextFlipPending + + } while (android_atomic_cmpxchg(oldvalue, newvalue, &(layer->swapState))); + + VERBOSE("request pageflip for layer=%d, buffer=%d, state=0x%08x", + int(i), int((layer->flags & eBufferIndex) >> eBufferIndexShift), + int(newvalue)); + + // from this point, the server can kick in at anytime and use the first + // buffer, so we cannot use it anymore, and we must use the 'other' + // buffer instead (or wait if it is not availlable yet, see lock_layer). + + return newvalue; +} + +void per_client_cblk_t::unlock_layer(size_t i) +{ + layer_cblk_t * const layer = layers + i; + android_atomic_and(~eLocked, &layer->swapState); +} + +// --------------------------------------------------------------------------- + +static inline int compare_type( const layer_state_t& lhs, + const layer_state_t& rhs) { + if (lhs.surface < rhs.surface) return -1; + if (lhs.surface > rhs.surface) return 1; + return 0; +} + +SurfaceComposerClient::SurfaceComposerClient() +{ + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + 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)); +} + +void SurfaceComposerClient::_init( + const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn) +{ + VERBOSE("Creating client %p, conn %p", this, conn.get()); + + mSignalServer = 0; + mPrebuiltLayerState = 0; + mTransactionOpen = 0; + mStatus = NO_ERROR; + mControl = 0; + + mClient = conn; + if (mClient == 0) { + mStatus = NO_INIT; + return; + } + + mClient->getControlBlocks(&mControlMemory); + mSignalServer = new SurfaceFlingerSynchro(sm); + mControl = static_cast<per_client_cblk_t *>(mControlMemory->pointer()); +} + +SurfaceComposerClient::~SurfaceComposerClient() +{ + VERBOSE("Destroying client %p, conn %p", this, mClient.get()); + dispose(); +} + +status_t SurfaceComposerClient::initCheck() const +{ + return mStatus; +} + +status_t SurfaceComposerClient::validateSurface( + per_client_cblk_t const* cblk, Surface const * surface) +{ + SurfaceID index = surface->ID(); + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", + index, surface->getIdentity()); + return NO_INIT; + } + + status_t err = cblk->validate(index); + if (err != NO_ERROR) { + LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", + index, surface->getIdentity(), err, strerror(-err)); + return err; + } + + if (surface->getIdentity() != uint32_t(cblk->layers[index].identity)) { + LOGE("using an invalid surface id=%d, identity=%u should be %d", + index, surface->getIdentity(), cblk->layers[index].identity); + return NO_INIT; + } + + return NO_ERROR; +} + +sp<IBinder> SurfaceComposerClient::connection() const +{ + return (mClient != 0) ? mClient->asBinder() : 0; +} + +sp<SurfaceComposerClient> +SurfaceComposerClient::clientForConnection(const sp<IBinder>& conn) +{ + sp<SurfaceComposerClient> client; + + { // scope for lock + Mutex::Autolock _l(gLock); + client = gActiveConnections.valueFor(conn); + } + + if (client == 0) { + // Need to make a new client. + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + 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; +} + +void SurfaceComposerClient::dispose() +{ + // this can be called more than once. + + sp<IMemory> controlMemory; + sp<ISurfaceFlingerClient> client; + sp<IMemoryHeap> surfaceHeap; + + { + Mutex::Autolock _lg(gLock); + Mutex::Autolock _lm(mLock); + + delete mSignalServer; + 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; + surfaceHeap = mSurfaceHeap; + mControlMemory.clear(); + mSurfaceHeap.clear(); + mControl = 0; + mStatus = NO_INIT; + } +} + +status_t SurfaceComposerClient::getDisplayInfo( + DisplayID dpy, DisplayInfo* info) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + + info->w = dcblk->w; + info->h = dcblk->h; + info->orientation = dcblk->orientation; + info->xdpi = dcblk->xdpi; + info->ydpi = dcblk->ydpi; + info->fps = dcblk->fps; + info->density = dcblk->density; + return getPixelFormatInfo(dcblk->format, &(info->pixelFormatInfo)); +} + +ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->w; +} + +ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->h; +} + +ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) +{ + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->orientation; +} + +ssize_t SurfaceComposerClient::getNumberOfDisplays() +{ + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + uint32_t connected = cblk->connected; + int n = 0; + while (connected) { + if (connected&1) n++; + connected >>= 1; + } + return n; +} + +sp<Surface> SurfaceComposerClient::createSurface( + int pid, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) +{ + sp<Surface> result; + if (mStatus == NO_ERROR) { + ISurfaceFlingerClient::surface_data_t data; + sp<ISurface> surface = mClient->createSurface(&data, pid, + display, w, h, format, flags); + if (surface != 0) { + if (uint32_t(data.token) < NUM_LAYERS_MAX) { + result = new Surface(this, surface, data, w, h, format, flags); + } + } + } + return result; +} + +status_t SurfaceComposerClient::destroySurface(SurfaceID sid) +{ + if (mStatus != NO_ERROR) + return mStatus; + + // it's okay to destroy a surface while a transaction is open, + // (transactions really are a client-side concept) + // however, this indicates probably a misuse of the API or a bug + // in the client code. + LOGW_IF(mTransactionOpen, + "Destroying surface while a transaction is open. " + "Client %p: destroying surface %d, mTransactionOpen=%d", + this, sid, mTransactionOpen); + + status_t err = mClient->destroySurface(sid); + return err; +} + +status_t SurfaceComposerClient::nextBuffer(Surface* surface, + Surface::SurfaceInfo* info) +{ + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + int32_t backIdx = surface->mBackbufferIndex; + layer_cblk_t* const lcblk = &(cblk->layers[index]); + const surface_info_t* const front = lcblk->surface + (1-backIdx); + info->w = front->w; + info->h = front->h; + info->format = front->format; + info->base = surface->heapBase(1-backIdx); + info->bits = reinterpret_cast<void*>(intptr_t(info->base) + front->bits_offset); + info->bpr = front->bpr; + + return 0; +} + +status_t SurfaceComposerClient::lockSurface( + Surface* surface, + Surface::SurfaceInfo* other, + Region* dirty, + bool blocking) +{ + Mutex::Autolock _l(surface->getLock()); + + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + int32_t backIdx = cblk->lock_layer(size_t(index), + per_client_cblk_t::BLOCKING); + if (backIdx >= 0) { + surface->mBackbufferIndex = backIdx; + layer_cblk_t* const lcblk = &(cblk->layers[index]); + const surface_info_t* const back = lcblk->surface + backIdx; + const surface_info_t* const front = lcblk->surface + (1-backIdx); + other->w = back->w; + other->h = back->h; + other->format = back->format; + other->base = surface->heapBase(backIdx); + other->bits = reinterpret_cast<void*>(intptr_t(other->base) + back->bits_offset); + other->bpr = back->bpr; + + const Rect bounds(other->w, other->h); + Region newDirtyRegion; + + if (back->flags & surface_info_t::eBufferDirty) { + /* it is safe to write *back here, because we're guaranteed + * SurfaceFlinger is not touching it (since it just granted + * access to us) */ + const_cast<surface_info_t*>(back)->flags &= + ~surface_info_t::eBufferDirty; + + // content is meaningless in this case and the whole surface + // needs to be redrawn. + + newDirtyRegion.set(bounds); + if (dirty) { + *dirty = newDirtyRegion; + } + + //if (bytesPerPixel(other->format) == 4) { + // android_memset32( + // (uint32_t*)other->bits, 0xFF00FF00, other->h * other->bpr); + //} else { + // android_memset16( // fill with green + // (uint16_t*)other->bits, 0x7E0, other->h * other->bpr); + //} + } + else + { + if (dirty) { + dirty->andSelf(Region(bounds)); + newDirtyRegion = *dirty; + } else { + newDirtyRegion.set(bounds); + } + + Region copyback; + if (!(lcblk->flags & eNoCopyBack)) { + const Region previousDirtyRegion(surface->dirtyRegion()); + copyback = previousDirtyRegion.subtract(newDirtyRegion); + } + + if (!copyback.isEmpty()) { + // copy front to back + GGLSurface cb; + cb.version = sizeof(GGLSurface); + cb.width = back->w; + cb.height = back->h; + cb.stride = back->stride; + cb.data = (GGLubyte*)surface->heapBase(backIdx); + cb.data += back->bits_offset; + cb.format = back->format; + + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = front->w; + t.height = front->h; + t.stride = front->stride; + t.data = (GGLubyte*)surface->heapBase(1-backIdx); + t.data += front->bits_offset; + t.format = front->format; + + //const Region copyback(lcblk->region + 1-backIdx); + copyBlt(cb, t, copyback); + } + } + + // update dirty region + surface->setDirtyRegion(newDirtyRegion); + } + return (backIdx < 0) ? status_t(backIdx) : status_t(NO_ERROR); +} + +void SurfaceComposerClient::_signal_server() +{ + mSignalServer->signal(); +} + +void SurfaceComposerClient::_send_dirty_region( + layer_cblk_t* lcblk, const Region& dirty) +{ + const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift; + flat_region_t* flat_region = lcblk->region + index; + status_t err = dirty.write(flat_region, sizeof(flat_region_t)); + if (err < NO_ERROR) { + // region doesn't fit, use the bounds + const Region reg(dirty.bounds()); + reg.write(flat_region, sizeof(flat_region_t)); + } +} + +status_t SurfaceComposerClient::unlockAndPostSurface(Surface* surface) +{ + Mutex::Autolock _l(surface->getLock()); + + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + Region dirty(surface->dirtyRegion()); + const Rect& swapRect(surface->swapRectangle()); + if (swapRect.isValid()) { + dirty.set(swapRect); + } + + // transmit the dirty region + layer_cblk_t* const lcblk = &(cblk->layers[index]); + _send_dirty_region(lcblk, dirty); + uint32_t newstate = cblk->unlock_layer_and_post(size_t(index)); + if (!(newstate & eNextFlipPending)) + _signal_server(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::unlockSurface(Surface* surface) +{ + Mutex::Autolock _l(surface->getLock()); + + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface); + if (err != NO_ERROR) + return err; + + layer_cblk_t* const lcblk = &(cblk->layers[index]); + cblk->unlock_layer(size_t(index)); + return NO_ERROR; +} + +void SurfaceComposerClient::openGlobalTransaction() +{ + Mutex::Autolock _l(gLock); + + 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 + } + } + } +} + +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); + if (N == 1) { + clients[0]->closeTransaction(); + } else { + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sm->openGlobalTransaction(); + for (size_t i=0; i<N; i++) { + clients[i]->closeTransaction(); + } + sm->closeGlobalTransaction(); + } +} + +status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) +{ + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + return sm->freezeDisplay(dpy, flags); +} + +status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) +{ + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + return sm->unfreezeDisplay(dpy, flags); +} + +int SurfaceComposerClient::setOrientation(DisplayID dpy, int orientation) +{ + const sp<ISurfaceComposer>& sm(_get_surface_manager()); + return sm->setOrientation(dpy, orientation); +} + +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()", + this, mTransactionOpen); + return INVALID_OPERATION; + } + + if (mTransactionOpen >= 2) { + mTransactionOpen--; + return NO_ERROR; + } + + mTransactionOpen = 0; + const ssize_t count = mStates.size(); + if (count) { + mClient->setState(count, mStates.array()); + mStates.clear(); + } + return NO_ERROR; +} + +layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface) +{ + SurfaceID index = surface->ID(); + per_client_cblk_t* const cblk = mControl; + status_t err = validateSurface(cblk, surface.get()); + if (err != NO_ERROR) + return 0; + + // API usage error, do nothing. + if (mTransactionOpen<=0) { + LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d", + this, int(index), mTransactionOpen); + return 0; + } + + // use mPrebuiltLayerState just to find out if we already have it + layer_state_t& dummy = *mPrebuiltLayerState; + dummy.surface = index; + ssize_t i = mStates.indexOf(dummy); + if (i < 0) { + // we don't have it, add an initialized layer_state to our list + i = mStates.add(dummy); + } + return mStates.editArray() + i; +} + +layer_state_t* SurfaceComposerClient::_lockLayerState(const sp<Surface>& surface) +{ + layer_state_t* s; + mLock.lock(); + s = _get_state_l(surface); + if (!s) mLock.unlock(); + return s; +} + +void SurfaceComposerClient::_unlockLayerState() +{ + mLock.unlock(); +} + +status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t y) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::ePositionChanged; + s->x = x; + s->y = y; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eSizeChanged; + s->w = w; + s->h = h; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eLayerChanged; + s->z = z; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::hide(Surface* surface) +{ + return setFlags(surface, ISurfaceComposer::eLayerHidden, + ISurfaceComposer::eLayerHidden); +} + +status_t SurfaceComposerClient::show(Surface* surface, int32_t) +{ + return setFlags(surface, 0, ISurfaceComposer::eLayerHidden); +} + +status_t SurfaceComposerClient::freeze(Surface* surface) +{ + return setFlags(surface, ISurfaceComposer::eLayerFrozen, + ISurfaceComposer::eLayerFrozen); +} + +status_t SurfaceComposerClient::unfreeze(Surface* surface) +{ + return setFlags(surface, 0, ISurfaceComposer::eLayerFrozen); +} + +status_t SurfaceComposerClient::setFlags(Surface* surface, + uint32_t flags, uint32_t mask) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eVisibilityChanged; + s->flags &= ~mask; + s->flags |= (flags & mask); + s->mask |= mask; + _unlockLayerState(); + return NO_ERROR; +} + + +status_t SurfaceComposerClient::setTransparentRegionHint( + Surface* surface, const Region& transparentRegion) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eTransparentRegionChanged; + s->transparentRegion = transparentRegion; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eAlphaChanged; + s->alpha = alpha; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setMatrix( + Surface* surface, + float dsdx, float dtdx, + float dsdy, float dtdy ) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eMatrixChanged; + layer_state_t::matrix22_t matrix; + matrix.dsdx = dsdx; + matrix.dtdx = dtdx; + matrix.dsdy = dsdy; + matrix.dtdy = dtdy; + s->matrix = matrix; + _unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setFreezeTint(Surface* surface, uint32_t tint) +{ + layer_state_t* s = _lockLayerState(surface); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eFreezeTintChanged; + s->tint = tint; + _unlockLayerState(); + return NO_ERROR; +} + +}; // namespace android + diff --git a/libs/ui/SurfaceFlingerSynchro.cpp b/libs/ui/SurfaceFlingerSynchro.cpp new file mode 100644 index 0000000..5cd9755 --- /dev/null +++ b/libs/ui/SurfaceFlingerSynchro.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceFlingerSynchro" + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <utils/IPCThreadState.h> +#include <utils/Log.h> + +#include <private/ui/SurfaceFlingerSynchro.h> + +namespace android { + +// --------------------------------------------------------------------------- + +SurfaceFlingerSynchro::Barrier::Barrier() + : state(CLOSED) { +} + +SurfaceFlingerSynchro::Barrier::~Barrier() { +} + +void SurfaceFlingerSynchro::Barrier::open() { + asm volatile ("":::"memory"); + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); +} + +void SurfaceFlingerSynchro::Barrier::close() { + Mutex::Autolock _l(lock); + state = CLOSED; +} + +void SurfaceFlingerSynchro::Barrier::waitAndClose() +{ + Mutex::Autolock _l(lock); + while (state == CLOSED) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + cv.wait(lock); + } + state = CLOSED; +} + +status_t SurfaceFlingerSynchro::Barrier::waitAndClose(nsecs_t timeout) +{ + Mutex::Autolock _l(lock); + while (state == CLOSED) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + int err = cv.waitRelative(lock, timeout); + if (err != 0) + return err; + } + state = CLOSED; + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +SurfaceFlingerSynchro::SurfaceFlingerSynchro(const sp<ISurfaceComposer>& flinger) + : mSurfaceComposer(flinger) +{ +} + +SurfaceFlingerSynchro::SurfaceFlingerSynchro() +{ +} + +SurfaceFlingerSynchro::~SurfaceFlingerSynchro() +{ +} + +status_t SurfaceFlingerSynchro::signal() +{ + mSurfaceComposer->signal(); + return NO_ERROR; +} + +status_t SurfaceFlingerSynchro::wait() +{ + mBarrier.waitAndClose(); + return NO_ERROR; +} + +status_t SurfaceFlingerSynchro::wait(nsecs_t timeout) +{ + if (timeout == 0) + return SurfaceFlingerSynchro::wait(); + return mBarrier.waitAndClose(timeout); +} + +void SurfaceFlingerSynchro::open() +{ + mBarrier.open(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android + diff --git a/libs/ui/Time.cpp b/libs/ui/Time.cpp new file mode 100644 index 0000000..b553913 --- /dev/null +++ b/libs/ui/Time.cpp @@ -0,0 +1,199 @@ +#include <utils/TimeUtils.h> +#include <stdio.h> +#include <cutils/tztime.h> + +namespace android { + +static void +dump(const Time& t) +{ + #ifdef HAVE_TM_GMTOFF + long tm_gmtoff = t.t.tm_gmtoff; + #else + long tm_gmtoff = 0; + #endif + printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n", + t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday, + t.t.tm_hour, t.t.tm_min, t.t.tm_sec, + t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday); +} + +Time::Time() +{ + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = 0; + t.tm_mon = 0; + t.tm_year = 0; + t.tm_wday = 0; + t.tm_yday = 0; + t.tm_isdst = -1; // we don't know, so let the C library determine + #ifdef HAVE_TM_GMTOFF + t.tm_gmtoff = 0; + #endif +} + + +#define COMPARE_FIELD(field) do { \ + int diff = a.t.field - b.t.field; \ + if (diff != 0) return diff; \ + } while(0) + +int +Time::compare(Time& a, Time& b) +{ + if (0 == strcmp(a.timezone, b.timezone)) { + // if the timezones are the same, we can easily compare the two + // times. Otherwise, convert to milliseconds and compare that. + // This requires that object be normalized. + COMPARE_FIELD(tm_year); + COMPARE_FIELD(tm_mon); + COMPARE_FIELD(tm_mday); + COMPARE_FIELD(tm_hour); + COMPARE_FIELD(tm_min); + COMPARE_FIELD(tm_sec); + return 0; + } else { + int64_t am = a.toMillis(false /* use isDst */); + int64_t bm = b.toMillis(false /* use isDst */); + int64_t diff = am-bm; + return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); + } +} + +static const int DAYS_PER_MONTH[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + +static inline int days_this_month(int year, int month) +{ + int n = DAYS_PER_MONTH[month]; + if (n != 28) { + return n; + } else { + int y = year; + return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28; + } +} + +void +Time::switchTimezone(const char* timezone) +{ + time_t seconds = mktime_tz(&(this->t), this->timezone); + localtime_tz(&seconds, &(this->t), timezone); +} + +String8 +Time::format(const char *format, const struct strftime_locale *locale) const +{ + char buf[257]; + int n = strftime_tz(buf, 257, format, &(this->t), locale); + if (n > 0) { + return String8(buf); + } else { + return String8(); + } +} + +static inline short +tochar(int n) +{ + return (n >= 0 && n <= 9) ? ('0'+n) : ' '; +} + +static inline short +next_char(int *m, int k) +{ + int n = *m / k; + *m = *m % k; + return tochar(n); +} + +void +Time::format2445(short* buf, bool hasTime) const +{ + int n; + + n = t.tm_year+1900; + buf[0] = next_char(&n, 1000); + buf[1] = next_char(&n, 100); + buf[2] = next_char(&n, 10); + buf[3] = tochar(n); + + n = t.tm_mon+1; + buf[4] = next_char(&n, 10); + buf[5] = tochar(n); + + n = t.tm_mday; + buf[6] = next_char(&n, 10); + buf[7] = tochar(n); + + if (hasTime) { + buf[8] = 'T'; + + n = t.tm_hour; + buf[9] = next_char(&n, 10); + buf[10] = tochar(n); + + n = t.tm_min; + buf[11] = next_char(&n, 10); + buf[12] = tochar(n); + + n = t.tm_sec; + buf[13] = next_char(&n, 10); + buf[14] = tochar(n); + bool inUtc = strcmp("UTC", timezone) == 0; + if (inUtc) { + buf[15] = 'Z'; + } + } +} + +String8 +Time::toString() const +{ + String8 str; + char* s = str.lockBuffer(150); + #ifdef HAVE_TM_GMTOFF + long tm_gmtoff = t.tm_gmtoff; + #else + long tm_gmtoff = 0; + #endif + sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)", + t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst, + (int)(((Time*)this)->toMillis(false /* use isDst */)/1000)); + str.unlockBuffer(); + return str; +} + +void +Time::setToNow() +{ + time_t seconds; + time(&seconds); + localtime_tz(&seconds, &(this->t), this->timezone); +} + +int64_t +Time::toMillis(bool ignoreDst) +{ + if (ignoreDst) { + this->t.tm_isdst = -1; + } + int64_t r = mktime_tz(&(this->t), this->timezone); + if (r == -1) + return -1; + return r * 1000; +} + +void +Time::set(int64_t millis) +{ + time_t seconds = millis / 1000; + localtime_tz(&seconds, &(this->t), this->timezone); +} + +}; // namespace android + |