Merge "Add line break between PLMN and SPN in windowshade." into gingerbread
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index dd44aa5..76307b2 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -110,6 +110,13 @@
*/
virtual void bootFinished() = 0;
+ /* Capture the specified screen. requires READ_FRAME_BUFFER permission
+ * This function will fail if there is a secure window on screen.
+ */
+ virtual status_t captureScreen(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+
/* Signal surfaceflinger that there might be some work to do
* This is an ASYNCHRONOUS call.
*/
@@ -133,7 +140,8 @@
SET_ORIENTATION,
FREEZE_DISPLAY,
UNFREEZE_DISPLAY,
- SIGNAL
+ SIGNAL,
+ CAPTURE_SCREEN
};
virtual status_t onTransact( uint32_t code,
diff --git a/include/ui/Input.h b/include/ui/Input.h
index b587e94..21baf32 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -40,10 +40,18 @@
/*
* Maximum number of pointers supported per motion event.
+ * Smallest number of pointers is 1.
*/
#define MAX_POINTERS 10
/*
+ * Maximum pointer id value supported in a motion event.
+ * Smallest pointer id is 0.
+ * (This is limited by our use of BitSet32 to track pointer assignments.)
+ */
+#define MAX_POINTER_ID 31
+
+/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
struct AInputEvent {
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 96b4fae..cc16012 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -27,6 +27,7 @@
#include <utils/String8.h>
#include <utils/Looper.h>
#include <utils/Pool.h>
+#include <utils/BitSet.h>
#include <stddef.h>
#include <unistd.h>
@@ -89,17 +90,13 @@
* AMOTION_EVENT_ACTION_OUTSIDE to this target. */
FLAG_OUTSIDE = 0x02,
- /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
- * In the case of a key event, it should be delivered with flag
- * AKEY_EVENT_FLAG_CANCELED set.
- * In the case of a motion event, it should be delivered with action
- * AMOTION_EVENT_ACTION_CANCEL instead. */
- FLAG_CANCEL = 0x04,
-
/* This flag indicates that the target of a MotionEvent is partly or wholly
* obscured by another visible window above it. The motion event should be
* delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
- FLAG_WINDOW_IS_OBSCURED = 0x08,
+ FLAG_WINDOW_IS_OBSCURED = 0x04,
+
+ /* This flag indicates that a motion event is being split across multiple windows. */
+ FLAG_SPLIT = 0x08,
};
// The input channel to be targeted.
@@ -111,6 +108,13 @@
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
float xOffset, yOffset;
+
+ // The window type of the input target.
+ int32_t windowType;
+
+ // The subset of pointer ids to include in motion events dispatched to this input target
+ // if FLAG_SPLIT is set.
+ BitSet32 pointerIds;
};
@@ -143,7 +147,7 @@
FLAG_SHOW_WALLPAPER = 0x00100000,
FLAG_TURN_SCREEN_ON = 0x00200000,
FLAG_DISMISS_KEYGUARD = 0x00400000,
- FLAG_IMMERSIVE = 0x00800000,
+ FLAG_SPLIT_TOUCH = 0x00800000,
FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
FLAG_COMPATIBLE_WINDOW = 0x20000000,
FLAG_SYSTEM_ERROR = 0x40000000,
@@ -276,7 +280,7 @@
const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
/* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) = 0;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
/* Checks whether a given application pid/uid has permission to inject input events
* into other applications.
@@ -415,6 +419,16 @@
T* prev;
};
+ struct InjectionState {
+ mutable int32_t refCount;
+
+ int32_t injectorPid;
+ int32_t injectorUid;
+ int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+ bool injectionIsAsync; // set to true if injection is not waiting for the result
+ int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
+ };
+
struct EventEntry : Link<EventEntry> {
enum {
TYPE_SENTINEL,
@@ -423,21 +437,14 @@
TYPE_MOTION
};
- int32_t refCount;
+ mutable int32_t refCount;
int32_t type;
nsecs_t eventTime;
-
- int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
- bool injectionIsAsync; // set to true if injection is not waiting for the result
- int32_t injectorPid; // -1 if not injected
- int32_t injectorUid; // -1 if not injected
+ InjectionState* injectionState;
bool dispatchInProgress; // initially false, set to true while dispatching
- int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
- inline bool isInjected() { return injectorPid >= 0; }
-
- void recycle();
+ inline bool isInjected() { return injectionState != NULL; }
};
struct ConfigurationChangedEntry : EventEntry {
@@ -463,8 +470,6 @@
INTERCEPT_KEY_RESULT_CONTINUE,
};
InterceptKeyResult interceptKeyResult; // set based on the interception result
-
- void recycle();
};
struct MotionSample {
@@ -521,6 +526,10 @@
inline bool hasForegroundTarget() const {
return targetFlags & InputTarget::FLAG_FOREGROUND;
}
+
+ inline bool isSplit() const {
+ return targetFlags & InputTarget::FLAG_SPLIT;
+ }
};
// A command entry captures state and behavior for an action to be performed in the
@@ -555,7 +564,6 @@
KeyEntry* keyEntry;
sp<InputChannel> inputChannel;
sp<InputApplicationHandle> inputApplicationHandle;
- int32_t windowType;
int32_t userActivityEventType;
};
@@ -611,6 +619,7 @@
public:
Allocator();
+ InjectionState* obtainInjectionState(int32_t injectorPid, int32_t injectorUid);
ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
KeyEntry* obtainKeyEntry(nsecs_t eventTime,
int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
@@ -626,6 +635,7 @@
int32_t targetFlags, float xOffset, float yOffset);
CommandEntry* obtainCommandEntry(Command command);
+ void releaseInjectionState(InjectionState* injectionState);
void releaseEventEntry(EventEntry* entry);
void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
void releaseKeyEntry(KeyEntry* entry);
@@ -633,10 +643,13 @@
void releaseDispatchEntry(DispatchEntry* entry);
void releaseCommandEntry(CommandEntry* entry);
+ void recycleKeyEntry(KeyEntry* entry);
+
void appendMotionSample(MotionEntry* motionEntry,
nsecs_t eventTime, const PointerCoords* pointerCoords);
private:
+ Pool<InjectionState> mInjectionStatePool;
Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
Pool<KeyEntry> mKeyEntryPool;
Pool<MotionEntry> mMotionEntryPool;
@@ -645,6 +658,7 @@
Pool<CommandEntry> mCommandEntryPool;
void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
+ void releaseEventEntryInjectionState(EventEntry* entry);
};
/* Tracks dispatched key and motion event state so that cancelation events can be
@@ -823,6 +837,7 @@
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
Condition mInjectionSyncFinishedCondition;
+ void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
// Throttling state.
@@ -858,23 +873,37 @@
// Dispatch state.
bool mDispatchEnabled;
bool mDispatchFrozen;
+
Vector<InputWindow> mWindows;
- Vector<InputWindow*> mWallpaperWindows;
+
+ const InputWindow* getWindowLocked(const sp<InputChannel>& inputChannel);
// Focus tracking for keys, trackball, etc.
- InputWindow* mFocusedWindow;
+ const InputWindow* mFocusedWindow;
// Focus tracking for touch.
- bool mTouchDown;
- InputWindow* mTouchedWindow; // primary target for current down
- bool mTouchedWindowIsObscured; // true if other windows may obscure the target
- Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
- struct OutsideTarget {
- InputWindow* window;
- bool obscured;
+ struct TouchedWindow {
+ const InputWindow* window;
+ int32_t targetFlags;
+ BitSet32 pointerIds;
+ sp<InputChannel> channel;
};
- Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
- Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+ struct TouchState {
+ bool down;
+ bool split;
+ Vector<TouchedWindow> windows;
+
+ TouchState();
+ ~TouchState();
+ void reset();
+ void copyFrom(const TouchState& other);
+ void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
+ void removeOutsideTouchWindows();
+ const InputWindow* getFirstForegroundWindow();
+ };
+
+ TouchState mTouchState;
+ TouchState mTempTouchState;
// Focused application.
InputApplication* mFocusedApplication;
@@ -899,8 +928,6 @@
// The input targets that were most recently identified for dispatch.
bool mCurrentInputTargetsValid; // false while targets are being recomputed
Vector<InputTarget> mCurrentInputTargets;
- int32_t mCurrentInputWindowType;
- sp<InputChannel> mCurrentInputChannel;
enum InputTargetWaitCause {
INPUT_TARGET_WAIT_CAUSE_NONE,
@@ -915,7 +942,7 @@
// Finding targets for input events.
void resetTargetsLocked();
- void commitTargetsLocked(const InputWindow* window);
+ void commitTargetsLocked();
int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
const InputApplication* application, const InputWindow* window,
nsecs_t* nextWakeupTime);
@@ -924,19 +951,19 @@
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
void resetANRTimeoutsLocked();
- int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
- nsecs_t* nextWakeupTime, InputWindow** outWindow);
- int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
- nsecs_t* nextWakeupTime, InputWindow** outWindow);
+ int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+ nsecs_t* nextWakeupTime);
+ int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+ nsecs_t* nextWakeupTime);
- void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
+ void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+ BitSet32 pointerIds);
void addMonitoringTargetsLocked();
- void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
- bool checkInjectionPermission(const InputWindow* window,
- int32_t injectorPid, int32_t injectorUid);
+ bool shouldPokeUserActivityForCurrentInputTargetsLocked();
+ void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType);
+ bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState);
bool isWindowObscuredLocked(const InputWindow* window);
bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
- void releaseTouchedWindowLocked();
String8 getApplicationWindowLabelLocked(const InputApplication* application,
const InputWindow* window);
@@ -955,6 +982,9 @@
void drainOutboundQueueLocked(Connection* connection);
static int handleReceiveCallback(int receiveFd, int events, void* data);
+ // Splitting motion events across windows.
+ MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+
// Dump state.
void dumpDispatchStateLocked(String8& dump);
void logDispatchStateLocked();
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 903c3c4..e85735a 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -549,10 +549,6 @@
const int32_t* keyCodes, uint8_t* outFlags);
protected:
- /* Maximum pointer id value supported.
- * (This is limited by our use of BitSet32 to track pointer assignments.) */
- static const uint32_t MAX_POINTER_ID = 31;
-
Mutex mLock;
struct VirtualKey {
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index 19c8bf0..f5dbcd9 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -38,6 +38,9 @@
// Clears the bit set.
inline void clear() { value = 0; }
+ // Returns the number of marked bits in the set.
+ inline uint32_t count() const { return __builtin_popcount(value); }
+
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return ! value; }
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index 7d90866..3f00b78 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -205,6 +205,7 @@
int pollInner(int timeoutMillis);
+ static void initTLSKey();
static void threadDestructor(void *st);
};
diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h
index 97d31f4..9668bde 100644
--- a/include/utils/ZipFileRO.h
+++ b/include/utils/ZipFileRO.h
@@ -24,8 +24,9 @@
#ifndef __LIBS_ZIPFILERO_H
#define __LIBS_ZIPFILERO_H
-#include "Errors.h"
-#include "FileMap.h"
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
#include <stdio.h>
#include <stdlib.h>
@@ -211,6 +212,9 @@
/* open Zip archive */
int mFd;
+ /* Lock for handling the file descriptor (seeks, etc) */
+ mutable Mutex mFdLock;
+
/* zip file name */
char* mFileName;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a3e117f..13c58f0 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -517,12 +517,26 @@
}
if ((flags & TF_ONE_WAY) == 0) {
+ #if 0
+ if (code == 4) { // relayout
+ LOGI(">>>>>> CALLING transaction 4");
+ } else {
+ LOGI(">>>>>> CALLING transaction %d", code);
+ }
+ #endif
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
+ #if 0
+ if (code == 4) { // relayout
+ LOGI("<<<<<< RETURNING transaction 4");
+ } else {
+ LOGI("<<<<<< RETURNING transaction %d", code);
+ }
+ #endif
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 5c111f6..040060e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -124,6 +124,21 @@
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
+ virtual status_t captureScreen(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width, uint32_t* height, PixelFormat* format)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeInt32(dpy);
+ remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+ *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
+ *width = reply.readInt32();
+ *height = reply.readInt32();
+ *format = reply.readInt32();
+ return reply.readInt32();
+ }
+
virtual void signal() const
{
Parcel data, reply;
@@ -190,6 +205,19 @@
sp<IBinder> b = getCblk()->asBinder();
reply->writeStrongBinder(b);
} break;
+ case CAPTURE_SCREEN: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ DisplayID dpy = data.readInt32();
+ sp<IMemoryHeap> heap;
+ uint32_t w, h;
+ PixelFormat f;
+ status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+ reply->writeStrongBinder(heap->asBinder());
+ reply->writeInt32(w);
+ reply->writeInt32(h);
+ reply->writeInt32(f);
+ reply->writeInt32(res);
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 1cf7592..b5744b3 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -69,6 +69,65 @@
return value ? "true" : "false";
}
+static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static bool isValidKeyAction(int32_t action) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ case AKEY_EVENT_ACTION_UP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool validateKeyEvent(int32_t action) {
+ if (! isValidKeyAction(action)) {
+ LOGE("Key event has invalid action code 0x%x", action);
+ return false;
+ }
+ return true;
+}
+
+static bool isValidMotionAction(int32_t action) {
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_MOVE:
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool validateMotionEvent(int32_t action, size_t pointerCount,
+ const int32_t* pointerIds) {
+ if (! isValidMotionAction(action)) {
+ LOGE("Motion event has invalid action code 0x%x", action);
+ return false;
+ }
+ if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
+ LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.",
+ pointerCount, MAX_POINTERS);
+ return false;
+ }
+ for (size_t i = 0; i < pointerCount; i++) {
+ if (pointerIds[i] < 0 || pointerIds[i] > MAX_POINTER_ID) {
+ LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
+ pointerIds[i], MAX_POINTER_ID);
+ return false;
+ }
+ }
+ return true;
+}
+
// --- InputWindow ---
@@ -91,7 +150,7 @@
mPolicy(policy),
mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
mDispatchEnabled(true), mDispatchFrozen(false),
- mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+ mFocusedWindow(NULL),
mFocusedApplication(NULL),
mCurrentInputTargetsValid(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
@@ -414,9 +473,10 @@
}
void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
- if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
#if DEBUG_DISPATCH_CYCLE
- LOGD("Inbound event was dropped. Setting injection result to failed.");
+ LOGD("Injected inbound event was dropped.");
#endif
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
}
@@ -424,10 +484,11 @@
}
bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
- return ! entry->isInjected()
- || entry->injectorUid == 0
+ InjectionState* injectionState = entry->injectionState;
+ return ! injectionState
+ || injectionState->injectorUid == 0
|| mPolicy->checkInjectEventsPermissionNonReentrant(
- entry->injectorPid, entry->injectorUid);
+ injectionState->injectorPid, injectionState->injectorUid);
}
void InputDispatcher::resetKeyRepeatLocked() {
@@ -444,7 +505,7 @@
// Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
if (entry->refCount == 1) {
- entry->recycle();
+ mAllocator.recycleKeyEntry(entry);
entry->eventTime = currentTime;
entry->policyFlags = policyFlags;
entry->repeatCount += 1;
@@ -496,8 +557,7 @@
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
bool trusted;
if (! dropEvent && mFocusedWindow) {
- trusted = checkInjectionPermission(mFocusedWindow,
- entry->injectorPid, entry->injectorUid);
+ trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState);
} else {
trusted = isEventFromReliableSourceLocked(entry);
}
@@ -559,9 +619,8 @@
// Identify targets.
if (! mCurrentInputTargetsValid) {
- InputWindow* window = NULL;
- int32_t injectionResult = findFocusedWindowLocked(currentTime,
- entry, nextWakeupTime, & window);
+ int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
+ entry, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
@@ -572,14 +631,16 @@
}
addMonitoringTargetsLocked();
- commitTargetsLocked(window);
+ commitTargetsLocked();
}
// Dispatch the key.
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
// Poke user activity.
- pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+ if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+ pokeUserActivityLocked(entry->eventTime, POWER_MANAGER_BUTTON_EVENT);
+ }
return true;
}
@@ -616,16 +677,15 @@
// Identify targets.
if (! mCurrentInputTargetsValid) {
- InputWindow* window = NULL;
int32_t injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
- injectionResult = findTouchedWindowLocked(currentTime,
- entry, nextWakeupTime, & window);
+ injectionResult = findTouchedWindowTargetsLocked(currentTime,
+ entry, nextWakeupTime);
} else {
// Non touch event. (eg. trackball)
- injectionResult = findFocusedWindowLocked(currentTime,
- entry, nextWakeupTime, & window);
+ injectionResult = findFocusedWindowTargetsLocked(currentTime,
+ entry, nextWakeupTime);
}
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
@@ -637,34 +697,36 @@
}
addMonitoringTargetsLocked();
- commitTargetsLocked(window);
+ commitTargetsLocked();
}
// Dispatch the motion.
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
// Poke user activity.
- int32_t eventType;
- if (isPointerEvent) {
- switch (entry->action) {
- case AMOTION_EVENT_ACTION_DOWN:
- eventType = POWER_MANAGER_TOUCH_EVENT;
- break;
- case AMOTION_EVENT_ACTION_UP:
- eventType = POWER_MANAGER_TOUCH_UP_EVENT;
- break;
- default:
- if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+ if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+ int32_t eventType;
+ if (isPointerEvent) {
+ switch (entry->action) {
+ case AMOTION_EVENT_ACTION_DOWN:
eventType = POWER_MANAGER_TOUCH_EVENT;
- } else {
- eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+ break;
+ case AMOTION_EVENT_ACTION_UP:
+ eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+ break;
+ default:
+ if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+ }
+ break;
}
- break;
+ } else {
+ eventType = POWER_MANAGER_BUTTON_EVENT;
}
- } else {
- eventType = POWER_MANAGER_BUTTON_EVENT;
+ pokeUserActivityLocked(entry->eventTime, eventType);
}
- pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
return true;
}
@@ -735,13 +797,10 @@
void InputDispatcher::resetTargetsLocked() {
mCurrentInputTargetsValid = false;
mCurrentInputTargets.clear();
- mCurrentInputChannel.clear();
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
-void InputDispatcher::commitTargetsLocked(const InputWindow* window) {
- mCurrentInputWindowType = window->layoutParamsType;
- mCurrentInputChannel = window->inputChannel;
+void InputDispatcher::commitTargetsLocked() {
mCurrentInputTargetsValid = true;
}
@@ -803,8 +862,8 @@
// Give up.
mInputTargetWaitTimeoutExpired = true;
- // Release the touch target.
- releaseTouchedWindowLocked();
+ // Release the touch targets.
+ mTouchState.reset();
// Input state will not be realistic. Mark it out of sync.
if (inputChannel.get()) {
@@ -834,9 +893,8 @@
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
}
-int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
- nsecs_t* nextWakeupTime, InputWindow** outWindow) {
- *outWindow = NULL;
+int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
+ const EventEntry* entry, nsecs_t* nextWakeupTime) {
mCurrentInputTargets.clear();
int32_t injectionResult;
@@ -861,7 +919,7 @@
}
// Check permissions.
- if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+ if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
@@ -888,8 +946,7 @@
// Success! Output targets.
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- *outWindow = mFocusedWindow;
- addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
+ addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
// Done.
Failed:
@@ -905,15 +962,14 @@
return injectionResult;
}
-int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
- nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
+ const MotionEntry* entry, nsecs_t* nextWakeupTime) {
enum InjectionPermission {
INJECTION_PERMISSION_UNKNOWN,
INJECTION_PERMISSION_GRANTED,
INJECTION_PERMISSION_DENIED
};
- *outWindow = NULL;
mCurrentInputTargets.clear();
nsecs_t startTime = now();
@@ -945,25 +1001,33 @@
bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
int32_t action = entry->action;
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
- int32_t injectionResult;
- InjectionPermission injectionPermission;
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- /* Case 1: ACTION_DOWN */
+ int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ mTempTouchState.reset();
+ mTempTouchState.down = true;
+ } else {
+ mTempTouchState.copyFrom(mTouchState);
+ }
- InputWindow* newTouchedWindow = NULL;
- mTempTouchedOutsideTargets.clear();
+ bool isSplit = mTempTouchState.split && mTempTouchState.down;
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+ || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
+ /* Case 1: New splittable pointer going down. */
- int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
- int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
- InputWindow* topErrorWindow = NULL;
- bool obscured = false;
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x);
+ int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y);
+ const InputWindow* newTouchedWindow = NULL;
+ const InputWindow* topErrorWindow = NULL;
// Traverse windows from front to back to find touched window and outside targets.
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
- InputWindow* window = & mWindows.editItemAt(i);
+ const InputWindow* window = & mWindows.editItemAt(i);
int32_t flags = window->layoutParamsFlags;
if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
@@ -979,17 +1043,15 @@
if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
newTouchedWindow = window;
- obscured = isWindowObscuredLocked(window);
}
break; // found touched window, exit window loop
}
}
- if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
- OutsideTarget outsideTarget;
- outsideTarget.window = window;
- outsideTarget.obscured = isWindowObscuredLocked(window);
- mTempTouchedOutsideTargets.push(outsideTarget);
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+ && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
+ mTempTouchState.addOrUpdateWindow(window,
+ InputTarget::FLAG_OUTSIDE, BitSet32(0));
}
}
}
@@ -1007,6 +1069,21 @@
goto Unresponsive;
}
+ // Figure out whether splitting will be allowed for this window.
+ if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+ // New window supports splitting.
+ isSplit = true;
+ } else if (isSplit) {
+ // New window does not support splitting but we have already split events.
+ // Assign the pointer to the first foreground window we find.
+ // (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) {
if (mFocusedApplication) {
@@ -1017,140 +1094,127 @@
#endif
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplication, NULL, nextWakeupTime);
- injectionPermission = INJECTION_PERMISSION_UNKNOWN;
goto Unresponsive;
}
LOGI("Dropping event because there is no touched window or focused application.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_UNKNOWN;
goto Failed;
}
- // Check permissions.
- if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- injectionPermission = INJECTION_PERMISSION_DENIED;
- goto Failed;
+ // Update the temporary touch state.
+ BitSet32 pointerIds;
+ if (isSplit) {
+ uint32_t pointerId = entry->pointerIds[pointerIndex];
+ pointerIds.markBit(pointerId);
}
-
- // If the touched window is paused then keep waiting.
- if (newTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("Waiting because touched window is paused.");
-#endif
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, newTouchedWindow, nextWakeupTime);
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- goto Unresponsive;
- }
-
- // If the touched window is still working on previous events then keep waiting.
- if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
-#if DEBUG_FOCUS
- LOGD("Waiting because touched window still processing previous input.");
-#endif
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, newTouchedWindow, nextWakeupTime);
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- goto Unresponsive;
- }
-
- // Success! Update the touch dispatch state for real.
- releaseTouchedWindowLocked();
-
- mTouchedWindow = newTouchedWindow;
- mTouchedWindowIsObscured = obscured;
-
- if (newTouchedWindow->hasWallpaper) {
- mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
- }
+ mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
} else {
- /* Case 2: Everything but ACTION_DOWN */
-
- // Check permissions.
- if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- injectionPermission = INJECTION_PERMISSION_DENIED;
- goto Failed;
- }
+ /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
// If the pointer is not currently down, then ignore the event.
- if (! mTouchDown) {
+ if (! mTempTouchState.down) {
LOGI("Dropping event because the pointer is not down.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Failed;
}
+ }
- // If there is no currently touched window then fail.
- if (! mTouchedWindow) {
+ // Check permission to inject into all touched foreground windows and ensure there
+ // is at least one touched foreground window.
+ {
+ bool haveForegroundWindow = false;
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ haveForegroundWindow = true;
+ if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+ }
+ }
+ if (! haveForegroundWindow) {
#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("Dropping event because there is no touched window to receive it.");
+ LOGD("Dropping event because there is no touched foreground window to receive it.");
#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
goto Failed;
}
- // If the touched window is paused then keep waiting.
- if (mTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("Waiting because touched window is paused.");
-#endif
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, mTouchedWindow, nextWakeupTime);
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- goto Unresponsive;
- }
+ // Permission granted to injection into all touched foreground windows.
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ }
- // If the touched window is still working on previous events then keep waiting.
- if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
-#if DEBUG_FOCUS
- LOGD("Waiting because touched window still processing previous input.");
+ // Ensure all touched foreground windows are ready for new input.
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ // If the touched window is paused then keep waiting.
+ if (touchedWindow.window->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
#endif
- injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
- NULL, mTouchedWindow, nextWakeupTime);
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- goto Unresponsive;
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, touchedWindow.window, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ // If the touched window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because touched window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, touchedWindow.window, nextWakeupTime);
+ goto Unresponsive;
+ }
+ }
+ }
+
+ // If this is the first pointer going down and the touched window has a wallpaper
+ // then also add the touched wallpaper windows so they are locked in for the duration
+ // of the touch gesture.
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
+ if (foregroundWindow->hasWallpaper) {
+ 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));
+ }
+ }
+ }
+ }
+
+ // 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;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- {
- size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
- for (size_t i = 0; i < numWallpaperWindows; i++) {
- addWindowTargetLocked(mTouchedWallpaperWindows[i],
- InputTarget::FLAG_WINDOW_IS_OBSCURED);
- }
-
- size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
- for (size_t i = 0; i < numOutsideTargets; i++) {
- const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
- int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
- if (outsideTarget.obscured) {
- outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
- addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
- }
- mTempTouchedOutsideTargets.clear();
-
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
- if (mTouchedWindowIsObscured) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
- addWindowTargetLocked(mTouchedWindow, targetFlags);
- *outWindow = mTouchedWindow;
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
+ addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags,
+ touchedWindow.pointerIds);
}
+ // Drop the outside touch window since we will not care about them in the next iteration.
+ mTempTouchState.removeOutsideTouchWindows();
+
Failed:
// Check injection permission once and for all.
if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
- if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
- entry->injectorPid, entry->injectorUid)) {
+ if (checkInjectionPermission(NULL, entry->injectionState)) {
injectionPermission = INJECTION_PERMISSION_GRANTED;
} else {
injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1159,25 +1223,41 @@
// Update final pieces of touch state if the injector had permission.
if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- if (mTouchDown) {
- // This is weird. We got a down but we thought it was already down!
+ if (maskedAction == AMOTION_EVENT_ACTION_UP
+ || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // All pointers up or canceled.
+ mTempTouchState.reset();
+ } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ // First pointer went down.
+ if (mTouchState.down) {
LOGW("Pointer down received while already down.");
- } else {
- mTouchDown = true;
}
+ } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ // One pointer went up.
+ if (isSplit) {
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ uint32_t pointerId = entry->pointerIds[pointerIndex];
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- // Since we failed to identify a target for this touch down, we may still
- // be holding on to an earlier target from a previous touch down. Release it.
- releaseTouchedWindowLocked();
+ for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+ TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+ if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+ touchedWindow.pointerIds.clearBit(pointerId);
+ if (touchedWindow.pointerIds.isEmpty()) {
+ mTempTouchState.windows.removeAt(i);
+ continue;
+ }
+ }
+ i += 1;
+ }
}
- } else if (action == AMOTION_EVENT_ACTION_UP) {
- mTouchDown = false;
- releaseTouchedWindowLocked();
}
+
+ // Save changes to touch state.
+ mTouchState.copyFrom(mTempTouchState);
} else {
- LOGW("Not updating touch focus because injection was denied.");
+#if DEBUG_FOCUS
+ LOGD("Not updating touch focus because injection was denied.");
+#endif
}
Unresponsive:
@@ -1185,20 +1265,15 @@
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
- LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
- "timeSpendWaitingForApplication=%0.1fms",
+ LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+ "timeSpentWaitingForApplication=%0.1fms",
injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
#endif
return injectionResult;
}
-void InputDispatcher::releaseTouchedWindowLocked() {
- mTouchedWindow = NULL;
- mTouchedWindowIsObscured = false;
- mTouchedWallpaperWindows.clear();
-}
-
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+ BitSet32 pointerIds) {
mCurrentInputTargets.push();
InputTarget& target = mCurrentInputTargets.editTop();
@@ -1206,6 +1281,8 @@
target.flags = targetFlags;
target.xOffset = - window->frameLeft;
target.yOffset = - window->frameTop;
+ target.windowType = window->layoutParamsType;
+ target.pointerIds = pointerIds;
}
void InputDispatcher::addMonitoringTargetsLocked() {
@@ -1217,22 +1294,27 @@
target.flags = 0;
target.xOffset = 0;
target.yOffset = 0;
+ target.windowType = InputWindow::TYPE_SYSTEM_OVERLAY;
}
}
bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
- int32_t injectorPid, int32_t injectorUid) {
- if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
- bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+ const InjectionState* injectionState) {
+ if (injectionState
+ && injectionState->injectorUid > 0
+ && (window == NULL || window->ownerUid != injectionState->injectorUid)) {
+ bool result = mPolicy->checkInjectEventsPermissionNonReentrant(
+ injectionState->injectorPid, injectionState->injectorUid);
if (! result) {
if (window) {
LOGW("Permission denied: injecting event from pid %d uid %d to window "
"with input channel %s owned by uid %d",
- injectorPid, injectorUid, window->inputChannel->getName().string(),
+ injectionState->injectorPid, injectionState->injectorUid,
+ window->inputChannel->getName().string(),
window->ownerUid);
} else {
LOGW("Permission denied: injecting event from pid %d uid %d",
- injectorPid, injectorUid);
+ injectionState->injectorPid, injectionState->injectorUid);
}
return false;
}
@@ -1282,12 +1364,19 @@
}
}
-void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
- int32_t windowType, int32_t eventType) {
+bool InputDispatcher::shouldPokeUserActivityForCurrentInputTargetsLocked() {
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ if (mCurrentInputTargets[i].windowType == InputWindow::TYPE_KEYGUARD) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doPokeUserActivityLockedInterruptible);
commandEntry->eventTime = eventTime;
- commandEntry->windowType = windowType;
commandEntry->userActivityEventType = eventType;
}
@@ -1296,12 +1385,19 @@
bool resumeWithAppendedMotionSample) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
- "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
+ "xOffset=%f, yOffset=%f, "
+ "windowType=%d, pointerIds=0x%x, "
+ "resumeWithAppendedMotionSample=%s",
connection->getInputChannelName(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
+ inputTarget->windowType, inputTarget->pointerIds.value,
toString(resumeWithAppendedMotionSample));
#endif
+ // Make sure we are never called for streaming when splitting across multiple windows.
+ bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
+ assert(! (resumeWithAppendedMotionSample && isSplit));
+
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
if (connection->status != Connection::STATUS_NORMAL) {
@@ -1310,6 +1406,23 @@
return;
}
+ // Split a motion event if needed.
+ if (isSplit) {
+ assert(eventEntry->type == EventEntry::TYPE_MOTION);
+
+ MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
+ if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+ MotionEntry* splitMotionEntry = splitMotionEvent(
+ originalMotionEntry, inputTarget->pointerIds);
+#if DEBUG_FOCUS
+ LOGD("channel '%s' ~ Split motion event.",
+ connection->getInputChannelName());
+ logOutboundMotionDetailsLocked(" ", splitMotionEntry);
+#endif
+ eventEntry = splitMotionEntry;
+ }
+ }
+
// Resume the dispatch cycle with a freshly appended motion sample.
// First we check that the last dispatch entry in the outbound queue is for the same
// motion event to which we appended the motion sample. If we find such a dispatch
@@ -1351,7 +1464,8 @@
// The dispatch entry is in progress and is still potentially open for streaming.
// Try to stream the new motion sample. This might fail if the consumer has already
// consumed the motion event (or if the channel is broken).
- MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ MotionSample* appendedMotionSample = motionEntry->lastSample;
status_t status = connection->inputPublisher.appendMotionSample(
appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
if (status == OK) {
@@ -1426,7 +1540,7 @@
DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
if (dispatchEntry->hasForegroundTarget()) {
- eventEntry->pendingForegroundDispatches += 1;
+ incrementPendingForegroundDispatchesLocked(eventEntry);
}
// Handle the case where we could not stream a new motion sample because the consumer has
@@ -1470,8 +1584,8 @@
dispatchEntry->inProgress = true;
// Update the connection's input state.
- InputState::Consistency consistency = connection->inputState.trackEvent(
- dispatchEntry->eventEntry);
+ EventEntry* eventEntry = dispatchEntry->eventEntry;
+ InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry);
#if FILTER_INPUT_EVENTS
// Filter out inconsistent sequences of input events.
@@ -1497,16 +1611,13 @@
// Publish the event.
status_t status;
- switch (dispatchEntry->eventEntry->type) {
+ switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Apply target flags.
int32_t action = keyEntry->action;
int32_t flags = keyEntry->flags;
- if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
- flags |= AKEY_EVENT_FLAG_CANCELED;
- }
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
@@ -1524,7 +1635,7 @@
}
case EventEntry::TYPE_MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
// Apply target flags.
int32_t action = motionEntry->action;
@@ -1532,9 +1643,6 @@
if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
action = AMOTION_EVENT_ACTION_OUTSIDE;
}
- if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
- action = AMOTION_EVENT_ACTION_CANCEL;
- }
if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
}
@@ -1617,7 +1725,7 @@
}
// Record information about the newly started dispatch cycle.
- connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
+ connection->lastEventTime = eventEntry->eventTime;
connection->lastDispatchTime = currentTime;
// Notify other system components.
@@ -1773,6 +1881,86 @@
} // release lock
}
+InputDispatcher::MotionEntry*
+InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+ assert(pointerIds.value != 0);
+
+ uint32_t splitPointerIndexMap[MAX_POINTERS];
+ int32_t splitPointerIds[MAX_POINTERS];
+ PointerCoords splitPointerCoords[MAX_POINTERS];
+
+ uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+ uint32_t splitPointerCount = 0;
+
+ for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
+ originalPointerIndex++) {
+ int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]);
+ if (pointerIds.hasBit(pointerId)) {
+ splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
+ splitPointerIds[splitPointerCount] = pointerId;
+ splitPointerCoords[splitPointerCount] =
+ originalMotionEntry->firstSample.pointerCoords[originalPointerIndex];
+ splitPointerCount += 1;
+ }
+ }
+ assert(splitPointerCount == pointerIds.count());
+
+ int32_t action = originalMotionEntry->action;
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+ || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+ int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex];
+ if (pointerIds.hasBit(pointerId)) {
+ if (pointerIds.count() == 1) {
+ // The first/last pointer went down/up.
+ action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+ ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ } else {
+ // A secondary pointer went down/up.
+ uint32_t splitPointerIndex = 0;
+ while (pointerId != splitPointerIds[splitPointerIndex]) {
+ splitPointerIndex += 1;
+ }
+ action = maskedAction | (splitPointerIndex
+ << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ }
+ } else {
+ // An unrelated pointer changed.
+ action = AMOTION_EVENT_ACTION_MOVE;
+ }
+ }
+
+ MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry(
+ originalMotionEntry->eventTime,
+ originalMotionEntry->deviceId,
+ originalMotionEntry->source,
+ originalMotionEntry->policyFlags,
+ action,
+ originalMotionEntry->flags,
+ originalMotionEntry->metaState,
+ originalMotionEntry->edgeFlags,
+ originalMotionEntry->xPrecision,
+ originalMotionEntry->yPrecision,
+ originalMotionEntry->downTime,
+ splitPointerCount, splitPointerIds, splitPointerCoords);
+
+ for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next;
+ originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) {
+ for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
+ splitPointerIndex++) {
+ uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
+ splitPointerCoords[splitPointerIndex] =
+ originalMotionSample->pointerCoords[originalPointerIndex];
+ }
+
+ mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime,
+ splitPointerCoords);
+ }
+
+ return splitMotionEntry;
+}
+
void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
#if DEBUG_INBOUND_EVENT_DETAILS
LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
@@ -1800,6 +1988,9 @@
eventTime, deviceId, source, policyFlags, action, flags,
keyCode, scanCode, metaState, downTime);
#endif
+ if (! validateKeyEvent(action)) {
+ return;
+ }
bool needWake;
{ // acquire lock
@@ -1839,6 +2030,9 @@
pointerCoords[i].orientation);
}
#endif
+ if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+ return;
+ }
bool needWake;
{ // acquire lock
@@ -1913,8 +2107,10 @@
DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
if (! dispatchEntry->inProgress
- || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
- // No motion event is being dispatched.
+ || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
+ || dispatchEntry->isSplit()) {
+ // No motion event is being dispatched, or it is being split across
+ // windows in which case we cannot stream.
continue;
}
@@ -1972,24 +2168,24 @@
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
- EventEntry* injectedEntry;
+ InjectionState* injectionState;
bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
- injectedEntry = createEntryFromInjectedInputEventLocked(event);
+ EventEntry* injectedEntry = createEntryFromInjectedInputEventLocked(event);
if (! injectedEntry) {
return INPUT_EVENT_INJECTION_FAILED;
}
- injectedEntry->refCount += 1;
- injectedEntry->injectorPid = injectorPid;
- injectedEntry->injectorUid = injectorUid;
-
+ injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid);
if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
- injectedEntry->injectionIsAsync = true;
+ injectionState->injectionIsAsync = true;
}
+ injectionState->refCount += 1;
+ injectedEntry->injectionState = injectionState;
+
needWake = enqueueInboundEventLocked(injectedEntry);
} // release lock
@@ -2005,7 +2201,7 @@
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
} else {
for (;;) {
- injectionResult = injectedEntry->injectionResult;
+ injectionResult = injectionState->injectionResult;
if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
break;
}
@@ -2025,10 +2221,10 @@
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
&& syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
- while (injectedEntry->pendingForegroundDispatches != 0) {
+ while (injectionState->pendingForegroundDispatches != 0) {
#if DEBUG_INJECTION
LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
- injectedEntry->pendingForegroundDispatches);
+ injectionState->pendingForegroundDispatches);
#endif
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
@@ -2045,7 +2241,7 @@
}
}
- mAllocator.releaseEventEntry(injectedEntry);
+ mAllocator.releaseInjectionState(injectionState);
} // release lock
#if DEBUG_INJECTION
@@ -2058,14 +2254,15 @@
}
void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
- if (entry->isInjected()) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState) {
#if DEBUG_INJECTION
LOGD("Setting input event injection result to %d. "
"injectorPid=%d, injectorUid=%d",
- injectionResult, entry->injectorPid, entry->injectorUid);
+ injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif
- if (entry->injectionIsAsync) {
+ if (injectionState->injectionIsAsync) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2083,41 +2280,26 @@
}
}
- entry->injectionResult = injectionResult;
+ injectionState->injectionResult = injectionResult;
mInjectionResultAvailableCondition.broadcast();
}
}
+void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState) {
+ injectionState->pendingForegroundDispatches += 1;
+ }
+}
+
void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
- entry->pendingForegroundDispatches -= 1;
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState) {
+ injectionState->pendingForegroundDispatches -= 1;
- if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
- mInjectionSyncFinishedCondition.broadcast();
- }
-}
-
-static bool isValidKeyAction(int32_t action) {
- switch (action) {
- case AKEY_EVENT_ACTION_DOWN:
- case AKEY_EVENT_ACTION_UP:
- return true;
- default:
- return false;
- }
-}
-
-static bool isValidMotionAction(int32_t action) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN:
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
- case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- case AMOTION_EVENT_ACTION_POINTER_UP:
- case AMOTION_EVENT_ACTION_OUTSIDE:
- return true;
- default:
- return false;
+ if (injectionState->pendingForegroundDispatches == 0) {
+ mInjectionSyncFinishedCondition.broadcast();
+ }
}
}
@@ -2126,9 +2308,7 @@
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
- if (! isValidKeyAction(keyEvent->getAction())) {
- LOGE("Dropping injected key event since it has invalid action code 0x%x",
- keyEvent->getAction());
+ if (! validateKeyEvent(keyEvent->getAction())) {
return NULL;
}
@@ -2144,16 +2324,10 @@
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
- if (! isValidMotionAction(motionEvent->getAction())) {
- LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
- motionEvent->getAction());
+ if (! validateMotionEvent(motionEvent->getAction(),
+ motionEvent->getPointerCount(), motionEvent->getPointerIds())) {
return NULL;
}
- if (motionEvent->getPointerCount() == 0
- || motionEvent->getPointerCount() > MAX_POINTERS) {
- LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
- motionEvent->getPointerCount());
- }
uint32_t policyFlags = POLICY_FLAG_INJECTED;
@@ -2182,6 +2356,16 @@
}
}
+const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) {
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ const InputWindow* window = & mWindows[i];
+ if (window->inputChannel == inputChannel) {
+ return window;
+ }
+ }
+ return NULL;
+}
+
void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
#if DEBUG_FOCUS
LOGD("setInputWindows");
@@ -2189,22 +2373,8 @@
{ // acquire lock
AutoMutex _l(mLock);
- // Clear old window pointers but remember their associated channels.
+ // Clear old window pointers.
mFocusedWindow = NULL;
-
- sp<InputChannel> touchedWindowChannel;
- if (mTouchedWindow) {
- touchedWindowChannel = mTouchedWindow->inputChannel;
- mTouchedWindow = NULL;
- }
- size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
- if (numTouchedWallpapers != 0) {
- for (size_t i = 0; i < numTouchedWallpapers; i++) {
- mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
- }
- mTouchedWallpaperWindows.clear();
- }
- mWallpaperWindows.clear();
mWindows.clear();
// Loop over new windows and rebuild the necessary window pointers for
@@ -2213,26 +2383,23 @@
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
- InputWindow* window = & mWindows.editItemAt(i);
+ const InputWindow* window = & mWindows.itemAt(i);
if (window->hasFocus) {
mFocusedWindow = window;
- }
-
- if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
- mWallpaperWindows.push(window);
-
- for (size_t j = 0; j < numTouchedWallpapers; j++) {
- if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
- mTouchedWallpaperWindows.push(window);
- }
- }
- }
-
- if (window->inputChannel == touchedWindowChannel) {
- mTouchedWindow = window;
+ break;
}
}
- mTempTouchedWallpaperChannels.clear();
+
+ for (size_t i = 0; i < mTouchState.windows.size(); ) {
+ TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
+ const InputWindow* window = getWindowLocked(touchedWindow.channel);
+ if (window) {
+ touchedWindow.window = window;
+ i += 1;
+ } else {
+ mTouchState.windows.removeAt(i);
+ }
+ }
#if DEBUG_FOCUS
logDispatchStateLocked();
@@ -2334,12 +2501,13 @@
}
dump.appendFormat(" focusedWindow: name='%s'\n",
mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
- dump.appendFormat(" touchedWindow: name='%s', touchDown=%d\n",
- mTouchedWindow != NULL ? mTouchedWindow->name.string() : "<null>",
- mTouchDown);
- for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
- dump.appendFormat(" touchedWallpaperWindows[%d]: name='%s'\n",
- i, mTouchedWallpaperWindows[i]->name.string());
+ 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, "
@@ -2594,8 +2762,7 @@
void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
mLock.unlock();
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
- commandEntry->userActivityEventType);
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
mLock.lock();
}
@@ -2627,17 +2794,32 @@
InputDispatcher::Allocator::Allocator() {
}
+InputDispatcher::InjectionState*
+InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) {
+ InjectionState* injectionState = mInjectionStatePool.alloc();
+ injectionState->refCount = 1;
+ injectionState->injectorPid = injectorPid;
+ injectionState->injectorUid = injectorUid;
+ injectionState->injectionIsAsync = false;
+ injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ injectionState->pendingForegroundDispatches = 0;
+ return injectionState;
+}
+
void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
nsecs_t eventTime) {
entry->type = type;
entry->refCount = 1;
entry->dispatchInProgress = false;
entry->eventTime = eventTime;
- entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
- entry->injectionIsAsync = false;
- entry->injectorPid = -1;
- entry->injectorUid = -1;
- entry->pendingForegroundDispatches = 0;
+ entry->injectionState = NULL;
+}
+
+void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) {
+ if (entry->injectionState) {
+ releaseInjectionState(entry->injectionState);
+ entry->injectionState = NULL;
+ }
}
InputDispatcher::ConfigurationChangedEntry*
@@ -2720,6 +2902,15 @@
return entry;
}
+void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) {
+ injectionState->refCount -= 1;
+ if (injectionState->refCount == 0) {
+ mInjectionStatePool.free(injectionState);
+ } else {
+ assert(injectionState->refCount > 0);
+ }
+}
+
void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
switch (entry->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED:
@@ -2741,6 +2932,7 @@
ConfigurationChangedEntry* entry) {
entry->refCount -= 1;
if (entry->refCount == 0) {
+ releaseEventEntryInjectionState(entry);
mConfigurationChangeEntryPool.free(entry);
} else {
assert(entry->refCount > 0);
@@ -2750,6 +2942,7 @@
void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
entry->refCount -= 1;
if (entry->refCount == 0) {
+ releaseEventEntryInjectionState(entry);
mKeyEntryPool.free(entry);
} else {
assert(entry->refCount > 0);
@@ -2759,6 +2952,7 @@
void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
entry->refCount -= 1;
if (entry->refCount == 0) {
+ releaseEventEntryInjectionState(entry);
for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
MotionSample* next = sample->next;
mMotionSamplePool.free(sample);
@@ -2793,22 +2987,12 @@
motionEntry->lastSample = sample;
}
+void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) {
+ releaseEventEntryInjectionState(keyEntry);
-// --- InputDispatcher::EventEntry ---
-
-void InputDispatcher::EventEntry::recycle() {
- injectionResult = INPUT_EVENT_INJECTION_PENDING;
- dispatchInProgress = false;
- pendingForegroundDispatches = 0;
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-void InputDispatcher::KeyEntry::recycle() {
- EventEntry::recycle();
- syntheticRepeat = false;
- interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+ keyEntry->dispatchInProgress = false;
+ keyEntry->syntheticRepeat = false;
+ keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
}
@@ -3057,6 +3241,72 @@
}
+// --- InputDispatcher::TouchState ---
+
+InputDispatcher::TouchState::TouchState() :
+ down(false), split(false) {
+}
+
+InputDispatcher::TouchState::~TouchState() {
+}
+
+void InputDispatcher::TouchState::reset() {
+ down = false;
+ split = false;
+ windows.clear();
+}
+
+void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
+ down = other.down;
+ split = other.split;
+ windows.clear();
+ windows.appendVector(other.windows);
+}
+
+void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
+ int32_t targetFlags, BitSet32 pointerIds) {
+ if (targetFlags & InputTarget::FLAG_SPLIT) {
+ split = true;
+ }
+
+ for (size_t i = 0; i < windows.size(); i++) {
+ TouchedWindow& touchedWindow = windows.editItemAt(i);
+ if (touchedWindow.window == window) {
+ touchedWindow.targetFlags |= targetFlags;
+ touchedWindow.pointerIds.value |= pointerIds.value;
+ return;
+ }
+ }
+
+ windows.push();
+
+ TouchedWindow& touchedWindow = windows.editTop();
+ touchedWindow.window = window;
+ touchedWindow.targetFlags = targetFlags;
+ touchedWindow.pointerIds = pointerIds;
+ touchedWindow.channel = window->inputChannel;
+}
+
+void InputDispatcher::TouchState::removeOutsideTouchWindows() {
+ for (size_t i = 0 ; i < windows.size(); ) {
+ if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) {
+ windows.removeAt(i);
+ } else {
+ i += 1;
+ }
+ }
+}
+
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+ for (size_t i = 0; i < windows.size(); i++) {
+ if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
+ return windows[i].window;
+ }
+ }
+ return NULL;
+}
+
+
// --- InputDispatcherThread ---
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 783cbc4..f2b029a 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -3366,7 +3366,7 @@
if (id > MAX_POINTER_ID) {
#if DEBUG_POINTERS
LOGD("Pointers: Ignoring driver provided pointer id %d because "
- "it is larger than max supported id %d for optimizations",
+ "it is larger than max supported id %d",
id, MAX_POINTER_ID);
#endif
havePointerIds = false;
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index b46279e..d2dd6eb 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -24,16 +24,15 @@
namespace android {
-static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
-static bool gHaveTLS = false;
-static pthread_key_t gTLS = 0;
-
// 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;
+static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
+static pthread_key_t gTLSKey = 0;
+
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks),
mResponseIndex(0) {
@@ -56,6 +55,7 @@
errno);
struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
@@ -69,6 +69,11 @@
close(mEpollFd);
}
+void Looper::initTLSKey() {
+ int result = pthread_key_create(& gTLSKey, threadDestructor);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+}
+
void Looper::threadDestructor(void *st) {
Looper* const self = static_cast<Looper*>(st);
if (self != NULL) {
@@ -83,7 +88,7 @@
looper->incStrong((void*)threadDestructor);
}
- pthread_setspecific(gTLS, looper.get());
+ pthread_setspecific(gTLSKey, looper.get());
if (old != NULL) {
old->decStrong((void*)threadDestructor);
@@ -91,17 +96,10 @@
}
sp<Looper> Looper::getForThread() {
- if (!gHaveTLS) {
- pthread_mutex_lock(&gTLSMutex);
- if (pthread_key_create(&gTLS, threadDestructor) != 0) {
- pthread_mutex_unlock(&gTLSMutex);
- return NULL;
- }
- gHaveTLS = true;
- pthread_mutex_unlock(&gTLSMutex);
- }
+ int result = pthread_once(& gTLSOnce, initTLSKey);
+ LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
- return (Looper*)pthread_getspecific(gTLS);
+ return (Looper*)pthread_getspecific(gTLSKey);
}
sp<Looper> Looper::prepare(int opts) {
@@ -331,6 +329,7 @@
request.data = data;
struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 2d53136..9fcae72 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -22,6 +22,7 @@
#include <utils/ZipFileRO.h>
#include <utils/Log.h>
#include <utils/misc.h>
+#include <utils/threads.h>
#include <zlib.h>
@@ -195,7 +196,7 @@
free(scanBuf);
return false;
} else if (header != kLFHSignature) {
- LOGV("Not a Zip archive (found 0x%08x)\n", val);
+ LOGV("Not a Zip archive (found 0x%08x)\n", header);
free(scanBuf);
return false;
}
@@ -496,15 +497,21 @@
}
unsigned char lfhBuf[kLFHLen];
- if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
- LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
- return false;
- }
- ssize_t actual =
- TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
- if (actual != sizeof(lfhBuf)) {
- LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
- return false;
+
+ {
+ AutoMutex _l(mFdLock);
+
+ if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+ LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ ssize_t actual =
+ 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) {
@@ -636,7 +643,7 @@
memcpy(buffer, ptr, uncompLen);
} else {
if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
- goto bail;
+ goto unmap;
}
if (compLen > kSequentialMin)
@@ -644,6 +651,8 @@
result = true;
+unmap:
+ file->release();
bail:
return result;
}
@@ -667,7 +676,7 @@
getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
- const FileMap* file = createEntryFileMap(entry);
+ FileMap* file = createEntryFileMap(entry);
if (file == NULL) {
goto bail;
}
@@ -678,21 +687,23 @@
ssize_t actual = write(fd, ptr, uncompLen);
if (actual < 0) {
LOGE("Write failed: %s\n", strerror(errno));
- goto bail;
+ goto unmap;
} else if ((size_t) actual != uncompLen) {
LOGE("Partial write during uncompress (%zd of %zd)\n",
(size_t)actual, (size_t)uncompLen);
- goto bail;
+ goto unmap;
} else {
LOGI("+++ successful write\n");
}
} else {
if (!inflateBuffer(fd, ptr, uncompLen, compLen))
- goto bail;
+ goto unmap;
}
result = true;
+unmap:
+ file->release();
bail:
return result;
}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 5d6ac26..747c829 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -136,30 +136,24 @@
*/
void* dso;
- char path[PATH_MAX];
int index = int(display);
driver_t* hnd = 0;
- const char* const format = "/system/lib/egl/lib%s_%s.so";
char const* tag = getTag(index, impl);
if (tag) {
- snprintf(path, PATH_MAX, format, "GLES", tag);
- dso = load_driver(path, cnx, EGL | GLESv1_CM | GLESv2);
+ dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2);
if (dso) {
hnd = new driver_t(dso);
} else {
// Always load EGL first
- snprintf(path, PATH_MAX, format, "EGL", tag);
- dso = load_driver(path, cnx, EGL);
+ dso = load_driver("EGL", tag, cnx, EGL);
if (dso) {
hnd = new driver_t(dso);
// TODO: make this more automated
- snprintf(path, PATH_MAX, format, "GLESv1_CM", tag);
- hnd->set( load_driver(path, cnx, GLESv1_CM), GLESv1_CM );
+ hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM );
- snprintf(path, PATH_MAX, format, "GLESv2", tag);
- hnd->set( load_driver(path, cnx, GLESv2), GLESv2 );
+ hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 );
}
}
}
@@ -222,12 +216,20 @@
}
}
-void *Loader::load_driver(const char* driver_absolute_path,
+void *Loader::load_driver(const char* kind, const char *tag,
egl_connection_t* cnx, uint32_t mask)
{
+ char driver_absolute_path[PATH_MAX];
+ const char* const search1 = "/vendor/lib/egl/lib%s_%s.so";
+ const char* const search2 = "/system/lib/egl/lib%s_%s.so";
+
+ snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag);
if (access(driver_absolute_path, R_OK)) {
- // this happens often, we don't want to log an error
- return 0;
+ snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag);
+ if (access(driver_absolute_path, R_OK)) {
+ // this happens often, we don't want to log an error
+ return 0;
+ }
}
void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 8659b0b..580d6e4 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -74,7 +74,7 @@
private:
Loader();
- void *load_driver(const char* driver, egl_connection_t* cnx, uint32_t mask);
+ void *load_driver(const char* kind, const char *tag, egl_connection_t* cnx, uint32_t mask);
static __attribute__((noinline))
void init_api(void* dso,
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index df21358..2d1a278 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -428,13 +428,14 @@
// ----------------------------------------------------------------------------
-static void gl_no_context() {
+static int gl_no_context() {
tls_t* tls = getTLS();
if (tls->logCallWithNoContext == EGL_TRUE) {
tls->logCallWithNoContext = EGL_FALSE;
LOGE("call to OpenGL ES API with no current context "
"(logged once per thread)");
}
+ return 0;
}
static void early_egl_init(void)
@@ -447,6 +448,7 @@
(uint32_t*)(void*)&gHooksNoContext,
addr,
sizeof(gHooksNoContext));
+
setGlThreadSpecific(&gHooksNoContext);
}
@@ -1344,16 +1346,18 @@
EGLint eglGetError(void)
{
EGLint result = EGL_SUCCESS;
+ EGLint err;
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
- EGLint err = EGL_SUCCESS;
+ err = EGL_SUCCESS;
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso)
err = cnx->egl.eglGetError();
if (err!=EGL_SUCCESS && result==EGL_SUCCESS)
result = err;
}
+ err = getError();
if (result == EGL_SUCCESS)
- result = getError();
+ result = err;
return result;
}
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 924737e..18dd483 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -58,6 +58,7 @@
"ldr r12, [r12, %[tls]] \n" \
"cmp r12, #0 \n" \
"ldrne pc, [r12, %[api]] \n" \
+ "mov r0, #0 \n" \
"bx lr \n" \
: \
: [tls] "J"(TLS_SLOT_OPENGL_API*4), \
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index d71ff76..ee29f12 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -114,6 +114,7 @@
"ldr r12, [r12, %[tls]] \n" \
"cmp r12, #0 \n" \
"ldrne pc, [r12, %[api]] \n" \
+ "mov r0, #0 \n" \
"bx lr \n" \
: \
: [tls] "J"(TLS_SLOT_OPENGL_API*4), \
diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp
index 850866a..493122d 100644
--- a/services/surfaceflinger/GLExtensions.cpp
+++ b/services/surfaceflinger/GLExtensions.cpp
@@ -92,6 +92,10 @@
// hack for Adreno 200
mHaveTextureExternal = true;
}
+
+ if (hasExtension("GL_OES_framebuffer_object")) {
+ mHaveFramebufferObject = true;
+ }
}
bool GLExtensions::hasExtension(char const* extension) const
diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h
index bbb284e..c86c66a 100644
--- a/services/surfaceflinger/GLExtensions.h
+++ b/services/surfaceflinger/GLExtensions.h
@@ -39,6 +39,7 @@
bool mHaveTextureExternal : 1;
bool mHaveNpot : 1;
bool mHaveDirectTexture : 1;
+ bool mHaveFramebufferObject : 1;
String8 mVendor;
String8 mRenderer;
@@ -66,6 +67,10 @@
return mHaveDirectTexture;
}
+ inline bool haveFramebufferObject() const {
+ return mHaveFramebufferObject;
+ }
+
void initWithGLStrings(
GLubyte const* vendor,
GLubyte const* renderer,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc241d4..2b06f6f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -75,6 +75,7 @@
mBootTime(systemTime()),
mHardwareTest("android.permission.HARDWARE_TEST"),
mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
+ mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
mDump("android.permission.DUMP"),
mVisibleRegionsDirty(false),
mDeferReleaseConsole(false),
@@ -1465,8 +1466,23 @@
"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
+ break;
+ }
+ case CAPTURE_SCREEN:
+ {
+ // codes that require permission check
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
+ LOGE("Permission Denial: "
+ "can't read framebuffer pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ break;
}
}
+
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1530,6 +1546,139 @@
// ---------------------------------------------------------------------------
+status_t SurfaceFlinger::captureScreen(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width, uint32_t* height, PixelFormat* format)
+{
+ // only one display supported for now
+ if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ return BAD_VALUE;
+
+ if (!GLExtensions::getInstance().haveFramebufferObject())
+ return INVALID_OPERATION;
+
+ class MessageCaptureScreen : public MessageBase {
+ SurfaceFlinger* flinger;
+ DisplayID dpy;
+ sp<IMemoryHeap>* heap;
+ uint32_t* w;
+ uint32_t* h;
+ PixelFormat* f;
+ status_t result;
+ public:
+ MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
+ sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+ : flinger(flinger), dpy(dpy),
+ heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+ {
+ }
+ status_t getResult() const {
+ return result;
+ }
+ virtual bool handler() {
+ Mutex::Autolock _l(flinger->mStateLock);
+
+ // if we have secure windows, never allow the screen capture
+ if (flinger->mSecureFrameBuffer)
+ return true;
+
+ // make sure to clear all GL error flags
+ while ( glGetError() != GL_NO_ERROR ) ;
+
+ // 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);
+ status_t res = postMessageSync(msg);
+ if (res == NO_ERROR) {
+ res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
{
sp<Layer> result;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8ecfc01..f09fdbc 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -193,6 +193,11 @@
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
virtual void signal() const;
+ virtual status_t captureScreen(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width,
+ uint32_t* height,
+ PixelFormat* format);
void screenReleased(DisplayID dpy);
void screenAcquired(DisplayID dpy);
@@ -361,6 +366,7 @@
nsecs_t mBootTime;
Permission mHardwareTest;
Permission mAccessSurfaceFlinger;
+ Permission mReadFramebuffer;
Permission mDump;
// Can only accessed from the main thread, these members
diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk
new file mode 100644
index 0000000..1cfb471
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ screencap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libskia \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-screencap
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES += \
+ external/skia/include/core \
+ external/skia/include/effects \
+ external/skia/include/images \
+ external/skia/src/ports \
+ external/skia/include/utils
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
new file mode 100644
index 0000000..9e893f4
--- /dev/null
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <binder/IMemory.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkImageEncoder.h>
+#include <SkBitmap.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ printf("usage: %s path\n", argv[0]);
+ exit(0);
+ }
+
+ const String16 name("SurfaceFlinger");
+ sp<ISurfaceComposer> composer;
+ getService(name, &composer);
+
+ sp<IMemoryHeap> heap;
+ uint32_t w, h;
+ PixelFormat f;
+ status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
+ exit(0);
+ }
+
+ printf("screen capture success: w=%u, h=%u, pixels=%p\n",
+ w, h, heap->getBase());
+
+ printf("saving file as PNG in %s ...\n", argv[1]);
+
+ SkBitmap b;
+ b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ b.setPixels(heap->getBase());
+ SkImageEncoder::EncodeFile(argv[1], b,
+ SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
+
+ return 0;
+}