Merge "Instead of using an RGB surface and conversion yuv420->rgb565"
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index d2d3bb8..e705c6f 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 @@
// 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 @@
// 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 96828c6..43b2fa9 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -34,14 +34,23 @@
// ----------------------------------------------------------------------------
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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 c77bc4c..e7c6e24 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -84,7 +84,6 @@
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 9b92c73..ba1c6b4 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -157,14 +157,6 @@
};
/*
- * 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 @@
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 @@
};
/*
+ * 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 @@
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 @@
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 @@
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
+ int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
@@ -495,7 +527,7 @@
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 @@
}
// 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 @@
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 119db81..95e4447 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -136,6 +136,7 @@
int32_t action;
int32_t flags;
int32_t metaState;
+ int32_t buttonState;
int32_t edgeFlags;
nsecs_t downTime;
float xOffset;
@@ -143,7 +144,7 @@
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 @@
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
+ int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
@@ -228,7 +230,7 @@
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 b1f5045..1bb04a7 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -70,6 +70,14 @@
~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 @@
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 @@
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 bc14ad5..16e3780 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -39,6 +39,8 @@
SET_CROP,
SET_TRANSFORM,
GET_ALLOCATOR,
+ QUERY,
+ SET_SYNCHRONOUS_MODE,
};
@@ -50,15 +52,10 @@
{
}
- 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 2619629..d7c449c 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 @@
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 @@
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);
- if (bufferCount < MIN_BUFFER_SLOTS) {
+ // 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 == 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 @@
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 @@
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 @@
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;
+
+ status_t err = OK;
+ if (!enabled) {
+ // going to asynchronous mode, drain the queue
+ while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
}
}
- if (found == INVALID_BUFFER_SLOT) {
- return -EBUSY;
- }
- *buf = found;
-
- const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
- if (buffer == NULL) {
- 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();
}
-
- if ((mUseDefaultSize) &&
- ((uint32_t(buffer->width) != mDefaultWidth) ||
- (uint32_t(buffer->height) != mDefaultHeight))) {
- return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
- }
- 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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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 ec6da43..6f10320 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -39,6 +39,9 @@
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::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::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 753e933..59a4cc5 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 @@
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 @@
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 @@
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 @@
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 a95f432..1ba38a7 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -302,6 +302,24 @@
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 @@
}
+// --- 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 @@
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
+ int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
@@ -371,20 +403,21 @@
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 @@
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 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 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 @@
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 @@
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 @@
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::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 @@
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 @@
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 93d0d1f..ffdfe66 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -366,6 +366,7 @@
int32_t flags,
int32_t edgeFlags,
int32_t metaState,
+ int32_t buttonState,
float xOffset,
float yOffset,
float xPrecision,
@@ -373,16 +374,17 @@
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 @@
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 @@
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 @@
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 @@
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 b77489e..e48d5b7 100644
--- a/libs/ui/tests/InputEvent_test.cpp
+++ b/libs/ui/tests/InputEvent_test.cpp
@@ -232,7 +232,14 @@
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 @@
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 @@
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 @@
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 @@
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 6e18a4f..fcc4cad 100644
--- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -156,13 +156,19 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 e41dd39..22034c5 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -681,6 +681,9 @@
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 adb3174..f963058 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 @@
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 @@
} 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 @@
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 @@
}
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 @@
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 @@
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 4ad9b51..e15875f 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -442,6 +442,267 @@
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 0d03361..03db8d7 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -338,6 +338,10 @@
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 2502f15..da26229 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -179,7 +179,8 @@
__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 @@
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 @@
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);
}