Merge "DO NOT MERGE: Disconnect PBAP while disconnecting HFP profile." into gingerbread
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
index 971a177..afa64f8 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -143,15 +143,20 @@
send(the_socket, message, length, 0);
}
-/* Here is the file format. Values are encrypted by AES CBC, and MD5 is used to
- * compute their checksums. To make the files portable, the length is stored in
- * network order. Note that the first four bytes are reserved for future use and
- * are always set to zero in this implementation. */
+/* Here is the file format. There are two parts in blob.value, the secret and
+ * the description. The secret is stored in ciphertext, and its original size
+ * can be found in blob.length. The description is stored after the secret in
+ * plaintext, and its size is specified in blob.info. The total size of the two
+ * parts must be no more than VALUE_SIZE bytes. The first three bytes of the
+ * file are reserved for future use and are always set to zero. Fields other
+ * than blob.info, blob.length, and blob.value are modified by encrypt_blob()
+ * and decrypt_blob(). Thus they should not be accessed from outside. */
static int the_entropy = -1;
static struct __attribute__((packed)) {
- uint32_t reserved;
+ uint8_t reserved[3];
+ uint8_t info;
uint8_t vector[AES_BLOCK_SIZE];
uint8_t encrypted[0];
uint8_t digest[MD5_DIGEST_LENGTH];
@@ -170,9 +175,13 @@
return SYSTEM_ERROR;
}
- length = blob.length + blob.value - blob.encrypted;
+ length = blob.length + (blob.value - blob.encrypted);
length = (length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
+ if (blob.info != 0) {
+ memmove(&blob.encrypted[length], &blob.value[blob.length], blob.info);
+ }
+
blob.length = htonl(blob.length);
MD5(blob.digested, length - (blob.digested - blob.encrypted), blob.digest);
@@ -180,8 +189,8 @@
AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, vector,
AES_ENCRYPT);
- blob.reserved = 0;
- length += blob.encrypted - (uint8_t *)&blob;
+ memset(blob.reserved, 0, sizeof(blob.reserved));
+ length += (blob.encrypted - (uint8_t *)&blob) + blob.info;
fd = open(".tmp", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
length -= write(fd, &blob, length);
@@ -200,7 +209,7 @@
length = read(fd, &blob, sizeof(blob));
close(fd);
- length -= blob.encrypted - (uint8_t *)&blob;
+ length -= (blob.encrypted - (uint8_t *)&blob) + blob.info;
if (length < blob.value - blob.encrypted || length % AES_BLOCK_SIZE != 0) {
return VALUE_CORRUPTED;
}
@@ -215,8 +224,13 @@
length -= blob.value - blob.digested;
blob.length = ntohl(blob.length);
- return (blob.length < 0 || blob.length > length) ? VALUE_CORRUPTED :
- NO_ERROR;
+ if (blob.length < 0 || blob.length > length) {
+ return VALUE_CORRUPTED;
+ }
+ if (blob.info != 0) {
+ memmove(&blob.value[blob.length], &blob.value[length], blob.info);
+ }
+ return NO_ERROR;
}
/* Here are the actions. Each of them is a function without arguments. All
@@ -266,6 +280,7 @@
char name[NAME_MAX];
int n = sprintf(name, "%u_", uid);
encode_key(&name[n], params[0].value, params[0].length);
+ blob.info = 0;
blob.length = params[1].length;
memcpy(blob.value, params[1].value, params[1].length);
return encrypt_blob(name, &encryption_key);
@@ -336,56 +351,88 @@
#define MASTER_KEY_FILE ".masterkey"
#define MASTER_KEY_SIZE 16
+#define SALT_SIZE 16
-static void generate_key(uint8_t *key, uint8_t *password, int length)
+static void set_key(uint8_t *key, uint8_t *password, int length, uint8_t *salt)
{
- PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
- sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
+ if (salt) {
+ PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, salt, SALT_SIZE,
+ 8192, MASTER_KEY_SIZE, key);
+ } else {
+ PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
+ sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
+ }
}
+/* Here is the history. To improve the security, the parameters to generate the
+ * master key has been changed. To make a seamless transition, we update the
+ * file using the same password when the user unlock it for the first time. If
+ * any thing goes wrong during the transition, the new file will not overwrite
+ * the old one. This avoids permanent damages of the existing data. */
+
static int8_t password()
{
uint8_t key[MASTER_KEY_SIZE];
AES_KEY aes_key;
- int n;
+ int8_t response = SYSTEM_ERROR;
if (state == UNINITIALIZED) {
- blob.length = MASTER_KEY_SIZE;
if (read(the_entropy, blob.value, MASTER_KEY_SIZE) != MASTER_KEY_SIZE) {
return SYSTEM_ERROR;
}
} else {
- generate_key(key, params[0].value, params[0].length);
+ int fd = open(MASTER_KEY_FILE, O_RDONLY);
+ uint8_t *salt = NULL;
+ if (fd != -1) {
+ int length = read(fd, &blob, sizeof(blob));
+ close(fd);
+ if (length > SALT_SIZE && blob.info == SALT_SIZE) {
+ salt = (uint8_t *)&blob + length - SALT_SIZE;
+ }
+ }
+
+ set_key(key, params[0].value, params[0].length, salt);
AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
- n = decrypt_blob(MASTER_KEY_FILE, &aes_key);
- if (n == SYSTEM_ERROR) {
+ response = decrypt_blob(MASTER_KEY_FILE, &aes_key);
+ if (response == SYSTEM_ERROR) {
return SYSTEM_ERROR;
}
- if (n != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
+ if (response != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
if (retry <= 0) {
reset();
return UNINITIALIZED;
}
return WRONG_PASSWORD + --retry;
}
+
+ if (!salt && params[1].length == -1) {
+ params[1] = params[0];
+ }
}
if (params[1].length == -1) {
memcpy(key, blob.value, MASTER_KEY_SIZE);
} else {
- generate_key(key, params[1].value, params[1].length);
+ uint8_t *salt = &blob.value[MASTER_KEY_SIZE];
+ if (read(the_entropy, salt, SALT_SIZE) != SALT_SIZE) {
+ return SYSTEM_ERROR;
+ }
+
+ set_key(key, params[1].value, params[1].length, salt);
AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
memcpy(key, blob.value, MASTER_KEY_SIZE);
- n = encrypt_blob(MASTER_KEY_FILE, &aes_key);
+ blob.info = SALT_SIZE;
+ blob.length = MASTER_KEY_SIZE;
+ response = encrypt_blob(MASTER_KEY_FILE, &aes_key);
}
- if (n == NO_ERROR) {
+ if (response == NO_ERROR) {
AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &encryption_key);
AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &decryption_key);
state = NO_ERROR;
retry = MAX_RETRY;
}
- return n;
+ return response;
}
static int8_t lock()
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index d689667..d6ae5e9 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -114,8 +114,9 @@
int32_t identity; // surface's identity (const)
int32_t token; // surface's token (for debugging)
- int32_t reserved32[1];
Statistics stats;
+ int8_t headBuf; // last retired buffer
+ uint8_t reservedBytes[3];
int32_t reserved;
BufferData buffers[NUM_BUFFER_MAX]; // 1024 bytes
};
@@ -201,6 +202,7 @@
status_t undoDequeue(int buf);
status_t lock(int buf);
+ status_t cancel(int buf);
status_t queue(int buf);
bool needNewBuffer(int buffer) const;
status_t setDirtyRegion(int buffer, const Region& reg);
@@ -230,8 +232,9 @@
inline ssize_t operator()();
};
- struct UndoDequeueUpdate : public UpdateBase {
- inline UndoDequeueUpdate(SharedBufferBase* sbb);
+ struct CancelUpdate : public UpdateBase {
+ int tail, buf;
+ inline CancelUpdate(SharedBufferBase* sbb, int tail, int buf);
inline ssize_t operator()();
};
@@ -256,7 +259,6 @@
int mNumBuffers;
int32_t tail;
- int32_t undoDequeueTail;
int32_t queued_head;
// statistics...
nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX];
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 76307b2..6533600 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -115,7 +115,8 @@
*/
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth, uint32_t reqHeight) = 0;
/* Signal surfaceflinger that there might be some work to do
* This is an ASYNCHRONOUS call.
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 7c5a39b..22684db 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -199,6 +199,7 @@
*/
static int setSwapInterval(ANativeWindow* window, int interval);
static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer);
+ static int cancelBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer);
static int query(ANativeWindow* window, int what, int* value);
@@ -207,6 +208,7 @@
int dequeueBuffer(android_native_buffer_t** buffer);
int lockBuffer(android_native_buffer_t* buffer);
int queueBuffer(android_native_buffer_t* buffer);
+ int cancelBuffer(android_native_buffer_t* buffer);
int query(int what, int* value);
int perform(int operation, va_list args);
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 8773d71..a80832d 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -170,6 +170,36 @@
};
// ---------------------------------------------------------------------------
+
+class ScreenshotClient
+{
+ sp<IMemoryHeap> mHeap;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ PixelFormat mFormat;
+public:
+ ScreenshotClient();
+
+ // frees the previous screenshot and capture a new one
+ status_t update();
+ status_t update(uint32_t reqWidth, uint32_t reqHeight);
+
+ // release memory occupied by the screenshot
+ void release();
+
+ // pixels are valid until this object is freed or
+ // release() or update() is called
+ void const* getPixels() const;
+
+ uint32_t getWidth() const;
+ uint32_t getHeight() const;
+ PixelFormat getFormat() const;
+ uint32_t getStride() const;
+ // size of allocated memory in bytes
+ size_t getSize() const;
+};
+
+// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index d6b09dc..d78e35f 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -142,8 +142,13 @@
public:
// Synthetic raw event type codes produced when devices are added or removed.
enum {
+ // Sent when a device is added.
DEVICE_ADDED = 0x10000000,
- DEVICE_REMOVED = 0x20000000
+ // Sent when a device is removed.
+ DEVICE_REMOVED = 0x20000000,
+ // Sent when all added/removed devices from the most recent scan have been reported.
+ // This event is always sent at least once.
+ FINISHED_DEVICE_SCAN = 0x30000000,
};
virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
@@ -181,6 +186,8 @@
*/
virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const = 0;
+
+ virtual void dump(String8& dump) = 0;
};
class EventHub : public EventHubInterface
@@ -211,16 +218,18 @@
virtual bool getEvent(RawEvent* outEvent);
+ virtual void dump(String8& dump);
+
protected:
virtual ~EventHub();
private:
bool openPlatformInput(void);
- int open_device(const char *device);
- int close_device(const char *device);
- int scan_dir(const char *dirname);
- int read_notify(int nfd);
+ int openDevice(const char *device);
+ int closeDevice(const char *device);
+ int scanDir(const char *dirname);
+ int readNotify(int nfd);
status_t mError;
@@ -239,8 +248,8 @@
~device_t();
};
- device_t* getDevice(int32_t deviceId) const;
- bool hasKeycode(device_t* device, int keycode) const;
+ device_t* getDeviceLocked(int32_t deviceId) const;
+ bool hasKeycodeLocked(device_t* device, int keycode) const;
int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
@@ -269,6 +278,7 @@
int mFDCount;
bool mOpened;
+ bool mNeedToSendFinishedDeviceScan;
List<String8> mExcludedDevices;
// device ids that report particular switches.
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 21baf32..ee40b85 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -73,7 +73,8 @@
* policy decisions such as waking from device sleep.
*/
enum {
- /* These flags originate in RawEvents and are generally set in the key map. */
+ /* These flags originate in RawEvents and are generally set in the key map.
+ * See also labels for policy flags in KeycodeLabels.h. */
POLICY_FLAG_WAKE = 0x00000001,
POLICY_FLAG_WAKE_DROPPED = 0x00000002,
@@ -83,6 +84,7 @@
POLICY_FLAG_ALT_GR = 0x00000020,
POLICY_FLAG_MENU = 0x00000040,
POLICY_FLAG_LAUNCHER = 0x00000080,
+ POLICY_FLAG_VIRTUAL = 0x00000100,
POLICY_FLAG_RAW_MASK = 0x0000ffff,
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index cc16012..47c5326 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -212,8 +212,15 @@
int32_t ownerPid;
int32_t ownerUid;
- bool visibleFrameIntersects(const InputWindow* other) const;
bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
+ bool frameContainsPoint(int32_t x, int32_t y) const;
+
+ /* Returns true if the window is of a trusted type that is allowed to silently
+ * overlay other windows for the purpose of implementing the secure views feature.
+ * Trusted overlays, such as IME windows, can partly obscure other windows without causing
+ * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
+ */
+ bool isTrustedOverlay() const;
};
@@ -962,7 +969,7 @@
bool shouldPokeUserActivityForCurrentInputTargetsLocked();
void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType);
bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState);
- bool isWindowObscuredLocked(const InputWindow* window);
+ bool isWindowObscuredAtPointLocked(const InputWindow* window, int32_t x, int32_t y) const;
bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
String8 getApplicationWindowLabelLocked(const InputApplication* application,
const InputWindow* window);
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index d3fbaf7..3619189 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -103,10 +103,6 @@
virtual bool getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation) = 0;
- /* Provides feedback for a virtual key down.
- */
- virtual void virtualKeyDownFeedback() = 0;
-
/* Intercepts a key event.
* The policy can use this method as an opportunity to perform power management functions
* and early event preprocessing such as updating policy flags.
@@ -283,14 +279,14 @@
// low-level input event decoding and device management
void process(const RawEvent* rawEvent);
- void addDevice(nsecs_t when, int32_t deviceId);
- void removeDevice(nsecs_t when, int32_t deviceId);
+ void addDevice(int32_t deviceId);
+ void removeDevice(int32_t deviceId);
InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
void configureExcludedDevices();
void consumeEvent(const RawEvent* rawEvent);
- void handleConfigurationChanged(nsecs_t when);
+ void handleConfigurationChanged();
// state management for all devices
Mutex mStateLock;
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index c8d6ffc..f71d9cd 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -142,6 +142,7 @@
{ NULL, 0 }
};
+// See also policy flags in Input.h.
static const KeycodeLabel FLAGS[] = {
{ "WAKE", 0x00000001 },
{ "WAKE_DROPPED", 0x00000002 },
@@ -151,6 +152,7 @@
{ "ALT_GR", 0x00000020 },
{ "MENU", 0x00000040 },
{ "LAUNCHER", 0x00000080 },
+ { "VIRTUAL", 0x00000100 },
{ NULL, 0 }
};
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index d59d72b..654d0f3 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -218,7 +218,17 @@
int (*perform)(struct ANativeWindow* window,
int operation, ... );
- void* reserved_proc[3];
+ /*
+ * hook used to cancel a buffer that has been dequeued.
+ * No synchronization is performed between dequeue() and cancel(), so
+ * either external synchronization is needed, or these functions must be
+ * called from the same thread.
+ */
+ int (*cancelBuffer)(struct ANativeWindow* window,
+ struct android_native_buffer_t* buffer);
+
+
+ void* reserved_proc[2];
};
// Backwards compatibility... please switch to ANativeWindow.
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index 3f00b78..cc51490 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -20,9 +20,22 @@
#include <utils/threads.h>
#include <utils/RefBase.h>
#include <utils/KeyedVector.h>
+#include <utils/Timers.h>
#include <android/looper.h>
+// Currently using poll() instead of epoll_wait() since it does a better job of meeting a
+// timeout deadline. epoll_wait() typically causes additional delays of up to 10ms
+// beyond the requested timeout.
+//#define LOOPER_USES_EPOLL
+//#define LOOPER_STATISTICS
+
+#ifdef LOOPER_USES_EPOLL
+#include <sys/epoll.h>
+#else
+#include <sys/poll.h>
+#endif
+
/*
* Declare a concrete type for the NDK's looper forward declaration.
*/
@@ -190,13 +203,54 @@
const bool mAllowNonCallbacks; // immutable
- int mEpollFd; // immutable
int mWakeReadPipeFd; // immutable
int mWakeWritePipeFd; // immutable
+ Mutex mLock;
+
+#ifdef LOOPER_USES_EPOLL
+ int mEpollFd; // immutable
// Locked list of file descriptor monitoring requests.
- Mutex mLock;
- KeyedVector<int, Request> mRequests;
+ KeyedVector<int, Request> mRequests; // guarded by mLock
+#else
+ // The lock guards state used to track whether there is a poll() in progress and whether
+ // there are any other threads waiting in wakeAndLock(). The condition variables
+ // are used to transfer control among these threads such that all waiters are
+ // serviced before a new poll can begin.
+ // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake
+ // until mPolling becomes false, then decrements mWaiters again.
+ // The poll() method blocks on mResume until mWaiters becomes 0, then sets
+ // mPolling to true, blocks until the poll completes, then resets mPolling to false
+ // and signals mResume if there are waiters.
+ bool mPolling; // guarded by mLock
+ uint32_t mWaiters; // guarded by mLock
+ Condition mAwake; // guarded by mLock
+ Condition mResume; // guarded by mLock
+
+ Vector<struct pollfd> mRequestedFds; // must hold mLock and mPolling must be false to modify
+ Vector<Request> mRequests; // must hold mLock and mPolling must be false to modify
+
+ ssize_t getRequestIndexLocked(int fd);
+ void wakeAndLock();
+#endif
+
+#ifdef LOOPER_STATISTICS
+ static const int SAMPLED_WAKE_CYCLES_TO_AGGREGATE = 100;
+ static const int SAMPLED_POLLS_TO_AGGREGATE = 1000;
+
+ nsecs_t mPendingWakeTime;
+ int mPendingWakeCount;
+
+ int mSampledWakeCycles;
+ int mSampledWakeCountSum;
+ nsecs_t mSampledWakeLatencySum;
+
+ int mSampledPolls;
+ int mSampledZeroPollCount;
+ int mSampledZeroPollLatencySum;
+ int mSampledTimeoutPollCount;
+ int mSampledTimeoutPollLatencySum;
+#endif
// This state is only used privately by pollOnce and does not require a lock since
// it runs on a single thread.
@@ -204,6 +258,8 @@
size_t mResponseIndex;
int pollInner(int timeoutMillis);
+ void awoken();
+ void pushResponse(int events, const Request& request);
static void initTLSKey();
static void threadDestructor(void *st);
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 9668bde..3c1f3ca 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -14,13 +14,19 @@
* limitations under the License.
*/
-//
-// Read-only access to Zip archives, with minimal heap allocation.
-//
-// This is similar to the more-complete ZipFile class, but no attempt
-// has been made to make them interchangeable. This class operates under
-// a very different set of assumptions and constraints.
-//
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ *
+ * This is similar to the more-complete ZipFile class, but no attempt
+ * has been made to make them interchangeable. This class operates under
+ * a very different set of assumptions and constraints.
+ *
+ * One such assumption is that if you're getting file descriptors for
+ * use with this class as a child of a fork() operation, you must be on
+ * a pread() to guarantee correct operation. This is because pread() can
+ * atomically read at a file offset without worrying about a lock around an
+ * lseek() + read() pair.
+ */
#ifndef __LIBS_ZIPFILERO_H
#define __LIBS_ZIPFILERO_H
@@ -55,6 +61,10 @@
* the record structure. However, this requires a private mapping of
* every page that the Central Directory touches. Easier to tuck a copy
* of the string length into the hash table entry.
+ *
+ * NOTE: If this is used on file descriptors inherited from a fork() operation,
+ * you must be on a platform that implements pread() to guarantee correctness
+ * on the shared file descriptors.
*/
class ZipFileRO {
public:
@@ -64,15 +74,8 @@
mNumEntries(-1), mDirectoryOffset(-1),
mHashTableSize(-1), mHashTable(NULL)
{}
- ~ZipFileRO() {
- free(mHashTable);
- if (mDirectoryMap)
- mDirectoryMap->release();
- if (mFd >= 0)
- close(mFd);
- if (mFileName)
- free(mFileName);
- }
+
+ ~ZipFileRO();
/*
* Open an archive.
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 040060e..d676f5e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -126,11 +126,14 @@
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format)
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth, uint32_t reqHeight)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeInt32(dpy);
+ data.writeInt32(reqWidth);
+ data.writeInt32(reqHeight);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
*heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
*width = reply.readInt32();
@@ -208,10 +211,13 @@
case CAPTURE_SCREEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayID dpy = data.readInt32();
+ uint32_t reqWidth = data.readInt32();
+ uint32_t reqHeight = data.readInt32();
sp<IMemoryHeap> heap;
uint32_t w, h;
PixelFormat f;
- status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+ status_t res = captureScreen(dpy, &heap, &w, &h, &f,
+ reqWidth, reqHeight);
reply->writeStrongBinder(heap->asBinder());
reply->writeInt32(w);
reply->writeInt32(h);
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 38b2fae..4bc5d9e 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -285,10 +285,12 @@
return NO_ERROR;
}
-SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
- : UpdateBase(sbb) {
+SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb,
+ int tail, int buf)
+ : UpdateBase(sbb), tail(tail), buf(buf) {
}
-ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
+ssize_t SharedBufferClient::CancelUpdate::operator()() {
+ stack.index[tail] = buf;
android_atomic_inc(&stack.available);
return NO_ERROR;
}
@@ -319,7 +321,7 @@
return BAD_VALUE;
// Preventively lock the current buffer before updating queued.
- android_atomic_write(stack.index[head], &stack.inUse);
+ android_atomic_write(stack.headBuf, &stack.inUse);
// Decrement the number of queued buffers
int32_t queued;
@@ -334,7 +336,9 @@
// the buffer we preventively locked upon entering this function
head = (head + 1) % numBuffers;
- android_atomic_write(stack.index[head], &stack.inUse);
+ const int8_t headBuf = stack.index[head];
+ stack.headBuf = headBuf;
+ android_atomic_write(headBuf, &stack.inUse);
// head is only modified here, so we don't need to use cmpxchg
android_atomic_write(head, &stack.head);
@@ -359,7 +363,7 @@
SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
int surface, int num, int32_t identity)
: SharedBufferBase(sharedClient, surface, identity),
- mNumBuffers(num), tail(0), undoDequeueTail(0)
+ mNumBuffers(num), tail(0)
{
SharedBufferStack& stack( *mSharedStack );
tail = computeTail();
@@ -395,7 +399,6 @@
DequeueUpdate update(this);
updateCondition( update );
- undoDequeueTail = tail;
int dequeued = stack.index[tail];
tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s",
@@ -408,14 +411,19 @@
status_t SharedBufferClient::undoDequeue(int buf)
{
+ return cancel(buf);
+}
+
+status_t SharedBufferClient::cancel(int buf)
+{
RWLock::AutoRLock _rd(mLock);
- // TODO: we can only undo the previous dequeue, we should
- // enforce that in the api
- UndoDequeueUpdate update(this);
+ // calculate the new position of the tail index (essentially tail--)
+ int localTail = (tail + mNumBuffers - 1) % mNumBuffers;
+ CancelUpdate update(this, localTail, buf);
status_t err = updateCondition( update );
if (err == NO_ERROR) {
- tail = undoDequeueTail;
+ tail = localTail;
}
return err;
}
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 560ea67..d44aab9 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -409,6 +409,7 @@
{
ANativeWindow::setSwapInterval = setSwapInterval;
ANativeWindow::dequeueBuffer = dequeueBuffer;
+ ANativeWindow::cancelBuffer = cancelBuffer;
ANativeWindow::lockBuffer = lockBuffer;
ANativeWindow::queueBuffer = queueBuffer;
ANativeWindow::query = query;
@@ -517,6 +518,12 @@
return self->dequeueBuffer(buffer);
}
+int Surface::cancelBuffer(ANativeWindow* window,
+ android_native_buffer_t* buffer) {
+ Surface* self = getSelf(window);
+ return self->cancelBuffer(buffer);
+}
+
int Surface::lockBuffer(ANativeWindow* window,
android_native_buffer_t* buffer) {
Surface* self = getSelf(window);
@@ -617,6 +624,33 @@
return err;
}
+int Surface::cancelBuffer(android_native_buffer_t* buffer)
+{
+ status_t err = validate();
+ switch (err) {
+ case NO_ERROR:
+ // no error, common case
+ break;
+ case INVALID_OPERATION:
+ // legitimate errors here
+ return err;
+ default:
+ // other errors happen because the surface is now invalid,
+ // for instance because it has been destroyed. In this case,
+ // we just fail silently (canceling a buffer is not technically
+ // an error at this point)
+ return NO_ERROR;
+ }
+
+ int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+ err = mSharedBufferClient->cancel(bufIdx);
+
+ LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err));
+ return err;
+}
+
+
int Surface::lockBuffer(android_native_buffer_t* buffer)
{
status_t err = validate();
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 4096ac6..f270461 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -545,5 +545,55 @@
}
// ----------------------------------------------------------------------------
+
+ScreenshotClient::ScreenshotClient()
+ : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
+}
+
+status_t ScreenshotClient::update() {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ mHeap = 0;
+ return s->captureScreen(0, &mHeap,
+ &mWidth, &mHeight, &mFormat, 0, 0);
+}
+
+status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ mHeap = 0;
+ return s->captureScreen(0, &mHeap,
+ &mWidth, &mHeight, &mFormat, reqWidth, reqHeight);
+}
+
+void ScreenshotClient::release() {
+ mHeap = 0;
+}
+
+void const* ScreenshotClient::getPixels() const {
+ return mHeap->getBase();
+}
+
+uint32_t ScreenshotClient::getWidth() const {
+ return mWidth;
+}
+
+uint32_t ScreenshotClient::getHeight() const {
+ return mHeight;
+}
+
+PixelFormat ScreenshotClient::getFormat() const {
+ return mFormat;
+}
+
+uint32_t ScreenshotClient::getStride() const {
+ return mWidth;
+}
+
+size_t ScreenshotClient::getSize() const {
+ return mHeap->getSize();
+}
+
+// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 1d38b4b..c0be3a0 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -73,6 +73,10 @@
#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
#endif
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+
namespace android {
static const char *WAKE_LOCK_ID = "KeyEvents";
@@ -84,6 +88,10 @@
return (v1 > v2) ? v1 : v2;
}
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
, keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
@@ -98,7 +106,7 @@
: mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
, mDevicesById(0), mNumDevicesById(0)
, mOpeningDevices(0), mClosingDevices(0)
- , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
+ , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
, mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
@@ -124,7 +132,7 @@
String8 EventHub::getDeviceName(int32_t deviceId) const
{
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device == NULL) return String8();
return device->name;
}
@@ -132,7 +140,7 @@
uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
{
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device == NULL) return 0;
return device->classes;
}
@@ -142,7 +150,7 @@
outAxisInfo->clear();
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device == NULL) return -1;
struct input_absinfo info;
@@ -167,7 +175,7 @@
if (scanCode >= 0 && scanCode <= KEY_MAX) {
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device != NULL) {
return getScanCodeStateLocked(device, scanCode);
}
@@ -188,7 +196,7 @@
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device != NULL) {
return getKeyCodeStateLocked(device, keyCode);
}
@@ -225,7 +233,7 @@
if (sw >= 0 && sw <= SW_MAX) {
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device != NULL) {
return getSwitchStateLocked(device, sw);
}
@@ -248,7 +256,7 @@
const int32_t* keyCodes, uint8_t* outFlags) const {
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device != NULL) {
return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
}
@@ -284,7 +292,7 @@
int32_t* outKeycode, uint32_t* outFlags) const
{
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device != NULL && device->layoutMap != NULL) {
status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
@@ -294,7 +302,7 @@
}
if (mHaveFirstKeyboard) {
- device = getDevice(mFirstKeyboardId);
+ device = getDeviceLocked(mFirstKeyboardId);
if (device != NULL && device->layoutMap != NULL) {
status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
@@ -311,11 +319,13 @@
void EventHub::addExcludedDevice(const char* deviceName)
{
+ AutoMutex _l(mLock);
+
String8 name(deviceName);
mExcludedDevices.push_back(name);
}
-EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
+EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
{
if (deviceId == 0) deviceId = mFirstKeyboardId;
int32_t id = deviceId & ID_MASK;
@@ -344,6 +354,7 @@
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
mOpened = true;
+ mNeedToSendFinishedDeviceScan = true;
}
for (;;) {
@@ -360,6 +371,7 @@
}
outEvent->type = DEVICE_REMOVED;
delete device;
+ mNeedToSendFinishedDeviceScan = true;
return true;
}
@@ -374,6 +386,13 @@
outEvent->deviceId = device->id;
}
outEvent->type = DEVICE_ADDED;
+ mNeedToSendFinishedDeviceScan = true;
+ return true;
+ }
+
+ if (mNeedToSendFinishedDeviceScan) {
+ mNeedToSendFinishedDeviceScan = false;
+ outEvent->type = FINISHED_DEVICE_SCAN;
return true;
}
@@ -441,10 +460,10 @@
}
}
- // read_notify() will modify mFDs and mFDCount, so this must be done after
+ // readNotify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
if(mFDs[0].revents & POLLIN) {
- read_notify(mFDs[0].fd);
+ readNotify(mFDs[0].fd);
}
// Poll for events. Mind the wake lock dance!
@@ -500,10 +519,9 @@
mFDs[0].fd = -1;
#endif
- res = scan_dir(device_path);
+ res = scanDir(device_path);
if(res < 0) {
LOGE("scan dir failed for %s\n", device_path);
- //open_device("/dev/input/event0");
}
return true;
@@ -531,8 +549,7 @@
AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
};
-int EventHub::open_device(const char *deviceName)
-{
+int EventHub::openDevice(const char *deviceName) {
int version;
int fd;
struct pollfd *new_mFDs;
@@ -782,22 +799,22 @@
property_set(propName, name);
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycode(device, AKEYCODE_Q)) {
+ if (hasKeycodeLocked(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
// See if this device has a DPAD.
- if (hasKeycode(device, AKEYCODE_DPAD_UP) &&
- hasKeycode(device, AKEYCODE_DPAD_DOWN) &&
- hasKeycode(device, AKEYCODE_DPAD_LEFT) &&
- hasKeycode(device, AKEYCODE_DPAD_RIGHT) &&
- hasKeycode(device, AKEYCODE_DPAD_CENTER)) {
+ if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES); i++) {
- if (hasKeycode(device, GAMEPAD_KEYCODES[i])) {
+ if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
}
@@ -830,7 +847,7 @@
return 0;
}
-bool EventHub::hasKeycode(device_t* device, int keycode) const
+bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
{
if (device->keyBitmask == NULL || device->layoutMap == NULL) {
return false;
@@ -849,10 +866,9 @@
return false;
}
-int EventHub::close_device(const char *deviceName)
-{
+int EventHub::closeDevice(const char *deviceName) {
AutoMutex _l(mLock);
-
+
int i;
for(i = 1; i < mFDCount; i++) {
if(strcmp(mDevices[i]->path.string(), deviceName) == 0) {
@@ -902,8 +918,7 @@
return -1;
}
-int EventHub::read_notify(int nfd)
-{
+int EventHub::readNotify(int nfd) {
#ifdef HAVE_INOTIFY
int res;
char devname[PATH_MAX];
@@ -913,7 +928,7 @@
int event_pos = 0;
struct inotify_event *event;
- LOGV("EventHub::read_notify nfd: %d\n", nfd);
+ LOGV("EventHub::readNotify nfd: %d\n", nfd);
res = read(nfd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
@@ -933,10 +948,10 @@
if(event->len) {
strcpy(filename, event->name);
if(event->mask & IN_CREATE) {
- open_device(devname);
+ openDevice(devname);
}
else {
- close_device(devname);
+ closeDevice(devname);
}
}
event_size = sizeof(*event) + event->len;
@@ -948,7 +963,7 @@
}
-int EventHub::scan_dir(const char *dirname)
+int EventHub::scanDir(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
@@ -966,10 +981,38 @@
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
- open_device(devname);
+ openDevice(devname);
}
closedir(dir);
return 0;
}
+void EventHub::dump(String8& dump) {
+ dump.append("Event Hub State:\n");
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard));
+ dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId);
+
+ dump.append(INDENT "Devices:\n");
+
+ for (int i = 0; i < mNumDevicesById; i++) {
+ const device_t* device = mDevicesById[i].device;
+ if (device) {
+ if (mFirstKeyboardId == device->id) {
+ dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n",
+ device->id, device->name.string());
+ } else {
+ dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string());
+ }
+ dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
+ dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
+ dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string());
+ }
+ }
+ } // release lock
+}
+
}; // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 9544a95..fe8555d 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -46,6 +46,9 @@
#include <errno.h>
#include <limits.h>
+#define INDENT " "
+#define INDENT2 " "
+
namespace android {
// Delay between reporting long touch events to the power manager.
@@ -131,18 +134,21 @@
// --- InputWindow ---
-bool InputWindow::visibleFrameIntersects(const InputWindow* other) const {
- return visibleFrameRight > other->visibleFrameLeft
- && visibleFrameLeft < other->visibleFrameRight
- && visibleFrameBottom > other->visibleFrameTop
- && visibleFrameTop < other->visibleFrameBottom;
-}
-
bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
return x >= touchableAreaLeft && x <= touchableAreaRight
&& y >= touchableAreaTop && y <= touchableAreaBottom;
}
+bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
+ return x >= frameLeft && x <= frameRight
+ && y >= frameTop && y <= frameBottom;
+}
+
+bool InputWindow::isTrustedOverlay() const {
+ return layoutParamsType == TYPE_INPUT_METHOD
+ || layoutParamsType == TYPE_INPUT_METHOD_DIALOG;
+}
+
// --- InputDispatcher ---
@@ -1050,8 +1056,12 @@
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
&& (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
- mTempTouchState.addOrUpdateWindow(window,
- InputTarget::FLAG_OUTSIDE, BitSet32(0));
+ int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+ if (isWindowObscuredAtPointLocked(window, x, y)) {
+ outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ mTempTouchState.addOrUpdateWindow(window, outsideTargetFlags, BitSet32(0));
}
}
}
@@ -1080,10 +1090,6 @@
// (May be NULL which is why we put this code block before the next check.)
newTouchedWindow = mTempTouchState.getFirstForegroundWindow();
}
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
- if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
- }
// If we did not find a touched window then fail.
if (! newTouchedWindow) {
@@ -1103,6 +1109,15 @@
goto Failed;
}
+ // Set target flags.
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+ if (isSplit) {
+ targetFlags |= InputTarget::FLAG_SPLIT;
+ }
+ if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+
// Update the temporary touch state.
BitSet32 pointerIds;
if (isSplit) {
@@ -1183,23 +1198,13 @@
for (size_t i = 0; i < mWindows.size(); i++) {
const InputWindow* window = & mWindows[i];
if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
- mTempTouchState.addOrUpdateWindow(window, 0, BitSet32(0));
+ mTempTouchState.addOrUpdateWindow(window,
+ InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0));
}
}
}
}
- // If a touched window has been obscured at any point during the touch gesture, set
- // the appropriate flag so we remember it for the entire gesture.
- for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
- TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
- if ((touchedWindow.targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) == 0) {
- if (isWindowObscuredLocked(touchedWindow.window)) {
- touchedWindow.targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
- }
- }
-
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
@@ -1323,14 +1328,15 @@
return true;
}
-bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
+bool InputDispatcher::isWindowObscuredAtPointLocked(
+ const InputWindow* window, int32_t x, int32_t y) const {
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
const InputWindow* other = & mWindows.itemAt(i);
if (other == window) {
break;
}
- if (other->visible && window->visibleFrameIntersects(other)) {
+ if (other->visible && ! other->isTrustedOverlay() && other->frameContainsPoint(x, y)) {
return true;
}
}
@@ -2490,74 +2496,96 @@
}
void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
- dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled);
- dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen);
+ dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
+ dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
if (mFocusedApplication) {
- dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+ dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
mFocusedApplication->name.string(),
mFocusedApplication->dispatchingTimeout / 1000000.0);
} else {
- dump.append(" focusedApplication: <null>\n");
+ dump.append(INDENT "FocusedApplication: <null>\n");
}
- dump.appendFormat(" focusedWindow: name='%s'\n",
+ dump.appendFormat(INDENT "FocusedWindow: name='%s'\n",
mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
- dump.appendFormat(" touchState: down=%s, split=%s\n", toString(mTouchState.down),
- toString(mTouchState.split));
- for (size_t i = 0; i < mTouchState.windows.size(); i++) {
- const TouchedWindow& touchedWindow = mTouchState.windows[i];
- dump.appendFormat(" touchedWindow[%d]: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
- i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
- touchedWindow.targetFlags);
- }
- for (size_t i = 0; i < mWindows.size(); i++) {
- dump.appendFormat(" windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
- "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], "
- "visibleFrame=[%d,%d][%d,%d], "
- "touchableArea=[%d,%d][%d,%d], "
- "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
- i, mWindows[i].name.string(),
- toString(mWindows[i].paused),
- toString(mWindows[i].hasFocus),
- toString(mWindows[i].hasWallpaper),
- toString(mWindows[i].visible),
- toString(mWindows[i].canReceiveKeys),
- mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
- mWindows[i].layer,
- mWindows[i].frameLeft, mWindows[i].frameTop,
- mWindows[i].frameRight, mWindows[i].frameBottom,
- mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop,
- mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom,
- mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
- mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
- mWindows[i].ownerPid, mWindows[i].ownerUid,
- mWindows[i].dispatchingTimeout / 1000000.0);
+
+ dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down));
+ dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split));
+ if (!mTouchState.windows.isEmpty()) {
+ dump.append(INDENT "TouchedWindows:\n");
+ for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTouchState.windows[i];
+ dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+ i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
+ touchedWindow.targetFlags);
+ }
+ } else {
+ dump.append(INDENT "TouchedWindows: <none>\n");
}
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- const sp<InputChannel>& channel = mMonitoringChannels[i];
- dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
- i, channel->getName().string());
+ if (!mWindows.isEmpty()) {
+ dump.append(INDENT "Windows:\n");
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ const InputWindow& window = mWindows[i];
+ dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+ "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
+ "frame=[%d,%d][%d,%d], "
+ "visibleFrame=[%d,%d][%d,%d], "
+ "touchableArea=[%d,%d][%d,%d], "
+ "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ i, window.name.string(),
+ toString(window.paused),
+ toString(window.hasFocus),
+ toString(window.hasWallpaper),
+ toString(window.visible),
+ toString(window.canReceiveKeys),
+ window.layoutParamsFlags, window.layoutParamsType,
+ window.layer,
+ window.frameLeft, window.frameTop,
+ window.frameRight, window.frameBottom,
+ window.visibleFrameLeft, window.visibleFrameTop,
+ window.visibleFrameRight, window.visibleFrameBottom,
+ window.touchableAreaLeft, window.touchableAreaTop,
+ window.touchableAreaRight, window.touchableAreaBottom,
+ window.ownerPid, window.ownerUid,
+ window.dispatchingTimeout / 1000000.0);
+ }
+ } else {
+ dump.append(INDENT "Windows: <none>\n");
}
- dump.appendFormat(" inboundQueue: length=%u", mInboundQueue.count());
+ if (!mMonitoringChannels.isEmpty()) {
+ dump.append(INDENT "MonitoringChannels:\n");
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ const sp<InputChannel>& channel = mMonitoringChannels[i];
+ dump.appendFormat(INDENT2 "%d: '%s'\n", i, channel->getName().string());
+ }
+ } else {
+ dump.append(INDENT "MonitoringChannels: <none>\n");
+ }
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- const Connection* connection = mActiveConnections[i];
- dump.appendFormat(" activeConnection[%d]: '%s', status=%s, outboundQueueLength=%u"
- "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
- i, connection->getInputChannelName(), connection->getStatusLabel(),
- connection->outboundQueue.count(),
- toString(connection->inputState.isNeutral()),
- toString(connection->inputState.isOutOfSync()));
+ dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
+
+ if (!mActiveConnections.isEmpty()) {
+ dump.append(INDENT "ActiveConnections:\n");
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ const Connection* connection = mActiveConnections[i];
+ dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u"
+ "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
+ i, connection->getInputChannelName(), connection->getStatusLabel(),
+ connection->outboundQueue.count(),
+ toString(connection->inputState.isNeutral()),
+ toString(connection->inputState.isOutOfSync()));
+ }
+ } else {
+ dump.append(INDENT "ActiveConnections: <none>\n");
}
if (isAppSwitchPendingLocked()) {
- dump.appendFormat(" appSwitch: pending, due in %01.1fms\n",
+ dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n",
(mAppSwitchDueTime - now()) / 1000000.0);
} else {
- dump.append(" appSwitch: not pending\n");
+ dump.append(INDENT "AppSwitch: not pending\n");
}
}
@@ -2774,6 +2802,7 @@
}
void InputDispatcher::dump(String8& dump) {
+ dump.append("Input Dispatcher State:\n");
dumpDispatchStateLocked(dump);
}
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 1d35e10..7adc764 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -226,11 +226,15 @@
void InputReader::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
- addDevice(rawEvent->when, rawEvent->deviceId);
+ addDevice(rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
- removeDevice(rawEvent->when, rawEvent->deviceId);
+ removeDevice(rawEvent->deviceId);
+ break;
+
+ case EventHubInterface::FINISHED_DEVICE_SCAN:
+ handleConfigurationChanged();
break;
default:
@@ -239,7 +243,7 @@
}
}
-void InputReader::addDevice(nsecs_t when, int32_t deviceId) {
+void InputReader::addDevice(int32_t deviceId) {
String8 name = mEventHub->getDeviceName(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
@@ -269,11 +273,9 @@
delete device;
return;
}
-
- handleConfigurationChanged(when);
}
-void InputReader::removeDevice(nsecs_t when, int32_t deviceId) {
+void InputReader::removeDevice(int32_t deviceId) {
bool removed = false;
InputDevice* device = NULL;
{ // acquire device registry writer lock
@@ -303,8 +305,6 @@
device->reset();
delete device;
-
- handleConfigurationChanged(when);
}
InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
@@ -372,7 +372,7 @@
} // release device registry reader lock
}
-void InputReader::handleConfigurationChanged(nsecs_t when) {
+void InputReader::handleConfigurationChanged() {
// Reset global meta state because it depends on the list of all configured devices.
updateGlobalMetaState();
@@ -380,6 +380,7 @@
updateInputConfiguration();
// Enqueue configuration changed.
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
mDispatcher->notifyConfigurationChanged(when);
}
@@ -575,6 +576,11 @@
}
void InputReader::dump(String8& dump) {
+ mEventHub->dump(dump);
+ dump.append("\n");
+
+ dump.append("Input Reader State:\n");
+
{ // acquire device registry reader lock
RWLock::AutoRLock _rl(mDeviceRegistryLock);
@@ -861,7 +867,6 @@
AutoMutex _l(mLock);
dump.append(INDENT2 "Keyboard Input Mapper:\n");
dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
- dump.appendFormat(INDENT3 "Sources: 0x%x\n", mSources);
dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
@@ -993,7 +998,10 @@
int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP;
int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
if (policyFlags & POLICY_FLAG_WOKE_HERE) {
- keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
+ keyEventFlags |= AKEY_EVENT_FLAG_WOKE_HERE;
+ }
+ if (policyFlags & POLICY_FLAG_VIRTUAL) {
+ keyEventFlags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
@@ -2162,10 +2170,7 @@
int32_t keyCode, int32_t scanCode, nsecs_t downTime) {
int32_t metaState = mContext->getGlobalMetaState();
- if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
- getPolicy()->virtualKeyDownFeedback();
- }
-
+ policyFlags |= POLICY_FLAG_VIRTUAL;
int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
@@ -2442,7 +2447,7 @@
yPrecision = mLocked.orientedYPrecision;
} // release lock
- getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TOUCHSCREEN, policyFlags,
+ getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,
motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
pointerCount, pointerIds, pointerCoords,
xPrecision, yPrecision, mDownTime);
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index d2dd6eb..a5363d6 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -19,16 +19,17 @@
#include <unistd.h>
#include <fcntl.h>
-#include <sys/epoll.h>
namespace android {
+#ifdef LOOPER_USES_EPOLL
// Hint for number of file descriptors to be associated with the epoll instance.
static const int EPOLL_SIZE_HINT = 8;
// Maximum number of file descriptors for which to retrieve poll events each iteration.
static const int EPOLL_MAX_EVENTS = 16;
+#endif
static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
static pthread_key_t gTLSKey = 0;
@@ -36,9 +37,6 @@
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks),
mResponseIndex(0) {
- mEpollFd = epoll_create(EPOLL_SIZE_HINT);
- LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
-
int wakeFds[2];
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
@@ -54,6 +52,11 @@
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
+#ifdef LOOPER_USES_EPOLL
+ // Allocate the epoll instance and register the wake pipe.
+ mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
+
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
@@ -61,12 +64,45 @@
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
+#else
+ // Add the wake pipe to the head of the request list with a null callback.
+ struct pollfd requestedFd;
+ requestedFd.fd = mWakeReadPipeFd;
+ requestedFd.events = POLLIN;
+ mRequestedFds.push(requestedFd);
+
+ Request request;
+ request.fd = mWakeReadPipeFd;
+ request.callback = NULL;
+ request.ident = 0;
+ request.data = NULL;
+ mRequests.push(request);
+
+ mPolling = false;
+ mWaiters = 0;
+#endif
+
+#ifdef LOOPER_STATISTICS
+ mPendingWakeTime = -1;
+ mPendingWakeCount = 0;
+ mSampledWakeCycles = 0;
+ mSampledWakeCountSum = 0;
+ mSampledWakeLatencySum = 0;
+
+ mSampledPolls = 0;
+ mSampledZeroPollCount = 0;
+ mSampledZeroPollLatencySum = 0;
+ mSampledTimeoutPollCount = 0;
+ mSampledTimeoutPollLatencySum = 0;
+#endif
}
Looper::~Looper() {
close(mWakeReadPipeFd);
close(mWakeWritePipeFd);
+#ifdef LOOPER_USES_EPOLL
close(mEpollFd);
+#endif
}
void Looper::initTLSKey() {
@@ -157,45 +193,61 @@
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
+
+ int result = ALOOPER_POLL_WAKE;
+ mResponses.clear();
+ mResponseIndex = 0;
+
+#ifdef LOOPER_STATISTICS
+ nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+
+#ifdef LOOPER_USES_EPOLL
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+ bool acquiredLock = false;
+#else
+ // Wait for wakeAndLock() waiters to run then set mPolling to true.
+ mLock.lock();
+ while (mWaiters != 0) {
+ mResume.wait(mLock);
+ }
+ mPolling = true;
+ mLock.unlock();
+
+ size_t requestedCount = mRequestedFds.size();
+ int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
+#endif
+
if (eventCount < 0) {
if (errno == EINTR) {
- return ALOOPER_POLL_WAKE;
+ goto Done;
}
LOGW("Poll failed with an unexpected error, errno=%d", errno);
- return ALOOPER_POLL_ERROR;
+ result = ALOOPER_POLL_ERROR;
+ goto Done;
}
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - timeout", this);
#endif
- return ALOOPER_POLL_TIMEOUT;
+ result = ALOOPER_POLL_TIMEOUT;
+ goto Done;
}
- int result = ALOOPER_POLL_WAKE;
- mResponses.clear();
- mResponseIndex = 0;
-
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
- bool acquiredLock = false;
+
+#ifdef LOOPER_USES_EPOLL
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
-#if DEBUG_POLL_AND_WAKE
- LOGD("%p ~ pollOnce - awoken", this);
-#endif
- char buffer[16];
- ssize_t nRead;
- do {
- nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
- } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+ awoken();
} else {
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
@@ -212,11 +264,7 @@
if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
-
- Response response;
- response.events = events;
- response.request = mRequests.valueAt(requestIndex);
- mResponses.push(response);
+ pushResponse(events, mRequests.valueAt(requestIndex));
} else {
LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
@@ -226,6 +274,66 @@
if (acquiredLock) {
mLock.unlock();
}
+Done: ;
+#else
+ for (size_t i = 0; i < requestedCount; i++) {
+ const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
+
+ short pollEvents = requestedFd.revents;
+ if (pollEvents) {
+ if (requestedFd.fd == mWakeReadPipeFd) {
+ if (pollEvents & POLLIN) {
+ awoken();
+ } else {
+ LOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents);
+ }
+ } else {
+ int events = 0;
+ if (pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT;
+ if (pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+ if (pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR;
+ if (pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP;
+ if (pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID;
+ pushResponse(events, mRequests.itemAt(i));
+ }
+ if (--eventCount == 0) {
+ break;
+ }
+ }
+ }
+
+Done:
+ // Set mPolling to false and wake up the wakeAndLock() waiters.
+ mLock.lock();
+ mPolling = false;
+ if (mWaiters != 0) {
+ mAwake.broadcast();
+ }
+ mLock.unlock();
+#endif
+
+#ifdef LOOPER_STATISTICS
+ nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mSampledPolls += 1;
+ if (timeoutMillis == 0) {
+ mSampledZeroPollCount += 1;
+ mSampledZeroPollLatencySum += pollEndTime - pollStartTime;
+ } else if (timeoutMillis > 0 && result == ALOOPER_POLL_TIMEOUT) {
+ mSampledTimeoutPollCount += 1;
+ mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime
+ - milliseconds_to_nanoseconds(timeoutMillis);
+ }
+ if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) {
+ LOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this,
+ 0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount,
+ 0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount);
+ mSampledPolls = 0;
+ mSampledZeroPollCount = 0;
+ mSampledZeroPollLatencySum = 0;
+ mSampledTimeoutPollCount = 0;
+ mSampledTimeoutPollLatencySum = 0;
+ }
+#endif
for (size_t i = 0; i < mResponses.size(); i++) {
const Response& response = mResponses.itemAt(i);
@@ -278,6 +386,13 @@
LOGD("%p ~ wake", this);
#endif
+#ifdef LOOPER_STATISTICS
+ // FIXME: Possible race with awoken() but this code is for testing only and is rarely enabled.
+ if (mPendingWakeCount++ == 0) {
+ mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+#endif
+
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
@@ -290,23 +405,51 @@
}
}
+void Looper::awoken() {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ awoken", this);
+#endif
+
+#ifdef LOOPER_STATISTICS
+ if (mPendingWakeCount == 0) {
+ LOGD("%p ~ awoken: spurious!", this);
+ } else {
+ mSampledWakeCycles += 1;
+ mSampledWakeCountSum += mPendingWakeCount;
+ mSampledWakeLatencySum += systemTime(SYSTEM_TIME_MONOTONIC) - mPendingWakeTime;
+ mPendingWakeCount = 0;
+ mPendingWakeTime = -1;
+ if (mSampledWakeCycles == SAMPLED_WAKE_CYCLES_TO_AGGREGATE) {
+ LOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this,
+ 0.000001f * float(mSampledWakeLatencySum) / mSampledWakeCycles,
+ float(mSampledWakeCountSum) / mSampledWakeCycles);
+ mSampledWakeCycles = 0;
+ mSampledWakeCountSum = 0;
+ mSampledWakeLatencySum = 0;
+ }
+ }
+#endif
+
+ char buffer[16];
+ ssize_t nRead;
+ do {
+ nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+}
+
+void Looper::pushResponse(int events, const Request& request) {
+ Response response;
+ response.events = events;
+ response.request = request;
+ mResponses.push(response);
+}
+
int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) {
#if DEBUG_CALLBACKS
LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
events, callback, data);
#endif
- int epollEvents = 0;
- if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
- if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
- if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR;
- if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP;
-
- if (epollEvents == 0) {
- LOGE("Invalid attempt to set a callback with no selected poll events.");
- return -1;
- }
-
if (! callback) {
if (! mAllowNonCallbacks) {
LOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
@@ -319,6 +462,11 @@
}
}
+#ifdef LOOPER_USES_EPOLL
+ int epollEvents = 0;
+ if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
+ if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+
{ // acquire lock
AutoMutex _l(mLock);
@@ -350,6 +498,33 @@
mRequests.replaceValueAt(requestIndex, request);
}
} // release lock
+#else
+ int pollEvents = 0;
+ if (events & ALOOPER_EVENT_INPUT) pollEvents |= POLLIN;
+ if (events & ALOOPER_EVENT_OUTPUT) pollEvents |= POLLOUT;
+
+ wakeAndLock(); // acquire lock
+
+ struct pollfd requestedFd;
+ requestedFd.fd = fd;
+ requestedFd.events = pollEvents;
+
+ Request request;
+ request.fd = fd;
+ request.ident = ident;
+ request.callback = callback;
+ request.data = data;
+ ssize_t index = getRequestIndexLocked(fd);
+ if (index < 0) {
+ mRequestedFds.push(requestedFd);
+ mRequests.push(request);
+ } else {
+ mRequestedFds.replaceAt(requestedFd, size_t(index));
+ mRequests.replaceAt(request, size_t(index));
+ }
+
+ mLock.unlock(); // release lock
+#endif
return 1;
}
@@ -358,6 +533,7 @@
LOGD("%p ~ removeFd - fd=%d", this, fd);
#endif
+#ifdef LOOPER_USES_EPOLL
{ // acquire lock
AutoMutex _l(mLock);
ssize_t requestIndex = mRequests.indexOfKey(fd);
@@ -372,8 +548,49 @@
}
mRequests.removeItemsAt(requestIndex);
- } // request lock
+ } // release lock
return 1;
+#else
+ wakeAndLock(); // acquire lock
+
+ ssize_t index = getRequestIndexLocked(fd);
+ if (index >= 0) {
+ mRequestedFds.removeAt(size_t(index));
+ mRequests.removeAt(size_t(index));
+ }
+
+ mLock.unlock(); // release lock
+ return index >= 0;
+#endif
}
+#ifndef LOOPER_USES_EPOLL
+ssize_t Looper::getRequestIndexLocked(int fd) {
+ size_t requestCount = mRequestedFds.size();
+
+ for (size_t i = 0; i < requestCount; i++) {
+ if (mRequestedFds.itemAt(i).fd == fd) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void Looper::wakeAndLock() {
+ mLock.lock();
+
+ mWaiters += 1;
+ while (mPolling) {
+ wake();
+ mAwake.wait(mLock);
+ }
+
+ mWaiters -= 1;
+ if (mWaiters == 0) {
+ mResume.signal();
+ }
+}
+#endif
+
} // namespace android
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 9fcae72..9b1f82f 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -86,6 +86,16 @@
*/
#define kZipEntryAdj 10000
+ZipFileRO::~ZipFileRO() {
+ free(mHashTable);
+ if (mDirectoryMap)
+ mDirectoryMap->release();
+ if (mFd >= 0)
+ TEMP_FAILURE_RETRY(close(mFd));
+ if (mFileName)
+ free(mFileName);
+}
+
/*
* Convert a ZipEntryRO to a hash table index, verifying that it's in a
* valid range.
@@ -122,7 +132,7 @@
mFileLength = lseek(fd, 0, SEEK_END);
if (mFileLength < kEOCDLen) {
- close(fd);
+ TEMP_FAILURE_RETRY(close(fd));
return UNKNOWN_ERROR;
}
@@ -152,7 +162,7 @@
bail:
free(mFileName);
mFileName = NULL;
- close(fd);
+ TEMP_FAILURE_RETRY(close(fd));
return UNKNOWN_ERROR;
}
@@ -498,6 +508,36 @@
unsigned char lfhBuf[kLFHLen];
+#ifdef HAVE_PREAD
+ /*
+ * This file descriptor might be from zygote's preloaded assets,
+ * so we need to do an pread() instead of a lseek() + read() to
+ * guarantee atomicity across the processes with the shared file
+ * descriptors.
+ */
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
+
+ if (actual != sizeof(lfhBuf)) {
+ LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, get4LE(lfhBuf));
+ return false;
+ }
+#else /* HAVE_PREAD */
+ /*
+ * For hosts don't have pread() we cannot guarantee atomic reads from
+ * an offset in a file. Android should never run on those platforms.
+ * File descriptors inherited from a fork() share file offsets and
+ * there would be nothing to protect from two different processes
+ * calling lseek() concurrently.
+ */
+
{
AutoMutex _l(mFdLock);
@@ -507,18 +547,21 @@
}
ssize_t actual =
- TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+ TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
if (actual != sizeof(lfhBuf)) {
LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
return false;
}
- }
- if (get4LE(lfhBuf) != kLFHSignature) {
- LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n",
- localHdrOffset, get4LE(lfhBuf), kLFHSignature);
- return false;
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ off_t actualOffset = lseek(mFd, 0, SEEK_CUR);
+ LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: offset=%zd data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, (size_t)actualOffset, get4LE(lfhBuf));
+ return false;
+ }
}
+#endif /* HAVE_PREAD */
off_t dataOffset = localHdrOffset + kLFHLen
+ get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
index afc92f8..cea1313 100644
--- a/libs/utils/tests/Looper_test.cpp
+++ b/libs/utils/tests/Looper_test.cpp
@@ -354,14 +354,6 @@
<< "addFd should return 1 because FD was added";
}
-TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) {
- Pipe pipe;
- int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
-
- EXPECT_EQ(-1, result)
- << "addFd should return -1 because arguments were invalid";
-}
-
TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
Pipe pipe;
int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 695cbfa..a060d31 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -214,6 +214,17 @@
}
}
+void Layer::drawForSreenShot() const
+{
+ bool currentFixedSize = mFixedSize;
+ bool currentBlending = mNeedsBlending;
+ const_cast<Layer*>(this)->mFixedSize = false;
+ const_cast<Layer*>(this)->mFixedSize = true;
+ LayerBase::drawForSreenShot();
+ const_cast<Layer*>(this)->mFixedSize = currentFixedSize;
+ const_cast<Layer*>(this)->mNeedsBlending = currentBlending;
+}
+
void Layer::onDraw(const Region& clip) const
{
Texture tex(mBufferManager.getActiveTexture());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e1d283b..263c372 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,7 @@
bool isFixedSize() const;
// LayerBase interface
+ virtual void drawForSreenShot() const;
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 6fc5010..758b408 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -317,6 +317,12 @@
onDraw(clip);
}
+void LayerBase::drawForSreenShot() const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ onDraw( Region(hw.bounds()) );
+}
+
void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
GLclampf green, GLclampf blue,
GLclampf alpha) const
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 8cba287..d688f65 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -115,6 +115,7 @@
* to perform the actual drawing.
*/
virtual void draw(const Region& clip) const;
+ virtual void drawForSreenShot() const;
/**
* onDraw - draws the surface.
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 0240748..87cabed 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -132,6 +132,12 @@
LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);
}
+void LayerBuffer::drawForSreenShot() const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ clearWithOpenGL( Region(hw.bounds()) );
+}
+
void LayerBuffer::onDraw(const Region& clip) const
{
sp<Source> source(getSource());
diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
index 1c0bf83..fece858 100644
--- a/services/surfaceflinger/LayerBuffer.h
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -64,6 +64,7 @@
virtual sp<LayerBaseClient::Surface> createSurface() const;
virtual status_t ditch();
virtual void onDraw(const Region& clip) const;
+ virtual void drawForSreenShot() const;
virtual uint32_t doTransaction(uint32_t flags);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2b06f6f..e5e87c6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1546,9 +1546,117 @@
// ---------------------------------------------------------------------------
+status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* w, uint32_t* h, PixelFormat* f,
+ uint32_t sw, uint32_t sh)
+{
+ status_t result = PERMISSION_DENIED;
+
+ // only one display supported for now
+ if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ return BAD_VALUE;
+
+ if (!GLExtensions::getInstance().haveFramebufferObject())
+ return INVALID_OPERATION;
+
+ // get screen geometry
+ const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
+ const uint32_t hw_w = hw.getWidth();
+ const uint32_t hw_h = hw.getHeight();
+
+ if ((sw > hw_w) || (sh > hw_h))
+ return BAD_VALUE;
+
+ sw = (!sw) ? hw_w : sw;
+ sh = (!sh) ? hw_h : sh;
+ const size_t size = sw * sh * 4;
+
+ // make sure to clear all GL error flags
+ while ( glGetError() != GL_NO_ERROR ) ;
+
+ // create a FBO
+ GLuint name, tname;
+ glGenRenderbuffersOES(1, &tname);
+ glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+ glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+ glGenFramebuffersOES(1, &name);
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+ GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+ GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+ if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+ // invert everything, b/c glReadPixel() below will invert the FB
+ glViewport(0, 0, sw, sh);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrthof(0, hw_w, 0, hw_h, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+
+ // redraw the screen entirely...
+ glClearColor(0,0,0,1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+ const size_t count = layers.size();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<LayerBase>& layer(layers[i]);
+ layer->drawForSreenShot();
+ }
+
+ // XXX: this is needed on tegra
+ glScissor(0, 0, sw, sh);
+
+ // check for errors and return screen capture
+ if (glGetError() != GL_NO_ERROR) {
+ // error while rendering
+ result = INVALID_OPERATION;
+ } else {
+ // allocate shared memory large enough to hold the
+ // screen capture
+ sp<MemoryHeapBase> base(
+ new MemoryHeapBase(size, 0, "screen-capture") );
+ void* const ptr = base->getBase();
+ if (ptr) {
+ // capture the screen with glReadPixels()
+ glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+ if (glGetError() == GL_NO_ERROR) {
+ *heap = base;
+ *w = sw;
+ *h = sh;
+ *f = PIXEL_FORMAT_RGBA_8888;
+ result = NO_ERROR;
+ }
+ } else {
+ result = NO_MEMORY;
+ }
+ }
+
+ glEnable(GL_SCISSOR_TEST);
+ glViewport(0, 0, hw_w, hw_h);
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+
+ } else {
+ result = BAD_VALUE;
+ }
+
+ // release FBO resources
+ glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+ glDeleteRenderbuffersOES(1, &tname);
+ glDeleteFramebuffersOES(1, &name);
+ return result;
+}
+
+
status_t SurfaceFlinger::captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
- uint32_t* width, uint32_t* height, PixelFormat* format)
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t sw, uint32_t sh)
{
// only one display supported for now
if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1564,12 +1672,15 @@
uint32_t* w;
uint32_t* h;
PixelFormat* f;
+ uint32_t sw;
+ uint32_t sh;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
- sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+ sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
+ uint32_t sw, uint32_t sh)
: flinger(flinger), dpy(dpy),
- heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+ heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
{
}
status_t getResult() const {
@@ -1582,94 +1693,15 @@
if (flinger->mSecureFrameBuffer)
return true;
- // make sure to clear all GL error flags
- while ( glGetError() != GL_NO_ERROR ) ;
+ result = flinger->captureScreenImplLocked(dpy,
+ heap, w, h, f, sw, sh);
- // get screen geometry
- const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
- const uint32_t sw = hw.getWidth();
- const uint32_t sh = hw.getHeight();
- const Region screenBounds(hw.bounds());
- const size_t size = sw * sh * 4;
-
- // create a FBO
- GLuint name, tname;
- glGenRenderbuffersOES(1, &tname);
- glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
- glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
- glGenFramebuffersOES(1, &name);
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
- glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
- GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
-
- GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
- if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
-
- // invert everything, b/c glReadPixel() below will invert the FB
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glOrthof(0, sw, 0, sh, 0, 1);
- glMatrixMode(GL_MODELVIEW);
-
- // redraw the screen entirely...
- glClearColor(0,0,0,1);
- glClear(GL_COLOR_BUFFER_BIT);
- const Vector< sp<LayerBase> >& layers(
- flinger->mVisibleLayersSortedByZ);
- const size_t count = layers.size();
- for (size_t i=0 ; i<count ; ++i) {
- const sp<LayerBase>& layer(layers[i]);
- if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
- // we cannot render LayerBuffer because it doens't
- // use OpenGL, and won't show-up in the FBO.
- continue;
- }
- layer->draw(screenBounds);
- }
-
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
-
- // check for errors and return screen capture
- if (glGetError() != GL_NO_ERROR) {
- // error while rendering
- result = INVALID_OPERATION;
- } else {
- // allocate shared memory large enough to hold the
- // screen capture
- sp<MemoryHeapBase> base(
- new MemoryHeapBase(size, 0, "screen-capture") );
- void* const ptr = base->getBase();
- if (ptr) {
- // capture the screen with glReadPixels()
- glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
- if (glGetError() == GL_NO_ERROR) {
- *heap = base;
- *w = sw;
- *h = sh;
- *f = PIXEL_FORMAT_RGBA_8888;
- result = NO_ERROR;
- }
- } else {
- result = NO_MEMORY;
- }
- }
- } else {
- result = BAD_VALUE;
- }
-
- // release FBO resources
- glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
- glDeleteRenderbuffersOES(1, &tname);
- glDeleteFramebuffersOES(1, &name);
return true;
}
};
sp<MessageBase> msg = new MessageCaptureScreen(this,
- dpy, heap, width, height, format);
+ dpy, heap, width, height, format, sw, sh);
status_t res = postMessageSync(msg);
if (res == NO_ERROR) {
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f09fdbc..f0a167b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -197,7 +197,9 @@
sp<IMemoryHeap>* heap,
uint32_t* width,
uint32_t* height,
- PixelFormat* format);
+ PixelFormat* format,
+ uint32_t reqWidth,
+ uint32_t reqHeight);
void screenReleased(DisplayID dpy);
void screenAcquired(DisplayID dpy);
@@ -318,6 +320,11 @@
void commitTransaction();
+ status_t captureScreenImplLocked(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth = 0, uint32_t reqHeight = 0);
+
friend class FreezeLock;
sp<FreezeLock> getFreezeLock() const;
inline void incFreezeCount() {
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
index 9e893f4..6cf1504 100644
--- a/services/surfaceflinger/tests/screencap/screencap.cpp
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -42,7 +42,7 @@
sp<IMemoryHeap> heap;
uint32_t w, h;
PixelFormat f;
- status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+ status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
if (err != NO_ERROR) {
fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
exit(0);