diff options
| -rw-r--r-- | include/gui/ISurfaceTexture.h | 27 | ||||
| -rw-r--r-- | include/gui/SurfaceTexture.h | 123 | ||||
| -rw-r--r-- | include/gui/SurfaceTextureClient.h | 1 | ||||
| -rw-r--r-- | include/ui/Input.h | 61 | ||||
| -rw-r--r-- | include/ui/InputTransport.h | 6 | ||||
| -rw-r--r-- | include/utils/BackupHelpers.h | 13 | ||||
| -rw-r--r-- | libs/gui/ISurfaceTexture.cpp | 66 | ||||
| -rw-r--r-- | libs/gui/SurfaceTexture.cpp | 489 | ||||
| -rw-r--r-- | libs/gui/SurfaceTextureClient.cpp | 60 | ||||
| -rw-r--r-- | libs/gui/tests/SurfaceTextureClient_test.cpp | 260 | ||||
| -rw-r--r-- | libs/ui/Input.cpp | 92 | ||||
| -rw-r--r-- | libs/ui/InputTransport.cpp | 16 | ||||
| -rw-r--r-- | libs/ui/tests/InputEvent_test.cpp | 25 | ||||
| -rw-r--r-- | libs/ui/tests/InputPublisherAndConsumer_test.cpp | 72 | ||||
| -rw-r--r-- | libs/utils/AssetManager.cpp | 3 | ||||
| -rw-r--r-- | libs/utils/BackupData.cpp | 16 | ||||
| -rw-r--r-- | libs/utils/BackupHelpers.cpp | 261 | ||||
| -rw-r--r-- | opengl/libagl/egl.cpp | 4 | ||||
| -rw-r--r-- | opengl/libs/EGL/Loader.cpp | 11 |
19 files changed, 1349 insertions, 257 deletions
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h index d2d3bb8f8c..e705c6f58b 100644 --- a/include/gui/ISurfaceTexture.h +++ b/include/gui/ISurfaceTexture.h @@ -31,20 +31,27 @@ namespace android { // ---------------------------------------------------------------------------- +class SurfaceTextureClient; + class ISurfaceTexture : public IInterface { public: DECLARE_META_INTERFACE(SurfaceTexture); - enum { BUFFER_NEEDS_REALLOCATION = 1 }; +protected: + friend class SurfaceTextureClient; + + enum { + BUFFER_NEEDS_REALLOCATION = 0x1, + RELEASE_ALL_BUFFERS = 0x2, + }; // requestBuffer requests a new buffer for the given index. The server (i.e. // the ISurfaceTexture implementation) assigns the newly created buffer to // the given slot index, and the client is expected to mirror the // slot->buffer mapping so that it's not necessary to transfer a // GraphicBuffer for every dequeue operation. - virtual sp<GraphicBuffer> requestBuffer(int slot, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage) = 0; + virtual sp<GraphicBuffer> requestBuffer(int slot) = 0; // setBufferCount sets the number of buffer slots available. Calling this // will also cause all buffer slots to be emptied. The caller should empty @@ -60,7 +67,8 @@ public: // in the contents of its associated buffer contents and call queueBuffer. // If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is // expected to call requestBuffer immediately. - virtual status_t dequeueBuffer(int *slot) = 0; + virtual status_t dequeueBuffer(int *slot, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) = 0; // queueBuffer indicates that the client has finished filling in the // contents of the buffer associated with slot and transfers ownership of @@ -85,6 +93,17 @@ public: // Holding this binder reference prevents SurfaceFlinger from freeing the // buffers before the client is done with them. virtual sp<IBinder> getAllocator() = 0; + + // query retrieves some information for this surface + // 'what' tokens allowed are that of android_natives.h + virtual int query(int what, int* value) = 0; + + // setSynchronousMode set whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be retired in order. + // The default mode is asynchronous. + virtual status_t setSynchronousMode(bool enabled) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 96828c640c..43b2fa92dd 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -34,14 +34,23 @@ namespace android { // ---------------------------------------------------------------------------- class IGraphicBufferAlloc; +class String8; class SurfaceTexture : public BnSurfaceTexture { public: enum { MIN_UNDEQUEUED_BUFFERS = 2 }; - enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1 }; + enum { + MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, + MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + }; enum { NUM_BUFFER_SLOTS = 32 }; struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called from queueBuffer() is the FIFO is + // empty. You can use SurfaceTexture::getQueuedCount() to + // figure out if there are more frames waiting. + // This is called without any lock held can be called concurrently by + // multiple threads. virtual void onFrameAvailable() = 0; }; @@ -56,15 +65,15 @@ public: // SurfaceTexture object (i.e. they are not owned by the client). virtual status_t setBufferCount(int bufferCount); - virtual sp<GraphicBuffer> requestBuffer(int buf, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage); + virtual sp<GraphicBuffer> requestBuffer(int buf); // dequeueBuffer gets the next buffer slot index for the client to use. If a // buffer slot is available then that slot index is written to the location // pointed to by the buf argument and a status of OK is returned. If no // slot is available then a status of -EBUSY is returned and buf is // unmodified. - virtual status_t dequeueBuffer(int *buf); + virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage); // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a // timestamp must be provided for the buffer. The timestamp is in @@ -76,6 +85,15 @@ public: virtual status_t setCrop(const Rect& reg); virtual status_t setTransform(uint32_t transform); + virtual int query(int what, int* value); + + // setSynchronousMode set whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be retired in order. + // The default mode is asynchronous. + virtual status_t setSynchronousMode(bool enabled); + // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // @@ -83,6 +101,16 @@ public: // target texture belongs is bound to the calling thread. status_t updateTexImage(); + // getqueuedCount returns the number of queued frames waiting in the + // FIFO. In asynchronous mode, this always returns 0 or 1 since + // frames are not accumulating in the FIFO. + size_t getQueuedCount() const; + + // setBufferCountServer set the buffer count. If the client has requested + // a buffer count using setBufferCount, the server-buffer count will + // take effect once the client sets the count back to zero. + status_t setBufferCountServer(int bufferCount); + // getTransformMatrix retrieves the 4x4 texture coordinate transform matrix // associated with the texture image set by the most recent call to // updateTexImage. @@ -140,6 +168,10 @@ public: // getCurrentTransform returns the transform of the current buffer uint32_t getCurrentTransform() const; + // dump our state in a String + void dump(String8& result) const; + void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; + protected: // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for @@ -154,9 +186,21 @@ private: EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); + status_t setBufferCountServerLocked(int bufferCount); + enum { INVALID_BUFFER_SLOT = -1 }; struct BufferSlot { + + BufferSlot() + : mEglImage(EGL_NO_IMAGE_KHR), + mEglDisplay(EGL_NO_DISPLAY), + mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mLastQueuedTransform(0), + mLastQueuedTimestamp(0) { + } + // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. sp<GraphicBuffer> mGraphicBuffer; @@ -167,11 +211,32 @@ private: // mEglDisplay is the EGLDisplay used to create mEglImage. EGLDisplay mEglDisplay; - // mOwnedByClient indicates whether the slot is currently accessible to a + // mBufferState indicates whether the slot is currently accessible to a // client and should not be used by the SurfaceTexture object. It gets // set to true when dequeueBuffer returns the slot and is reset to false // when the client calls either queueBuffer or cancelBuffer on the slot. - bool mOwnedByClient; + enum { DEQUEUED=-2, FREE=-1, QUEUED=0 }; + int8_t mBufferState; + + + // mRequestBufferCalled is used for validating that the client did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching client bugs. + bool mRequestBufferCalled; + + // mLastQueuedCrop is the crop rectangle for the buffer that was most + // recently queued. This gets set to mNextCrop each time queueBuffer gets + // called. + Rect mLastQueuedCrop; + + // mLastQueuedTransform is the transform identifier for the buffer that was + // most recently queued. This gets set to mNextTransform each time + // queueBuffer gets called. + uint32_t mLastQueuedTransform; + + // mLastQueuedTimestamp is the timestamp for the buffer that was most + // recently queued. This gets set by queueBuffer. + int64_t mLastQueuedTimestamp; }; // mSlots is the array of buffer slots that must be mirrored on the client @@ -193,16 +258,19 @@ private: // in requestBuffers() if a format of zero is specified. uint32_t mPixelFormat; - // mUseDefaultSize indicates whether or not the default size should be used - // that is, if the last requestBuffer has been called with both width - // and height null. - bool mUseDefaultSize; - // mBufferCount is the number of buffer slots that the client and server - // must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by - // calling setBufferCount. + // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed + // by calling setBufferCount or setBufferCountServer int mBufferCount; + // mRequestedBufferCount is the number of buffer slots requested by the + // client. The default is zero, which means the client doesn't care how + // many buffers there is. + int mClientBufferCount; + + // mServerBufferCount buffer count requested by the server-side + int mServerBufferCount; + // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, // indicating that no buffer slot is currently bound to the texture. Note, @@ -233,25 +301,6 @@ private: // gets set to mLastQueuedTimestamp each time updateTexImage is called. int64_t mCurrentTimestamp; - // mLastQueued is the buffer slot index of the most recently enqueued buffer. - // At construction time it is initialized to INVALID_BUFFER_SLOT, and is - // updated each time queueBuffer is called. - int mLastQueued; - - // mLastQueuedCrop is the crop rectangle for the buffer that was most - // recently queued. This gets set to mNextCrop each time queueBuffer gets - // called. - Rect mLastQueuedCrop; - - // mLastQueuedTransform is the transform identifier for the buffer that was - // most recently queued. This gets set to mNextTransform each time - // queueBuffer gets called. - uint32_t mLastQueuedTransform; - - // mLastQueuedTimestamp is the timestamp for the buffer that was most - // recently queued. This gets set by queueBuffer. - int64_t mLastQueuedTimestamp; - // mNextCrop is the crop rectangle that will be used for the next buffer // that gets queued. It is set by calling setCrop. Rect mNextCrop; @@ -274,6 +323,16 @@ private: // queueBuffer. sp<FrameAvailableListener> mFrameAvailableListener; + // mSynchronousMode whether we're in synchronous mode or not + bool mSynchronousMode; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector<int> Fifo; + Fifo mQueue; + // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index c77bc4cd27..e7c6e247b2 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -84,7 +84,6 @@ private: int getConnectedApi() const; enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS }; - enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS }; enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; diff --git a/include/ui/Input.h b/include/ui/Input.h index 9b92c73c42..ba1c6b4716 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -157,14 +157,6 @@ enum { }; /* - * Button state. - */ -enum { - // Primary button pressed (left mouse button). - BUTTON_STATE_PRIMARY = 1 << 0, -}; - -/* * Describes the basic configuration of input devices that are present. */ struct InputConfiguration { @@ -216,6 +208,8 @@ struct PointerCoords { status_t setAxisValue(int32_t axis, float value); float* editAxisValue(int32_t axis); + void scale(float scale); + #ifdef HAVE_ANDROID_OS status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; @@ -233,6 +227,29 @@ private: }; /* + * Pointer property data. + */ +struct PointerProperties { + // The id of the pointer. + int32_t id; + + // The pointer tool type. + int32_t toolType; + + inline void clear() { + id = -1; + toolType = 0; + } + + bool operator==(const PointerProperties& other) const; + inline bool operator!=(const PointerProperties& other) const { + return !(*this == other); + } + + void copyFrom(const PointerProperties& other); +}; + +/* * Input events. */ class InputEvent : public AInputEvent { @@ -344,6 +361,8 @@ public: inline void setMetaState(int32_t metaState) { mMetaState = metaState; } + inline int32_t getButtonState() const { return mButtonState; } + inline float getXOffset() const { return mXOffset; } inline float getYOffset() const { return mYOffset; } @@ -354,9 +373,21 @@ public: inline nsecs_t getDownTime() const { return mDownTime; } - inline size_t getPointerCount() const { return mPointerIds.size(); } + inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } - inline int32_t getPointerId(size_t pointerIndex) const { return mPointerIds[pointerIndex]; } + inline size_t getPointerCount() const { return mPointerProperties.size(); } + + inline const PointerProperties* getPointerProperties(size_t pointerIndex) const { + return &mPointerProperties[pointerIndex]; + } + + inline int32_t getPointerId(size_t pointerIndex) const { + return mPointerProperties[pointerIndex].id; + } + + inline int32_t getToolType(size_t pointerIndex) const { + return mPointerProperties[pointerIndex].toolType; + } inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } @@ -488,6 +519,7 @@ public: int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, float xOffset, float yOffset, float xPrecision, @@ -495,7 +527,7 @@ public: nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, - const int32_t* pointerIds, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -521,7 +553,9 @@ public: } // Low-level accessors. - inline const int32_t* getPointerIds() const { return mPointerIds.array(); } + inline const PointerProperties* getPointerProperties() const { + return mPointerProperties.array(); + } inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } inline const PointerCoords* getSamplePointerCoords() const { return mSamplePointerCoords.array(); @@ -532,12 +566,13 @@ protected: int32_t mFlags; int32_t mEdgeFlags; int32_t mMetaState; + int32_t mButtonState; float mXOffset; float mYOffset; float mXPrecision; float mYPrecision; nsecs_t mDownTime; - Vector<int32_t> mPointerIds; + Vector<PointerProperties> mPointerProperties; Vector<nsecs_t> mSampleEventTimes; Vector<PointerCoords> mSamplePointerCoords; }; diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 119db8112c..95e4447c6a 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -136,6 +136,7 @@ struct InputMessage { int32_t action; int32_t flags; int32_t metaState; + int32_t buttonState; int32_t edgeFlags; nsecs_t downTime; float xOffset; @@ -143,7 +144,7 @@ struct InputMessage { float xPrecision; float yPrecision; size_t pointerCount; - int32_t pointerIds[MAX_POINTERS]; + PointerProperties pointerProperties[MAX_POINTERS]; size_t sampleCount; SampleData sampleData[0]; // variable length } motion; @@ -221,6 +222,7 @@ public: int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, float xOffset, float yOffset, float xPrecision, @@ -228,7 +230,7 @@ public: nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, - const int32_t* pointerIds, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); /* Appends a motion sample to a motion event unless already consumed. diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index b1f504512f..1bb04a7123 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -70,6 +70,14 @@ public: ~BackupDataWriter(); status_t WriteEntityHeader(const String8& key, size_t dataSize); + + /* Note: WriteEntityData will write arbitrary data into the file without + * validation or a previously-supplied header. The full backup implementation + * uses it this way to generate a controlled binary stream that is not + * entity-structured. If the implementation here is changed, either this + * use case must remain valid, or the full backup implementation should be + * adjusted to use some other appropriate mechanism. + */ status_t WriteEntityData(const void* data, size_t size); void SetKeyPrefix(const String8& keyPrefix); @@ -103,7 +111,7 @@ public: bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); - status_t SkipEntityData(); // must be called with the pointer at the begining of the data. + status_t SkipEntityData(); // must be called with the pointer at the beginning of the data. ssize_t ReadEntityData(void* data, size_t size); private: @@ -126,6 +134,9 @@ private: int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* const* files, char const* const *keys, int fileCount); +int write_tarfile(const String8& packageName, const String8& domain, + const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream); + class RestoreHelperBase { public: diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp index bc14ad5514..16e37802b2 100644 --- a/libs/gui/ISurfaceTexture.cpp +++ b/libs/gui/ISurfaceTexture.cpp @@ -39,6 +39,8 @@ enum { SET_CROP, SET_TRANSFORM, GET_ALLOCATOR, + QUERY, + SET_SYNCHRONOUS_MODE, }; @@ -50,15 +52,10 @@ public: { } - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) { Parcel data, reply; data.writeInterfaceToken(ISurfaceTexture::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; bool nonNull = reply.readInt32(); @@ -79,9 +76,14 @@ public: return err; } - virtual status_t dequeueBuffer(int *buf) { + virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) { Parcel data, reply; data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); remote()->transact(DEQUEUE_BUFFER, data, &reply); *buf = reply.readInt32(); int result = reply.readInt32(); @@ -132,6 +134,27 @@ public: remote()->transact(GET_ALLOCATOR, data, &reply); return reply.readStrongBinder(); } + + virtual int query(int what, int* value) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(what); + remote()->transact(QUERY, data, &reply); + value[0] = reply.readInt32(); + status_t result = reply.readInt32(); + return result; + } + + virtual status_t setSynchronousMode(bool enabled) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(enabled); + remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + }; IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture"); @@ -145,12 +168,7 @@ status_t BnSurfaceTexture::onTransact( case REQUEST_BUFFER: { CHECK_INTERFACE(ISurfaceTexture, 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)); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx)); reply->writeInt32(buffer != 0); if (buffer != 0) { reply->write(*buffer); @@ -166,8 +184,12 @@ status_t BnSurfaceTexture::onTransact( } break; case DEQUEUE_BUFFER: { CHECK_INTERFACE(ISurfaceTexture, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); int buf; - int result = dequeueBuffer(&buf); + int result = dequeueBuffer(&buf, w, h, format, usage); reply->writeInt32(buf); reply->writeInt32(result); return NO_ERROR; @@ -210,6 +232,22 @@ status_t BnSurfaceTexture::onTransact( reply->writeStrongBinder(result); return NO_ERROR; } break; + case QUERY: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int value; + int what = data.readInt32(); + int res = query(what, &value); + reply->writeInt32(value); + reply->writeInt32(res); + return NO_ERROR; + } break; + case SET_SYNCHRONOUS_MODE: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + bool enabled = data.readInt32(); + status_t res = setSynchronousMode(enabled); + reply->writeInt32(res); + return NO_ERROR; + } break; } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 2619629be8..d7c449c353 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -34,6 +34,7 @@ #include <surfaceflinger/IGraphicBufferAlloc.h> #include <utils/Log.h> +#include <utils/String8.h> namespace android { @@ -81,23 +82,17 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : mDefaultWidth(1), mDefaultHeight(1), mPixelFormat(PIXEL_FORMAT_RGBA_8888), - mUseDefaultSize(true), - mBufferCount(MIN_BUFFER_SLOTS), + mBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mClientBufferCount(0), + mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT), mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES), mCurrentTransform(0), mCurrentTimestamp(0), - mLastQueued(INVALID_BUFFER_SLOT), - mLastQueuedTransform(0), - mLastQueuedTimestamp(0), mNextTransform(0), - mTexName(tex) { + mTexName(tex), + mSynchronousMode(false) { LOGV("SurfaceTexture::SurfaceTexture"); - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[i].mEglDisplay = EGL_NO_DISPLAY; - mSlots[i].mOwnedByClient = false; - } sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mNextCrop.makeInvalid(); @@ -108,18 +103,80 @@ SurfaceTexture::~SurfaceTexture() { freeAllBuffers(); } +status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) { + if (bufferCount > NUM_BUFFER_SLOTS) + return BAD_VALUE; + + // special-case, nothing to do + if (bufferCount == mBufferCount) + return OK; + + if (!mClientBufferCount && + bufferCount >= mBufferCount) { + // easy, we just have more buffers + mBufferCount = bufferCount; + mServerBufferCount = bufferCount; + mDequeueCondition.signal(); + } else { + // we're here because we're either + // - reducing the number of available buffers + // - or there is a client-buffer-count in effect + + // less than 2 buffers is never allowed + if (bufferCount < 2) + return BAD_VALUE; + + // when there is non client-buffer-count in effect, the client is not + // allowed to dequeue more than one buffer at a time, + // so the next time they dequeue a buffer, we know that they don't + // own one. the actual resizing will happen during the next + // dequeueBuffer. + + mServerBufferCount = bufferCount; + } + return OK; +} + +status_t SurfaceTexture::setBufferCountServer(int bufferCount) { + Mutex::Autolock lock(mMutex); + return setBufferCountServerLocked(bufferCount); +} + status_t SurfaceTexture::setBufferCount(int bufferCount) { LOGV("SurfaceTexture::setBufferCount"); + Mutex::Autolock lock(mMutex); + + // Error out if the user has dequeued buffers + for (int i=0 ; i<mBufferCount ; i++) { + if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { + LOGE("setBufferCount: client owns some buffers"); + return -EINVAL; + } + } - if (bufferCount < MIN_BUFFER_SLOTS) { + if (bufferCount == 0) { + const int minBufferSlots = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + mClientBufferCount = 0; + bufferCount = (mServerBufferCount >= minBufferSlots) ? + mServerBufferCount : minBufferSlots; + return setBufferCountServerLocked(bufferCount); + } + + // We don't allow the client to set a buffer-count less than + // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it. + if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) { return BAD_VALUE; } - Mutex::Autolock lock(mMutex); + // here we're guaranteed that the client doesn't have dequeued buffers + // and will release all of its buffer references. freeAllBuffers(); mBufferCount = bufferCount; + mClientBufferCount = bufferCount; mCurrentTexture = INVALID_BUFFER_SLOT; - mLastQueued = INVALID_BUFFER_SLOT; + mQueue.clear(); + mDequeueCondition.signal(); return OK; } @@ -133,8 +190,7 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) return OK; } -sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { +sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) { LOGV("SurfaceTexture::requestBuffer"); Mutex::Autolock lock(mMutex); if (buf < 0 || mBufferCount <= buf) { @@ -142,11 +198,123 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf, mBufferCount, buf); return 0; } + mSlots[buf].mRequestBufferCalled = true; + return mSlots[buf].mGraphicBuffer; +} + +status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) { + LOGV("SurfaceTexture::dequeueBuffer"); + if ((w && !h) || (!w & h)) { - LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf); - return 0; + LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + + status_t returnFlags(OK); + + int found, foundSync; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffer needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + if (!mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded))) { + // wait for the FIFO to drain + while (!mQueue.isEmpty()) { + mDequeueCondition.wait(mMutex); + } + minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + } + + + if (!mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded))) { + // here we're guaranteed that mQueue is empty + freeAllBuffers(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } + + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; + } + if (state == BufferSlot::FREE || i == mCurrentTexture) { + foundSync = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + return -EINVAL; + } + + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + + // we're in synchronous mode and didn't find a buffer, we need to wait + // for for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + mDequeueCondition.wait(mMutex); + } + } + + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; + } + + if (found == INVALID_BUFFER_SLOT) { + return -EBUSY; } + const int buf = found; + *outBuf = found; + const bool useDefaultSize = !w && !h; if (useDefaultSize) { // use the default size @@ -160,78 +328,115 @@ sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf, format = mPixelFormat; } - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage)); - if (graphicBuffer == 0) { - LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed"); - } else { - mUseDefaultSize = useDefaultSize; + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage)); + if (graphicBuffer == 0) { + LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed"); + return NO_MEMORY; + } if (updateFormat) { mPixelFormat = format; } mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; } + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; } - return graphicBuffer; + return returnFlags; } -status_t SurfaceTexture::dequeueBuffer(int *buf) { - LOGV("SurfaceTexture::dequeueBuffer"); +status_t SurfaceTexture::setSynchronousMode(bool enabled) { Mutex::Autolock lock(mMutex); - int found = INVALID_BUFFER_SLOT; - for (int i = 0; i < mBufferCount; i++) { - if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) { - mSlots[i].mOwnedByClient = true; - found = i; - break; - } - } - if (found == INVALID_BUFFER_SLOT) { - return -EBUSY; - } - - *buf = found; - const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); - if (buffer == NULL) { - return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + status_t err = OK; + if (!enabled) { + // going to asynchronous mode, drain the queue + while (mSynchronousMode != enabled && !mQueue.isEmpty()) { + mDequeueCondition.wait(mMutex); + } } - if ((mUseDefaultSize) && - ((uint32_t(buffer->width) != mDefaultWidth) || - (uint32_t(buffer->height) != mDefaultHeight))) { - return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + if (mSynchronousMode != enabled) { + // - if we're going to asynchronous mode, the queue is guaranteed to be + // empty here + // - if the client set the number of buffers, we're guaranteed that + // we have at least 3 (because we don't allow less) + mSynchronousMode = enabled; + mDequeueCondition.signal(); } - return OK; + return err; } status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { LOGV("SurfaceTexture::queueBuffer"); + + sp<FrameAvailableListener> listener; + + { // scope for the lock Mutex::Autolock lock(mMutex); - if (buf < 0 || mBufferCount <= buf) { + if (buf < 0 || buf >= mBufferCount) { LOGE("queueBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); return -EINVAL; - } else if (!mSlots[buf].mOwnedByClient) { - LOGE("queueBuffer: slot %d is not owned by the client", buf); + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + LOGE("queueBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); return -EINVAL; - } else if (mSlots[buf].mGraphicBuffer == 0) { + } else if (buf == mCurrentTexture) { + LOGE("queueBuffer: slot %d is current!", buf); + return -EINVAL; + } else if (!mSlots[buf].mRequestBufferCalled) { LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", buf); return -EINVAL; } - mSlots[buf].mOwnedByClient = false; - mLastQueued = buf; - mLastQueuedCrop = mNextCrop; - mLastQueuedTransform = mNextTransform; - mLastQueuedTimestamp = timestamp; - if (mFrameAvailableListener != 0) { - mFrameAvailableListener->onFrameAvailable(); + + if (mQueue.empty()) { + listener = mFrameAvailableListener; + } + + if (mSynchronousMode) { + // in synchronous mode we queue all buffers in a FIFO + mQueue.push_back(buf); + } else { + // in asynchronous mode we only keep the most recent buffer + if (mQueue.empty()) { + mQueue.push_back(buf); + } else { + Fifo::iterator front(mQueue.begin()); + // buffer currently queued is freed + mSlots[*front].mBufferState = BufferSlot::FREE; + // and we record the new buffer index in the queued list + *front = buf; + } + } + + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mSlots[buf].mLastQueuedCrop = mNextCrop; + mSlots[buf].mLastQueuedTransform = mNextTransform; + mSlots[buf].mLastQueuedTimestamp = timestamp; + mDequeueCondition.signal(); + } // scope for the lock + + // call back without lock held + if (listener != 0) { + listener->onFrameAvailable(); } return OK; } @@ -239,15 +444,17 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) { void SurfaceTexture::cancelBuffer(int buf) { LOGV("SurfaceTexture::cancelBuffer"); Mutex::Autolock lock(mMutex); - if (buf < 0 || mBufferCount <= buf) { - LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, - buf); + if (buf < 0 || buf >= mBufferCount) { + LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); return; - } else if (!mSlots[buf].mOwnedByClient) { - LOGE("cancelBuffer: slot %d is not owned by the client", buf); + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); return; } - mSlots[buf].mOwnedByClient = false; + mSlots[buf].mBufferState = BufferSlot::FREE; + mDequeueCondition.signal(); } status_t SurfaceTexture::setCrop(const Rect& crop) { @@ -266,18 +473,31 @@ status_t SurfaceTexture::setTransform(uint32_t transform) { status_t SurfaceTexture::updateTexImage() { LOGV("SurfaceTexture::updateTexImage"); + Mutex::Autolock lock(mMutex); - // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, + int buf = mCurrentTexture; + if (!mQueue.empty()) { + // in asynchronous mode the list is guaranteed to be one buffer deep, + // while in synchronous mode we use the oldest buffer + Fifo::iterator front(mQueue.begin()); + buf = *front; + mQueue.erase(front); + if (mQueue.isEmpty()) { + mDequeueCondition.signal(); + } + } + + // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT, // so this check will fail until a buffer gets queued. - if (mCurrentTexture != mLastQueued) { + if (mCurrentTexture != buf) { // Update the GL texture object. - EGLImageKHR image = mSlots[mLastQueued].mEglImage; + EGLImageKHR image = mSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { EGLDisplay dpy = eglGetCurrentDisplay(); - image = createImage(dpy, mSlots[mLastQueued].mGraphicBuffer); - mSlots[mLastQueued].mEglImage = image; - mSlots[mLastQueued].mEglDisplay = dpy; + image = createImage(dpy, mSlots[buf].mGraphicBuffer); + mSlots[buf].mEglImage = image; + mSlots[buf].mEglDisplay = dpy; if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. @@ -287,11 +507,10 @@ status_t SurfaceTexture::updateTexImage() { GLint error; while ((error = glGetError()) != GL_NO_ERROR) { - LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); + LOGW("updateTexImage: clearing GL error: %#04x", error); } - GLenum target = getTextureTarget( - mSlots[mLastQueued].mGraphicBuffer->format); + GLenum target = getTextureTarget(mSlots[buf].mGraphicBuffer->format); if (target != mCurrentTextureTarget) { glDeleteTextures(1, &mTexName); } @@ -301,20 +520,29 @@ status_t SurfaceTexture::updateTexImage() { bool failed = false; while ((error = glGetError()) != GL_NO_ERROR) { LOGE("error binding external texture image %p (slot %d): %#04x", - image, mLastQueued, error); + image, buf, error); failed = true; } if (failed) { return -EINVAL; } + if (mCurrentTexture != INVALID_BUFFER_SLOT) { + // the current buffer becomes FREE if it was still in the queued + // state. If it has already been given to the client + // (synchronous mode), then it stays in DEQUEUED state. + if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) + mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; + } + // Update the SurfaceTexture state. - mCurrentTexture = mLastQueued; + mCurrentTexture = buf; mCurrentTextureTarget = target; - mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; - mCurrentCrop = mLastQueuedCrop; - mCurrentTransform = mLastQueuedTransform; - mCurrentTimestamp = mLastQueuedTimestamp; + mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; + mCurrentCrop = mSlots[buf].mLastQueuedCrop; + mCurrentTransform = mSlots[buf].mLastQueuedTransform; + mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp; + mDequeueCondition.signal(); } else { // We always bind the texture even if we don't update its contents. glBindTexture(mCurrentTextureTarget, mTexName); @@ -322,6 +550,11 @@ status_t SurfaceTexture::updateTexImage() { return OK; } +size_t SurfaceTexture::getQueuedCount() const { + Mutex::Autolock lock(mMutex); + return mQueue.size(); +} + bool SurfaceTexture::isExternalFormat(uint32_t format) { switch (format) { @@ -470,7 +703,7 @@ sp<IBinder> SurfaceTexture::getAllocator() { void SurfaceTexture::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mGraphicBuffer = 0; - mSlots[i].mOwnedByClient = false; + mSlots[i].mBufferState = BufferSlot::FREE; if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; @@ -510,6 +743,98 @@ uint32_t SurfaceTexture::getCurrentTransform() const { return mCurrentTransform; } +int SurfaceTexture::query(int what, int* outValue) +{ + Mutex::Autolock lock(mMutex); + int value; + switch (what) { + case NATIVE_WINDOW_WIDTH: + value = mDefaultWidth; + if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0) + value = mCurrentTextureBuf->width; + break; + case NATIVE_WINDOW_HEIGHT: + value = mDefaultHeight; + if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0) + value = mCurrentTextureBuf->height; + break; + case NATIVE_WINDOW_FORMAT: + value = mPixelFormat; + break; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + value = mSynchronousMode ? + (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS; + break; + default: + return BAD_VALUE; + } + outValue[0] = value; + return NO_ERROR; +} + +void SurfaceTexture::dump(String8& result) const +{ + char buffer[1024]; + dump(result, "", buffer, 1024); +} + +void SurfaceTexture::dump(String8& result, const char* prefix, + char* buffer, size_t SIZE) const +{ + Mutex::Autolock _l(mMutex); + snprintf(buffer, SIZE, + "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], " + "mPixelFormat=%d, mTexName=%d\n", + prefix, mBufferCount, mSynchronousMode, mDefaultWidth, mDefaultHeight, + mPixelFormat, mTexName); + result.append(buffer); + + String8 fifo; + int fifoSize = 0; + Fifo::const_iterator i(mQueue.begin()); + while (i != mQueue.end()) { + snprintf(buffer, SIZE, "%02d ", *i++); + fifoSize++; + fifo.append(buffer); + } + + snprintf(buffer, SIZE, + "%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d, target=0x%04x}\n" + "%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n" + , + prefix, mCurrentCrop.left, + mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom, + mCurrentTransform, mCurrentTexture, mCurrentTextureTarget, + prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right, mNextCrop.bottom, + mCurrentTransform, fifoSize, fifo.string() + ); + result.append(buffer); + + struct { + const char * operator()(int state) const { + switch (state) { + case BufferSlot::DEQUEUED: return "DEQUEUED"; + case BufferSlot::QUEUED: return "QUEUED"; + case BufferSlot::FREE: return "FREE"; + default: return "Unknown"; + } + } + } stateName; + + for (int i=0 ; i<mBufferCount ; i++) { + const BufferSlot& slot(mSlots[i]); + snprintf(buffer, SIZE, + "%s%s[%02d] state=%-8s, crop=[%d,%d,%d,%d], transform=0x%02x, " + "timestamp=%lld\n" + , + prefix, (i==mCurrentTexture)?">":" ", i, stateName(slot.mBufferState), + slot.mLastQueuedCrop.left, slot.mLastQueuedCrop.top, + slot.mLastQueuedCrop.right, slot.mLastQueuedCrop.bottom, + slot.mLastQueuedTransform, slot.mLastQueuedTimestamp + ); + result.append(buffer); + } +} static void mtxMul(float out[16], const float a[16], const float b[16]) { out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index ec6da4324d..6f103208fd 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -39,6 +39,9 @@ SurfaceTextureClient::SurfaceTextureClient( ANativeWindow::query = query; ANativeWindow::perform = perform; + const_cast<int&>(ANativeWindow::minSwapInterval) = 0; + const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; + // Get a reference to the allocator. mAllocator = mSurfaceTexture->getAllocator(); } @@ -90,27 +93,40 @@ int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) { } int SurfaceTextureClient::setSwapInterval(int interval) { - return INVALID_OPERATION; + // EGL specification states: + // interval is silently clamped to minimum and maximum implementation + // dependent values before being stored. + // Although we don't have to, we apply the same logic here. + + if (interval < minSwapInterval) + interval = minSwapInterval; + + if (interval > maxSwapInterval) + interval = maxSwapInterval; + + status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false); + + return res; } int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { LOGV("SurfaceTextureClient::dequeueBuffer"); Mutex::Autolock lock(mMutex); int buf = -1; - status_t err = mSurfaceTexture->dequeueBuffer(&buf); - if (err < 0) { - LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer failed: %d", err); - return err; + status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight, + mReqFormat, mReqUsage); + if (result < 0) { + LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)" + "failed: %d", result, mReqWidth, mReqHeight, mReqFormat, mReqUsage); + return result; } sp<GraphicBuffer>& gbuf(mSlots[buf]); - if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION || - gbuf == 0 || - (mReqWidth && gbuf->getWidth() != mReqWidth) || - (mReqHeight && gbuf->getHeight() != mReqHeight) || - (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) || - (gbuf->getUsage() & mReqUsage) != mReqUsage) { - gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight, - mReqFormat, mReqUsage); + if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) { + freeAllBuffers(); + } + + if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { + gbuf = mSurfaceTexture->requestBuffer(buf); if (gbuf == 0) { LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); return NO_MEMORY; @@ -163,29 +179,17 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { int SurfaceTextureClient::query(int what, int* value) const { LOGV("SurfaceTextureClient::query"); - Mutex::Autolock lock(mMutex); switch (what) { - case NATIVE_WINDOW_WIDTH: - *value = mQueryWidth ? mQueryWidth : mReqWidth; - return NO_ERROR; - case NATIVE_WINDOW_HEIGHT: - *value = mQueryHeight ? mQueryHeight : mReqHeight; - return NO_ERROR; - case NATIVE_WINDOW_FORMAT: - *value = mQueryFormat ? mQueryFormat : mReqFormat; - return NO_ERROR; - case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - *value = MIN_UNDEQUEUED_BUFFERS; - return NO_ERROR; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: - // SurfaceTextureClient currently never queues frames to SurfaceFlinger. + // TODO: this is not needed anymore *value = 0; return NO_ERROR; case NATIVE_WINDOW_CONCRETE_TYPE: + // TODO: this is not needed anymore *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT; return NO_ERROR; } - return BAD_VALUE; + return mSurfaceTexture->query(what, value); } int SurfaceTextureClient::perform(int operation, va_list args) diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 753e93391b..59a4cc5b24 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -17,23 +17,78 @@ #include <EGL/egl.h> #include <gtest/gtest.h> #include <gui/SurfaceTextureClient.h> +#include <utils/threads.h> namespace android { class SurfaceTextureClientTest : public ::testing::Test { protected: + SurfaceTextureClientTest(): + mEglDisplay(EGL_NO_DISPLAY), + mEglSurface(EGL_NO_SURFACE), + mEglContext(EGL_NO_CONTEXT) { + } + virtual void SetUp() { mST = new SurfaceTexture(123); mSTC = new SurfaceTextureClient(mST); + + // We need a valid GL context so we can test updateTexImage() + // This initializes EGL and create a dummy GL context with a + // pbuffer render target. + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + + EGLint majorVersion, minorVersion; + EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLConfig myConfig; + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), + &myConfig, 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLint pbufferAttribs[] = { + EGL_WIDTH, 16, + EGL_HEIGHT, 16, + EGL_NONE }; + mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, pbufferAttribs); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mEglContext); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); } virtual void TearDown() { mST.clear(); mSTC.clear(); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(mEglDisplay, mEglContext); + eglDestroySurface(mEglDisplay, mEglSurface); + eglTerminate(mEglDisplay); + } + + virtual EGLint const* getConfigAttribs() { + static EGLint sDefaultConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_NONE + }; + + return sDefaultConfigAttribs; } sp<SurfaceTexture> mST; sp<SurfaceTextureClient> mSTC; + EGLDisplay mEglDisplay; + EGLSurface mEglSurface; + EGLContext mEglContext; }; TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { @@ -94,8 +149,8 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(), NULL); - ASSERT_NE(EGL_NO_SURFACE, eglSurface); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_NE(EGL_NO_SURFACE, eglSurface); + EXPECT_EQ(EGL_SUCCESS, eglGetError()); eglTerminate(dpy); } @@ -204,6 +259,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) { sp<ANativeWindow> anw(mSTC); sp<SurfaceTexture> st(mST); ANativeWindowBuffer* buf[2]; + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); @@ -225,6 +281,7 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { sp<ANativeWindow> anw(mSTC); sp<SurfaceTexture> st(mST); ANativeWindowBuffer* buf[2]; + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); @@ -247,4 +304,203 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1])); } +TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + ASSERT_EQ(OK, st->setSynchronousMode(false)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(OK, st->updateTexImage()); + + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(OK, st->updateTexImage()); +} + +TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + EXPECT_NE(buf[0], buf[1]); + EXPECT_NE(buf[1], buf[2]); + EXPECT_NE(buf[2], buf[0]); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]); +} + +TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + EXPECT_NE(buf[0], buf[1]); + EXPECT_NE(buf[1], buf[2]); + EXPECT_NE(buf[2], buf[0]); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]); +} + +TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]); + + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + EXPECT_NE(buf[0], buf[1]); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); + + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + EXPECT_NE(buf[1], buf[2]); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]); +} + +TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + android_native_buffer_t* firstBuf; + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &firstBuf)); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), firstBuf)); + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), firstBuf); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + EXPECT_NE(buf[0], buf[1]); + EXPECT_NE(buf[1], buf[2]); + EXPECT_NE(buf[2], buf[0]); + EXPECT_EQ(firstBuf, buf[2]); +} + +TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[2])); + + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + + EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); + + EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + + ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2])); +} + +TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + + class MyThread : public Thread { + sp<SurfaceTexture> st; + EGLContext ctx; + EGLSurface sur; + EGLDisplay dpy; + bool mBufferRetired; + Mutex mLock; + virtual bool threadLoop() { + eglMakeCurrent(dpy, sur, sur, ctx); + usleep(20000); + Mutex::Autolock _l(mLock); + st->updateTexImage(); + mBufferRetired = true; + eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + return false; + } + public: + MyThread(const sp<SurfaceTexture>& st) + : st(st), mBufferRetired(false) { + ctx = eglGetCurrentContext(); + sur = eglGetCurrentSurface(EGL_DRAW); + dpy = eglGetCurrentDisplay(); + eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + ~MyThread() { + eglMakeCurrent(dpy, sur, sur, ctx); + } + void bufferDequeued() { + Mutex::Autolock _l(mLock); + EXPECT_EQ(true, mBufferRetired); + } + }; + + android_native_buffer_t* buf[3]; + ASSERT_EQ(OK, st->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + // dequeue/queue/update so we have a current buffer + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + st->updateTexImage(); + + MyThread* thread = new MyThread(st); + sp<Thread> threadBase(thread); + + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + thread->run(); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + thread->bufferDequeued(); + thread->requestExitAndWait(); +} + } diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index a95f432619..1ba38a7ee9 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -302,6 +302,24 @@ float* PointerCoords::editAxisValue(int32_t axis) { return &values[index]; } +static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { + float* value = c.editAxisValue(axis); + if (value) { + *value *= scaleFactor; + } +} + +void PointerCoords::scale(float scaleFactor) { + // No need to scale pressure or size since they are normalized. + // No need to scale orientation since it is meaningless to do so. + scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); +} + #ifdef HAVE_ANDROID_OS status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); @@ -355,6 +373,19 @@ void PointerCoords::copyFrom(const PointerCoords& other) { } +// --- PointerProperties --- + +bool PointerProperties::operator==(const PointerProperties& other) const { + return id == other.id + && toolType == other.toolType; +} + +void PointerProperties::copyFrom(const PointerProperties& other) { + id = other.id; + toolType = other.toolType; +} + + // --- MotionEvent --- void MotionEvent::initialize( @@ -364,6 +395,7 @@ void MotionEvent::initialize( int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, float xOffset, float yOffset, float xPrecision, @@ -371,20 +403,21 @@ void MotionEvent::initialize( nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, - const int32_t* pointerIds, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source); mAction = action; mFlags = flags; mEdgeFlags = edgeFlags; mMetaState = metaState; + mButtonState = buttonState; mXOffset = xOffset; mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; mDownTime = downTime; - mPointerIds.clear(); - mPointerIds.appendArray(pointerIds, pointerCount); + mPointerProperties.clear(); + mPointerProperties.appendArray(pointerProperties, pointerCount); mSampleEventTimes.clear(); mSamplePointerCoords.clear(); addSample(eventTime, pointerCoords); @@ -396,12 +429,13 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mFlags = other->mFlags; mEdgeFlags = other->mEdgeFlags; mMetaState = other->mMetaState; + mButtonState = other->mButtonState; mXOffset = other->mXOffset; mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; mDownTime = other->mDownTime; - mPointerIds = other->mPointerIds; + mPointerProperties = other->mPointerProperties; if (keepHistory) { mSampleEventTimes = other->mSampleEventTimes; @@ -436,11 +470,9 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - value += mXOffset; - break; + return value + mXOffset; case AMOTION_EVENT_AXIS_Y: - value += mYOffset; - break; + return value + mYOffset; } return value; } @@ -460,19 +492,17 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - value += mXOffset; - break; + return value + mXOffset; case AMOTION_EVENT_AXIS_Y: - value += mYOffset; - break; + return value + mYOffset; } return value; } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { - size_t pointerCount = mPointerIds.size(); + size_t pointerCount = mPointerProperties.size(); for (size_t i = 0; i < pointerCount; i++) { - if (mPointerIds.itemAt(i) == pointerId) { + if (mPointerProperties.itemAt(i).id == pointerId) { return i; } } @@ -484,13 +514,6 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } -static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { - float* value = c.editAxisValue(axis); - if (value) { - *value *= scaleFactor; - } -} - void MotionEvent::scale(float scaleFactor) { mXOffset *= scaleFactor; mYOffset *= scaleFactor; @@ -499,15 +522,7 @@ void MotionEvent::scale(float scaleFactor) { size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - PointerCoords& c = mSamplePointerCoords.editItemAt(i); - // No need to scale pressure or size since they are normalized. - // No need to scale orientation since it is meaningless to do so. - scaleAxisValue(c, AMOTION_EVENT_AXIS_X, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_Y, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); + mSamplePointerCoords.editItemAt(i).scale(scaleFactor); } } @@ -584,21 +599,25 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mFlags = parcel->readInt32(); mEdgeFlags = parcel->readInt32(); mMetaState = parcel->readInt32(); + mButtonState = parcel->readInt32(); mXOffset = parcel->readFloat(); mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); mDownTime = parcel->readInt64(); - mPointerIds.clear(); - mPointerIds.setCapacity(pointerCount); + mPointerProperties.clear(); + mPointerProperties.setCapacity(pointerCount); mSampleEventTimes.clear(); mSampleEventTimes.setCapacity(sampleCount); mSamplePointerCoords.clear(); mSamplePointerCoords.setCapacity(sampleCount * pointerCount); for (size_t i = 0; i < pointerCount; i++) { - mPointerIds.push(parcel->readInt32()); + mPointerProperties.push(); + PointerProperties& properties = mPointerProperties.editTop(); + properties.id = parcel->readInt32(); + properties.toolType = parcel->readInt32(); } while (sampleCount-- > 0) { @@ -615,7 +634,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { } status_t MotionEvent::writeToParcel(Parcel* parcel) const { - size_t pointerCount = mPointerIds.size(); + size_t pointerCount = mPointerProperties.size(); size_t sampleCount = mSampleEventTimes.size(); parcel->writeInt32(pointerCount); @@ -627,6 +646,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mFlags); parcel->writeInt32(mEdgeFlags); parcel->writeInt32(mMetaState); + parcel->writeInt32(mButtonState); parcel->writeFloat(mXOffset); parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); @@ -634,7 +654,9 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { - parcel->writeInt32(mPointerIds.itemAt(i)); + const PointerProperties& properties = mPointerProperties.itemAt(i); + parcel->writeInt32(properties.id); + parcel->writeInt32(properties.toolType); } const PointerCoords* pc = mSamplePointerCoords.array(); diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index 93d0d1f8a1..ffdfe663af 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -366,6 +366,7 @@ status_t InputPublisher::publishMotionEvent( int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, float xOffset, float yOffset, float xPrecision, @@ -373,16 +374,17 @@ status_t InputPublisher::publishMotionEvent( nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, - const int32_t* pointerIds, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, " - "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, xOffset=%f, yOffset=%f, " + "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " + "xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " "pointerCount=%d", mChannel->getName().string(), - deviceId, source, action, flags, edgeFlags, metaState, xOffset, yOffset, - xPrecision, yPrecision, downTime, eventTime, pointerCount); + deviceId, source, action, flags, edgeFlags, metaState, buttonState, + xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); #endif if (pointerCount > MAX_POINTERS || pointerCount < 1) { @@ -400,6 +402,7 @@ status_t InputPublisher::publishMotionEvent( mSharedMessage->motion.flags = flags; mSharedMessage->motion.edgeFlags = edgeFlags; mSharedMessage->motion.metaState = metaState; + mSharedMessage->motion.buttonState = buttonState; mSharedMessage->motion.xOffset = xOffset; mSharedMessage->motion.yOffset = yOffset; mSharedMessage->motion.xPrecision = xPrecision; @@ -411,7 +414,7 @@ status_t InputPublisher::publishMotionEvent( mSharedMessage->motion.sampleData[0].eventTime = eventTime; for (size_t i = 0; i < pointerCount; i++) { - mSharedMessage->motion.pointerIds[i] = pointerIds[i]; + mSharedMessage->motion.pointerProperties[i].copyFrom(pointerProperties[i]); mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]); } @@ -694,6 +697,7 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { mSharedMessage->motion.flags, mSharedMessage->motion.edgeFlags, mSharedMessage->motion.metaState, + mSharedMessage->motion.buttonState, mSharedMessage->motion.xOffset, mSharedMessage->motion.yOffset, mSharedMessage->motion.xPrecision, @@ -701,7 +705,7 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { mSharedMessage->motion.downTime, mSharedMessage->motion.sampleData[0].eventTime, mSharedMessage->motion.pointerCount, - mSharedMessage->motion.pointerIds, + mSharedMessage->motion.pointerProperties, mSharedMessage->motion.sampleData[0].coords); size_t sampleCount = mSharedMessage->motion.sampleCount; diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp index b77489e158..e48d5b7dc5 100644 --- a/libs/ui/tests/InputEvent_test.cpp +++ b/libs/ui/tests/InputEvent_test.cpp @@ -232,7 +232,14 @@ const float MotionEventTest::X_OFFSET = 1.0f; const float MotionEventTest::Y_OFFSET = 1.1f; void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { - int32_t pointerIds[] = { 1, 2 }; + PointerProperties pointerProperties[2]; + pointerProperties[0].clear(); + pointerProperties[0].id = 1; + pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + pointerProperties[1].clear(); + pointerProperties[1].id = 2; + pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + PointerCoords pointerCoords[2]; pointerCoords[0].clear(); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); @@ -256,10 +263,10 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, - 2, pointerIds, pointerCoords); + 2, pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -311,6 +318,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); + ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -319,7 +327,9 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(2U, event->getPointerCount()); ASSERT_EQ(1, event->getPointerId(0)); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, event->getToolType(0)); ASSERT_EQ(2, event->getPointerId(1)); + ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, event->getToolType(1)); ASSERT_EQ(2U, event->getHistorySize()); @@ -534,19 +544,20 @@ TEST_F(MotionEventTest, Transform) { const float ROTATION = ARC * 2; const size_t pointerCount = 11; - int pointerIds[pointerCount]; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { float angle = float(i * ARC * PI_180); - pointerIds[i] = i; + pointerProperties[i].clear(); + pointerProperties[i].id = i; pointerCoords[i].clear(); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2); pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, - 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index 6e18a4f1ab..fcc4cadb71 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -156,13 +156,19 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; const float xOffset = -10; const float yOffset = -20; const float xPrecision = 0.25; const float yPrecision = 0.5; const nsecs_t downTime = 3; const size_t pointerCount = 3; - const int32_t pointerIds[pointerCount] = { 2, 0, 1 }; + PointerProperties pointerProperties[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = (i + 2) % pointerCount; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } Vector<nsecs_t> sampleEventTimes; Vector<PointerCoords> samplePointerCoords; @@ -186,8 +192,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( } status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags, - metaState, xOffset, yOffset, xPrecision, yPrecision, - downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array()); + metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, + downTime, sampleEventTimes[0], pointerCount, + pointerProperties, samplePointerCoords.array()); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -234,6 +241,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); EXPECT_EQ(metaState, motionEvent->getMetaState()); + EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(downTime, motionEvent->getDownTime()); @@ -243,7 +251,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); - EXPECT_EQ(pointerIds[i], motionEvent->getPointerId(i)); + EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); + EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); } for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) { @@ -352,17 +361,20 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsErr ASSERT_NO_FATAL_FAILURE(Initialize()); const size_t pointerCount = 1; - int32_t pointerIds[pointerCount] = { 0 }; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - pointerCoords[0].clear(); + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerIds, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerIds, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(INVALID_OPERATION, status) << "publisher publishMotionEvent should return INVALID_OPERATION because "; "the publisher was not reset"; @@ -373,11 +385,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha ASSERT_NO_FATAL_FAILURE(Initialize()); const size_t pointerCount = 0; - int32_t pointerIds[pointerCount]; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerIds, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -387,11 +399,15 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreater ASSERT_NO_FATAL_FAILURE(Initialize()); const size_t pointerCount = MAX_POINTERS + 1; - int32_t pointerIds[pointerCount]; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - pointerCount, pointerIds, pointerCoords); + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -432,11 +448,15 @@ TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEven ASSERT_NO_FATAL_FAILURE(Initialize()); const size_t pointerCount = MAX_POINTERS; - int32_t pointerIds[pointerCount]; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN, - 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status); status = mPublisher->appendMotionSample(0, pointerCoords); @@ -449,11 +469,15 @@ TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_Ret ASSERT_NO_FATAL_FAILURE(Initialize()); const size_t pointerCount = MAX_POINTERS; - int32_t pointerIds[pointerCount]; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE, - 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status); status = mPublisher->sendDispatchSignal(); @@ -476,11 +500,15 @@ TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsE ASSERT_NO_FATAL_FAILURE(Initialize()); const size_t pointerCount = MAX_POINTERS; - int32_t pointerIds[pointerCount]; + PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerCoords[i].clear(); + } status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE, - 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status); for (int count = 1;; count++) { diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index e41dd39682..22034c5935 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -681,6 +681,9 @@ const ResTable* AssetManager::getResTable(bool required) const delete ass; } } + if (idmap != NULL) { + delete idmap; + } } if (required && !rt) LOGW("Unable to find resources file resources.arsc"); diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index adb3174990..f963058fab 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -20,12 +20,15 @@ #include <utils/ByteOrder.h> #include <stdio.h> +#include <string.h> #include <unistd.h> #include <cutils/log.h> namespace android { +static const bool DEBUG = false; + /* * File Format (v1): * @@ -75,6 +78,7 @@ BackupDataWriter::write_padding_for(int n) paddingSize = padding_extra(n); if (paddingSize > 0) { uint32_t padding = 0xbcbcbcbc; + if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n); amt = write(m_fd, &padding, paddingSize); if (amt != paddingSize) { m_status = errno; @@ -107,8 +111,8 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - if (false) { - LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), + if (DEBUG) { + LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), dataSize); } @@ -121,6 +125,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); + if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); amt = write(m_fd, &header, sizeof(entity_header_v1)); if (amt != sizeof(entity_header_v1)) { m_status = errno; @@ -128,6 +133,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; + if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1); amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; @@ -145,7 +151,12 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) status_t BackupDataWriter::WriteEntityData(const void* data, size_t size) { + if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size); + if (m_status != NO_ERROR) { + if (DEBUG) { + LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); + } return m_status; } @@ -155,6 +166,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) ssize_t amt = write(m_fd, data, size); if (amt != (ssize_t)size) { m_status = errno; + if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status)); return m_status; } m_pos += amt; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 4ad9b51795..e15875f380 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -442,6 +442,267 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD return 0; } +// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of +// returning the initial dest, return a pointer to the trailing NUL. +static char* strcpy_ptr(char* dest, const char* str) { + if (dest && str) { + while ((*dest = *str) != 0) { + dest++; + str++; + } + } + return dest; +} + +static void calc_tar_checksum(char* buf) { + // [ 148 : 8 ] checksum -- to be calculated with this field as space chars + memset(buf + 148, ' ', 8); + + uint16_t sum = 0; + for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { + sum += *p; + } + + // Now write the real checksum value: + // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC + sprintf(buf + 148, "%06o", sum); // the trailing space is already in place +} + +// Returns number of bytes written +static int write_pax_header_entry(char* buf, const char* key, const char* value) { + // start with the size of "1 key=value\n" + int len = strlen(key) + strlen(value) + 4; + if (len > 9) len++; + if (len > 99) len++; + if (len > 999) len++; + // since PATH_MAX is 4096 we don't expect to have to generate any single + // header entry longer than 9999 characters + + return sprintf(buf, "%d %s=%s\n", len, key, value); +} + +int write_tarfile(const String8& packageName, const String8& domain, + const String8& rootpath, const String8& filepath, BackupDataWriter* writer) +{ + // In the output stream everything is stored relative to the root + const char* relstart = filepath.string() + rootpath.length(); + if (*relstart == '/') relstart++; // won't be true when path == rootpath + String8 relpath(relstart); + + // If relpath is empty, it means this is the top of one of the standard named + // domain directories, so we should just skip it + if (relpath.length() == 0) { + return 0; + } + + // Too long a name for the ustar format? + // "apps/" + packagename + '/' + domainpath < 155 chars + // relpath < 100 chars + bool needExtended = false; + if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { + needExtended = true; + } + + int err = 0; + struct stat64 s; + if (lstat64(filepath.string(), &s) != 0) { + err = errno; + LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); + return err; + } + + String8 fullname; // for pax later on + String8 prefix; + + const int isdir = S_ISDIR(s.st_mode); + + // !!! TODO: use mmap when possible to avoid churning the buffer cache + // !!! TODO: this will break with symlinks; need to use readlink(2) + int fd = open(filepath.string(), O_RDONLY); + if (fd < 0) { + err = errno; + LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); + return err; + } + + // read/write up to this much at a time. + const size_t BUFSIZE = 32 * 1024; + char* buf = new char[BUFSIZE]; + char* paxHeader = buf + 512; // use a different chunk of it as separate scratch + char* paxData = buf + 1024; + + if (buf == NULL) { + LOGE("Out of mem allocating transfer buffer"); + err = ENOMEM; + goto cleanup; + } + + // Good to go -- first construct the standard tar header at the start of the buffer + memset(buf, 0, BUFSIZE); + + // Magic fields for the ustar file format + strcat(buf + 257, "ustar"); + strcat(buf + 263, "00"); + + // [ 265 : 32 ] user name, ignored on restore + // [ 297 : 32 ] group name, ignored on restore + + // [ 100 : 8 ] file mode + snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT); + + // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time + // [ 116 : 8 ] gid -- ignored in Android format + snprintf(buf + 108, 8, "0%lo", s.st_uid); + snprintf(buf + 116, 8, "0%lo", s.st_gid); + + // [ 124 : 12 ] file size in bytes + if (s.st_size > 077777777777LL) { + // very large files need a pax extended size header + needExtended = true; + } + snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size); + + // [ 136 : 12 ] last mod time as a UTC time_t + snprintf(buf + 136, 12, "%0lo", s.st_mtime); + + // [ 156 : 1 ] link/file type + uint8_t type; + if (isdir) { + type = '5'; // tar magic: '5' == directory + } else if (S_ISREG(s.st_mode)) { + type = '0'; // tar magic: '0' == normal file + } else { + LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); + goto cleanup; + } + buf[156] = type; + + // [ 157 : 100 ] name of linked file [not implemented] + + { + // Prefix and main relative path. Path lengths have been preflighted. + if (packageName.length() > 0) { + prefix = "apps/"; + prefix += packageName; + } + if (domain.length() > 0) { + prefix.appendPath(domain); + } + + // pax extended means we don't put in a prefix field, and put a different + // string in the basic name field. We can also construct the full path name + // out of the substrings we've now built. + fullname = prefix; + fullname.appendPath(relpath); + + // ustar: + // [ 0 : 100 ]; file name/path + // [ 345 : 155 ] filename path prefix + // We only use the prefix area if fullname won't fit in the path + if (fullname.length() > 100) { + strncpy(buf, relpath.string(), 100); + strncpy(buf + 345, prefix.string(), 155); + } else { + strncpy(buf, fullname.string(), 100); + } + } + + // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used + + LOGI(" Name: %s", fullname.string()); + + // If we're using a pax extended header, build & write that here; lengths are + // already preflighted + if (needExtended) { + char sizeStr[32]; // big enough for a 64-bit unsigned value in decimal + char* p = paxData; + + // construct the pax extended header data block + memset(paxData, 0, BUFSIZE - (paxData - buf)); + int len; + + // size header -- calc len in digits by actually rendering the number + // to a string - brute force but simple + snprintf(sizeStr, sizeof(sizeStr), "%lld", s.st_size); + p += write_pax_header_entry(p, "size", sizeStr); + + // fullname was generated above with the ustar paths + p += write_pax_header_entry(p, "path", fullname.string()); + + // Now we know how big the pax data is + int paxLen = p - paxData; + + // Now build the pax *header* templated on the ustar header + memcpy(paxHeader, buf, 512); + + String8 leaf = fullname.getPathLeaf(); + memset(paxHeader, 0, 100); // rewrite the name area + snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string()); + memset(paxHeader + 345, 0, 155); // rewrite the prefix area + strncpy(paxHeader + 345, prefix.string(), 155); + + paxHeader[156] = 'x'; // mark it as a pax extended header + + // [ 124 : 12 ] size of pax extended header data + memset(paxHeader + 124, 0, 12); + snprintf(paxHeader + 124, 12, "%011o", p - paxData); + + // Checksum and write the pax block header + calc_tar_checksum(paxHeader); + writer->WriteEntityData(paxHeader, 512); + + // Now write the pax data itself + int paxblocks = (paxLen + 511) / 512; + writer->WriteEntityData(paxData, 512 * paxblocks); + } + + // Checksum and write the 512-byte ustar file header block to the output + calc_tar_checksum(buf); + writer->WriteEntityData(buf, 512); + + // Now write the file data itself, for real files. We honor tar's convention that + // only full 512-byte blocks are sent to write(). + if (!isdir) { + off64_t toWrite = s.st_size; + while (toWrite > 0) { + size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE; + ssize_t nRead = read(fd, buf, toRead); + if (nRead < 0) { + err = errno; + LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), + err, strerror(err)); + break; + } else if (nRead == 0) { + LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, + filepath.string()); + err = EIO; + break; + } + + // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This + // depends on the OS guarantee that for ordinary files, read() will never return + // less than the number of bytes requested. + ssize_t partial = (nRead+512) % 512; + if (partial > 0) { + ssize_t remainder = 512 - partial; + memset(buf + nRead, 0, remainder); + nRead += remainder; + } + writer->WriteEntityData(buf, nRead); + toWrite -= nRead; + } + } + +cleanup: + delete [] buf; +done: + close(fd); + return err; +} +// end tarfile + + + #define RESTORE_BUF_SIZE (8*1024) RestoreHelperBase::RestoreHelperBase() diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 0d03361b00..03db8d777a 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -338,6 +338,10 @@ egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy, nativeWindow(window), buffer(0), previousBuffer(0), module(0), bits(NULL) { + hw_module_t const* pModule; + hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule); + module = reinterpret_cast<gralloc_module_t const*>(pModule); + pixelFormatTable = gglGetPixelFormatTable(); // keep a reference on the window diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 2502f159d9..da2622965b 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -179,7 +179,8 @@ void Loader::init_api(void* dso, __eglMustCastToProperFunctionPointerType* curr, getProcAddressType getProcAddress) { - char scrap[256]; + const size_t SIZE = 256; + char scrap[SIZE]; while (*api) { char const * name = *api; __eglMustCastToProperFunctionPointerType f = @@ -191,7 +192,7 @@ void Loader::init_api(void* dso, if (f == NULL) { // Try without the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; - if ((index>0 && (index<255)) && (!strcmp(name+index, "OES"))) { + if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) { strncpy(scrap, name, index); scrap[index] = 0; f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap); @@ -201,10 +202,8 @@ void Loader::init_api(void* dso, if (f == NULL) { // Try with the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; - if ((index>0 && (index<252)) && (strcmp(name+index, "OES"))) { - strncpy(scrap, name, index); - scrap[index] = 0; - strcat(scrap, "OES"); + if (index>0 && strcmp(name+index, "OES")) { + snprintf(scrap, SIZE, "%sOES", name); f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap); //LOGD_IF(f, "found <%s> instead", scrap); } |