diff options
Diffstat (limited to 'libs/gui')
-rw-r--r-- | libs/gui/Android.mk | 11 | ||||
-rw-r--r-- | libs/gui/IGraphicBufferAlloc.cpp | 108 | ||||
-rw-r--r-- | libs/gui/ISurface.cpp | 101 | ||||
-rw-r--r-- | libs/gui/ISurfaceComposer.cpp | 326 | ||||
-rw-r--r-- | libs/gui/ISurfaceComposerClient.cpp | 231 | ||||
-rw-r--r-- | libs/gui/LayerState.cpp | 61 | ||||
-rw-r--r-- | libs/gui/SharedBufferStack.cpp | 714 | ||||
-rw-r--r-- | libs/gui/Surface.cpp | 1152 | ||||
-rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 611 | ||||
-rw-r--r-- | libs/gui/tests/Android.mk | 2 | ||||
-rw-r--r-- | libs/gui/tests/Surface_test.cpp | 141 |
11 files changed, 3455 insertions, 3 deletions
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index d1a6af1..58bb0d3 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -10,7 +10,15 @@ LOCAL_SRC_FILES:= \ SensorEventQueue.cpp \ SensorManager.cpp \ SurfaceTexture.cpp \ - SurfaceTextureClient.cpp + SurfaceTextureClient.cpp \ + ISurfaceComposer.cpp \ + ISurface.cpp \ + ISurfaceComposerClient.cpp \ + IGraphicBufferAlloc.cpp \ + LayerState.cpp \ + SharedBufferStack.cpp \ + Surface.cpp \ + SurfaceComposerClient.cpp \ LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -21,7 +29,6 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libEGL \ libGLESv2 \ - libsurfaceflinger_client LOCAL_MODULE:= libgui diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp new file mode 100644 index 0000000..e05da72 --- /dev/null +++ b/libs/gui/IGraphicBufferAlloc.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2011 The Android Open 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 <binder/Parcel.h> + +#include <ui/GraphicBuffer.h> + +#include <surfaceflinger/IGraphicBufferAlloc.h> + +// --------------------------------------------------------------------------- + +namespace android { + +enum { + CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, +}; + +class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc> +{ +public: + BpGraphicBufferAlloc(const sp<IBinder>& impl) + : BpInterface<IGraphicBufferAlloc>(impl) + { + } + + virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h, + PixelFormat format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferAlloc::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply); + sp<GraphicBuffer> graphicBuffer; + bool nonNull = (bool)reply.readInt32(); + if (nonNull) { + graphicBuffer = new GraphicBuffer(); + reply.read(*graphicBuffer); + } + return graphicBuffer; + } + + virtual void freeAllGraphicBuffersExcept(int bufIdx) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferAlloc::getInterfaceDescriptor()); + data.writeInt32(bufIdx); + remote()->transact(FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc"); + +// ---------------------------------------------------------------------- + +status_t BnGraphicBufferAlloc::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // codes that don't require permission check + + switch(code) { + case CREATE_GRAPHIC_BUFFER: { + CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + PixelFormat format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage)); + reply->writeInt32(result != 0); + if (result != 0) { + reply->write(*result); + } + return NO_ERROR; + } break; + case FREE_ALL_GRAPHIC_BUFFERS_EXCEPT: { + CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); + int bufIdx = data.readInt32(); + freeAllGraphicBuffersExcept(bufIdx); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/gui/ISurface.cpp b/libs/gui/ISurface.cpp new file mode 100644 index 0000000..23b90af --- /dev/null +++ b/libs/gui/ISurface.cpp @@ -0,0 +1,101 @@ +/* + * 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 "ISurface" + +#include <stdio.h> +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> + +#include <ui/GraphicBuffer.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> + +namespace android { + +// ---------------------------------------------------------------------- + +class BpSurface : public BpInterface<ISurface> +{ +public: + BpSurface(const sp<IBinder>& impl) + : BpInterface<ISurface>(impl) + { + } + + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(bufferIdx); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + remote()->transact(REQUEST_BUFFER, data, &reply); + sp<GraphicBuffer> buffer = new GraphicBuffer(); + reply.read(*buffer); + return buffer; + } + + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + remote()->transact(SET_BUFFER_COUNT, data, &reply); + status_t err = reply.readInt32(); + return err; + } +}; + +IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); + +// ---------------------------------------------------------------------- + +status_t BnSurface::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case REQUEST_BUFFER: { + CHECK_INTERFACE(ISurface, data, reply); + int bufferIdx = data.readInt32(); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage)); + if (buffer == NULL) + return BAD_VALUE; + return reply->write(*buffer); + } + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(ISurface, data, reply); + int bufferCount = data.readInt32(); + status_t err = setBufferCount(bufferCount); + reply->writeInt32(err); + return NO_ERROR; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp new file mode 100644 index 0000000..8951c3f --- /dev/null +++ b/libs/gui/ISurfaceComposer.cpp @@ -0,0 +1,326 @@ +/* + * 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 <binder/Parcel.h> +#include <binder/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <surfaceflinger/ISurfaceComposer.h> + +#include <ui/DisplayInfo.h> + +#include <utils/Log.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<ISurfaceComposerClient> createConnection() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); + return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); + } + + virtual sp<ISurfaceComposerClient> createClientConnection() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply); + return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); + } + + virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply); + return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder()); + } + + virtual sp<IMemoryHeap> getCblk() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply); + return interface_cast<IMemoryHeap>(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, uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(orientation); + data.writeInt32(flags); + 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 captureScreen(DisplayID dpy, + sp<IMemoryHeap>* heap, + uint32_t* width, uint32_t* height, PixelFormat* format, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(dpy); + data.writeInt32(reqWidth); + data.writeInt32(reqHeight); + data.writeInt32(minLayerZ); + data.writeInt32(maxLayerZ); + remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder()); + *width = reply.readInt32(); + *height = reply.readInt32(); + *format = reply.readInt32(); + return reply.readInt32(); + } + + virtual status_t turnElectronBeamOff(int32_t mode) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_OFF, data, &reply); + return reply.readInt32(); + } + + virtual status_t turnElectronBeamOn(int32_t mode) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_ON, data, &reply); + return reply.readInt32(); + } + + virtual void signal() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual bool authenticateSurface(const sp<ISurface>& surface) const + { + Parcel data, reply; + int err = NO_ERROR; + err = data.writeInterfaceToken( + ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error writing " + "interface descriptor: %s (%d)", strerror(-err), -err); + return false; + } + err = data.writeStrongBinder(surface->asBinder()); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error writing strong " + "binder to parcel: %s (%d)", strerror(-err), -err); + return false; + } + err = remote()->transact(BnSurfaceComposer::AUTHENTICATE_SURFACE, data, + &reply); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error performing " + "transaction: %s (%d)", strerror(-err), -err); + return false; + } + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error retrieving " + "result: %s (%d)", strerror(-err), -err); + return false; + } + return result != 0; + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); + +// ---------------------------------------------------------------------- + +status_t BnSurfaceComposer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case CREATE_CONNECTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = createConnection()->asBinder(); + reply->writeStrongBinder(b); + } break; + case CREATE_CLIENT_CONNECTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = createClientConnection()->asBinder(); + reply->writeStrongBinder(b); + } break; + case CREATE_GRAPHIC_BUFFER_ALLOC: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); + reply->writeStrongBinder(b); + } break; + case OPEN_GLOBAL_TRANSACTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + openGlobalTransaction(); + } break; + case CLOSE_GLOBAL_TRANSACTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + closeGlobalTransaction(); + } break; + case SET_ORIENTATION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + DisplayID dpy = data.readInt32(); + int orientation = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( setOrientation(dpy, orientation, flags) ); + } break; + case FREEZE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + DisplayID dpy = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( freezeDisplay(dpy, flags) ); + } break; + case UNFREEZE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + DisplayID dpy = data.readInt32(); + uint32_t flags = data.readInt32(); + reply->writeInt32( unfreezeDisplay(dpy, flags) ); + } break; + case BOOT_FINISHED: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + bootFinished(); + } break; + case SIGNAL: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + signal(); + } break; + case GET_CBLK: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = getCblk()->asBinder(); + reply->writeStrongBinder(b); + } break; + case CAPTURE_SCREEN: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + DisplayID dpy = data.readInt32(); + uint32_t reqWidth = data.readInt32(); + uint32_t reqHeight = data.readInt32(); + uint32_t minLayerZ = data.readInt32(); + uint32_t maxLayerZ = data.readInt32(); + sp<IMemoryHeap> heap; + uint32_t w, h; + PixelFormat f; + status_t res = captureScreen(dpy, &heap, &w, &h, &f, + reqWidth, reqHeight, minLayerZ, maxLayerZ); + reply->writeStrongBinder(heap->asBinder()); + reply->writeInt32(w); + reply->writeInt32(h); + reply->writeInt32(f); + reply->writeInt32(res); + } break; + case TURN_ELECTRON_BEAM_OFF: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int32_t mode = data.readInt32(); + status_t res = turnElectronBeamOff(mode); + reply->writeInt32(res); + } break; + case TURN_ELECTRON_BEAM_ON: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int32_t mode = data.readInt32(); + status_t res = turnElectronBeamOn(mode); + reply->writeInt32(res); + } break; + case AUTHENTICATE_SURFACE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); + int32_t result = authenticateSurface(surface) ? 1 : 0; + reply->writeInt32(result); + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp new file mode 100644 index 0000000..7730eb1 --- /dev/null +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -0,0 +1,231 @@ +/* + * 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 <binder/Parcel.h> +#include <binder/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <ui/Point.h> +#include <ui/Rect.h> + +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/ISurfaceComposerClient.h> +#include <private/surfaceflinger/LayerState.h> + +// --------------------------------------------------------------------------- + +/* ideally AID_GRAPHICS would be in a semi-public header + * or there would be a way to map a user/group name to its id + */ +#ifndef AID_GRAPHICS +#define AID_GRAPHICS 1003 +#endif + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +enum { + GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, + GET_TOKEN, + CREATE_SURFACE, + DESTROY_SURFACE, + SET_STATE +}; + +class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> +{ +public: + BpSurfaceComposerClient(const sp<IBinder>& impl) + : BpInterface<ISurfaceComposerClient>(impl) + { + } + + virtual sp<IMemoryHeap> getControlBlock() const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); + remote()->transact(GET_CBLK, data, &reply); + return interface_cast<IMemoryHeap>(reply.readStrongBinder()); + } + + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); + data.writeStrongBinder(sur->asBinder()); + remote()->transact(GET_TOKEN, data, &reply); + return reply.readInt32(); + } + + virtual sp<ISurface> createSurface( surface_data_t* params, + int pid, + const String8& name, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); + data.writeInt32(pid); + data.writeString8(name); + 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(ISurfaceComposerClient::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(ISurfaceComposerClient::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(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); + +// ---------------------------------------------------------------------- + +status_t BnSurfaceComposerClient::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // codes that don't require permission check + + switch(code) { + case GET_CBLK: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + sp<IMemoryHeap> ctl(getControlBlock()); + reply->writeStrongBinder(ctl->asBinder()); + return NO_ERROR; + } break; + case GET_TOKEN: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder()); + ssize_t token = getTokenForSurface(sur); + reply->writeInt32(token); + return NO_ERROR; + } break; + } + + // these must be checked + + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + LOGE("Permission Denial: " + "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + + switch(code) { + case CREATE_SURFACE: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + surface_data_t params; + int32_t pid = data.readInt32(); + String8 name = data.readString8(); + 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, name, display, w, h, + format, flags); + params.writeToParcel(reply); + reply->writeStrongBinder(s->asBinder()); + return NO_ERROR; + } break; + case DESTROY_SURFACE: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + reply->writeInt32( destroySurface( data.readInt32() ) ); + return NO_ERROR; + } break; + case SET_STATE: { + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); + int32_t count = data.readInt32(); + layer_state_t* states = new layer_state_t[count]; + for (int i=0 ; i<count ; i++) + 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 ISurfaceComposerClient::surface_data_t::readFromParcel(const Parcel& parcel) +{ + token = parcel.readInt32(); + identity = parcel.readInt32(); + width = parcel.readInt32(); + height = parcel.readInt32(); + format = parcel.readInt32(); + return NO_ERROR; +} + +status_t ISurfaceComposerClient::surface_data_t::writeToParcel(Parcel* parcel) const +{ + parcel->writeInt32(token); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp new file mode 100644 index 0000000..01c4c7e --- /dev/null +++ b/libs/gui/LayerState.cpp @@ -0,0 +1,61 @@ +/* + * 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 <binder/Parcel.h> +#include <private/surfaceflinger/LayerState.h> + +namespace android { + +status_t layer_state_t::write(Parcel& output) const +{ + status_t err; + + size_t len = transparentRegion.write(NULL, 0); + err = output.writeInt32(len); + if (err < NO_ERROR) return err; + + void* buf = output.writeInplace(len); + if (buf == NULL) return NO_MEMORY; + + err = transparentRegion.write(buf, len); + if (err < NO_ERROR) return err; + + // NOTE: regions are at the end of the structure + size_t size = sizeof(layer_state_t); + size -= sizeof(transparentRegion); + err = output.write(this, size); + return err; +} + +status_t layer_state_t::read(const Parcel& input) +{ + status_t err; + size_t len = input.readInt32(); + void const* buf = input.readInplace(len); + if (buf == NULL) return NO_MEMORY; + + err = transparentRegion.read(buf); + if (err < NO_ERROR) return err; + + // NOTE: regions are at the end of the structure + size_t size = sizeof(layer_state_t); + size -= sizeof(transparentRegion); + input.read(this, size); + return NO_ERROR; +} + +}; // namespace android diff --git a/libs/gui/SharedBufferStack.cpp b/libs/gui/SharedBufferStack.cpp new file mode 100644 index 0000000..7505d53 --- /dev/null +++ b/libs/gui/SharedBufferStack.cpp @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SharedBufferStack" + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Debug.h> +#include <utils/Log.h> +#include <utils/threads.h> + +#include <private/surfaceflinger/SharedBufferStack.h> + +#include <ui/Rect.h> +#include <ui/Region.h> + +#define DEBUG_ATOMICS 0 + +namespace android { +// ---------------------------------------------------------------------------- + +SharedClient::SharedClient() + : lock(Mutex::SHARED), cv(Condition::SHARED) +{ +} + +SharedClient::~SharedClient() { +} + + +// these functions are used by the clients +status_t SharedClient::validate(size_t i) const { + if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX)) + return BAD_INDEX; + return surfaces[i].status; +} + +// ---------------------------------------------------------------------------- + + +SharedBufferStack::SharedBufferStack() +{ +} + +void SharedBufferStack::init(int32_t i) +{ + status = NO_ERROR; + identity = i; +} + +status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + + buffers[buffer].crop.l = uint16_t(crop.left); + buffers[buffer].crop.t = uint16_t(crop.top); + buffers[buffer].crop.r = uint16_t(crop.right); + buffers[buffer].crop.b = uint16_t(crop.bottom); + return NO_ERROR; +} + +status_t SharedBufferStack::setTransform(int buffer, uint8_t transform) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + buffers[buffer].transform = transform; + return NO_ERROR; +} + +status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + + FlatRegion& reg(buffers[buffer].dirtyRegion); + if (dirty.isEmpty()) { + reg.count = 0; + return NO_ERROR; + } + + size_t count; + Rect const* r = dirty.getArray(&count); + if (count > FlatRegion::NUM_RECT_MAX) { + const Rect bounds(dirty.getBounds()); + reg.count = 1; + reg.rects[0].l = uint16_t(bounds.left); + reg.rects[0].t = uint16_t(bounds.top); + reg.rects[0].r = uint16_t(bounds.right); + reg.rects[0].b = uint16_t(bounds.bottom); + } else { + reg.count = count; + for (size_t i=0 ; i<count ; i++) { + reg.rects[i].l = uint16_t(r[i].left); + reg.rects[i].t = uint16_t(r[i].top); + reg.rects[i].r = uint16_t(r[i].right); + reg.rects[i].b = uint16_t(r[i].bottom); + } + } + return NO_ERROR; +} + +Region SharedBufferStack::getDirtyRegion(int buffer) const +{ + Region res; + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return res; + + const FlatRegion& reg(buffers[buffer].dirtyRegion); + if (reg.count > FlatRegion::NUM_RECT_MAX) + return res; + + if (reg.count == 1) { + const Rect r( + reg.rects[0].l, + reg.rects[0].t, + reg.rects[0].r, + reg.rects[0].b); + res.set(r); + } else { + for (size_t i=0 ; i<reg.count ; i++) { + const Rect r( + reg.rects[i].l, + reg.rects[i].t, + reg.rects[i].r, + reg.rects[i].b); + res.orSelf(r); + } + } + return res; +} + +Rect SharedBufferStack::getCrop(int buffer) const +{ + Rect res(-1, -1); + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return res; + res.left = buffers[buffer].crop.l; + res.top = buffers[buffer].crop.t; + res.right = buffers[buffer].crop.r; + res.bottom = buffers[buffer].crop.b; + return res; +} + +uint32_t SharedBufferStack::getTransform(int buffer) const +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return 0; + return buffers[buffer].transform; +} + + +// ---------------------------------------------------------------------------- + +SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, + int surface, int32_t identity) + : mSharedClient(sharedClient), + mSharedStack(sharedClient->surfaces + surface), + mIdentity(identity) +{ +} + +SharedBufferBase::~SharedBufferBase() +{ +} + +status_t SharedBufferBase::getStatus() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.status; +} + +int32_t SharedBufferBase::getIdentity() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.identity; +} + +String8 SharedBufferBase::dump(char const* prefix) const +{ + const size_t SIZE = 1024; + char buffer[SIZE]; + String8 result; + SharedBufferStack& stack( *mSharedStack ); + snprintf(buffer, SIZE, + "%s[ head=%2d, available=%2d, queued=%2d ] " + "reallocMask=%08x, identity=%d, status=%d", + prefix, stack.head, stack.available, stack.queued, + stack.reallocMask, stack.identity, stack.status); + result.append(buffer); + result.append("\n"); + return result; +} + +status_t SharedBufferBase::waitForCondition(const ConditionBase& condition) +{ + const SharedBufferStack& stack( *mSharedStack ); + SharedClient& client( *mSharedClient ); + const nsecs_t TIMEOUT = s2ns(1); + const int identity = mIdentity; + + Mutex::Autolock _l(client.lock); + while ((condition()==false) && + (stack.identity == identity) && + (stack.status == NO_ERROR)) + { + status_t err = client.cv.waitRelative(client.lock, TIMEOUT); + // handle errors and timeouts + if (CC_UNLIKELY(err != NO_ERROR)) { + if (err == TIMED_OUT) { + if (condition()) { + LOGE("waitForCondition(%s) timed out (identity=%d), " + "but condition is true! We recovered but it " + "shouldn't happen." , condition.name(), stack.identity); + break; + } else { + LOGW("waitForCondition(%s) timed out " + "(identity=%d, status=%d). " + "CPU may be pegged. trying again.", condition.name(), + stack.identity, stack.status); + } + } else { + LOGE("waitForCondition(%s) error (%s) ", + condition.name(), strerror(-err)); + return err; + } + } + } + return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; +} +// ============================================================================ +// conditions and updates +// ============================================================================ + +SharedBufferClient::DequeueCondition::DequeueCondition( + SharedBufferClient* sbc) : ConditionBase(sbc) { +} +bool SharedBufferClient::DequeueCondition::operator()() const { + return stack.available > 0; +} + +SharedBufferClient::LockCondition::LockCondition( + SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) { +} +bool SharedBufferClient::LockCondition::operator()() const { + // NOTE: if stack.head is messed up, we could crash the client + // or cause some drawing artifacts. This is okay, as long as it is + // limited to the client. + return (buf != stack.index[stack.head]); +} + +SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition( + SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs), + mNumBuffers(numBuffers) { +} +bool SharedBufferServer::BuffersAvailableCondition::operator()() const { + return stack.available == mNumBuffers; +} + +// ---------------------------------------------------------------------------- + +SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) + : UpdateBase(sbb) { +} +ssize_t SharedBufferClient::QueueUpdate::operator()() { + android_atomic_inc(&stack.queued); + return NO_ERROR; +} + +SharedBufferClient::DequeueUpdate::DequeueUpdate(SharedBufferBase* sbb) + : UpdateBase(sbb) { +} +ssize_t SharedBufferClient::DequeueUpdate::operator()() { + if (android_atomic_dec(&stack.available) == 0) { + LOGW("dequeue probably called from multiple threads!"); + } + return NO_ERROR; +} + +SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb, + int tail, int buf) + : UpdateBase(sbb), tail(tail), buf(buf) { +} +ssize_t SharedBufferClient::CancelUpdate::operator()() { + stack.index[tail] = buf; + android_atomic_inc(&stack.available); + return NO_ERROR; +} + +SharedBufferServer::RetireUpdate::RetireUpdate( + SharedBufferBase* sbb, int numBuffers) + : UpdateBase(sbb), numBuffers(numBuffers) { +} +ssize_t SharedBufferServer::RetireUpdate::operator()() { + int32_t head = stack.head; + if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + // Decrement the number of queued buffers + int32_t queued; + do { + queued = stack.queued; + if (queued == 0) { + return NOT_ENOUGH_DATA; + } + } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); + + // lock the buffer before advancing head, which automatically unlocks + // the buffer we preventively locked upon entering this function + + head = (head + 1) % numBuffers; + const int8_t headBuf = stack.index[head]; + stack.headBuf = headBuf; + + // head is only modified here, so we don't need to use cmpxchg + android_atomic_write(head, &stack.head); + + // now that head has moved, we can increment the number of available buffers + android_atomic_inc(&stack.available); + return head; +} + +SharedBufferServer::StatusUpdate::StatusUpdate( + SharedBufferBase* sbb, status_t status) + : UpdateBase(sbb), status(status) { +} + +ssize_t SharedBufferServer::StatusUpdate::operator()() { + android_atomic_write(status, &stack.status); + return NO_ERROR; +} + +// ============================================================================ + +SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, + int surface, int num, int32_t identity) + : SharedBufferBase(sharedClient, surface, identity), + mNumBuffers(num), tail(0) +{ + SharedBufferStack& stack( *mSharedStack ); + tail = computeTail(); + queued_head = stack.head; +} + +int32_t SharedBufferClient::computeTail() const +{ + SharedBufferStack& stack( *mSharedStack ); + return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; +} + +ssize_t SharedBufferClient::dequeue() +{ + SharedBufferStack& stack( *mSharedStack ); + + RWLock::AutoRLock _rd(mLock); + + const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); + + //LOGD("[%d] about to dequeue a buffer", + // mSharedStack->identity); + DequeueCondition condition(this); + status_t err = waitForCondition(condition); + if (err != NO_ERROR) + return ssize_t(err); + + DequeueUpdate update(this); + updateCondition( update ); + + int dequeued = stack.index[tail]; + tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); + LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s", + dequeued, tail, dump("").string()); + + mDequeueTime[dequeued] = dequeueTime; + + return dequeued; +} + +status_t SharedBufferClient::undoDequeue(int buf) +{ + return cancel(buf); +} + +status_t SharedBufferClient::cancel(int buf) +{ + RWLock::AutoRLock _rd(mLock); + + // calculate the new position of the tail index (essentially tail--) + int localTail = (tail + mNumBuffers - 1) % mNumBuffers; + CancelUpdate update(this, localTail, buf); + status_t err = updateCondition( update ); + if (err == NO_ERROR) { + tail = localTail; + } + return err; +} + +status_t SharedBufferClient::lock(int buf) +{ + RWLock::AutoRLock _rd(mLock); + + SharedBufferStack& stack( *mSharedStack ); + LockCondition condition(this, buf); + status_t err = waitForCondition(condition); + return err; +} + +status_t SharedBufferClient::queue(int buf) +{ + RWLock::AutoRLock _rd(mLock); + + SharedBufferStack& stack( *mSharedStack ); + + queued_head = (queued_head + 1) % mNumBuffers; + stack.index[queued_head] = buf; + + QueueUpdate update(this); + status_t err = updateCondition( update ); + LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); + + const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); + stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); + + return err; +} + +bool SharedBufferClient::needNewBuffer(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + const uint32_t mask = 1<<(31-buf); + return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0; +} + +status_t SharedBufferClient::setCrop(int buf, const Rect& crop) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setCrop(buf, crop); +} + +status_t SharedBufferClient::setTransform(int buf, uint32_t transform) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setTransform(buf, uint8_t(transform)); +} + +status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setDirtyRegion(buf, reg); +} + +status_t SharedBufferClient::setBufferCount( + int bufferCount, const SetBufferCountCallback& ipc) +{ + SharedBufferStack& stack( *mSharedStack ); + if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN) + return BAD_VALUE; + + RWLock::AutoWLock _wr(mLock); + + status_t err = ipc(bufferCount); + if (err == NO_ERROR) { + mNumBuffers = bufferCount; + queued_head = (stack.head + stack.queued) % mNumBuffers; + tail = computeTail(); + } + return err; +} + +// ---------------------------------------------------------------------------- + +SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, + int surface, int num, int32_t identity) + : SharedBufferBase(sharedClient, surface, identity), + mNumBuffers(num) +{ + mSharedStack->init(identity); + mSharedStack->token = surface; + mSharedStack->head = num-1; + mSharedStack->available = num; + mSharedStack->queued = 0; + mSharedStack->reallocMask = 0; + memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers)); + for (int i=0 ; i<num ; i++) { + mBufferList.add(i); + mSharedStack->index[i] = i; + } +} + +SharedBufferServer::~SharedBufferServer() +{ +} + +ssize_t SharedBufferServer::retireAndLock() +{ + RWLock::AutoRLock _l(mLock); + + RetireUpdate update(this, mNumBuffers); + ssize_t buf = updateCondition( update ); + if (buf >= 0) { + if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + SharedBufferStack& stack( *mSharedStack ); + buf = stack.index[buf]; + LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", + int(buf), dump("").string()); + } + return buf; +} + +void SharedBufferServer::setStatus(status_t status) +{ + if (status < NO_ERROR) { + StatusUpdate update(this, status); + updateCondition( update ); + } +} + +status_t SharedBufferServer::reallocateAll() +{ + RWLock::AutoRLock _l(mLock); + + SharedBufferStack& stack( *mSharedStack ); + uint32_t mask = mBufferList.getMask(); + android_atomic_or(mask, &stack.reallocMask); + return NO_ERROR; +} + +status_t SharedBufferServer::reallocateAllExcept(int buffer) +{ + RWLock::AutoRLock _l(mLock); + + SharedBufferStack& stack( *mSharedStack ); + BufferList temp(mBufferList); + temp.remove(buffer); + uint32_t mask = temp.getMask(); + android_atomic_or(mask, &stack.reallocMask); + return NO_ERROR; +} + +int32_t SharedBufferServer::getQueuedCount() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.queued; +} + +Region SharedBufferServer::getDirtyRegion(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getDirtyRegion(buf); +} + +Rect SharedBufferServer::getCrop(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getCrop(buf); +} + +uint32_t SharedBufferServer::getTransform(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getTransform(buf); +} + +/* + * NOTE: this is not thread-safe on the server-side, meaning + * 'head' cannot move during this operation. The client-side + * can safely operate an usual. + * + */ +status_t SharedBufferServer::resize(int newNumBuffers) +{ + if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN || + (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) { + return BAD_VALUE; + } + + RWLock::AutoWLock _l(mLock); + + if (newNumBuffers < mNumBuffers) { + return shrink(newNumBuffers); + } else { + return grow(newNumBuffers); + } +} + +status_t SharedBufferServer::grow(int newNumBuffers) +{ + SharedBufferStack& stack( *mSharedStack ); + const int numBuffers = mNumBuffers; + const int extra = newNumBuffers - numBuffers; + + // read the head, make sure it's valid + int32_t head = stack.head; + if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + int base = numBuffers; + int32_t avail = stack.available; + int tail = head - avail + 1; + + if (tail >= 0) { + int8_t* const index = const_cast<int8_t*>(stack.index); + const int nb = numBuffers - head; + memmove(&index[head + extra], &index[head], nb); + base = head; + // move head 'extra' ahead, this doesn't impact stack.index[head]; + stack.head = head + extra; + } + stack.available += extra; + + // fill the new free space with unused buffers + BufferList::const_iterator curr(mBufferList.free_begin()); + for (int i=0 ; i<extra ; i++) { + stack.index[base+i] = *curr; + mBufferList.add(*curr); + ++curr; + } + + mNumBuffers = newNumBuffers; + return NO_ERROR; +} + +status_t SharedBufferServer::shrink(int newNumBuffers) +{ + SharedBufferStack& stack( *mSharedStack ); + + // Shrinking is only supported if there are no buffers currently dequeued. + int32_t avail = stack.available; + int32_t queued = stack.queued; + if (avail + queued != mNumBuffers) { + return INVALID_OPERATION; + } + + // Wait for any queued buffers to be displayed. + BuffersAvailableCondition condition(this, mNumBuffers); + status_t err = waitForCondition(condition); + if (err < 0) { + return err; + } + + // Reset head to index 0 and make it refer to buffer 0. The same renaming + // (head -> 0) is done in the BufferManager. + int32_t head = stack.head; + int8_t* index = const_cast<int8_t*>(stack.index); + for (int8_t i = 0; i < newNumBuffers; i++) { + index[i] = i; + } + stack.head = 0; + stack.headBuf = 0; + + // Free the buffers from the end of the list that are no longer needed. + for (int i = newNumBuffers; i < mNumBuffers; i++) { + mBufferList.remove(i); + } + + // Tell the client to reallocate all the buffers. + reallocateAll(); + + mNumBuffers = newNumBuffers; + stack.available = newNumBuffers; + + return NO_ERROR; +} + +SharedBufferStack::Statistics SharedBufferServer::getStats() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.stats; +} + +// --------------------------------------------------------------------------- +status_t SharedBufferServer::BufferList::add(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (mList & mask) + return ALREADY_EXISTS; + mList |= mask; + return NO_ERROR; +} + +status_t SharedBufferServer::BufferList::remove(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (!(mList & mask)) + return NAME_NOT_FOUND; + mList &= ~mask; + return NO_ERROR; +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp new file mode 100644 index 0000000..0dfbf01 --- /dev/null +++ b/libs/gui/Surface.cpp @@ -0,0 +1,1152 @@ +/* + * 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 <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <utils/Errors.h> +#include <utils/threads.h> +#include <utils/CallStack.h> +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/IMemory.h> + +#include <ui/DisplayInfo.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferMapper.h> +#include <ui/GraphicLog.h> +#include <ui/Rect.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <private/surfaceflinger/SharedBufferStack.h> +#include <private/surfaceflinger/LayerState.h> + +namespace android { + +// ---------------------------------------------------------------------- + +static status_t copyBlt( + const sp<GraphicBuffer>& dst, + const sp<GraphicBuffer>& src, + const Region& reg) +{ + // src and dst with, height and format must be identical. no verification + // is done here. + status_t err; + uint8_t const * src_bits = NULL; + err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); + LOGE_IF(err, "error locking src buffer %s", strerror(-err)); + + uint8_t* dst_bits = NULL; + err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits); + LOGE_IF(err, "error locking dst buffer %s", strerror(-err)); + + Region::const_iterator head(reg.begin()); + Region::const_iterator tail(reg.end()); + if (head != tail && src_bits && dst_bits) { + const size_t bpp = bytesPerPixel(src->format); + const size_t dbpr = dst->stride * bpp; + const size_t sbpr = src->stride * bpp; + + while (head != tail) { + const Rect& r(*head++); + ssize_t h = r.height(); + if (h <= 0) continue; + size_t size = r.width() * bpp; + uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; + uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + + if (src_bits) + src->unlock(); + + if (dst_bits) + dst->unlock(); + + return err; +} + +// ============================================================================ +// SurfaceControl +// ============================================================================ + +SurfaceControl::SurfaceControl( + const sp<SurfaceComposerClient>& client, + const sp<ISurface>& surface, + const ISurfaceComposerClient::surface_data_t& data, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) + : mClient(client), mSurface(surface), + mToken(data.token), mIdentity(data.identity), + mWidth(data.width), mHeight(data.height), mFormat(data.format), + mFlags(flags) +{ +} + +SurfaceControl::~SurfaceControl() +{ + destroy(); +} + +void SurfaceControl::destroy() +{ + if (isValid()) { + mClient->destroySurface(mToken); + } + + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. + mClient.clear(); + mSurface.clear(); + IPCThreadState::self()->flushCommands(); +} + +void SurfaceControl::clear() +{ + // here, the window manager tells us explicitly that we should destroy + // the surface's resource. Soon after this call, it will also release + // its last reference (which will call the dtor); however, it is possible + // that a client living in the same process still holds references which + // would delay the call to the dtor -- that is why we need this explicit + // "clear()" call. + destroy(); +} + +bool SurfaceControl::isSameSurface( + const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) +{ + if (lhs == 0 || rhs == 0) + return false; + return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); +} + +status_t SurfaceControl::setLayer(int32_t layer) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setLayer(mToken, layer); +} +status_t SurfaceControl::setPosition(int32_t x, int32_t y) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setPosition(mToken, x, y); +} +status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setSize(mToken, w, h); +} +status_t SurfaceControl::hide() { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->hide(mToken); +} +status_t SurfaceControl::show(int32_t layer) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->show(mToken, layer); +} +status_t SurfaceControl::freeze() { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->freeze(mToken); +} +status_t SurfaceControl::unfreeze() { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->unfreeze(mToken); +} +status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setFlags(mToken, flags, mask); +} +status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setTransparentRegionHint(mToken, transparent); +} +status_t SurfaceControl::setAlpha(float alpha) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setAlpha(mToken, alpha); +} +status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); +} +status_t SurfaceControl::setFreezeTint(uint32_t tint) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setFreezeTint(mToken, tint); +} + +status_t SurfaceControl::validate() const +{ + if (mToken<0 || mClient==0) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, mClient.get()); + return NO_INIT; + } + return NO_ERROR; +} + +status_t SurfaceControl::writeSurfaceToParcel( + const sp<SurfaceControl>& control, Parcel* parcel) +{ + sp<ISurface> sur; + uint32_t identity = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t format = 0; + uint32_t flags = 0; + if (SurfaceControl::isValid(control)) { + sur = control->mSurface; + identity = control->mIdentity; + width = control->mWidth; + height = control->mHeight; + format = control->mFormat; + flags = control->mFlags; + } + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; +} + +sp<Surface> SurfaceControl::getSurface() const +{ + Mutex::Autolock _l(mLock); + if (mSurfaceData == 0) { + mSurfaceData = new Surface(const_cast<SurfaceControl*>(this)); + } + return mSurfaceData; +} + +// ============================================================================ +// Surface +// ============================================================================ + +class SurfaceClient : public Singleton<SurfaceClient> +{ + // all these attributes are constants + sp<ISurfaceComposer> mComposerService; + sp<ISurfaceComposerClient> mClient; + status_t mStatus; + SharedClient* mControl; + sp<IMemoryHeap> mControlMemory; + + SurfaceClient() + : Singleton<SurfaceClient>(), mStatus(NO_INIT) + { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + mComposerService = sf; + mClient = sf->createClientConnection(); + if (mClient != NULL) { + mControlMemory = mClient->getControlBlock(); + if (mControlMemory != NULL) { + mControl = static_cast<SharedClient *>( + mControlMemory->getBase()); + if (mControl) { + mStatus = NO_ERROR; + } + } + } + } + friend class Singleton<SurfaceClient>; +public: + status_t initCheck() const { + return mStatus; + } + SharedClient* getSharedClient() const { + return mControl; + } + ssize_t getTokenForSurface(const sp<ISurface>& sur) const { + // TODO: we could cache a few tokens here to avoid an IPC + return mClient->getTokenForSurface(sur); + } + void signalServer() const { + mComposerService->signal(); + } +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient); + +// --------------------------------------------------------------------------- + +Surface::Surface(const sp<SurfaceControl>& surface) + : mBufferMapper(GraphicBufferMapper::get()), + mClient(SurfaceClient::getInstance()), + mSharedBufferClient(NULL), + mInitCheck(NO_INIT), + mSurface(surface->mSurface), + mIdentity(surface->mIdentity), + mFormat(surface->mFormat), mFlags(surface->mFlags), + mWidth(surface->mWidth), mHeight(surface->mHeight) +{ + init(); +} + +Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref) + : mBufferMapper(GraphicBufferMapper::get()), + mClient(SurfaceClient::getInstance()), + mSharedBufferClient(NULL), + mInitCheck(NO_INIT) +{ + mSurface = interface_cast<ISurface>(ref); + mIdentity = parcel.readInt32(); + mWidth = parcel.readInt32(); + mHeight = parcel.readInt32(); + mFormat = parcel.readInt32(); + mFlags = parcel.readInt32(); + init(); +} + +status_t Surface::writeToParcel( + const sp<Surface>& surface, Parcel* parcel) +{ + sp<ISurface> sur; + uint32_t identity = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t format = 0; + uint32_t flags = 0; + if (Surface::isValid(surface)) { + sur = surface->mSurface; + identity = surface->mIdentity; + width = surface->mWidth; + height = surface->mHeight; + format = surface->mFormat; + flags = surface->mFlags; + } else if (surface != 0 && surface->mSurface != 0) { + LOGW("Parceling invalid surface with non-NULL ISurface as NULL: " + "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, " + "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d", + surface->mSurface.get(), surface->mIdentity, surface->mWidth, + surface->mHeight, surface->mFormat, surface->mFlags, + surface->mInitCheck); + } + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; + +} + + +Mutex Surface::sCachedSurfacesLock; +DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces; + +sp<Surface> Surface::readFromParcel(const Parcel& data) { + Mutex::Autolock _l(sCachedSurfacesLock); + sp<IBinder> binder(data.readStrongBinder()); + sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote(); + if (surface == 0) { + surface = new Surface(data, binder); + sCachedSurfaces.add(binder, surface); + } + if (surface->mSurface == 0) { + surface = 0; + } + cleanCachedSurfacesLocked(); + return surface; +} + +// Remove the stale entries from the surface cache. This should only be called +// with sCachedSurfacesLock held. +void Surface::cleanCachedSurfacesLocked() { + for (int i = sCachedSurfaces.size()-1; i >= 0; --i) { + wp<Surface> s(sCachedSurfaces.valueAt(i)); + if (s == 0 || s.promote() == 0) { + sCachedSurfaces.removeItemsAt(i); + } + } +} + +void Surface::init() +{ + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::cancelBuffer = cancelBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; + + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; + const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; + // FIXME: set real values here + const_cast<int&>(ANativeWindow::minSwapInterval) = 1; + const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; + const_cast<uint32_t&>(ANativeWindow::flags) = 0; + + mNextBufferTransform = 0; + mConnected = 0; + mSwapRectangle.makeInvalid(); + mNextBufferCrop = Rect(0,0); + // two buffers by default + mBuffers.setCapacity(2); + mBuffers.insertAt(0, 2); + + if (mSurface != 0 && mClient.initCheck() == NO_ERROR) { + int32_t token = mClient.getTokenForSurface(mSurface); + if (token >= 0) { + mSharedBufferClient = new SharedBufferClient( + mClient.getSharedClient(), token, 2, mIdentity); + mInitCheck = mClient.getSharedClient()->validate(token); + } else { + LOGW("Not initializing the shared buffer client because token = %d", + token); + } + } +} + +Surface::~Surface() +{ + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. + mBuffers.clear(); + mSurface.clear(); + delete mSharedBufferClient; + IPCThreadState::self()->flushCommands(); +} + +bool Surface::isValid() { + return mInitCheck == NO_ERROR; +} + +status_t Surface::validate(bool inCancelBuffer) const +{ + // check that we initialized ourself properly + if (mInitCheck != NO_ERROR) { + LOGE("invalid token (identity=%u)", mIdentity); + return mInitCheck; + } + + // verify the identity of this surface + uint32_t identity = mSharedBufferClient->getIdentity(); + if (mIdentity != identity) { + LOGE("[Surface] using an invalid surface, " + "identity=%u should be %d", + mIdentity, identity); + CallStack stack; + stack.update(); + stack.dump("Surface"); + return BAD_INDEX; + } + + // check the surface didn't become invalid + status_t err = mSharedBufferClient->getStatus(); + if (err != NO_ERROR) { + if (!inCancelBuffer) { + LOGE("surface (identity=%u) is invalid, err=%d (%s)", + mIdentity, err, strerror(-err)); + CallStack stack; + stack.update(); + stack.dump("Surface"); + } + return err; + } + + return NO_ERROR; +} + +sp<ISurface> Surface::getISurface() const { + return mSurface; +} + +// ---------------------------------------------------------------------------- + +int Surface::setSwapInterval(ANativeWindow* window, int interval) { + return 0; +} + +int Surface::dequeueBuffer(ANativeWindow* window, + android_native_buffer_t** buffer) { + Surface* self = getSelf(window); + return self->dequeueBuffer(buffer); +} + +int Surface::cancelBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->cancelBuffer(buffer); +} + +int Surface::lockBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->lockBuffer(buffer); +} + +int Surface::queueBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->queueBuffer(buffer); +} + +int Surface::query(ANativeWindow* window, + int what, int* value) { + Surface* self = getSelf(window); + return self->query(what, value); +} + +int Surface::perform(ANativeWindow* window, + int operation, ...) { + va_list args; + va_start(args, operation); + Surface* self = getSelf(window); + int res = self->perform(operation, args); + va_end(args); + return res; +} + +// ---------------------------------------------------------------------------- + +bool Surface::needNewBuffer(int bufIdx, + uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const +{ + Mutex::Autolock _l(mSurfaceLock); + + // Always call needNewBuffer(), since it clears the needed buffers flags + bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); + bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]); + bool newNeewBuffer = needNewBuffer || !validBuffer; + if (newNeewBuffer) { + mBufferInfo.get(pWidth, pHeight, pFormat, pUsage); + } + return newNeewBuffer; +} + +int Surface::dequeueBuffer(android_native_buffer_t** buffer) +{ + status_t err = validate(); + if (err != NO_ERROR) + return err; + + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1); + + ssize_t bufIdx = mSharedBufferClient->dequeue(); + + logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx); + + if (bufIdx < 0) { + LOGE("error dequeuing a buffer (%s)", strerror(bufIdx)); + return bufIdx; + } + + // grow the buffer array if needed + const size_t size = mBuffers.size(); + const size_t needed = bufIdx+1; + if (size < needed) { + mBuffers.insertAt(size, needed-size); + } + + uint32_t w, h, format, usage; + if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) { + err = getBufferLocked(bufIdx, w, h, format, usage); + LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)", + bufIdx, w, h, format, usage, strerror(-err)); + if (err == NO_ERROR) { + // reset the width/height with the what we get from the buffer + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); + mWidth = uint32_t(backBuffer->width); + mHeight = uint32_t(backBuffer->height); + } + } + + // if we still don't have a buffer here, we probably ran out of memory + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); + if (!err && backBuffer==0) { + err = NO_MEMORY; + } + + if (err == NO_ERROR) { + mDirtyRegion.set(backBuffer->width, backBuffer->height); + *buffer = backBuffer.get(); + } else { + mSharedBufferClient->undoDequeue(bufIdx); + } + + return err; +} + +int Surface::cancelBuffer(android_native_buffer_t* buffer) +{ + status_t err = validate(true); + switch (err) { + case NO_ERROR: + // no error, common case + break; + case BAD_INDEX: + // legitimate errors here + return err; + default: + // other errors happen because the surface is now invalid, + // for instance because it has been destroyed. In this case, + // we just fail silently (canceling a buffer is not technically + // an error at this point) + return NO_ERROR; + } + + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + + err = mSharedBufferClient->cancel(bufIdx); + + LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err)); + return err; +} + + +int Surface::lockBuffer(android_native_buffer_t* buffer) +{ + status_t err = validate(); + if (err != NO_ERROR) + return err; + + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + + GraphicLog& logger(GraphicLog::getInstance()); + logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx); + + err = mSharedBufferClient->lock(bufIdx); + + logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx); + + LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); + return err; +} + +int Surface::queueBuffer(android_native_buffer_t* buffer) +{ + status_t err = validate(); + if (err != NO_ERROR) + return err; + + if (mSwapRectangle.isValid()) { + mDirtyRegion.set(mSwapRectangle); + } + + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + + GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx); + + mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); + mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); + mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); + err = mSharedBufferClient->queue(bufIdx); + LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); + + if (err == NO_ERROR) { + // TODO: can we avoid this IPC if we know there is one pending? + mClient.signalServer(); + } + return err; +} + +int Surface::query(int what, int* value) +{ + switch (what) { + case NATIVE_WINDOW_WIDTH: + *value = int(mWidth); + return NO_ERROR; + case NATIVE_WINDOW_HEIGHT: + *value = int(mHeight); + return NO_ERROR; + case NATIVE_WINDOW_FORMAT: + *value = int(mFormat); + return NO_ERROR; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + *value = MIN_UNDEQUEUED_BUFFERS; + return NO_ERROR; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + *value = sf->authenticateSurface(mSurface) ? 1 : 0; + return NO_ERROR; + } + case NATIVE_WINDOW_CONCRETE_TYPE: + *value = NATIVE_WINDOW_SURFACE; + return NO_ERROR; + } + return BAD_VALUE; +} + +int Surface::perform(int operation, va_list args) +{ + status_t err = validate(); + if (err != NO_ERROR) + return err; + + int res = NO_ERROR; + switch (operation) { + case NATIVE_WINDOW_SET_USAGE: + dispatch_setUsage( args ); + break; + case NATIVE_WINDOW_CONNECT: + res = dispatch_connect( args ); + break; + case NATIVE_WINDOW_DISCONNECT: + res = dispatch_disconnect( args ); + break; + case NATIVE_WINDOW_SET_CROP: + res = dispatch_crop( args ); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatch_set_buffer_count( args ); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatch_set_buffers_geometry( args ); + break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatch_set_buffers_transform( args ); + break; + case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: + res = dispatch_set_buffers_timestamp( args ); + break; + default: + res = NAME_NOT_FOUND; + break; + } + return res; +} + +void Surface::dispatch_setUsage(va_list args) { + int usage = va_arg(args, int); + setUsage( usage ); +} +int Surface::dispatch_connect(va_list args) { + int api = va_arg(args, int); + return connect( api ); +} +int Surface::dispatch_disconnect(va_list args) { + int api = va_arg(args, int); + return disconnect( api ); +} +int Surface::dispatch_crop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return crop( reinterpret_cast<Rect const*>(rect) ); +} +int Surface::dispatch_set_buffer_count(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} +int Surface::dispatch_set_buffers_geometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + return setBuffersGeometry(w, h, f); +} + +int Surface::dispatch_set_buffers_transform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} + +int Surface::dispatch_set_buffers_timestamp(va_list args) { + int64_t timestamp = va_arg(args, int64_t); + return setBuffersTimestamp(timestamp); +} + +void Surface::setUsage(uint32_t reqUsage) +{ + Mutex::Autolock _l(mSurfaceLock); + mBufferInfo.set(reqUsage); +} + +int Surface::connect(int api) +{ + Mutex::Autolock _l(mSurfaceLock); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + if (mConnected) { + err = -EINVAL; + } else { + mConnected = api; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +int Surface::disconnect(int api) +{ + Mutex::Autolock _l(mSurfaceLock); + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + if (mConnected == api) { + mConnected = 0; + } else { + err = -EINVAL; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +int Surface::crop(Rect const* rect) +{ + Mutex::Autolock _l(mSurfaceLock); + // TODO: validate rect size + + if (rect == NULL || rect->isEmpty()) { + mNextBufferCrop = Rect(0,0); + } else { + mNextBufferCrop = *rect; + } + + return NO_ERROR; +} + +int Surface::setBufferCount(int bufferCount) +{ + sp<ISurface> s(mSurface); + if (s == 0) return NO_INIT; + + class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { + sp<ISurface> surface; + virtual status_t operator()(int bufferCount) const { + return surface->setBufferCount(bufferCount); + } + public: + SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { } + } ipc(s); + + status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); + LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + // Clear out any references to the old buffers. + mBuffers.clear(); + } + + return err; +} + +int Surface::setBuffersGeometry(int w, int h, int format) +{ + if (w<0 || h<0 || format<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock _l(mSurfaceLock); + if (mConnected == NATIVE_WINDOW_API_EGL) { + return INVALID_OPERATION; + } + + mBufferInfo.set(w, h, format); + if (format != 0) { + // we update the format of the surface as reported by query(). + // this is to allow applications to change the format of a surface's + // buffer, and have it reflected in EGL; which is needed for + // EGLConfig validation. + mFormat = format; + } + + mNextBufferCrop = Rect(0,0); + + return NO_ERROR; +} + +int Surface::setBuffersTransform(int transform) +{ + Mutex::Autolock _l(mSurfaceLock); + mNextBufferTransform = transform; + return NO_ERROR; +} + +int Surface::setBuffersTimestamp(int64_t timestamp) +{ + // Surface doesn't really have anything meaningful to do with timestamps + // so they'll just be dropped here. + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +int Surface::getConnectedApi() const +{ + Mutex::Autolock _l(mSurfaceLock); + return mConnected; +} + +// ---------------------------------------------------------------------------- + +status_t Surface::lock(SurfaceInfo* info, bool blocking) { + return Surface::lock(info, NULL, blocking); +} + +status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) +{ + if (getConnectedApi()) { + LOGE("Surface::lock(%p) failed. Already connected to another API", + (ANativeWindow*)this); + CallStack stack; + stack.update(); + stack.dump(""); + return INVALID_OPERATION; + } + + if (mApiLock.tryLock() != NO_ERROR) { + LOGE("calling Surface::lock from different threads!"); + CallStack stack; + stack.update(); + stack.dump(""); + return WOULD_BLOCK; + } + + /* Here we're holding mApiLock */ + + if (mLockedBuffer != 0) { + LOGE("Surface::lock failed, already locked"); + mApiLock.unlock(); + return INVALID_OPERATION; + } + + // we're intending to do software rendering from this point + setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + + android_native_buffer_t* out; + status_t err = dequeueBuffer(&out); + LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); + if (err == NO_ERROR) { + sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); + err = lockBuffer(backBuffer.get()); + LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)", + getBufferIndex(backBuffer), strerror(-err)); + if (err == NO_ERROR) { + const Rect bounds(backBuffer->width, backBuffer->height); + const Region boundsRegion(bounds); + Region scratch(boundsRegion); + Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); + newDirtyRegion &= boundsRegion; + + // figure out if we can copy the frontbuffer back + const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); + const bool canCopyBack = (frontBuffer != 0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + backBuffer->format == frontBuffer->format && + !(mFlags & ISurfaceComposer::eDestroyBackbuffer)); + + // the dirty region we report to surfaceflinger is the one + // given by the user (as opposed to the one *we* return to the + // user). + mDirtyRegion = newDirtyRegion; + + if (canCopyBack) { + // copy the area that is invalid and not repainted this round + const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); + if (!copyback.isEmpty()) + copyBlt(backBuffer, frontBuffer, copyback); + } else { + // if we can't copy-back anything, modify the user's dirty + // region to make sure they redraw the whole buffer + newDirtyRegion = boundsRegion; + } + + // keep track of the are of the buffer that is "clean" + // (ie: that will be redrawn) + mOldDirtyRegion = newDirtyRegion; + + void* vaddr; + status_t res = backBuffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr); + + LOGW_IF(res, "failed locking buffer (handle = %p)", + backBuffer->handle); + + mLockedBuffer = backBuffer; + other->w = backBuffer->width; + other->h = backBuffer->height; + other->s = backBuffer->stride; + other->usage = backBuffer->usage; + other->format = backBuffer->format; + other->bits = vaddr; + } + } + mApiLock.unlock(); + return err; +} + +status_t Surface::unlockAndPost() +{ + if (mLockedBuffer == 0) { + LOGE("Surface::unlockAndPost failed, no locked buffer"); + return INVALID_OPERATION; + } + + status_t err = mLockedBuffer->unlock(); + LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); + + err = queueBuffer(mLockedBuffer.get()); + LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)", + getBufferIndex(mLockedBuffer), strerror(-err)); + + mPostedBuffer = mLockedBuffer; + mLockedBuffer = 0; + return err; +} + +void Surface::setSwapRectangle(const Rect& r) { + Mutex::Autolock _l(mSurfaceLock); + mSwapRectangle = r; +} + +int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const +{ + int idx = buffer->getIndex(); + if (idx < 0) { + // The buffer doesn't have an index set. See if the handle the same as + // one of the buffers for which we do know the index. This can happen + // e.g. if GraphicBuffer is used to wrap an android_native_buffer_t that + // was dequeued from an ANativeWindow. + for (size_t i = 0; i < mBuffers.size(); i++) { + if (mBuffers[i] != 0 && buffer->handle == mBuffers[i]->handle) { + idx = mBuffers[i]->getIndex(); + break; + } + } + } + return idx; +} + +status_t Surface::getBufferLocked(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) +{ + sp<ISurface> s(mSurface); + if (s == 0) return NO_INIT; + + status_t err = NO_MEMORY; + + // free the current buffer + sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); + if (currentBuffer != 0) { + currentBuffer.clear(); + } + + sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage); + LOGE_IF(buffer==0, + "ISurface::getBuffer(%d, %08x) returned NULL", + index, usage); + if (buffer != 0) { // this should always happen by construction + LOGE_IF(buffer->handle == NULL, + "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " + "returned a buffer with a null handle", + mIdentity, index, w, h, format, usage); + err = mSharedBufferClient->getStatus(); + LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); + if (!err && buffer->handle != NULL) { + currentBuffer = buffer; + currentBuffer->setIndex(index); + } else { + err = err<0 ? err : status_t(NO_MEMORY); + } + } + return err; +} + +// ---------------------------------------------------------------------------- +Surface::BufferInfo::BufferInfo() + : mWidth(0), mHeight(0), mFormat(0), + mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0) +{ +} + +void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) { + if ((mWidth != w) || (mHeight != h) || (mFormat != format)) { + mWidth = w; + mHeight = h; + mFormat = format; + mDirty |= GEOMETRY; + } +} + +void Surface::BufferInfo::set(uint32_t usage) { + mUsage = usage; +} + +void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const { + *pWidth = mWidth; + *pHeight = mHeight; + *pFormat = mFormat; + *pUsage = mUsage; +} + +bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const { + // make sure we AT LEAST have the usage flags we want + if (mDirty || buffer==0 || + ((buffer->usage & mUsage) != mUsage)) { + mDirty = 0; + return false; + } + return true; +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp new file mode 100644 index 0000000..d336724 --- /dev/null +++ b/libs/gui/SurfaceComposerClient.cpp @@ -0,0 +1,611 @@ +/* + * 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 <sys/types.h> + +#include <utils/Errors.h> +#include <utils/threads.h> +#include <utils/SortedVector.h> +#include <utils/Log.h> +#include <utils/Singleton.h> + +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> + +#include <ui/DisplayInfo.h> + +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/ISurfaceComposerClient.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <private/surfaceflinger/LayerState.h> +#include <private/surfaceflinger/SharedBufferStack.h> + + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService); + +ComposerService::ComposerService() +: Singleton<ComposerService>() { + const String16 name("SurfaceFlinger"); + while (getService(name, &mComposerService) != NO_ERROR) { + usleep(250000); + } + mServerCblkMemory = mComposerService->getCblk(); + mServerCblk = static_cast<surface_flinger_cblk_t volatile *>( + mServerCblkMemory->getBase()); +} + +sp<ISurfaceComposer> ComposerService::getComposerService() { + return ComposerService::getInstance().mComposerService; +} + +surface_flinger_cblk_t const volatile * ComposerService::getControlBlock() { + return ComposerService::getInstance().mServerCblk; +} + +static inline sp<ISurfaceComposer> getComposerService() { + return ComposerService::getComposerService(); +} + +static inline surface_flinger_cblk_t const volatile * get_cblk() { + return ComposerService::getControlBlock(); +} + +// --------------------------------------------------------------------------- + +class Composer : public Singleton<Composer> +{ + Mutex mLock; + SortedVector< wp<SurfaceComposerClient> > mActiveConnections; + SortedVector<sp<SurfaceComposerClient> > mOpenTransactions; + + Composer() : Singleton<Composer>() { + } + + void addClientImpl(const sp<SurfaceComposerClient>& client) { + Mutex::Autolock _l(mLock); + mActiveConnections.add(client); + } + + void removeClientImpl(const sp<SurfaceComposerClient>& client) { + Mutex::Autolock _l(mLock); + mActiveConnections.remove(client); + } + + void openGlobalTransactionImpl() + { + Mutex::Autolock _l(mLock); + if (mOpenTransactions.size()) { + LOGE("openGlobalTransaction() called more than once. skipping."); + return; + } + const size_t N = mActiveConnections.size(); + for (size_t i=0; i<N; i++) { + sp<SurfaceComposerClient> client(mActiveConnections[i].promote()); + if (client != 0 && mOpenTransactions.indexOf(client) < 0) { + if (client->openTransaction() == NO_ERROR) { + mOpenTransactions.add(client); + } else { + LOGE("openTransaction on client %p failed", client.get()); + // let it go, it'll fail later when the user + // tries to do something with the transaction + } + } + } + } + + void closeGlobalTransactionImpl() + { + mLock.lock(); + SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions); + mOpenTransactions.clear(); + mLock.unlock(); + + sp<ISurfaceComposer> sm(getComposerService()); + sm->openGlobalTransaction(); + const size_t N = clients.size(); + for (size_t i=0; i<N; i++) { + clients[i]->closeTransaction(); + } + sm->closeGlobalTransaction(); + } + + friend class Singleton<Composer>; + +public: + static void addClient(const sp<SurfaceComposerClient>& client) { + Composer::getInstance().addClientImpl(client); + } + static void removeClient(const sp<SurfaceComposerClient>& client) { + Composer::getInstance().removeClientImpl(client); + } + static void openGlobalTransaction() { + Composer::getInstance().openGlobalTransactionImpl(); + } + static void closeGlobalTransaction() { + Composer::getInstance().closeGlobalTransactionImpl(); + } +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(Composer); + +// --------------------------------------------------------------------------- + +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() + : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT) +{ +} + +void SurfaceComposerClient::onFirstRef() +{ + sp<ISurfaceComposer> sm(getComposerService()); + if (sm != 0) { + sp<ISurfaceComposerClient> conn = sm->createConnection(); + if (conn != 0) { + mClient = conn; + Composer::addClient(this); + mPrebuiltLayerState = new layer_state_t; + mStatus = NO_ERROR; + } + } +} + +SurfaceComposerClient::~SurfaceComposerClient() +{ + delete mPrebuiltLayerState; + dispose(); +} + +status_t SurfaceComposerClient::initCheck() const +{ + return mStatus; +} + +sp<IBinder> SurfaceComposerClient::connection() const +{ + return (mClient != 0) ? mClient->asBinder() : 0; +} + +status_t SurfaceComposerClient::linkToComposerDeath( + const sp<IBinder::DeathRecipient>& recipient, + void* cookie, uint32_t flags) +{ + sp<ISurfaceComposer> sm(getComposerService()); + return sm->asBinder()->linkToDeath(recipient, cookie, flags); +} + +void SurfaceComposerClient::dispose() +{ + // this can be called more than once. + sp<ISurfaceComposerClient> client; + Mutex::Autolock _lm(mLock); + if (mClient != 0) { + Composer::removeClient(this); + client = mClient; // hold ref while lock is held + mClient.clear(); + } + mStatus = NO_INIT; +} + +status_t SurfaceComposerClient::getDisplayInfo( + DisplayID dpy, DisplayInfo* info) +{ + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + return BAD_VALUE; + + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + + 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)>=SharedBufferStack::NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->w; +} + +ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) +{ + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + return dcblk->h; +} + +ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) +{ + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + return BAD_VALUE; + volatile surface_flinger_cblk_t const * cblk = get_cblk(); + volatile display_cblk_t const * dcblk = cblk->displays + dpy; + 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<SurfaceControl> SurfaceComposerClient::createSurface( + int pid, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) +{ + String8 name; + const size_t SIZE = 128; + char buffer[SIZE]; + snprintf(buffer, SIZE, "<pid_%d>", getpid()); + name.append(buffer); + + return SurfaceComposerClient::createSurface(pid, name, display, + w, h, format, flags); +} + +sp<SurfaceControl> SurfaceComposerClient::createSurface( + int pid, + const String8& name, + DisplayID display, + uint32_t w, + uint32_t h, + PixelFormat format, + uint32_t flags) +{ + sp<SurfaceControl> result; + if (mStatus == NO_ERROR) { + ISurfaceComposerClient::surface_data_t data; + sp<ISurface> surface = mClient->createSurface(&data, pid, name, + display, w, h, format, flags); + if (surface != 0) { + result = new SurfaceControl(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; +} + +void SurfaceComposerClient::openGlobalTransaction() +{ + Composer::openGlobalTransaction(); +} + +void SurfaceComposerClient::closeGlobalTransaction() +{ + Composer::closeGlobalTransaction(); +} + +status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) +{ + sp<ISurfaceComposer> sm(getComposerService()); + return sm->freezeDisplay(dpy, flags); +} + +status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) +{ + sp<ISurfaceComposer> sm(getComposerService()); + return sm->unfreezeDisplay(dpy, flags); +} + +int SurfaceComposerClient::setOrientation(DisplayID dpy, + int orientation, uint32_t flags) +{ + sp<ISurfaceComposer> sm(getComposerService()); + return sm->setOrientation(dpy, orientation, flags); +} + +status_t SurfaceComposerClient::openTransaction() +{ + if (mStatus != NO_ERROR) + return mStatus; + Mutex::Autolock _l(mLock); + mTransactionOpen++; + return NO_ERROR; +} + +status_t SurfaceComposerClient::closeTransaction() +{ + if (mStatus != NO_ERROR) + return mStatus; + + Mutex::Autolock _l(mLock); + 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(SurfaceID index) +{ + // 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(SurfaceID id) +{ + layer_state_t* s; + mLock.lock(); + s = get_state_l(id); + if (!s) mLock.unlock(); + return s; +} + +void SurfaceComposerClient::unlockLayerState() +{ + mLock.unlock(); +} + +status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::ePositionChanged; + s->x = x; + s->y = y; + unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eSizeChanged; + s->w = w; + s->h = h; + unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eLayerChanged; + s->z = z; + unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::hide(SurfaceID id) +{ + return setFlags(id, ISurfaceComposer::eLayerHidden, + ISurfaceComposer::eLayerHidden); +} + +status_t SurfaceComposerClient::show(SurfaceID id, int32_t) +{ + return setFlags(id, 0, ISurfaceComposer::eLayerHidden); +} + +status_t SurfaceComposerClient::freeze(SurfaceID id) +{ + return setFlags(id, ISurfaceComposer::eLayerFrozen, + ISurfaceComposer::eLayerFrozen); +} + +status_t SurfaceComposerClient::unfreeze(SurfaceID id) +{ + return setFlags(id, 0, ISurfaceComposer::eLayerFrozen); +} + +status_t SurfaceComposerClient::setFlags(SurfaceID id, + uint32_t flags, uint32_t mask) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eVisibilityChanged; + s->flags &= ~mask; + s->flags |= (flags & mask); + s->mask |= mask; + unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setTransparentRegionHint( + SurfaceID id, const Region& transparentRegion) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eTransparentRegionChanged; + s->transparentRegion = transparentRegion; + unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eAlphaChanged; + s->alpha = alpha; + unlockLayerState(); + return NO_ERROR; +} + +status_t SurfaceComposerClient::setMatrix( + SurfaceID id, + float dsdx, float dtdx, + float dsdy, float dtdy ) +{ + layer_state_t* s = lockLayerState(id); + 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(SurfaceID id, uint32_t tint) +{ + layer_state_t* s = lockLayerState(id); + if (!s) return BAD_INDEX; + s->what |= ISurfaceComposer::eFreezeTintChanged; + s->tint = tint; + unlockLayerState(); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +ScreenshotClient::ScreenshotClient() + : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) { +} + +status_t ScreenshotClient::update() { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, 0, 0, + 0, -1UL); +} + +status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, + 0, -1UL); +} + +status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, + minLayerZ, maxLayerZ); +} + +void ScreenshotClient::release() { + mHeap = 0; +} + +void const* ScreenshotClient::getPixels() const { + return mHeap->getBase(); +} + +uint32_t ScreenshotClient::getWidth() const { + return mWidth; +} + +uint32_t ScreenshotClient::getHeight() const { + return mHeight; +} + +PixelFormat ScreenshotClient::getFormat() const { + return mFormat; +} + +uint32_t ScreenshotClient::getStride() const { + return mWidth; +} + +size_t ScreenshotClient::getSize() const { + return mHeap->getSize(); +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk index 7516299..ecd0995 100644 --- a/libs/gui/tests/Android.mk +++ b/libs/gui/tests/Android.mk @@ -9,6 +9,7 @@ LOCAL_MODULE := SurfaceTexture_test LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ + Surface_test.cpp \ SurfaceTextureClient_test.cpp \ SurfaceTexture_test.cpp \ @@ -20,7 +21,6 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libgui \ libstlport \ - libsurfaceflinger_client \ libui \ libutils \ diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp new file mode 100644 index 0000000..fd07479 --- /dev/null +++ b/libs/gui/tests/Surface_test.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2011 The Android Open 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 <gtest/gtest.h> + +#include <binder/IMemory.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <utils/String8.h> + +namespace android { + +class SurfaceTest : public ::testing::Test { +protected: + virtual void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface(getpid(), + String8("Test Surface"), 0, 32, 32, PIXEL_FORMAT_RGB_888, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + + mSurface = mSurfaceControl->getSurface(); + ASSERT_TRUE(mSurface != NULL); + } + + virtual void TearDown() { + mComposerClient->dispose(); + } + + sp<Surface> mSurface; + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; +}; + +TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) { + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + +TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { + mSurfaceControl.clear(); + + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + +// This test probably doesn't belong here. +TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersFail) { + sp<ANativeWindow> anw(mSurface); + + // Verify the screenshot works with no protected buffers. + sp<IMemoryHeap> heap; + uint32_t w=0, h=0; + PixelFormat fmt=0; + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 64, 64, 0, + 40000)); + ASSERT_TRUE(heap != NULL); + + // Set the PROTECTED usage bit and verify that the screenshot fails. Note + // that we need to dequeue a buffer in order for it to actually get + // allocated in SurfaceFlinger. + ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), + GRALLOC_USAGE_PROTECTED)); + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); + android_native_buffer_t* buf = 0; + for (int i = 0; i < 4; i++) { + // Loop to make sure SurfaceFlinger has retired a protected buffer. + ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf)); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf)); + } + heap = 0; + w = h = fmt = 0; + ASSERT_EQ(INVALID_OPERATION, sf->captureScreen(0, &heap, &w, &h, &fmt, + 64, 64, 0, 40000)); + ASSERT_TRUE(heap == NULL); + + // XXX: This should not be needed, but it seems that the new buffers don't + // correctly show up after the upcoming dequeue/lock/queue loop without it. + // We should look into this at some point. + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); + + // Un-set the PROTECTED usage bit and verify that the screenshot works + // again. Note that we have to change the buffers geometry to ensure that + // the buffers get reallocated, as the new usage bits are a subset of the + // old. + ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(anw.get(), 32, 32, 0)); + for (int i = 0; i < 4; i++) { + // Loop to make sure SurfaceFlinger has retired a protected buffer. + ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf)); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf)); + } + heap = 0; + w = h = fmt = 0; + ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 64, 64, 0, + 40000)); + ASSERT_TRUE(heap != NULL); +} + +TEST_F(SurfaceTest, ConcreteTypeIsSurface) { + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); +} + +} |