Merge "Fix volume problems with insert revert" into gingerbread
diff --git a/api/9.xml b/api/9.xml
index b266352..9ff1287 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -167793,16 +167793,6 @@
visibility="public"
>
</method>
-<field name="mDeviceId"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
</class>
<class name="KeyCharacterMap"
extends="java.lang.Object"
diff --git a/api/current.xml b/api/current.xml
index fbdf02a..d2e1f05 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -46618,6 +46618,16 @@
visibility="public"
>
</field>
+<field name="nativeLibraryDir"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="permission"
type="java.lang.String"
transient="false"
@@ -96627,6 +96637,19 @@
<parameter name="title" type="java.lang.String">
</parameter>
</method>
+<method name="setVisibleInDownloadsUi"
+ return="android.net.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isVisible" type="boolean">
+</parameter>
+</method>
<field name="NETWORK_MOBILE"
type="int"
transient="false"
@@ -177727,7 +177750,7 @@
native="false"
synchronized="false"
static="false"
- final="true"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -177775,26 +177798,6 @@
visibility="public"
>
</field>
-<field name="mDeviceId"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
-<field name="mSource"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
</class>
<class name="InputQueue"
extends="java.lang.Object"
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index cf24433..0db954d 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -355,8 +355,6 @@
/**
* Full path to the directory where native JNI libraries are stored.
- *
- * {@hide}
*/
public String nativeLibraryDir;
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e04367a..e8237c9 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -276,6 +276,7 @@
private String mMediaType;
private boolean mRoamingAllowed = true;
private int mAllowedNetworkTypes = ~0; // default to all network types allowed
+ private boolean mIsVisibleInDownloadsUi = true;
/**
* @param uri the HTTP URI to download.
@@ -387,6 +388,17 @@
}
/**
+ * Set whether this download should be displayed in the system's Downloads UI. True by
+ * default.
+ * @param isVisible whether to display this download in the Downloads UI
+ * @return this object
+ */
+ public Request setVisibleInDownloadsUi(boolean isVisible) {
+ mIsVisibleInDownloadsUi = isVisible;
+ return this;
+ }
+
+ /**
* @return ContentValues to be passed to DownloadProvider.insert()
*/
ContentValues toContentValues(String packageName) {
@@ -418,6 +430,7 @@
values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
+ values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
return values;
}
@@ -458,6 +471,7 @@
private Integer mStatusFlags = null;
private String mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
private int mOrderDirection = ORDER_DESCENDING;
+ private boolean mOnlyIncludeVisibleInDownloadsUi = false;
/**
* Include only the download with the given ID.
@@ -479,6 +493,19 @@
}
/**
+ * Controls whether this query includes downloads not visible in the system's Downloads UI.
+ * @param value if true, this query will only include downloads that should be displayed in
+ * the system's Downloads UI; if false (the default), this query will include
+ * both visible and invisible downloads.
+ * @return this object
+ * @hide
+ */
+ public Query setOnlyIncludeVisibleInDownloadsUi(boolean value) {
+ mOnlyIncludeVisibleInDownloadsUi = value;
+ return this;
+ }
+
+ /**
* Change the sort order of the returned Cursor.
*
* @param column one of the COLUMN_* constants; currently, only
@@ -511,7 +538,7 @@
*/
Cursor runQuery(ContentResolver resolver, String[] projection) {
Uri uri = Downloads.CONTENT_URI;
- String selection = null;
+ List<String> selectionParts = new ArrayList<String>();
if (mId != null) {
uri = Uri.withAppendedPath(uri, mId.toString());
@@ -536,9 +563,14 @@
parts.add("(" + statusClause(">=", 400)
+ " AND " + statusClause("<", 600) + ")");
}
- selection = joinStrings(" OR ", parts);
+ selectionParts.add(joinStrings(" OR ", parts));
}
+ if (mOnlyIncludeVisibleInDownloadsUi) {
+ selectionParts.add(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI + " != '0'");
+ }
+
+ String selection = joinStrings(" AND ", selectionParts);
String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
String orderBy = mOrderByColumn + " " + orderDirection;
@@ -628,6 +660,34 @@
}
/**
+ * Restart the given download, which must have already completed (successfully or not). This
+ * method will only work when called from within the download manager's process.
+ * @param id the ID of the download
+ * @hide
+ */
+ public void restartDownload(long id) {
+ Cursor cursor = query(new Query().setFilterById(id));
+ try {
+ if (!cursor.moveToFirst()) {
+ throw new IllegalArgumentException("No download with id " + id);
+ }
+ int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
+ if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
+ throw new IllegalArgumentException("Cannot restart incomplete download: " + id);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);
+ values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
+ values.putNull(Downloads.Impl._DATA);
+ values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
+ mResolver.update(getDownloadUri(id), values, null, null);
+ }
+
+ /**
* Get the DownloadProvider URI for the download with the given ID.
*/
private Uri getDownloadUri(long id) {
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index adb11c8..2813863 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -224,8 +224,8 @@
msg.next = prev.next;
prev.next = msg;
}
- nativeWake();
}
+ nativeWake();
return true;
}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 6bf0d5b..603e598 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -880,6 +880,14 @@
*/
public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
+ /**
+ * Whether or not this download should be displayed in the system's Downloads UI. Defaults
+ * to true.
+ * <P>Type: INTEGER</P>
+ * <P>Owner can Init/Read</P>
+ */
+ public static final String COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI = "is_visible_in_downloads_ui";
+
/*
* Lists the destinations that an application can specify for a download.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f37ef99..fd60115 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3481,13 +3481,7 @@
*/
public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED);
- if (allowedProviders != null) {
- return (allowedProviders.equals(provider) ||
- allowedProviders.contains("," + provider + ",") ||
- allowedProviders.startsWith(provider + ",") ||
- allowedProviders.endsWith("," + provider));
- }
- return false;
+ return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
}
/**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 9589bf3..8675d05 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1651,7 +1651,36 @@
return mode;
}
-
+
+ /**
+ * Does a comma-delimited list 'delimitedString' contain a certain item?
+ * (without allocating memory)
+ *
+ * @hide
+ */
+ public static boolean delimitedStringContains(
+ String delimitedString, char delimiter, String item) {
+ if (isEmpty(delimitedString) || isEmpty(item)) {
+ return false;
+ }
+ int pos = -1;
+ int length = delimitedString.length();
+ while ((pos = delimitedString.indexOf(item, pos + 1)) != -1) {
+ if (pos > 0 && delimitedString.charAt(pos - 1) != delimiter) {
+ continue;
+ }
+ int expectedDelimiterPos = pos + item.length();
+ if (expectedDelimiterPos == length) {
+ // Match at end of string.
+ return true;
+ }
+ if (delimitedString.charAt(expectedDelimiterPos) == delimiter) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static Object sLock = new Object();
private static char[] sTemp = null;
}
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 9afd16e..184e0fc 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -23,7 +23,9 @@
* Common base class for input events.
*/
public abstract class InputEvent implements Parcelable {
+ /** @hide */
protected int mDeviceId;
+ /** @hide */
protected int mSource;
/** @hide */
@@ -76,7 +78,7 @@
mSource = source;
}
- public final int describeContents() {
+ public int describeContents() {
return 0;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c01068f..4bb11bb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -463,6 +463,11 @@
private static final int TOUCH_DONE_MODE = 7;
private static final int TOUCH_PINCH_DRAG = 8;
+ /**
+ * True if we have a touch panel capable of detecting smooth pan/scale at the same time
+ */
+ private boolean mAllowPanAndScale;
+
// Whether to forward the touch events to WebCore
private boolean mForwardTouchEvents = false;
@@ -976,9 +981,11 @@
void updateMultiTouchSupport(Context context) {
WebSettings settings = getSettings();
- mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+ final PackageManager pm = context.getPackageManager();
+ mSupportMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
&& settings.supportZoom() && settings.getBuiltInZoomControls();
+ mAllowPanAndScale = pm.hasSystemFeature(
+ PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
if (mSupportMultiTouch && (mScaleDetector == null)) {
mScaleDetector = new ScaleGestureDetector(context,
new ScaleDetectorListener());
@@ -5008,11 +5015,13 @@
// FIXME: we may consider to give WebKit an option to handle multi-touch
// events later.
if (mSupportMultiTouch && ev.getPointerCount() > 1) {
- if (mMinZoomScale < mMaxZoomScale) {
+ if (mAllowPanAndScale || mMinZoomScale < mMaxZoomScale) {
mScaleDetector.onTouchEvent(ev);
if (mScaleDetector.isInProgress()) {
mLastTouchTime = eventTime;
- return true;
+ if (!mAllowPanAndScale) {
+ return true;
+ }
}
x = mScaleDetector.getFocusX();
y = mScaleDetector.getFocusY();
@@ -5214,15 +5223,21 @@
mLastTouchTime = eventTime;
break;
}
- // if it starts nearly horizontal or vertical, enforce it
- int ax = Math.abs(deltaX);
- int ay = Math.abs(deltaY);
- if (ax > MAX_SLOPE_FOR_DIAG * ay) {
- mSnapScrollMode = SNAP_X;
- mSnapPositive = deltaX > 0;
- } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
- mSnapScrollMode = SNAP_Y;
- mSnapPositive = deltaY > 0;
+
+ // Only lock dragging to one axis if we don't have a scale in progress.
+ // Scaling implies free-roaming movement. Note we'll only ever get here
+ // if mAllowPanAndScale is true.
+ if (mScaleDetector != null && !mScaleDetector.isInProgress()) {
+ // if it starts nearly horizontal or vertical, enforce it
+ int ax = Math.abs(deltaX);
+ int ay = Math.abs(deltaY);
+ if (ax > MAX_SLOPE_FOR_DIAG * ay) {
+ mSnapScrollMode = SNAP_X;
+ mSnapPositive = deltaX > 0;
+ } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
+ mSnapScrollMode = SNAP_Y;
+ mSnapPositive = deltaY > 0;
+ }
}
mTouchMode = TOUCH_DRAG_MODE;
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 8b618c7..6e11cff 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -293,4 +293,34 @@
inputStream.close();
}
}
+
+ // Remove the native binaries of a given package. This simply
+ // gets rid of the files in the 'lib' sub-directory.
+ public static void removeNativeBinariesLI(String nativeLibraryPath) {
+ if (DEBUG_NATIVE) {
+ Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryPath);
+ }
+
+ /*
+ * Just remove any file in the directory. Since the directory is owned
+ * by the 'system' UID, the application is not supposed to have written
+ * anything there.
+ */
+ File binaryDir = new File(nativeLibraryPath);
+ if (binaryDir.exists()) {
+ File[] binaries = binaryDir.listFiles();
+ if (binaries != null) {
+ for (int nn = 0; nn < binaries.length; nn++) {
+ if (DEBUG_NATIVE) {
+ Slog.d(TAG, " Deleting " + binaries[nn].getName());
+ }
+ if (!binaries[nn].delete()) {
+ Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
+ }
+ }
+ }
+ // Do not delete 'lib' directory itself, or this will prevent
+ // installation of future updates.
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 939f118..12cf853 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -84,6 +84,7 @@
private boolean mCurDown;
private int mCurNumPointers;
private int mMaxNumPointers;
+ private int mActivePointerId;
private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
private final VelocityTracker mVelocity;
@@ -123,6 +124,7 @@
PointerState ps = new PointerState();
mPointers.add(ps);
+ mActivePointerId = 0;
mVelocity = VelocityTracker.obtain();
@@ -183,14 +185,15 @@
final int NP = mPointers.size();
// Labels
- if (NP > 0) {
- final PointerState ps = mPointers.get(0);
+ if (mActivePointerId >= 0) {
+ final PointerState ps = mPointers.get(mActivePointerId);
+
canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint);
canvas.drawText(mText.clear()
.append("P: ").append(mCurNumPointers)
.append(" / ").append(mMaxNumPointers)
.toString(), 1, base, mTextPaint);
-
+
final int N = ps.mTraceCount;
if ((mCurDown && ps.mCurDown) || N == 0) {
canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint);
@@ -355,6 +358,11 @@
NP++;
}
+ if (mActivePointerId < 0 ||
+ ! mPointers.get(mActivePointerId).mCurDown) {
+ mActivePointerId = id;
+ }
+
final PointerState ps = mPointers.get(id);
ps.mCurDown = true;
if (mPrintCoords) {
@@ -396,6 +404,7 @@
}
if (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL
|| (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
@@ -407,10 +416,14 @@
Log.i(TAG, mText.clear().append("Pointer ")
.append(id + 1).append(": UP").toString());
}
-
- if (action == MotionEvent.ACTION_UP) {
+
+ if (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL) {
mCurDown = false;
} else {
+ if (mActivePointerId == id) {
+ mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
+ }
ps.addTrace(Float.NaN, Float.NaN);
}
}
@@ -438,9 +451,9 @@
// HACK
// A quick and dirty string builder implementation optimized for GC.
- // Using the basic StringBuilder implementation causes the application grind to a halt when
- // more than a couple of pointers are down due to the number of temporary objects allocated
- // while formatting strings for drawing or logging.
+ // Using String.format causes the application grind to a halt when
+ // more than a couple of pointers are down due to the number of
+ // temporary objects allocated while formatting strings for drawing or logging.
private static final class FasterStringBuilder {
private char[] mChars;
private int mLength;
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index a5229cc..1beba53 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -349,6 +349,26 @@
}
}
+ @SmallTest
+ public void testDelimitedStringContains() {
+ assertFalse(TextUtils.delimitedStringContains("", ',', null));
+ assertFalse(TextUtils.delimitedStringContains(null, ',', ""));
+ // Whole match
+ assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps"));
+ // At beginning.
+ assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps"));
+ assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps"));
+ // In middle, both without, before & after a false match.
+ assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps"));
+ assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps"));
+ assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps"));
+ // At the end.
+ assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps"));
+ assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps"));
+ // Not present (but with a false match)
+ assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps"));
+ }
+
/**
* CharSequence wrapper for testing the cases where text is copied into
* a char array instead of working from a String or a Spanned.
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index f00f2db..a06208a 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -30,6 +30,7 @@
#include <stddef.h>
#include <unistd.h>
+#include <limits.h>
namespace android {
@@ -108,9 +109,12 @@
// Flags for the input target.
int32_t flags;
- // The timeout for event delivery to this target in nanoseconds. Or -1 if none.
+ // The timeout for event delivery to this target in nanoseconds, or -1 to wait indefinitely.
nsecs_t timeout;
+ // The time already spent waiting for this target in nanoseconds, or 0 if none.
+ nsecs_t timeSpentWaitingForApplication;
+
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
float xOffset, yOffset;
@@ -118,6 +122,122 @@
/*
+ * An input window describes the bounds of a window that can receive input.
+ */
+struct InputWindow {
+ // Window flags from WindowManager.LayoutParams
+ enum {
+ FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ FLAG_DIM_BEHIND = 0x00000002,
+ FLAG_BLUR_BEHIND = 0x00000004,
+ FLAG_NOT_FOCUSABLE = 0x00000008,
+ FLAG_NOT_TOUCHABLE = 0x00000010,
+ FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+ FLAG_KEEP_SCREEN_ON = 0x00000080,
+ FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ FLAG_FULLSCREEN = 0x00000400,
+ FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ FLAG_DITHER = 0x00001000,
+ FLAG_SECURE = 0x00002000,
+ FLAG_SCALED = 0x00004000,
+ FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+ FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+ FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+ FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+ FLAG_SHOW_WALLPAPER = 0x00100000,
+ FLAG_TURN_SCREEN_ON = 0x00200000,
+ FLAG_DISMISS_KEYGUARD = 0x00400000,
+ FLAG_IMMERSIVE = 0x00800000,
+ FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+ FLAG_COMPATIBLE_WINDOW = 0x20000000,
+ FLAG_SYSTEM_ERROR = 0x40000000,
+ };
+
+ // Window types from WindowManager.LayoutParams
+ enum {
+ FIRST_APPLICATION_WINDOW = 1,
+ TYPE_BASE_APPLICATION = 1,
+ TYPE_APPLICATION = 2,
+ TYPE_APPLICATION_STARTING = 3,
+ LAST_APPLICATION_WINDOW = 99,
+ FIRST_SUB_WINDOW = 1000,
+ TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
+ TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+ TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+ TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
+ LAST_SUB_WINDOW = 1999,
+ FIRST_SYSTEM_WINDOW = 2000,
+ TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
+ TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
+ TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
+ TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
+ TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
+ TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
+ TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
+ TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
+ TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
+ TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
+ TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
+ TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+ TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
+ TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
+ LAST_SYSTEM_WINDOW = 2999,
+ };
+
+ sp<InputChannel> inputChannel;
+ int32_t layoutParamsFlags;
+ int32_t layoutParamsType;
+ nsecs_t dispatchingTimeout;
+ int32_t frameLeft;
+ int32_t frameTop;
+ int32_t frameRight;
+ int32_t frameBottom;
+ int32_t visibleFrameLeft;
+ int32_t visibleFrameTop;
+ int32_t visibleFrameRight;
+ int32_t visibleFrameBottom;
+ int32_t touchableAreaLeft;
+ int32_t touchableAreaTop;
+ int32_t touchableAreaRight;
+ int32_t touchableAreaBottom;
+ bool visible;
+ bool hasFocus;
+ bool hasWallpaper;
+ bool paused;
+ int32_t ownerPid;
+ int32_t ownerUid;
+
+ bool visibleFrameIntersects(const InputWindow* other) const;
+ bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
+};
+
+
+/*
+ * A private handle type used by the input manager to track the window.
+ */
+class InputApplicationHandle : public RefBase {
+protected:
+ InputApplicationHandle() { }
+ virtual ~InputApplicationHandle() { }
+};
+
+
+/*
+ * An input application describes properties of an application that can receive input.
+ */
+struct InputApplication {
+ String8 name;
+ nsecs_t dispatchingTimeout;
+ sp<InputApplicationHandle> handle;
+};
+
+
+/*
* Input dispatcher policy interface.
*
* The input reader policy is used by the input reader to interact with the Window Manager
@@ -135,14 +255,16 @@
/* Notifies the system that a configuration change has occurred. */
virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+ /* Notifies the system that an application is not responding.
+ * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) = 0;
+
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
/* Notifies the system that an input channel is not responding.
- * Returns true and a new timeout value if the dispatcher should keep waiting.
- * Otherwise returns false. */
- virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
- nsecs_t& outNewTimeout) = 0;
+ * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
+ virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;
/* Notifies the system that an input channel recovered from ANR. */
virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
@@ -153,29 +275,27 @@
/* Gets the key repeat inter-key delay. */
virtual nsecs_t getKeyRepeatDelay() = 0;
- /* Waits for key event input targets to become available.
- * If the event is being injected, injectorPid and injectorUid should specify the
- * process id and used id of the injecting application, otherwise they should both
- * be -1.
- * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
- virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets) = 0;
-
- /* Waits for motion event targets to become available.
- * If the event is being injected, injectorPid and injectorUid should specify the
- * process id and used id of the injecting application, otherwise they should both
- * be -1.
- * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
- virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets) = 0;
-
/* Gets the maximum suggested event delivery rate per second.
* This value is used to throttle motion event movement actions on a per-device
* basis. It is not intended to be a hard limit.
*/
virtual int32_t getMaxEventsPerSecond() = 0;
+
+ /* Allows the policy a chance to intercept a key before dispatching. */
+ virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
+ 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;
+
+ /* Checks whether a given application pid/uid has permission to inject input events
+ * into other applications.
+ *
+ * This method is special in that its implementation promises to be non-reentrant and
+ * is safe to call while holding other locks. (Most other methods make no such guarantees!)
+ */
+ virtual bool checkInjectEventsPermissionNonReentrant(
+ int32_t injectorPid, int32_t injectorUid) = 0;
};
@@ -187,6 +307,11 @@
virtual ~InputDispatcherInterface() { }
public:
+ /* Dumps the state of the input dispatcher.
+ *
+ * This method may be called on any thread (usually by the input manager). */
+ virtual void dump(String8& dump) = 0;
+
/* Runs a single iteration of the dispatch loop.
* Nominally processes one queued event, a timeout, or a response from an input consumer.
*
@@ -199,7 +324,6 @@
* These methods should only be called on the input reader thread.
*/
virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
- virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
@@ -219,6 +343,24 @@
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
+ /* Sets the list of input windows.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setInputWindows(const Vector<InputWindow>& inputWindows) = 0;
+
+ /* Sets the focused application.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setFocusedApplication(const InputApplication* inputApplication) = 0;
+
+ /* Sets the input dispatching mode.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
+
/* Preempts input dispatch in progress by making pending synchronous
* dispatches asynchronous instead. This method is generally called during a focus
* transition from one application to the next so as to enable the new application
@@ -230,10 +372,11 @@
virtual void preemptInputDispatch() = 0;
/* Registers or unregister input channels that may be used as targets for input events.
+ * If monitor is true, the channel will receive a copy of all input events.
*
* These methods may be called on any thread (usually by the input manager).
*/
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) = 0;
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
};
@@ -261,10 +404,11 @@
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+ virtual void dump(String8& dump);
+
virtual void dispatchOnce();
virtual void notifyConfigurationChanged(nsecs_t eventTime);
- virtual void notifyAppSwitchComing(nsecs_t eventTime);
virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime);
@@ -277,9 +421,12 @@
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
+ virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
+ virtual void setFocusedApplication(const InputApplication* inputApplication);
+ virtual void setInputDispatchMode(bool enabled, bool frozen);
virtual void preemptInputDispatch();
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
private:
@@ -310,6 +457,8 @@
int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress
inline bool isInjected() { return injectorPid >= 0; }
+
+ void recycle();
};
struct ConfigurationChangedEntry : EventEntry {
@@ -326,6 +475,17 @@
int32_t metaState;
int32_t repeatCount;
nsecs_t downTime;
+
+ bool syntheticRepeat; // set to true for synthetic key repeats
+
+ enum InterceptKeyResult {
+ INTERCEPT_KEY_RESULT_UNKNOWN,
+ INTERCEPT_KEY_RESULT_SKIP,
+ INTERCEPT_KEY_RESULT_CONTINUE,
+ };
+ InterceptKeyResult interceptKeyResult; // set based on the interception result
+
+ void recycle();
};
struct MotionSample {
@@ -380,9 +540,13 @@
// will be set to NULL.
MotionSample* tailMotionSample;
- inline bool isSyncTarget() {
+ inline bool isSyncTarget() const {
return targetFlags & InputTarget::FLAG_SYNC;
}
+
+ inline void preemptSyncTarget() {
+ targetFlags &= ~ InputTarget::FLAG_SYNC;
+ }
};
// A command entry captures state and behavior for an action to be performed in the
@@ -413,37 +577,43 @@
// parameters for the command (usage varies by command)
sp<Connection> connection;
+ nsecs_t eventTime;
+ KeyEntry* keyEntry;
+ sp<InputChannel> inputChannel;
+ sp<InputApplicationHandle> inputApplicationHandle;
+ int32_t windowType;
+ int32_t userActivityEventType;
};
// Generic queue implementation.
template <typename T>
struct Queue {
- T head;
- T tail;
+ T headSentinel;
+ T tailSentinel;
inline Queue() {
- head.prev = NULL;
- head.next = & tail;
- tail.prev = & head;
- tail.next = NULL;
+ headSentinel.prev = NULL;
+ headSentinel.next = & tailSentinel;
+ tailSentinel.prev = & headSentinel;
+ tailSentinel.next = NULL;
}
- inline bool isEmpty() {
- return head.next == & tail;
+ inline bool isEmpty() const {
+ return headSentinel.next == & tailSentinel;
}
inline void enqueueAtTail(T* entry) {
- T* last = tail.prev;
+ T* last = tailSentinel.prev;
last->next = entry;
entry->prev = last;
- entry->next = & tail;
- tail.prev = entry;
+ entry->next = & tailSentinel;
+ tailSentinel.prev = entry;
}
inline void enqueueAtHead(T* entry) {
- T* first = head.next;
- head.next = entry;
- entry->prev = & head;
+ T* first = headSentinel.next;
+ headSentinel.next = entry;
+ entry->prev = & headSentinel;
entry->next = first;
first->prev = entry;
}
@@ -454,7 +624,7 @@
}
inline T* dequeueAtHead() {
- T* first = head.next;
+ T* first = headSentinel.next;
dequeue(first);
return first;
}
@@ -476,7 +646,8 @@
float xPrecision, float yPrecision,
nsecs_t downTime, uint32_t pointerCount,
const int32_t* pointerIds, const PointerCoords* pointerCoords);
- DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
+ DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
+ int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout);
CommandEntry* obtainCommandEntry(Command command);
void releaseEventEntry(EventEntry* entry);
@@ -500,6 +671,85 @@
void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
};
+ /* Tracks dispatched key and motion event state so that cancelation events can be
+ * synthesized when events are dropped. */
+ class InputState {
+ public:
+ // Specifies whether a given event will violate input state consistency.
+ enum Consistency {
+ // The event is consistent with the current input state.
+ CONSISTENT,
+ // The event is inconsistent with the current input state but applications
+ // will tolerate it. eg. Down followed by another down.
+ TOLERABLE,
+ // The event is inconsistent with the current input state and will probably
+ // cause applications to crash. eg. Up without prior down, move with
+ // unexpected number of pointers.
+ BROKEN
+ };
+
+ InputState();
+ ~InputState();
+
+ // Returns true if there is no state to be canceled.
+ bool isNeutral() const;
+
+ // Returns true if the input state believes it is out of sync.
+ bool isOutOfSync() const;
+
+ // Sets the input state to be out of sync if it is not neutral.
+ void setOutOfSync();
+
+ // Resets the input state out of sync flag.
+ void resetOutOfSync();
+
+ // Records tracking information for an event that has just been published.
+ // Returns whether the event is consistent with the current input state.
+ Consistency trackEvent(const EventEntry* entry);
+
+ // Records tracking information for a key event that has just been published.
+ // Returns whether the event is consistent with the current input state.
+ Consistency trackKey(const KeyEntry* entry);
+
+ // Records tracking information for a motion event that has just been published.
+ // Returns whether the event is consistent with the current input state.
+ Consistency trackMotion(const MotionEntry* entry);
+
+ // Synthesizes cancelation events for the current state.
+ void synthesizeCancelationEvents(Allocator* allocator,
+ Vector<EventEntry*>& outEvents) const;
+
+ // Clears the current state.
+ void clear();
+
+ private:
+ bool mIsOutOfSync;
+
+ struct KeyMemento {
+ int32_t deviceId;
+ int32_t source;
+ int32_t keyCode;
+ int32_t scanCode;
+ nsecs_t downTime;
+ };
+
+ struct MotionMemento {
+ int32_t deviceId;
+ int32_t source;
+ float xPrecision;
+ float yPrecision;
+ nsecs_t downTime;
+ uint32_t pointerCount;
+ int32_t pointerIds[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+
+ void setPointers(const MotionEntry* entry);
+ };
+
+ Vector<KeyMemento> mKeyMementos;
+ Vector<MotionMemento> mMotionMementos;
+ };
+
/* Manages the dispatch state associated with a single input channel. */
class Connection : public RefBase {
protected:
@@ -520,6 +770,7 @@
Status status;
sp<InputChannel> inputChannel;
InputPublisher inputPublisher;
+ InputState inputState;
Queue<DispatchEntry> outboundQueue;
nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
@@ -540,28 +791,34 @@
// Determine whether this connection has a pending synchronous dispatch target.
// Since there can only ever be at most one such target at a time, if there is one,
// it must be at the tail because nothing else can be enqueued after it.
- inline bool hasPendingSyncTarget() {
- return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget();
+ inline bool hasPendingSyncTarget() const {
+ return ! outboundQueue.isEmpty() && outboundQueue.tailSentinel.prev->isSyncTarget();
+ }
+
+ // Assuming there is a pending sync target, make it async.
+ inline void preemptSyncTarget() {
+ outboundQueue.tailSentinel.prev->preemptSyncTarget();
}
// Gets the time since the current event was originally obtained from the input driver.
- inline double getEventLatencyMillis(nsecs_t currentTime) {
+ inline double getEventLatencyMillis(nsecs_t currentTime) const {
return (currentTime - lastEventTime) / 1000000.0;
}
// Gets the time since the current event entered the outbound dispatch queue.
- inline double getDispatchLatencyMillis(nsecs_t currentTime) {
+ inline double getDispatchLatencyMillis(nsecs_t currentTime) const {
return (currentTime - lastDispatchTime) / 1000000.0;
}
// Gets the time since the current event ANR was declared, if applicable.
- inline double getANRLatencyMillis(nsecs_t currentTime) {
+ inline double getANRLatencyMillis(nsecs_t currentTime) const {
return (currentTime - lastANRTime) / 1000000.0;
}
status_t initialize();
void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
+ void resetTimeout(nsecs_t currentTime);
};
sp<InputDispatcherPolicyInterface> mPolicy;
@@ -571,9 +828,26 @@
Allocator mAllocator;
sp<PollLoop> mPollLoop;
+ EventEntry* mPendingEvent;
Queue<EventEntry> mInboundQueue;
Queue<CommandEntry> mCommandQueue;
+ Vector<EventEntry*> mTempCancelationEvents;
+
+ void dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, nsecs_t keyRepeatDelay,
+ nsecs_t* nextWakeupTime);
+
+ // Enqueues an inbound event. Returns true if mPollLoop->wake() should be called.
+ bool enqueueInboundEventLocked(EventEntry* entry);
+
+ // App switch latency optimization.
+ nsecs_t mAppSwitchDueTime;
+
+ static bool isAppSwitchKey(int32_t keyCode);
+ bool isAppSwitchPendingLocked();
+ bool detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry);
+ void resetPendingAppSwitchLocked(bool handled);
+
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
@@ -591,20 +865,15 @@
// the duration.
Vector<Connection*> mTimedOutConnections;
- // Preallocated key and motion event objects used only to ask the input dispatcher policy
- // for the targets of an event that is to be dispatched.
- KeyEvent mReusableKeyEvent;
- MotionEvent mReusableMotionEvent;
+ // Input channels that will receive a copy of all input events.
+ Vector<sp<InputChannel> > mMonitoringChannels;
- // The input targets that were most recently identified for dispatch.
- // If there is a synchronous event dispatch in progress, the current input targets will
- // remain unchanged until the dispatch has completed or been aborted.
- Vector<InputTarget> mCurrentInputTargets;
- bool mCurrentInputTargetsValid; // false while targets are being recomputed
+ // Preallocated key event object used for policy inquiries.
+ KeyEvent mReusableKeyEvent;
// Event injection and synchronization.
Condition mInjectionResultAvailableCondition;
- EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
+ EventEntry* createEntryFromInjectedInputEventLocked(const InputEvent* event);
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
Condition mInjectionSyncFinishedCondition;
@@ -622,36 +891,108 @@
} mThrottleState;
// Key repeat tracking.
- // XXX Move this up to the input reader instead.
struct KeyRepeatState {
KeyEntry* lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
} mKeyRepeatState;
void resetKeyRepeatLocked();
+ KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime, nsecs_t keyRepeatTimeout);
// Deferred command processing.
bool runCommandsLockedInterruptible();
CommandEntry* postCommandLocked(Command command);
- // Process events that have just been dequeued from the head of the input queue.
- void processConfigurationChangedLockedInterruptible(
- nsecs_t currentTime, ConfigurationChangedEntry* entry);
- void processKeyLockedInterruptible(
- nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout);
- void processKeyRepeatLockedInterruptible(
- nsecs_t currentTime, nsecs_t keyRepeatTimeout);
- void processMotionLockedInterruptible(
- nsecs_t currentTime, MotionEntry* entry);
+ // Inbound event processing.
+ void drainInboundQueueLocked();
+ void releasePendingEventLocked(bool wasDropped);
+ void releaseInboundEventLocked(EventEntry* entry, bool wasDropped);
+ bool isEventFromReliableSourceLocked(EventEntry* entry);
- // Identify input targets for an event and dispatch to them.
- void identifyInputTargetsAndDispatchKeyLockedInterruptible(
- nsecs_t currentTime, KeyEntry* entry);
- void identifyInputTargetsAndDispatchMotionLockedInterruptible(
- nsecs_t currentTime, MotionEntry* entry);
+ // Dispatch state.
+ bool mDispatchEnabled;
+ bool mDispatchFrozen;
+ Vector<InputWindow> mWindows;
+ Vector<InputWindow*> mWallpaperWindows;
+
+ // Focus tracking for keys, trackball, etc.
+ 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;
+ };
+ Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
+ Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+ // Focused application.
+ InputApplication* mFocusedApplication;
+ InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+ void releaseFocusedApplicationLocked();
+
+ // Dispatch inbound events.
+ bool dispatchConfigurationChangedLocked(
+ nsecs_t currentTime, ConfigurationChangedEntry* entry);
+ bool dispatchKeyLocked(
+ nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+ nsecs_t* nextWakeupTime);
+ bool dispatchMotionLocked(
+ nsecs_t currentTime, MotionEntry* entry,
+ nsecs_t* nextWakeupTime);
void dispatchEventToCurrentInputTargetsLocked(
nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
+ void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry);
+ void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry);
+
+ // The input targets that were most recently identified for dispatch.
+ // If there is a synchronous event dispatch in progress, the current input targets will
+ // remain unchanged until the dispatch has completed or been aborted.
+ bool mCurrentInputTargetsValid; // false while targets are being recomputed
+ Vector<InputTarget> mCurrentInputTargets;
+ int32_t mCurrentInputWindowType;
+ sp<InputChannel> mCurrentInputChannel;
+
+ enum InputTargetWaitCause {
+ INPUT_TARGET_WAIT_CAUSE_NONE,
+ INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
+ INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
+ };
+
+ InputTargetWaitCause mInputTargetWaitCause;
+ nsecs_t mInputTargetWaitStartTime;
+ nsecs_t mInputTargetWaitTimeoutTime;
+ bool mInputTargetWaitTimeoutExpired;
+
+ // Finding targets for input events.
+ void startFindingTargetsLocked();
+ void finishFindingTargetsLocked(const InputWindow* window);
+ int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
+ const InputApplication* application, const InputWindow* window,
+ nsecs_t* nextWakeupTime);
+ void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout);
+ nsecs_t getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(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);
+
+ void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication);
+ 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 isWindowObscuredLocked(const InputWindow* window);
+ void releaseTouchedWindowLocked();
+
// Manage the dispatch cycle for a single connection.
// These methods are deliberately not Interruptible because doing all of the work
// with the mutex held makes it easier to ensure that connection invariants are maintained.
@@ -659,15 +1000,25 @@
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
- void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
+ nsecs_t timeSpentWaitingForApplication);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+ void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, nsecs_t newTimeout);
void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool broken);
+ void drainOutboundQueueLocked(Connection* connection, DispatchEntry* firstDispatchEntryToDrain);
static bool handleReceiveCallback(int receiveFd, int events, void* data);
+ // Preempting input dispatch.
+ bool preemptInputDispatchInnerLocked();
+
+ // Dump state.
+ void dumpDispatchStateLocked(String8& dump);
+ void logDispatchStateLocked();
+
// Add or remove a connection to the mActiveConnections vector.
void activateConnectionLocked(Connection* connection);
void deactivateConnectionLocked(Connection* connection);
@@ -683,9 +1034,13 @@
nsecs_t currentTime, const sp<Connection>& connection);
// Outbound policy interactions.
+ void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
+ void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
+ void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry);
+ void doTargetsNotReadyTimeoutLockedInterruptible(CommandEntry* commandEntry);
};
/* Enqueues and dispatches input events, endlessly. */
@@ -702,4 +1057,4 @@
} // namespace android
-#endif // _UI_INPUT_DISPATCHER_PRIV_H
+#endif // _UI_INPUT_DISPATCHER_H
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index 4012c69..568568b 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -72,51 +72,11 @@
/* Stops the input manager threads and waits for them to exit. */
virtual status_t stop() = 0;
- /* Registers an input channel prior to using it as the target of an event. */
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
+ /* Gets the input reader. */
+ virtual sp<InputReaderInterface> getReader() = 0;
- /* Unregisters an input channel. */
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
-
- /* Injects an input event and optionally waits for sync.
- * The synchronization mode determines whether the method blocks while waiting for
- * input injection to proceed.
- * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
- */
- virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
-
- /* Preempts input dispatch in progress by making pending synchronous
- * dispatches asynchronous instead. This method is generally called during a focus
- * transition from one application to the next so as to enable the new application
- * to start receiving input as soon as possible without having to wait for the
- * old application to finish up.
- */
- virtual void preemptInputDispatch() = 0;
-
- /* Gets input device configuration. */
- virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0;
-
- /* Gets information about the specified input device.
- * Returns OK if the device information was obtained or NAME_NOT_FOUND if there
- * was no such device.
- */
- virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0;
-
- /* Gets the list of all registered device ids. */
- virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds) = 0;
-
- /* Queries current input state. */
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) = 0;
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) = 0;
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw) = 0;
-
- /* Determines whether physical keys exist for the given framework-domain key codes. */
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
+ /* Gets the input dispatcher. */
+ virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
class InputManager : public InputManagerInterface {
@@ -137,25 +97,8 @@
virtual status_t start();
virtual status_t stop();
- virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
- virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
-
- virtual int32_t injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
-
- virtual void preemptInputDispatch();
-
- virtual void getInputConfiguration(InputConfiguration* outConfiguration);
- virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo);
- virtual void getInputDeviceIds(Vector<int32_t>& outDeviceIds);
- virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode);
- virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode);
- virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
- int32_t sw);
- virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
+ virtual sp<InputReaderInterface> getReader();
+ virtual sp<InputDispatcherInterface> getDispatcher();
private:
sp<InputReaderInterface> mReader;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 7a089a45..903c3c4 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -95,10 +95,6 @@
// The input dispatcher should dispatch the input to the application.
ACTION_DISPATCH = 0x00000001,
-
- // The input dispatcher should perform special filtering in preparation for
- // a pending app switch.
- ACTION_APP_SWITCH_COMING = 0x00000002,
};
/* Gets information about the display with the specified id.
@@ -168,6 +164,11 @@
virtual ~InputReaderInterface() { }
public:
+ /* Dumps the state of the input reader.
+ *
+ * This method may be called on any thread (usually by the input manager). */
+ virtual void dump(String8& dump) = 0;
+
/* Runs a single iteration of the processing loop.
* Nominally reads and processes one incoming message from the EventHub.
*
@@ -240,6 +241,8 @@
const sp<InputDispatcherInterface>& dispatcher);
virtual ~InputReader();
+ virtual void dump(String8& dump);
+
virtual void loopOnce();
virtual void getInputConfiguration(InputConfiguration* outConfiguration);
@@ -305,6 +308,9 @@
GetStateFunc getStateFunc);
bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
const int32_t* keyCodes, uint8_t* outFlags);
+
+ // dump state
+ void dumpDeviceInfo(String8& dump);
};
@@ -759,9 +765,11 @@
} mLocked;
virtual void configureParameters();
+ virtual void logParameters();
virtual void configureRawAxes();
virtual void logRawAxes();
virtual bool configureSurfaceLocked();
+ virtual void logMotionRangesLocked();
virtual void configureVirtualKeysLocked();
virtual void parseCalibration();
virtual void resolveCalibration();
diff --git a/include/ui/PowerManager.h b/include/ui/PowerManager.h
new file mode 100644
index 0000000..5434b4f
--- /dev/null
+++ b/include/ui/PowerManager.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_POWER_MANAGER_H
+#define _UI_POWER_MANAGER_H
+
+
+namespace android {
+
+enum {
+ POWER_MANAGER_OTHER_EVENT = 0,
+ POWER_MANAGER_CHEEK_EVENT = 1,
+ POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+ // up events or LONG_TOUCH events.
+ POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+ POWER_MANAGER_TOUCH_UP_EVENT = 4,
+ POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+ POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
+};
+
+} // namespace android
+
+#endif // _UI_POWER_MANAGER_H
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index bc616eb..c2dfe5d 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -172,22 +172,36 @@
void* data;
};
- const bool mAllowNonCallbacks;
-
+ const bool mAllowNonCallbacks; // immutable
+
+ int mWakeReadPipeFd; // immutable
+ int mWakeWritePipeFd; // immutable
+
+ // 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.
Mutex mLock;
- bool mPolling;
- uint32_t mWaiters;
- Condition mAwake;
- Condition mResume;
+ bool mPolling; // guarded by mLock
+ uint32_t mWaiters; // guarded by mLock
+ Condition mAwake; // guarded by mLock
+ Condition mResume; // guarded by mLock
- int mWakeReadPipeFd;
- int mWakeWritePipeFd;
-
+ // The next two vectors are only mutated when mPolling is false since they must
+ // not be changed while the poll() system call is in progress. To mutate these
+ // vectors, the poll() must first be awoken then the lock acquired.
Vector<struct pollfd> mRequestedFds;
Vector<RequestedCallback> mRequestedCallbacks;
- Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
- Vector<PendingCallback> mPendingFds; // used privately by pollOnce
+ // This state is only used privately by pollOnce and does not require a lock since
+ // it runs on a single thread.
+ Vector<PendingCallback> mPendingCallbacks;
+ Vector<PendingCallback> mPendingFds;
size_t mPendingFdsPos;
void openWakePipe();
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index df232d4..b8a26b0 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -31,8 +31,15 @@
// Log debug messages about input event throttling.
#define DEBUG_THROTTLING 0
+// Log debug messages about input focus tracking.
+#define DEBUG_FOCUS 0
+
+// Log debug messages about the app switch latency optimization.
+#define DEBUG_APP_SWITCH 0
+
#include <cutils/log.h>
#include <ui/InputDispatcher.h>
+#include <ui/PowerManager.h>
#include <stddef.h>
#include <unistd.h>
@@ -41,31 +48,62 @@
namespace android {
-// TODO, this needs to be somewhere else, perhaps in the policy
-static inline bool isMovementKey(int32_t keyCode) {
- return keyCode == AKEYCODE_DPAD_UP
- || keyCode == AKEYCODE_DPAD_DOWN
- || keyCode == AKEYCODE_DPAD_LEFT
- || keyCode == AKEYCODE_DPAD_RIGHT;
-}
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Amount of time to allow for all pending events to be processed when an app switch
+// key is on the way. This is used to preempt input dispatch and drop input events
+// when an application takes too long to respond and the user has pressed an app switch key.
+const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
+
static inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
+
+// --- 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;
+}
+
+
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
- mPolicy(policy) {
+ mPolicy(policy),
+ mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
+ mDispatchEnabled(true), mDispatchFrozen(false),
+ mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+ mFocusedApplication(NULL),
+ mCurrentInputTargetsValid(false),
+ mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mPollLoop = new PollLoop(false);
- mInboundQueue.head.refCount = -1;
- mInboundQueue.head.type = EventEntry::TYPE_SENTINEL;
- mInboundQueue.head.eventTime = LONG_LONG_MIN;
+ mInboundQueue.headSentinel.refCount = -1;
+ mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN;
- mInboundQueue.tail.refCount = -1;
- mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL;
- mInboundQueue.tail.eventTime = LONG_LONG_MAX;
+ mInboundQueue.tailSentinel.refCount = -1;
+ mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX;
mKeyRepeatState.lastKeyEntry = NULL;
@@ -77,189 +115,302 @@
mThrottleState.originalSampleCount = 0;
LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
#endif
-
- mCurrentInputTargetsValid = false;
}
InputDispatcher::~InputDispatcher() {
- resetKeyRepeatLocked();
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ resetKeyRepeatLocked();
+ releasePendingEventLocked(true);
+ drainInboundQueueLocked();
+ }
while (mConnectionsByReceiveFd.size() != 0) {
unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
}
-
- for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) {
- EventEntry* next = entry->next;
- mAllocator.releaseEventEntry(next);
- entry = next;
- }
}
void InputDispatcher::dispatchOnce() {
nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
- bool skipPoll = false;
- nsecs_t currentTime;
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
- currentTime = now();
+ dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
- // Reset the key repeat timer whenever we disallow key events, even if the next event
- // is not a key. This is to ensure that we abort a key repeat if the device is just coming
- // out of sleep.
- // XXX we should handle resetting input state coming out of sleep more generally elsewhere
- if (keyRepeatTimeout < 0) {
- resetKeyRepeatLocked();
+ if (runCommandsLockedInterruptible()) {
+ nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
-
- // Detect and process timeouts for all connections and determine if there are any
- // synchronous event dispatches pending. This step is entirely non-interruptible.
- bool hasPendingSyncTarget = false;
- size_t activeConnectionCount = mActiveConnections.size();
- for (size_t i = 0; i < activeConnectionCount; i++) {
- Connection* connection = mActiveConnections.itemAt(i);
-
- if (connection->hasPendingSyncTarget()) {
- hasPendingSyncTarget = true;
- }
-
- nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
- if (connectionTimeoutTime <= currentTime) {
- mTimedOutConnections.add(connection);
- } else if (connectionTimeoutTime < nextWakeupTime) {
- nextWakeupTime = connectionTimeoutTime;
- }
- }
-
- size_t timedOutConnectionCount = mTimedOutConnections.size();
- for (size_t i = 0; i < timedOutConnectionCount; i++) {
- Connection* connection = mTimedOutConnections.itemAt(i);
- timeoutDispatchCycleLocked(currentTime, connection);
- skipPoll = true;
- }
- mTimedOutConnections.clear();
-
- // If we don't have a pending sync target, then we can begin delivering a new event.
- // (Otherwise we wait for dispatch to complete for that target.)
- if (! hasPendingSyncTarget) {
- if (mInboundQueue.isEmpty()) {
- if (mKeyRepeatState.lastKeyEntry) {
- if (currentTime >= mKeyRepeatState.nextRepeatTime) {
- processKeyRepeatLockedInterruptible(currentTime, keyRepeatDelay);
- skipPoll = true;
- } else {
- if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
- nextWakeupTime = mKeyRepeatState.nextRepeatTime;
- }
- }
- }
- } else {
- // Inbound queue has at least one entry.
- EventEntry* entry = mInboundQueue.head.next;
-
- // Consider throttling the entry if it is a move event and there are no
- // other events behind it in the queue. Due to movement batching, additional
- // samples may be appended to this event by the time the throttling timeout
- // expires.
- // TODO Make this smarter and consider throttling per device independently.
- if (entry->type == EventEntry::TYPE_MOTION) {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
- int32_t deviceId = motionEntry->deviceId;
- uint32_t source = motionEntry->source;
- if (motionEntry->next == & mInboundQueue.tail
- && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
- && deviceId == mThrottleState.lastDeviceId
- && source == mThrottleState.lastSource) {
- nsecs_t nextTime = mThrottleState.lastEventTime
- + mThrottleState.minTimeBetweenEvents;
- if (currentTime < nextTime) {
- // Throttle it!
-#if DEBUG_THROTTLING
- LOGD("Throttling - Delaying motion event for "
- "device 0x%x, source 0x%08x by up to %0.3fms.",
- deviceId, source, (nextTime - currentTime) * 0.000001);
-#endif
- if (nextTime < nextWakeupTime) {
- nextWakeupTime = nextTime;
- }
- if (mThrottleState.originalSampleCount == 0) {
- mThrottleState.originalSampleCount =
- motionEntry->countSamples();
- }
- goto Throttle;
- }
- }
-
-#if DEBUG_THROTTLING
- if (mThrottleState.originalSampleCount != 0) {
- uint32_t count = motionEntry->countSamples();
- LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
- count - mThrottleState.originalSampleCount,
- mThrottleState.originalSampleCount, count);
- mThrottleState.originalSampleCount = 0;
- }
-#endif
-
- mThrottleState.lastEventTime = entry->eventTime < currentTime
- ? entry->eventTime : currentTime;
- mThrottleState.lastDeviceId = deviceId;
- mThrottleState.lastSource = source;
- }
-
- // Start processing the entry but leave it on the queue until later so that the
- // input reader can keep appending samples onto a motion event between the
- // time we started processing it and the time we finally enqueue dispatch
- // entries for it.
- switch (entry->type) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
- ConfigurationChangedEntry* typedEntry =
- static_cast<ConfigurationChangedEntry*>(entry);
- processConfigurationChangedLockedInterruptible(currentTime, typedEntry);
- break;
- }
-
- case EventEntry::TYPE_KEY: {
- KeyEntry* typedEntry = static_cast<KeyEntry*>(entry);
- processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout);
- break;
- }
-
- case EventEntry::TYPE_MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(entry);
- processMotionLockedInterruptible(currentTime, typedEntry);
- break;
- }
-
- default:
- assert(false);
- break;
- }
-
- // Dequeue and release the event entry that we just processed.
- mInboundQueue.dequeue(entry);
- mAllocator.releaseEventEntry(entry);
- skipPoll = true;
-
- Throttle: ;
- }
- }
-
- // Run any deferred commands.
- skipPoll |= runCommandsLockedInterruptible();
} // release lock
- // If we dispatched anything, don't poll just now. Wait for the next iteration.
- // Contents may have shifted during flight.
- if (skipPoll) {
+ // Wait for callback or timeout or wake. (make sure we round up, not down)
+ nsecs_t currentTime = now();
+ int32_t timeoutMillis;
+ if (nextWakeupTime > currentTime) {
+ uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
+ timeout = (timeout + 999999LL) / 1000000LL;
+ timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
+ } else {
+ timeoutMillis = 0;
+ }
+
+ mPollLoop->pollOnce(timeoutMillis);
+}
+
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
+ nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
+ nsecs_t currentTime = now();
+
+ // Reset the key repeat timer whenever we disallow key events, even if the next event
+ // is not a key. This is to ensure that we abort a key repeat if the device is just coming
+ // out of sleep.
+ if (keyRepeatTimeout < 0) {
+ resetKeyRepeatLocked();
+ }
+
+ // If dispatching is disabled, drop all events in the queue.
+ if (! mDispatchEnabled) {
+ if (mPendingEvent || ! mInboundQueue.isEmpty()) {
+ LOGI("Dropping pending events because input dispatch is disabled.");
+ releasePendingEventLocked(true);
+ drainInboundQueueLocked();
+ }
return;
}
- // Wait for callback or timeout or wake. (make sure we round up, not down)
- nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
- int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
- mPollLoop->pollOnce(timeoutMillis);
+ // If dispatching is frozen, do not process timeouts or try to deliver any new events.
+ if (mDispatchFrozen) {
+#if DEBUG_FOCUS
+ LOGD("Dispatch frozen. Waiting some more.");
+#endif
+ return;
+ }
+
+ // Optimize latency of app switches.
+ // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
+ // been pressed. When it expires, we preempt dispatch and drop all other pending events.
+ bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+ if (mAppSwitchDueTime < *nextWakeupTime) {
+ *nextWakeupTime = mAppSwitchDueTime;
+ }
+
+ // Detect and process timeouts for all connections and determine if there are any
+ // synchronous event dispatches pending. This step is entirely non-interruptible.
+ bool havePendingSyncTarget = false;
+ size_t activeConnectionCount = mActiveConnections.size();
+ for (size_t i = 0; i < activeConnectionCount; i++) {
+ Connection* connection = mActiveConnections.itemAt(i);
+
+ if (connection->hasPendingSyncTarget()) {
+ if (isAppSwitchDue) {
+ connection->preemptSyncTarget();
+ } else {
+ havePendingSyncTarget = true;
+ }
+ }
+
+ nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
+ if (connectionTimeoutTime <= currentTime) {
+ mTimedOutConnections.add(connection);
+ } else if (connectionTimeoutTime < *nextWakeupTime) {
+ *nextWakeupTime = connectionTimeoutTime;
+ }
+ }
+
+ size_t timedOutConnectionCount = mTimedOutConnections.size();
+ for (size_t i = 0; i < timedOutConnectionCount; i++) {
+ Connection* connection = mTimedOutConnections.itemAt(i);
+ timeoutDispatchCycleLocked(currentTime, connection);
+ *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+ mTimedOutConnections.clear();
+
+ // If we have a pending synchronous target, skip dispatch.
+ if (havePendingSyncTarget) {
+ return;
+ }
+
+ // Ready to start a new event.
+ // If we don't already have a pending event, go grab one.
+ if (! mPendingEvent) {
+ if (mInboundQueue.isEmpty()) {
+ if (isAppSwitchDue) {
+ // The inbound queue is empty so the app switch key we were waiting
+ // for will never arrive. Stop waiting for it.
+ resetPendingAppSwitchLocked(false);
+ isAppSwitchDue = false;
+ }
+
+ // Synthesize a key repeat if appropriate.
+ if (mKeyRepeatState.lastKeyEntry) {
+ if (currentTime >= mKeyRepeatState.nextRepeatTime) {
+ mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
+ } else {
+ if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
+ *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
+ }
+ }
+ }
+ if (! mPendingEvent) {
+ return;
+ }
+ } else {
+ // Inbound queue has at least one entry.
+ EventEntry* entry = mInboundQueue.headSentinel.next;
+
+ // Throttle the entry if it is a move event and there are no
+ // other events behind it in the queue. Due to movement batching, additional
+ // samples may be appended to this event by the time the throttling timeout
+ // expires.
+ // TODO Make this smarter and consider throttling per device independently.
+ if (entry->type == EventEntry::TYPE_MOTION) {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ int32_t deviceId = motionEntry->deviceId;
+ uint32_t source = motionEntry->source;
+ if (! isAppSwitchDue
+ && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event
+ && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+ && deviceId == mThrottleState.lastDeviceId
+ && source == mThrottleState.lastSource) {
+ nsecs_t nextTime = mThrottleState.lastEventTime
+ + mThrottleState.minTimeBetweenEvents;
+ if (currentTime < nextTime) {
+ // Throttle it!
+#if DEBUG_THROTTLING
+ LOGD("Throttling - Delaying motion event for "
+ "device 0x%x, source 0x%08x by up to %0.3fms.",
+ deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+ if (nextTime < *nextWakeupTime) {
+ *nextWakeupTime = nextTime;
+ }
+ if (mThrottleState.originalSampleCount == 0) {
+ mThrottleState.originalSampleCount =
+ motionEntry->countSamples();
+ }
+ return;
+ }
+ }
+
+#if DEBUG_THROTTLING
+ if (mThrottleState.originalSampleCount != 0) {
+ uint32_t count = motionEntry->countSamples();
+ LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+ count - mThrottleState.originalSampleCount,
+ mThrottleState.originalSampleCount, count);
+ mThrottleState.originalSampleCount = 0;
+ }
+#endif
+
+ mThrottleState.lastEventTime = entry->eventTime < currentTime
+ ? entry->eventTime : currentTime;
+ mThrottleState.lastDeviceId = deviceId;
+ mThrottleState.lastSource = source;
+ }
+
+ mInboundQueue.dequeue(entry);
+ mPendingEvent = entry;
+ }
+ }
+
+ // Now we have an event to dispatch.
+ assert(mPendingEvent != NULL);
+ bool wasDispatched = false;
+ bool wasDropped = false;
+ switch (mPendingEvent->type) {
+ case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ ConfigurationChangedEntry* typedEntry =
+ static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+ wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+ break;
+ }
+
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+ if (isAppSwitchPendingLocked()) {
+ if (isAppSwitchKey(typedEntry->keyCode)) {
+ resetPendingAppSwitchLocked(true);
+ } else if (isAppSwitchDue) {
+ LOGI("Dropping key because of pending overdue app switch.");
+ wasDropped = true;
+ break;
+ }
+ }
+ wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
+ nextWakeupTime);
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+ if (isAppSwitchDue) {
+ LOGI("Dropping motion because of pending overdue app switch.");
+ wasDropped = true;
+ break;
+ }
+ wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime);
+ break;
+ }
+
+ default:
+ assert(false);
+ wasDropped = true;
+ break;
+ }
+
+ if (wasDispatched || wasDropped) {
+ releasePendingEventLocked(wasDropped);
+ *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+}
+
+bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+ bool needWake = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(entry);
+
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY:
+ needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry));
+ break;
+ }
+
+ return needWake;
+}
+
+bool InputDispatcher::isAppSwitchKey(int32_t keyCode) {
+ return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool InputDispatcher::isAppSwitchPendingLocked() {
+ return mAppSwitchDueTime != LONG_LONG_MAX;
+}
+
+bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) {
+ if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP
+ && ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
+ && isAppSwitchKey(inboundKeyEntry->keyCode)
+ && isEventFromReliableSourceLocked(inboundKeyEntry)) {
+#if DEBUG_APP_SWITCH
+ LOGD("App switch is pending!");
+#endif
+ mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT;
+ return true; // need wake
+ }
+ return false;
+}
+
+void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
+ mAppSwitchDueTime = LONG_LONG_MAX;
+
+#if DEBUG_APP_SWITCH
+ if (handled) {
+ LOGD("App switch has arrived.");
+ } else {
+ LOGD("App switch was abandoned.");
+ }
+#endif
}
bool InputDispatcher::runCommandsLockedInterruptible() {
@@ -285,78 +436,52 @@
return commandEntry;
}
-void InputDispatcher::processConfigurationChangedLockedInterruptible(
- nsecs_t currentTime, ConfigurationChangedEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
-#endif
-
- // Reset key repeating in case a keyboard device was added or removed or something.
- resetKeyRepeatLocked();
-
- mLock.unlock();
-
- mPolicy->notifyConfigurationChanged(entry->eventTime);
-
- mLock.lock();
-}
-
-void InputDispatcher::processKeyLockedInterruptible(
- nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("processKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
- entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action,
- entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
- entry->downTime);
-#endif
-
- if (entry->action == AKEY_EVENT_ACTION_DOWN && ! entry->isInjected()) {
- if (mKeyRepeatState.lastKeyEntry
- && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
- // We have seen two identical key downs in a row which indicates that the device
- // driver is automatically generating key repeats itself. We take note of the
- // repeat here, but we disable our own next key repeat timer since it is clear that
- // we will not need to synthesize key repeats ourselves.
- entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
- resetKeyRepeatLocked();
- mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
- } else {
- // Not a repeat. Save key down state in case we do see a repeat later.
- resetKeyRepeatLocked();
- mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
- }
- mKeyRepeatState.lastKeyEntry = entry;
- entry->refCount += 1;
- } else {
- resetKeyRepeatLocked();
+void InputDispatcher::drainInboundQueueLocked() {
+ while (! mInboundQueue.isEmpty()) {
+ EventEntry* entry = mInboundQueue.dequeueAtHead();
+ releaseInboundEventLocked(entry, true /*wasDropped*/);
}
-
- identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
}
-void InputDispatcher::processKeyRepeatLockedInterruptible(
+void InputDispatcher::releasePendingEventLocked(bool wasDropped) {
+ if (mPendingEvent) {
+ releaseInboundEventLocked(mPendingEvent, wasDropped);
+ mPendingEvent = NULL;
+ }
+}
+
+void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) {
+ if (wasDropped) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("Pending event was dropped.");
+#endif
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ }
+ mAllocator.releaseEventEntry(entry);
+}
+
+bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
+ return ! entry->isInjected()
+ || entry->injectorUid == 0
+ || mPolicy->checkInjectEventsPermissionNonReentrant(
+ entry->injectorPid, entry->injectorUid);
+}
+
+void InputDispatcher::resetKeyRepeatLocked() {
+ if (mKeyRepeatState.lastKeyEntry) {
+ mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
+ mKeyRepeatState.lastKeyEntry = NULL;
+ }
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
nsecs_t currentTime, nsecs_t keyRepeatDelay) {
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
- // Search the inbound queue for a key up corresponding to this device.
- // It doesn't make sense to generate a key repeat event if the key is already up.
- for (EventEntry* queuedEntry = mInboundQueue.head.next;
- queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
- if (queuedEntry->type == EventEntry::TYPE_KEY) {
- KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
- if (queuedKeyEntry->deviceId == entry->deviceId
- && entry->action == AKEY_EVENT_ACTION_UP) {
- resetKeyRepeatLocked();
- return;
- }
- }
- }
-
- // Synthesize a key repeat.
// 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();
entry->eventTime = currentTime;
entry->policyFlags = policyFlags;
entry->repeatCount += 1;
@@ -371,31 +496,194 @@
entry = newEntry;
}
+ entry->syntheticRepeat = true;
+
+ // Increment reference count since we keep a reference to the event in
+ // mKeyRepeatState.lastKeyEntry in addition to the one we return.
+ entry->refCount += 1;
if (entry->repeatCount == 1) {
entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
}
mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
-
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
- "repeatCount=%d, downTime=%lld",
- entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
- entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
- entry->repeatCount, entry->downTime);
-#endif
-
- identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
+ return entry;
}
-void InputDispatcher::processMotionLockedInterruptible(
- nsecs_t currentTime, MotionEntry* entry) {
+bool InputDispatcher::dispatchConfigurationChangedLocked(
+ nsecs_t currentTime, ConfigurationChangedEntry* entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+ // Reset key repeating in case a keyboard device was added or removed or something.
+ resetKeyRepeatLocked();
+
+ // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+ commandEntry->eventTime = entry->eventTime;
+ return true;
+}
+
+bool InputDispatcher::dispatchKeyLocked(
+ nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+ nsecs_t* nextWakeupTime) {
+ // Preprocessing.
+ if (! entry->dispatchInProgress) {
+ logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+
+ if (entry->repeatCount == 0
+ && entry->action == AKEY_EVENT_ACTION_DOWN
+ && ! entry->isInjected()) {
+ if (mKeyRepeatState.lastKeyEntry
+ && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ // We have seen two identical key downs in a row which indicates that the device
+ // driver is automatically generating key repeats itself. We take note of the
+ // repeat here, but we disable our own next key repeat timer since it is clear that
+ // we will not need to synthesize key repeats ourselves.
+ entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+ resetKeyRepeatLocked();
+ mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+ } else {
+ // Not a repeat. Save key down state in case we do see a repeat later.
+ resetKeyRepeatLocked();
+ mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+ }
+ mKeyRepeatState.lastKeyEntry = entry;
+ entry->refCount += 1;
+ } else if (! entry->syntheticRepeat) {
+ resetKeyRepeatLocked();
+ }
+
+ entry->dispatchInProgress = true;
+ startFindingTargetsLocked();
+ }
+
+ // Identify targets.
+ if (! mCurrentInputTargetsValid) {
+ InputWindow* window = NULL;
+ int32_t injectionResult = findFocusedWindowLocked(currentTime,
+ entry, nextWakeupTime, & window);
+ if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ return false;
+ }
+
+ setInjectionResultLocked(entry, injectionResult);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return true;
+ }
+
+ addMonitoringTargetsLocked();
+ finishFindingTargetsLocked(window);
+ }
+
+ // Give the policy a chance to intercept the key.
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+ commandEntry->inputChannel = mCurrentInputChannel;
+ commandEntry->keyEntry = entry;
+ entry->refCount += 1;
+ return false; // wait for the command to run
+ }
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+ return true;
+ }
+
+ // Dispatch the key.
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+
+ // Poke user activity.
+ pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+ return true;
+}
+
+void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
+ "downTime=%lld",
+ prefix,
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+ entry->downTime);
+#endif
+}
+
+bool InputDispatcher::dispatchMotionLocked(
+ nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) {
+ // Preprocessing.
+ if (! entry->dispatchInProgress) {
+ logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+
+ entry->dispatchInProgress = true;
+ startFindingTargetsLocked();
+ }
+
+ bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+
+ // Identify targets.
+ if (! mCurrentInputTargetsValid) {
+ InputWindow* window = NULL;
+ int32_t injectionResult;
+ if (isPointerEvent) {
+ // Pointer event. (eg. touchscreen)
+ injectionResult = findTouchedWindowLocked(currentTime,
+ entry, nextWakeupTime, & window);
+ } else {
+ // Non touch event. (eg. trackball)
+ injectionResult = findFocusedWindowLocked(currentTime,
+ entry, nextWakeupTime, & window);
+ }
+ if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ return false;
+ }
+
+ setInjectionResultLocked(entry, injectionResult);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return true;
+ }
+
+ addMonitoringTargetsLocked();
+ finishFindingTargetsLocked(window);
+ }
+
+ // 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) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+ }
+ break;
+ }
+ } else {
+ eventType = POWER_MANAGER_BUTTON_EVENT;
+ }
+ pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
+ return true;
+}
+
+
+void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
"action=0x%x, flags=0x%x, "
"metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+ prefix,
entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
entry->action, entry->flags,
entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
@@ -403,7 +691,7 @@
// Print the most recent sample that we have available, this may change due to batching.
size_t sampleCount = 1;
- MotionSample* sample = & entry->firstSample;
+ const MotionSample* sample = & entry->firstSample;
for (; sample->next != NULL; sample = sample->next) {
sampleCount += 1;
}
@@ -425,68 +713,6 @@
LOGD(" ... Total movement samples currently batched %d ...", sampleCount);
}
#endif
-
- identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry);
-}
-
-void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
- nsecs_t currentTime, KeyEntry* entry) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("identifyInputTargetsAndDispatchKey");
-#endif
-
- entry->dispatchInProgress = true;
- mCurrentInputTargetsValid = false;
- mLock.unlock();
-
- mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
- entry->downTime, entry->eventTime);
-
- mCurrentInputTargets.clear();
- int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
- entry->policyFlags, entry->injectorPid, entry->injectorUid,
- mCurrentInputTargets);
-
- mLock.lock();
- mCurrentInputTargetsValid = true;
-
- setInjectionResultLocked(entry, injectionResult);
-
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
- }
-}
-
-void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
- nsecs_t currentTime, MotionEntry* entry) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("identifyInputTargetsAndDispatchMotion");
-#endif
-
- entry->dispatchInProgress = true;
- mCurrentInputTargetsValid = false;
- mLock.unlock();
-
- mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
- entry->edgeFlags, entry->metaState,
- 0, 0, entry->xPrecision, entry->yPrecision,
- entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
- entry->firstSample.pointerCoords);
-
- mCurrentInputTargets.clear();
- int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
- entry->policyFlags, entry->injectorPid, entry->injectorUid,
- mCurrentInputTargets);
-
- mLock.lock();
- mCurrentInputTargetsValid = true;
-
- setInjectionResultLocked(entry, injectionResult);
-
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
- }
}
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
@@ -494,7 +720,7 @@
#if DEBUG_DISPATCH_CYCLE
LOGD("dispatchEventToCurrentInputTargets - "
"resumeWithAppendedMotionSample=%s",
- resumeWithAppendedMotionSample ? "true" : "false");
+ toString(resumeWithAppendedMotionSample));
#endif
assert(eventEntry->dispatchInProgress); // should already have been set to true
@@ -515,6 +741,515 @@
}
}
+void InputDispatcher::startFindingTargetsLocked() {
+ mCurrentInputTargetsValid = false;
+ mCurrentInputTargets.clear();
+ mCurrentInputChannel.clear();
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) {
+ mCurrentInputWindowType = window->layoutParamsType;
+ mCurrentInputChannel = window->inputChannel;
+ mCurrentInputTargetsValid = true;
+}
+
+int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
+ const EventEntry* entry, const InputApplication* application, const InputWindow* window,
+ nsecs_t* nextWakeupTime) {
+ if (application == NULL && window == NULL) {
+ if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
+#if DEBUG_FOCUS
+ LOGD("Waiting for system to become ready for input.");
+#endif
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
+ mInputTargetWaitStartTime = currentTime;
+ mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
+ mInputTargetWaitTimeoutExpired = false;
+ }
+ } else {
+ if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+#if DEBUG_FOCUS
+ LOGD("Waiting for application to become ready for input: name=%s, window=%s",
+ application ? application->name.string() : "<unknown>",
+ window ? window->inputChannel->getName().string() : "<unknown>");
+#endif
+ nsecs_t timeout = window ? window->dispatchingTimeout :
+ application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
+ mInputTargetWaitStartTime = currentTime;
+ mInputTargetWaitTimeoutTime = currentTime + timeout;
+ mInputTargetWaitTimeoutExpired = false;
+ }
+ }
+
+ if (mInputTargetWaitTimeoutExpired) {
+ return INPUT_EVENT_INJECTION_TIMED_OUT;
+ }
+
+ if (currentTime >= mInputTargetWaitTimeoutTime) {
+ LOGI("Application is not ready for input: name=%s, window=%s,"
+ "%01.1fms since event, %01.1fms since wait started",
+ application ? application->name.string() : "<unknown>",
+ window ? window->inputChannel->getName().string() : "<unknown>",
+ (currentTime - entry->eventTime) / 1000000.0,
+ (currentTime - mInputTargetWaitStartTime) / 1000000.0);
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible);
+ if (application) {
+ commandEntry->inputApplicationHandle = application->handle;
+ }
+ if (window) {
+ commandEntry->inputChannel = window->inputChannel;
+ }
+
+ // Force poll loop to wake up immediately on next iteration once we get the
+ // ANR response back from the policy.
+ *nextWakeupTime = LONG_LONG_MIN;
+ return INPUT_EVENT_INJECTION_PENDING;
+ } else {
+ // Force poll loop to wake up when timeout is due.
+ if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
+ *nextWakeupTime = mInputTargetWaitTimeoutTime;
+ }
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+}
+
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) {
+ if (newTimeout > 0) {
+ // Extend the timeout.
+ mInputTargetWaitTimeoutTime = now() + newTimeout;
+ } else {
+ // Give up.
+ mInputTargetWaitTimeoutExpired = true;
+ }
+}
+
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(
+ nsecs_t currentTime) {
+ if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+ return currentTime - mInputTargetWaitStartTime;
+ }
+ return 0;
+}
+
+void InputDispatcher::resetANRTimeoutsLocked() {
+#if DEBUG_FOCUS
+ LOGD("Resetting ANR timeouts.");
+#endif
+
+ // Reset timeouts for all active connections.
+ nsecs_t currentTime = now();
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ connection->resetTimeout(currentTime);
+ }
+
+ // Reset input target wait timeout.
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
+ nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+ *outWindow = NULL;
+ mCurrentInputTargets.clear();
+
+ int32_t injectionResult;
+
+ // If there is no currently focused window and no focused application
+ // then drop the event.
+ if (! mFocusedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may eventually add a window: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, NULL, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ LOGI("Dropping event because there is no focused window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+
+ // If the currently focused window is paused then keep waiting.
+ if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window is paused.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, mFocusedWindow, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ // Success! Output targets.
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ *outWindow = mFocusedWindow;
+ addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC,
+ getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+
+ // Done.
+Failed:
+Unresponsive:
+#if DEBUG_FOCUS
+ LOGD("findFocusedWindow finished: injectionResult=%d",
+ injectionResult);
+ logDispatchStateLocked();
+#endif
+ return injectionResult;
+}
+
+int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
+ nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+ enum InjectionPermission {
+ INJECTION_PERMISSION_UNKNOWN,
+ INJECTION_PERMISSION_GRANTED,
+ INJECTION_PERMISSION_DENIED
+ };
+
+ *outWindow = NULL;
+ mCurrentInputTargets.clear();
+
+ nsecs_t startTime = now();
+
+ // For security reasons, we defer updating the touch state until we are sure that
+ // event injection will be allowed.
+ //
+ // FIXME In the original code, screenWasOff could never be set to true.
+ // The reason is that the POLICY_FLAG_WOKE_HERE
+ // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+ // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
+ // actually enqueued using the policyFlags that appeared in the final EV_SYN
+ // events upon which no preprocessing took place. So policyFlags was always 0.
+ // In the new native input dispatcher we're a bit more careful about event
+ // preprocessing so the touches we receive can actually have non-zero policyFlags.
+ // Unfortunately we obtain undesirable behavior.
+ //
+ // Here's what happens:
+ //
+ // When the device dims in anticipation of going to sleep, touches
+ // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+ // the device to brighten and reset the user activity timer.
+ // Touches on other windows (such as the launcher window)
+ // are dropped. Then after a moment, the device goes to sleep. Oops.
+ //
+ // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+ // instead of POLICY_FLAG_WOKE_HERE...
+ //
+ bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+ int32_t action = entry->action;
+
+ // 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 */
+
+ InputWindow* newTouchedWindow = NULL;
+ mTempTouchedOutsideTargets.clear();
+
+ 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;
+
+ // 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);
+ int32_t flags = window->layoutParamsFlags;
+
+ if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
+ if (! topErrorWindow) {
+ topErrorWindow = window;
+ }
+ }
+
+ if (window->visible) {
+ if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
+ bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
+ | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
+ 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 there is an error window but it is not taking focus (typically because
+ // it is invisible) then wait for it. Any other focused window may in
+ // fact be in ANR state.
+ if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because system error window is pending.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, NULL, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ goto Unresponsive;
+ }
+
+ // If we did not find a touched window then fail.
+ if (! newTouchedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no touched window but there is a "
+ "focused application that may eventually add a new window: '%s'.",
+ mFocusedApplication->name.string());
+#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;
+ }
+
+ // 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;
+ }
+
+ // Success! Update the touch dispatch state for real.
+ releaseTouchedWindowLocked();
+
+ mTouchedWindow = newTouchedWindow;
+ mTouchedWindowIsObscured = obscured;
+
+ if (newTouchedWindow->hasWallpaper) {
+ mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+ }
+ } 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;
+ }
+
+ // If the pointer is not currently down, then ignore the event.
+ if (! mTouchDown) {
+ 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) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Dropping event because there is no touched 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;
+ }
+ }
+
+ // 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, 0);
+ }
+
+ 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, 0);
+ }
+ mTempTouchedOutsideTargets.clear();
+
+ int32_t targetFlags = InputTarget::FLAG_SYNC;
+ if (mTouchedWindowIsObscured) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+ addWindowTargetLocked(mTouchedWindow, targetFlags,
+ getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime));
+ *outWindow = mTouchedWindow;
+ }
+
+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)) {
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ } else {
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ }
+ }
+
+ // 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!
+ LOGW("Pointer down received while already down.");
+ } else {
+ mTouchDown = true;
+ }
+
+ 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();
+ }
+ } else if (action == AMOTION_EVENT_ACTION_UP) {
+ mTouchDown = false;
+ releaseTouchedWindowLocked();
+ }
+ } else {
+ LOGW("Not updating touch focus because injection was denied.");
+ }
+
+Unresponsive:
+#if DEBUG_FOCUS
+ LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d",
+ injectionResult, injectionPermission);
+ logDispatchStateLocked();
+#endif
+ return injectionResult;
+}
+
+void InputDispatcher::releaseTouchedWindowLocked() {
+ mTouchedWindow = NULL;
+ mTouchedWindowIsObscured = false;
+ mTouchedWallpaperWindows.clear();
+}
+
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication) {
+ mCurrentInputTargets.push();
+
+ InputTarget& target = mCurrentInputTargets.editTop();
+ target.inputChannel = window->inputChannel;
+ target.flags = targetFlags;
+ target.timeout = window->dispatchingTimeout;
+ target.timeSpentWaitingForApplication = timeSpentWaitingForApplication;
+ target.xOffset = - window->frameLeft;
+ target.yOffset = - window->frameTop;
+}
+
+void InputDispatcher::addMonitoringTargetsLocked() {
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ mCurrentInputTargets.push();
+
+ InputTarget& target = mCurrentInputTargets.editTop();
+ target.inputChannel = mMonitoringChannels[i];
+ target.flags = 0;
+ target.timeout = -1;
+ target.timeSpentWaitingForApplication = 0;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ }
+}
+
+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);
+ 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(),
+ window->ownerUid);
+ } else {
+ LOGW("Permission denied: injecting event from pid %d uid %d",
+ injectorPid, injectorUid);
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) {
+ 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)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
+ int32_t windowType, int32_t eventType) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doPokeUserActivityLockedInterruptible);
+ commandEntry->eventTime = eventTime;
+ commandEntry->windowType = windowType;
+ commandEntry->userActivityEventType = eventType;
+}
+
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample) {
@@ -523,15 +1258,21 @@
"xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
inputTarget->xOffset, inputTarget->yOffset,
- resumeWithAppendedMotionSample ? "true" : "false");
+ toString(resumeWithAppendedMotionSample));
#endif
// Skip this event if the connection status is not normal.
- // We don't want to queue outbound events at all if the connection is broken or
+ // We don't want to enqueue additional outbound events if the connection is broken or
// not responding.
if (connection->status != Connection::STATUS_NORMAL) {
- LOGV("channel '%s' ~ Dropping event because the channel status is %s",
- connection->getStatusLabel());
+ LOGW("channel '%s' ~ Dropping event because the channel status is %s",
+ connection->getInputChannelName(), connection->getStatusLabel());
+
+ // If the connection is not responding but the user is poking the application anyways,
+ // retrigger the original timeout.
+ if (connection->status == Connection::STATUS_NOT_RESPONDING) {
+ timeoutDispatchCycleLocked(currentTime, connection);
+ }
return;
}
@@ -612,17 +1353,45 @@
}
}
+ // Bring the input state back in line with reality in case it drifted off during an ANR.
+ if (connection->inputState.isOutOfSync()) {
+ mTempCancelationEvents.clear();
+ connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents);
+ connection->inputState.resetOutOfSync();
+
+ if (! mTempCancelationEvents.isEmpty()) {
+ LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync "
+ "with reality.",
+ connection->getInputChannelName(), mTempCancelationEvents.size());
+
+ for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
+ EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
+ switch (cancelationEventEntry->type) {
+ case EventEntry::TYPE_KEY:
+ logOutboundKeyDetailsLocked(" ",
+ static_cast<KeyEntry*>(cancelationEventEntry));
+ break;
+ case EventEntry::TYPE_MOTION:
+ logOutboundMotionDetailsLocked(" ",
+ static_cast<MotionEntry*>(cancelationEventEntry));
+ break;
+ }
+
+ DispatchEntry* cancelationDispatchEntry =
+ mAllocator.obtainDispatchEntry(cancelationEventEntry,
+ 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout);
+ connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+
+ mAllocator.releaseEventEntry(cancelationEventEntry);
+ }
+ }
+ }
+
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
- DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref
- dispatchEntry->targetFlags = inputTarget->flags;
- dispatchEntry->xOffset = inputTarget->xOffset;
- dispatchEntry->yOffset = inputTarget->yOffset;
- dispatchEntry->timeout = inputTarget->timeout;
- dispatchEntry->inProgress = false;
- dispatchEntry->headMotionSample = NULL;
- dispatchEntry->tailMotionSample = NULL;
-
+ DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
+ inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset,
+ inputTarget->timeout);
if (dispatchEntry->isSyncTarget()) {
eventEntry->pendingSyncDispatches += 1;
}
@@ -647,12 +1416,13 @@
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty) {
activateConnectionLocked(connection.get());
- startDispatchCycleLocked(currentTime, connection);
+ startDispatchCycleLocked(currentTime, connection,
+ inputTarget->timeSpentWaitingForApplication);
}
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
+ const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ startDispatchCycle",
connection->getInputChannelName());
@@ -661,12 +1431,37 @@
assert(connection->status == Connection::STATUS_NORMAL);
assert(! connection->outboundQueue.isEmpty());
- DispatchEntry* dispatchEntry = connection->outboundQueue.head.next;
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
assert(! dispatchEntry->inProgress);
- // TODO throttle successive ACTION_MOVE motion events for the same device
- // possible implementation could set a brief poll timeout here and resume starting the
- // dispatch cycle when elapsed
+ // Mark the dispatch entry as in progress.
+ dispatchEntry->inProgress = true;
+
+ // Update the connection's input state.
+ InputState::Consistency consistency = connection->inputState.trackEvent(
+ dispatchEntry->eventEntry);
+
+#if FILTER_INPUT_EVENTS
+ // Filter out inconsistent sequences of input events.
+ // The input system may drop or inject events in a way that could violate implicit
+ // invariants on input state and potentially cause an application to crash
+ // or think that a key or pointer is stuck down. Technically we make no guarantees
+ // of consistency but it would be nice to improve on this where possible.
+ // XXX: This code is a proof of concept only. Not ready for prime time.
+ if (consistency == InputState::TOLERABLE) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's "
+ "current input state but that is likely to be tolerated by the application.",
+ connection->getInputChannelName());
+#endif
+ } else if (consistency == InputState::BROKEN) {
+ LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's "
+ "current input state and that is likely to cause the application to crash.",
+ connection->getInputChannelName());
+ startNextDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+#endif
// Publish the event.
status_t status;
@@ -790,12 +1585,10 @@
}
// Record information about the newly started dispatch cycle.
- dispatchEntry->inProgress = true;
-
connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
connection->lastDispatchTime = currentTime;
- nsecs_t timeout = dispatchEntry->timeout;
+ nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication;
connection->setNextTimeoutTime(currentTime, timeout);
// Notify other system components.
@@ -844,9 +1637,14 @@
return;
}
+ startNextDispatchCycleLocked(currentTime, connection);
+}
+
+void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
// Start the next dispatch cycle for this connection.
while (! connection->outboundQueue.isEmpty()) {
- DispatchEntry* dispatchEntry = connection->outboundQueue.head.next;
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
if (dispatchEntry->inProgress) {
// Finish or resume current event in progress.
if (dispatchEntry->tailMotionSample) {
@@ -855,7 +1653,7 @@
dispatchEntry->inProgress = false;
dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
dispatchEntry->tailMotionSample = NULL;
- startDispatchCycleLocked(currentTime, connection);
+ startDispatchCycleLocked(currentTime, connection, 0);
return;
}
// Finished.
@@ -868,7 +1666,7 @@
// If the head is not in progress, then we must have already dequeued the in
// progress event, which means we actually aborted it (due to ANR).
// So just start the next event for this connection.
- startDispatchCycleLocked(currentTime, connection);
+ startDispatchCycleLocked(currentTime, connection, 0);
return;
}
}
@@ -884,58 +1682,74 @@
connection->getInputChannelName());
#endif
- if (connection->status != Connection::STATUS_NORMAL) {
+ if (connection->status == Connection::STATUS_NORMAL) {
+ // Enter the not responding state.
+ connection->status = Connection::STATUS_NOT_RESPONDING;
+ connection->lastANRTime = currentTime;
+ } else if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+ // Connection is broken or dead.
return;
}
- // Enter the not responding state.
- connection->status = Connection::STATUS_NOT_RESPONDING;
- connection->lastANRTime = currentTime;
-
// Notify other system components.
- // This enqueues a command which will eventually either call
- // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
+ // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked.
onDispatchCycleANRLocked(currentTime, connection);
}
void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, nsecs_t newTimeout) {
#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked",
- connection->getInputChannelName());
+ LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld",
+ connection->getInputChannelName(), newTimeout);
#endif
if (connection->status != Connection::STATUS_NOT_RESPONDING) {
return;
}
- // Resume normal dispatch.
- connection->status = Connection::STATUS_NORMAL;
- connection->setNextTimeoutTime(currentTime, newTimeout);
+ if (newTimeout > 0) {
+ // The system has decided to give the application some more time.
+ // Keep waiting synchronously and resume normal dispatch.
+ connection->status = Connection::STATUS_NORMAL;
+ connection->setNextTimeoutTime(currentTime, newTimeout);
+ } else {
+ // The system is about to throw up an ANR dialog and has requested that we abort dispatch.
+ // Reset the timeout.
+ connection->nextTimeoutTime = LONG_LONG_MAX;
+
+ // Input state will no longer be realistic.
+ connection->inputState.setOutOfSync();
+
+ if (! connection->outboundQueue.isEmpty()) {
+ // Make the current pending dispatch asynchronous (if it isn't already) so that
+ // subsequent events can be delivered to the ANR dialog or to another application.
+ DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next;
+ currentDispatchEntry->preemptSyncTarget();
+
+ // Drain all but the first entry in the outbound queue. We keep the first entry
+ // since that is the one that dispatch is stuck on. We throw away the others
+ // so that we don't spam the application with stale messages if it eventually
+ // wakes up and recovers from the ANR.
+ drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next);
+ }
+ }
}
void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, bool broken) {
#if DEBUG_DISPATCH_CYCLE
LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
- connection->getInputChannelName(), broken ? "true" : "false");
+ connection->getInputChannelName(), toString(broken));
#endif
// Clear the pending timeout.
connection->nextTimeoutTime = LONG_LONG_MAX;
- // Clear the outbound queue.
- if (! connection->outboundQueue.isEmpty()) {
- do {
- DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
- if (dispatchEntry->isSyncTarget()) {
- decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
- }
- mAllocator.releaseDispatchEntry(dispatchEntry);
- } while (! connection->outboundQueue.isEmpty());
+ // Input state will no longer be realistic.
+ connection->inputState.setOutOfSync();
- deactivateConnectionLocked(connection.get());
- }
+ // Clear the outbound queue.
+ drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next);
// Handle the case where the connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
@@ -950,6 +1764,26 @@
}
}
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection,
+ DispatchEntry* firstDispatchEntryToDrain) {
+ for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain;
+ dispatchEntry != & connection->outboundQueue.tailSentinel;) {
+ DispatchEntry* next = dispatchEntry->next;
+ connection->outboundQueue.dequeue(dispatchEntry);
+
+ if (dispatchEntry->isSyncTarget()) {
+ decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry);
+ }
+ mAllocator.releaseDispatchEntry(dispatchEntry);
+
+ dispatchEntry = next;
+ }
+
+ if (connection->outboundQueue.isEmpty()) {
+ deactivateConnectionLocked(connection);
+ }
+}
+
bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
InputDispatcher* d = static_cast<InputDispatcher*>(data);
@@ -1000,57 +1834,19 @@
LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
#endif
- bool wasEmpty;
+ bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
-
- wasEmpty = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(newEntry);
+ needWake = enqueueInboundEventLocked(newEntry);
} // release lock
- if (wasEmpty) {
+ if (needWake) {
mPollLoop->wake();
}
}
-void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime);
-#endif
-
- // Remove movement keys from the queue from most recent to least recent, stopping at the
- // first non-movement key.
- // TODO: Include a detailed description of why we do this...
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) {
- EventEntry* prev = entry->prev;
-
- if (entry->type == EventEntry::TYPE_KEY) {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
- if (isMovementKey(keyEntry->keyCode)) {
- LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
- keyEntry->keyCode, keyEntry->action);
- mInboundQueue.dequeue(keyEntry);
-
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
-
- mAllocator.releaseKeyEntry(keyEntry);
- } else {
- // stop at last non-movement key
- break;
- }
- }
-
- entry = prev;
- }
- } // release lock
-}
-
void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
uint32_t policyFlags, int32_t action, int32_t flags,
int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
@@ -1061,7 +1857,7 @@
keyCode, scanCode, metaState, downTime);
#endif
- bool wasEmpty;
+ bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
@@ -1070,11 +1866,10 @@
deviceId, source, policyFlags, action, flags, keyCode, scanCode,
metaState, repeatCount, downTime);
- wasEmpty = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(newEntry);
+ needWake = enqueueInboundEventLocked(newEntry);
} // release lock
- if (wasEmpty) {
+ if (needWake) {
mPollLoop->wake();
}
}
@@ -1101,7 +1896,7 @@
}
#endif
- bool wasEmpty;
+ bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
@@ -1112,8 +1907,8 @@
// Try to append a move sample to the tail of the inbound queue for this device.
// Give up if we encounter a non-move motion event for this device since that
// means we cannot append any new samples until a new motion event has started.
- for (EventEntry* entry = mInboundQueue.tail.prev;
- entry != & mInboundQueue.head; entry = entry->prev) {
+ for (EventEntry* entry = mInboundQueue.tailSentinel.prev;
+ entry != & mInboundQueue.headSentinel; entry = entry->prev) {
if (entry->type != EventEntry::TYPE_MOTION) {
// Keep looking for motion events.
continue;
@@ -1140,18 +1935,6 @@
LOGD("Appended motion sample onto batch for most recent "
"motion event for this device in the inbound queue.");
#endif
-
- // Sanity check for special case because dispatch is interruptible.
- // The dispatch logic is partially interruptible and releases its lock while
- // identifying targets. However, as soon as the targets have been identified,
- // the dispatcher proceeds to write a dispatch entry into all relevant outbound
- // queues and then promptly removes the motion entry from the queue.
- //
- // Consequently, we should never observe the case where the inbound queue contains
- // an in-progress motion entry unless the current input targets are invalid
- // (currently being computed). Check for this!
- assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid));
-
return; // done!
}
@@ -1178,7 +1961,7 @@
for (size_t i = 0; i < mActiveConnections.size(); i++) {
Connection* connection = mActiveConnections.itemAt(i);
if (! connection->outboundQueue.isEmpty()) {
- DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
+ DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev;
if (dispatchEntry->isSyncTarget()) {
if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
goto NoBatchingOrStreaming;
@@ -1220,11 +2003,10 @@
xPrecision, yPrecision, downTime,
pointerCount, pointerIds, pointerCoords);
- wasEmpty = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(newEntry);
+ needWake = enqueueInboundEventLocked(newEntry);
} // release lock
- if (wasEmpty) {
+ if (needWake) {
mPollLoop->wake();
}
}
@@ -1240,11 +2022,15 @@
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
EventEntry* injectedEntry;
- bool wasEmpty;
+ bool needWake;
{ // acquire lock
AutoMutex _l(mLock);
- injectedEntry = createEntryFromInputEventLocked(event);
+ injectedEntry = createEntryFromInjectedInputEventLocked(event);
+ if (! injectedEntry) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
injectedEntry->refCount += 1;
injectedEntry->injectorPid = injectorPid;
injectedEntry->injectorUid = injectorUid;
@@ -1253,12 +2039,10 @@
injectedEntry->injectionIsAsync = true;
}
- wasEmpty = mInboundQueue.isEmpty();
- mInboundQueue.enqueueAtTail(injectedEntry);
-
+ needWake = enqueueInboundEventLocked(injectedEntry);
} // release lock
- if (wasEmpty) {
+ if (needWake) {
mPollLoop->wake();
}
@@ -1361,11 +2145,42 @@
}
}
-InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
+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;
+ }
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked(
const InputEvent* event) {
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());
+ return NULL;
+ }
+
uint32_t policyFlags = POLICY_FLAG_INJECTED;
KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
@@ -1378,6 +2193,17 @@
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());
+ 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;
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -1405,33 +2231,143 @@
}
}
-void InputDispatcher::resetKeyRepeatLocked() {
- if (mKeyRepeatState.lastKeyEntry) {
- mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
- mKeyRepeatState.lastKeyEntry = NULL;
+void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
+#if DEBUG_FOCUS
+ LOGD("setInputWindows");
+#endif
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ 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();
+ }
+
+ bool hadFocusedWindow = mFocusedWindow != NULL;
+
+ mFocusedWindow = NULL;
+ mWallpaperWindows.clear();
+
+ mWindows.clear();
+ mWindows.appendVector(inputWindows);
+
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(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;
+ }
+ }
+
+ mTempTouchedWallpaperChannels.clear();
+
+ if ((hadFocusedWindow && ! mFocusedWindow)
+ || (mFocusedWindow && ! mFocusedWindow->visible)) {
+ preemptInputDispatchInnerLocked();
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mPollLoop->wake();
+}
+
+void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
+#if DEBUG_FOCUS
+ LOGD("setFocusedApplication");
+#endif
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ releaseFocusedApplicationLocked();
+
+ if (inputApplication) {
+ mFocusedApplicationStorage = *inputApplication;
+ mFocusedApplication = & mFocusedApplicationStorage;
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mPollLoop->wake();
+}
+
+void InputDispatcher::releaseFocusedApplicationLocked() {
+ if (mFocusedApplication) {
+ mFocusedApplication = NULL;
+ mFocusedApplicationStorage.handle.clear();
+ }
+}
+
+void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+ LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+ bool changed;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ if (mDispatchFrozen && ! frozen) {
+ resetANRTimeoutsLocked();
+ }
+
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+ changed = true;
+ } else {
+ changed = false;
+ }
+
+#if DEBUG_FOCUS
+ logDispatchStateLocked();
+#endif
+ } // release lock
+
+ if (changed) {
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mPollLoop->wake();
}
}
void InputDispatcher::preemptInputDispatch() {
-#if DEBUG_DISPATCH_CYCLE
+#if DEBUG_FOCUS
LOGD("preemptInputDispatch");
#endif
- bool preemptedOne = false;
+ bool preemptedOne;
{ // acquire lock
AutoMutex _l(mLock);
-
- for (size_t i = 0; i < mActiveConnections.size(); i++) {
- Connection* connection = mActiveConnections[i];
- if (connection->hasPendingSyncTarget()) {
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
- connection->getInputChannelName());
-#endif
- connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
- preemptedOne = true;
- }
- }
+ preemptedOne = preemptInputDispatchInnerLocked();
} // release lock
if (preemptedOne) {
@@ -1440,9 +2376,99 @@
}
}
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+bool InputDispatcher::preemptInputDispatchInnerLocked() {
+ bool preemptedOne = false;
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+ connection->getInputChannelName());
+#endif
+ connection->preemptSyncTarget();
+ preemptedOne = true;
+ }
+ }
+ return preemptedOne;
+}
+
+void InputDispatcher::logDispatchStateLocked() {
+ String8 dump;
+ dumpDispatchStateLocked(dump);
+ LOGD("%s", dump.string());
+}
+
+void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
+ dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled);
+ dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen);
+
+ if (mFocusedApplication) {
+ dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+ mFocusedApplication->name.string(),
+ mFocusedApplication->dispatchingTimeout / 1000000.0);
+ } else {
+ dump.append(" focusedApplication: <null>\n");
+ }
+ dump.appendFormat(" focusedWindow: '%s'\n",
+ mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+ dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n",
+ mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+ mTouchDown);
+ for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+ dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n",
+ i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+ }
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ dump.appendFormat(" windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+ "visible=%s, flags=0x%08x, type=0x%08x, "
+ "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].inputChannel->getName().string(),
+ toString(mWindows[i].paused),
+ toString(mWindows[i].hasFocus),
+ toString(mWindows[i].hasWallpaper),
+ toString(mWindows[i].visible),
+ mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+ 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);
+ }
+
+ 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());
+ }
+
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ const Connection* connection = mActiveConnections[i];
+ dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, "
+ "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n",
+ i, connection->getInputChannelName(), connection->getStatusLabel(),
+ toString(connection->hasPendingSyncTarget()),
+ toString(connection->inputState.isNeutral()),
+ toString(connection->inputState.isOutOfSync()));
+ }
+
+ if (isAppSwitchPendingLocked()) {
+ dump.appendFormat(" appSwitch: pending, due in %01.1fms\n",
+ (mAppSwitchDueTime - now()) / 1000000.0);
+ } else {
+ dump.append(" appSwitch: not pending\n");
+ }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
#if DEBUG_REGISTRATION
- LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
+ LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
+ toString(monitor));
#endif
{ // acquire lock
@@ -1465,6 +2491,10 @@
int32_t receiveFd = inputChannel->getReceivePipeFd();
mConnectionsByReceiveFd.add(receiveFd, connection);
+ if (monitor) {
+ mMonitoringChannels.push(inputChannel);
+ }
+
mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
runCommandsLockedInterruptible();
@@ -1492,6 +2522,13 @@
connection->status = Connection::STATUS_ZOMBIE;
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ if (mMonitoringChannels[i] == inputChannel) {
+ mMonitoringChannels.removeAt(i);
+ break;
+ }
+ }
+
mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
nsecs_t currentTime = now();
@@ -1578,6 +2615,15 @@
commandEntry->connection = connection;
}
+void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
+
+ mLock.lock();
+}
+
void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
@@ -1598,17 +2644,12 @@
if (connection->status != Connection::STATUS_ZOMBIE) {
mLock.unlock();
- nsecs_t newTimeout;
- bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
+ nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel);
mLock.lock();
nsecs_t currentTime = now();
- if (resume) {
- resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
- } else {
- abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
- }
+ resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
}
}
@@ -1625,6 +2666,57 @@
}
}
+void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
+ CommandEntry* commandEntry) {
+ KeyEntry* entry = commandEntry->keyEntry;
+ mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+ entry->downTime, entry->eventTime);
+
+ mLock.unlock();
+
+ bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
+ & mReusableKeyEvent, entry->policyFlags);
+
+ mLock.lock();
+
+ entry->interceptKeyResult = consumed
+ ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
+ : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ mAllocator.releaseKeyEntry(entry);
+}
+
+void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
+ commandEntry->userActivityEventType);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ nsecs_t newTimeout;
+ if (commandEntry->inputChannel.get()) {
+ newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
+ } else if (commandEntry->inputApplicationHandle.get()) {
+ newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle);
+ } else {
+ newTimeout = 0;
+ }
+
+ mLock.lock();
+
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout);
+}
+
+void InputDispatcher::dump(String8& dump) {
+ dumpDispatchStateLocked(dump);
+}
+
// --- InputDispatcher::Allocator ---
@@ -1668,6 +2760,8 @@
entry->metaState = metaState;
entry->repeatCount = repeatCount;
entry->downTime = downTime;
+ entry->syntheticRepeat = false;
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
return entry;
}
@@ -1702,10 +2796,18 @@
}
InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
- EventEntry* eventEntry) {
+ EventEntry* eventEntry,
+ int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) {
DispatchEntry* entry = mDispatchEntryPool.alloc();
entry->eventEntry = eventEntry;
eventEntry->refCount += 1;
+ entry->targetFlags = targetFlags;
+ entry->xOffset = xOffset;
+ entry->yOffset = yOffset;
+ entry->timeout = timeout;
+ entry->inProgress = false;
+ entry->headMotionSample = NULL;
+ entry->tailMotionSample = NULL;
return entry;
}
@@ -1788,6 +2890,25 @@
motionEntry->lastSample = sample;
}
+
+// --- InputDispatcher::EventEntry ---
+
+void InputDispatcher::EventEntry::recycle() {
+ injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ dispatchInProgress = false;
+ pendingSyncDispatches = 0;
+}
+
+
+// --- InputDispatcher::KeyEntry ---
+
+void InputDispatcher::KeyEntry::recycle() {
+ EventEntry::recycle();
+ syntheticRepeat = false;
+ interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+}
+
+
// --- InputDispatcher::MotionEntry ---
uint32_t InputDispatcher::MotionEntry::countSamples() const {
@@ -1798,6 +2919,189 @@
return count;
}
+
+// --- InputDispatcher::InputState ---
+
+InputDispatcher::InputState::InputState() :
+ mIsOutOfSync(false) {
+}
+
+InputDispatcher::InputState::~InputState() {
+}
+
+bool InputDispatcher::InputState::isNeutral() const {
+ return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
+}
+
+bool InputDispatcher::InputState::isOutOfSync() const {
+ return mIsOutOfSync;
+}
+
+void InputDispatcher::InputState::setOutOfSync() {
+ if (! isNeutral()) {
+ mIsOutOfSync = true;
+ }
+}
+
+void InputDispatcher::InputState::resetOutOfSync() {
+ mIsOutOfSync = false;
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent(
+ const EventEntry* entry) {
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY:
+ return trackKey(static_cast<const KeyEntry*>(entry));
+
+ case EventEntry::TYPE_MOTION:
+ return trackMotion(static_cast<const MotionEntry*>(entry));
+
+ default:
+ return CONSISTENT;
+ }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
+ const KeyEntry* entry) {
+ int32_t action = entry->action;
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ KeyMemento& memento = mKeyMementos.editItemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source
+ && memento.keyCode == entry->keyCode
+ && memento.scanCode == entry->scanCode) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_UP:
+ mKeyMementos.removeAt(i);
+ if (isNeutral()) {
+ mIsOutOfSync = false;
+ }
+ return CONSISTENT;
+
+ case AKEY_EVENT_ACTION_DOWN:
+ return TOLERABLE;
+
+ default:
+ return BROKEN;
+ }
+ }
+ }
+
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN: {
+ mKeyMementos.push();
+ KeyMemento& memento = mKeyMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.keyCode = entry->keyCode;
+ memento.scanCode = entry->scanCode;
+ memento.downTime = entry->downTime;
+ return CONSISTENT;
+ }
+
+ default:
+ return BROKEN;
+ }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion(
+ const MotionEntry* entry) {
+ int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ MotionMemento& memento = mMotionMementos.editItemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source) {
+ switch (action) {
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ mMotionMementos.removeAt(i);
+ if (isNeutral()) {
+ mIsOutOfSync = false;
+ }
+ return CONSISTENT;
+
+ case AMOTION_EVENT_ACTION_DOWN:
+ return TOLERABLE;
+
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ if (entry->pointerCount == memento.pointerCount + 1) {
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ if (entry->pointerCount == memento.pointerCount - 1) {
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ case AMOTION_EVENT_ACTION_MOVE:
+ if (entry->pointerCount == memento.pointerCount) {
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ default:
+ return BROKEN;
+ }
+ }
+ }
+
+ switch (action) {
+ case AMOTION_EVENT_ACTION_DOWN: {
+ mMotionMementos.push();
+ MotionMemento& memento = mMotionMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.xPrecision = entry->xPrecision;
+ memento.yPrecision = entry->yPrecision;
+ memento.downTime = entry->downTime;
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+
+ default:
+ return BROKEN;
+ }
+}
+
+void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+ pointerCount = entry->pointerCount;
+ for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ pointerIds[i] = entry->pointerIds[i];
+ pointerCoords[i] = entry->lastSample->pointerCoords[i];
+ }
+}
+
+void InputDispatcher::InputState::synthesizeCancelationEvents(
+ Allocator* allocator, Vector<EventEntry*>& outEvents) const {
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ const KeyMemento& memento = mKeyMementos.itemAt(i);
+ outEvents.push(allocator->obtainKeyEntry(now(),
+ memento.deviceId, memento.source, 0,
+ AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
+ memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
+ }
+
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ const MotionMemento& memento = mMotionMementos.itemAt(i);
+ outEvents.push(allocator->obtainMotionEntry(now(),
+ memento.deviceId, memento.source, 0,
+ AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
+ memento.xPrecision, memento.yPrecision, memento.downTime,
+ memento.pointerCount, memento.pointerIds, memento.pointerCoords));
+ }
+}
+
+void InputDispatcher::InputState::clear() {
+ mKeyMementos.clear();
+ mMotionMementos.clear();
+ mIsOutOfSync = false;
+}
+
+
// --- InputDispatcher::Connection ---
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
@@ -1818,6 +3122,14 @@
nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
}
+void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) {
+ if (outboundQueue.isEmpty()) {
+ nextTimeoutTime = LONG_LONG_MAX;
+ } else {
+ setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout);
+ }
+}
+
const char* InputDispatcher::Connection::getStatusLabel() const {
switch (status) {
case STATUS_NORMAL:
@@ -1839,8 +3151,8 @@
InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
const EventEntry* eventEntry) const {
- for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev;
- dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) {
+ for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev;
+ dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) {
if (dispatchEntry->eventEntry == eventEntry) {
return dispatchEntry;
}
@@ -1848,9 +3160,11 @@
return NULL;
}
+
// --- InputDispatcher::CommandEntry ---
-InputDispatcher::CommandEntry::CommandEntry() {
+InputDispatcher::CommandEntry::CommandEntry() :
+ keyEntry(NULL) {
}
InputDispatcher::CommandEntry::~CommandEntry() {
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index ed4f07b..09fce38 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -72,52 +72,12 @@
return OK;
}
-status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) {
- return mDispatcher->registerInputChannel(inputChannel);
+sp<InputReaderInterface> InputManager::getReader() {
+ return mReader;
}
-status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
- return mDispatcher->unregisterInputChannel(inputChannel);
-}
-
-int32_t InputManager::injectInputEvent(const InputEvent* event,
- int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
- return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
-}
-
-void InputManager::preemptInputDispatch() {
- mDispatcher->preemptInputDispatch();
-}
-
-void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) {
- mReader->getInputConfiguration(outConfiguration);
-}
-
-status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
- return mReader->getInputDeviceInfo(deviceId, outDeviceInfo);
-}
-
-void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
- mReader->getInputDeviceIds(outDeviceIds);
-}
-
-int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t scanCode) {
- return mReader->getScanCodeState(deviceId, sourceMask, scanCode);
-}
-
-int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
- int32_t keyCode) {
- return mReader->getKeyCodeState(deviceId, sourceMask, keyCode);
-}
-
-int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) {
- return mReader->getSwitchState(deviceId, sourceMask, sw);
-}
-
-bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask,
- size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
- return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+sp<InputDispatcherInterface> InputManager::getDispatcher() {
+ return mDispatcher;
}
} // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index d57b38c..88084c0 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -573,6 +573,60 @@
} // release device registy reader lock
}
+void InputReader::dump(String8& dump) {
+ dumpDeviceInfo(dump);
+}
+
+static void dumpMotionRange(String8& dump,
+ const char* name, const InputDeviceInfo::MotionRange* range) {
+ if (range) {
+ dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
+ name, range->min, range->max, range->flat, range->fuzz);
+ }
+}
+
+#define DUMP_MOTION_RANGE(range) \
+ dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
+
+void InputReader::dumpDeviceInfo(String8& dump) {
+ Vector<int32_t> deviceIds;
+ getInputDeviceIds(deviceIds);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < deviceIds.size(); i++) {
+ int32_t deviceId = deviceIds[i];
+
+ status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
+ if (result == NAME_NOT_FOUND) {
+ continue;
+ } else if (result != OK) {
+ dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
+ result);
+ continue;
+ }
+
+ dump.appendFormat(" Device %d: '%s'\n",
+ deviceInfo.getId(), deviceInfo.getName().string());
+ dump.appendFormat(" sources = 0x%08x\n",
+ deviceInfo.getSources());
+ dump.appendFormat(" keyboardType = %d\n",
+ deviceInfo.getKeyboardType());
+
+ dump.append(" motion ranges:\n");
+ DUMP_MOTION_RANGE(X);
+ DUMP_MOTION_RANGE(Y);
+ DUMP_MOTION_RANGE(PRESSURE);
+ DUMP_MOTION_RANGE(SIZE);
+ DUMP_MOTION_RANGE(TOUCH_MAJOR);
+ DUMP_MOTION_RANGE(TOUCH_MINOR);
+ DUMP_MOTION_RANGE(TOOL_MAJOR);
+ DUMP_MOTION_RANGE(TOOL_MINOR);
+ DUMP_MOTION_RANGE(ORIENTATION);
+ }
+}
+
+#undef DUMP_MOTION_RANGE
+
// --- InputReaderThread ---
@@ -740,10 +794,6 @@
}
bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) {
- if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
- getDispatcher()->notifyAppSwitchComing(when);
- }
-
return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
}
@@ -1249,20 +1299,12 @@
mLocked.orientedRanges.haveOrientation = false;
}
-static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
- if (axis.valid) {
- LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
- name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
- } else {
- LOGI(INDENT "Raw %s axis: unknown range", name);
- }
-}
-
void TouchInputMapper::configure() {
InputMapper::configure();
// Configure basic parameters.
configureParameters();
+ logParameters();
// Configure absolute axis information.
configureRawAxes();
@@ -1287,6 +1329,18 @@
mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
}
+void TouchInputMapper::logParameters() {
+ if (mParameters.useBadTouchFilter) {
+ LOGI(INDENT "Bad touch filter enabled.");
+ }
+ if (mParameters.useAveragingTouchFilter) {
+ LOGI(INDENT "Averaging touch filter enabled.");
+ }
+ if (mParameters.useJumpyTouchFilter) {
+ LOGI(INDENT "Jumpy touch filter enabled.");
+ }
+}
+
void TouchInputMapper::configureRawAxes() {
mRawAxes.x.clear();
mRawAxes.y.clear();
@@ -1298,6 +1352,15 @@
mRawAxes.orientation.clear();
}
+static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+ if (axis.valid) {
+ LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+ name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+ } else {
+ LOGI(INDENT "Raw %s axis: unknown range", name);
+ }
+}
+
void TouchInputMapper::logRawAxes() {
logAxisInfo(mRawAxes.x, "x");
logAxisInfo(mRawAxes.y, "y");
@@ -1331,8 +1394,10 @@
bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
if (sizeChanged) {
- LOGI("Device configured: id=0x%x, name=%s (display size was changed)",
+ LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
getDeviceId(), getDeviceName().string());
+ LOGI(INDENT "Width: %dpx", width);
+ LOGI(INDENT "Height: %dpx", height);
mLocked.surfaceWidth = width;
mLocked.surfaceHeight = height;
@@ -1500,9 +1565,41 @@
mLocked.orientedRanges.y.fuzz = orientedYScale;
}
+ if (sizeChanged) {
+ logMotionRangesLocked();
+ }
+
return true;
}
+static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
+ if (range) {
+ LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
+ name, range->min, range->max, range->flat, range->fuzz);
+ } else {
+ LOGI(INDENT "Output %s range: unsupported", name);
+ }
+}
+
+void TouchInputMapper::logMotionRangesLocked() {
+ logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
+ logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
+ logMotionRangeInfo(mLocked.orientedRanges.havePressure
+ ? & mLocked.orientedRanges.pressure : NULL, "pressure");
+ logMotionRangeInfo(mLocked.orientedRanges.haveSize
+ ? & mLocked.orientedRanges.size : NULL, "size");
+ logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+ ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
+ ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+ ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
+ ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
+ logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
+ ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+}
+
void TouchInputMapper::configureVirtualKeysLocked() {
assert(mRawAxes.x.valid && mRawAxes.y.valid);
@@ -1768,16 +1865,18 @@
}
void TouchInputMapper::logCalibration() {
+ LOGI(INDENT "Calibration:");
+
// Touch Area
switch (mCalibration.touchAreaCalibration) {
case Calibration::TOUCH_AREA_CALIBRATION_NONE:
- LOGI(INDENT " touch.touchArea.calibration: none");
+ LOGI(INDENT INDENT "touch.touchArea.calibration: none");
break;
case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
- LOGI(INDENT " touch.touchArea.calibration: geometric");
+ LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
break;
case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
- LOGI(INDENT " touch.touchArea.calibration: pressure");
+ LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
break;
default:
assert(false);
@@ -1786,40 +1885,40 @@
// Tool Area
switch (mCalibration.toolAreaCalibration) {
case Calibration::TOOL_AREA_CALIBRATION_NONE:
- LOGI(INDENT " touch.toolArea.calibration: none");
+ LOGI(INDENT INDENT "touch.toolArea.calibration: none");
break;
case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
- LOGI(INDENT " touch.toolArea.calibration: geometric");
+ LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
break;
case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
- LOGI(INDENT " touch.toolArea.calibration: linear");
+ LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
break;
default:
assert(false);
}
if (mCalibration.haveToolAreaLinearScale) {
- LOGI(INDENT " touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+ LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
}
if (mCalibration.haveToolAreaLinearBias) {
- LOGI(INDENT " touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+ LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
}
if (mCalibration.haveToolAreaIsSummed) {
- LOGI(INDENT " touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+ LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
}
// Pressure
switch (mCalibration.pressureCalibration) {
case Calibration::PRESSURE_CALIBRATION_NONE:
- LOGI(INDENT " touch.pressure.calibration: none");
+ LOGI(INDENT INDENT "touch.pressure.calibration: none");
break;
case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
- LOGI(INDENT " touch.pressure.calibration: physical");
+ LOGI(INDENT INDENT "touch.pressure.calibration: physical");
break;
case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
- LOGI(INDENT " touch.pressure.calibration: amplitude");
+ LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
break;
default:
assert(false);
@@ -1827,10 +1926,10 @@
switch (mCalibration.pressureSource) {
case Calibration::PRESSURE_SOURCE_PRESSURE:
- LOGI(INDENT " touch.pressure.source: pressure");
+ LOGI(INDENT INDENT "touch.pressure.source: pressure");
break;
case Calibration::PRESSURE_SOURCE_TOUCH:
- LOGI(INDENT " touch.pressure.source: touch");
+ LOGI(INDENT INDENT "touch.pressure.source: touch");
break;
case Calibration::PRESSURE_SOURCE_DEFAULT:
break;
@@ -1839,16 +1938,16 @@
}
if (mCalibration.havePressureScale) {
- LOGI(INDENT " touch.pressure.scale: %f", mCalibration.pressureScale);
+ LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
}
// Size
switch (mCalibration.sizeCalibration) {
case Calibration::SIZE_CALIBRATION_NONE:
- LOGI(INDENT " touch.size.calibration: none");
+ LOGI(INDENT INDENT "touch.size.calibration: none");
break;
case Calibration::SIZE_CALIBRATION_NORMALIZED:
- LOGI(INDENT " touch.size.calibration: normalized");
+ LOGI(INDENT INDENT "touch.size.calibration: normalized");
break;
default:
assert(false);
@@ -1857,10 +1956,10 @@
// Orientation
switch (mCalibration.orientationCalibration) {
case Calibration::ORIENTATION_CALIBRATION_NONE:
- LOGI(INDENT " touch.orientation.calibration: none");
+ LOGI(INDENT INDENT "touch.orientation.calibration: none");
break;
case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
- LOGI(INDENT " touch.orientation.calibration: interpolated");
+ LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
break;
default:
assert(false);
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
index 6d3eeee..fe76cd0 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -119,7 +119,8 @@
if (outData != NULL) *outData = pending.data;
return pending.ident;
}
-
+
+ // Wait for wakeAndLock() waiters to run then set mPolling to true.
mLock.lock();
while (mWaiters != 0) {
mResume.wait(mLock);
@@ -127,6 +128,7 @@
mPolling = true;
mLock.unlock();
+ // Poll.
int32_t result;
size_t requestedCount = mRequestedFds.size();
@@ -168,6 +170,7 @@
}
#endif
+ // Process the poll results.
mPendingCallbacks.clear();
mPendingFds.clear();
mPendingFdsPos = 0;
@@ -218,6 +221,7 @@
}
Done:
+ // Set mPolling to false and wake up the wakeAndLock() waiters.
mLock.lock();
mPolling = false;
if (mWaiters != 0) {
@@ -357,11 +361,13 @@
void PollLoop::wakeAndLock() {
mLock.lock();
+
mWaiters += 1;
while (mPolling) {
wake();
mAwake.wait(mLock);
}
+
mWaiters -= 1;
if (mWaiters == 0) {
mResume.signal();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index b9232c8..dffccf8 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1317,18 +1317,20 @@
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN: {
- AudioManager audioManager = (AudioManager) getContext().getSystemService(
- Context.AUDIO_SERVICE);
- if (audioManager != null) {
- /*
- * Play a sound. This is done on key up since we don't want the
- * sound to play when a user holds down volume down to mute.
- */
- audioManager.adjustSuggestedStreamVolume(
- AudioManager.ADJUST_SAME,
- mVolumeControlStreamType,
- AudioManager.FLAG_PLAY_SOUND);
- mVolumeKeyUpTime = SystemClock.uptimeMillis();
+ if (!event.isCanceled()) {
+ AudioManager audioManager = (AudioManager) getContext().getSystemService(
+ Context.AUDIO_SERVICE);
+ if (audioManager != null) {
+ /*
+ * Play a sound. This is done on key up since we don't want the
+ * sound to play when a user holds down volume down to mute.
+ */
+ audioManager.adjustSuggestedStreamVolume(
+ AudioManager.ADJUST_SAME,
+ mVolumeControlStreamType,
+ AudioManager.FLAG_PLAY_SOUND);
+ mVolumeKeyUpTime = SystemClock.uptimeMillis();
+ }
}
return true;
}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 314dd8a..90d036a 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -433,11 +433,6 @@
}
@SuppressWarnings("unused")
- public void notifyAppSwitchComing() {
- mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
- }
-
- @SuppressWarnings("unused")
public boolean filterTouchEvents() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_filterTouchEvents);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c61baad..9efc708 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1751,24 +1751,28 @@
p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
}
+ p.println(" ");
if (client != null) {
- p.println(" ");
pw.flush();
try {
client.client.asBinder().dump(fd, args);
} catch (RemoteException e) {
p.println("Input method client dead: " + e);
}
+ } else {
+ p.println("No input method client.");
}
+ p.println(" ");
if (method != null) {
- p.println(" ");
pw.flush();
try {
method.asBinder().dump(fd, args);
} catch (RemoteException e) {
p.println("Input method service dead: " + e);
}
+ } else {
+ p.println("No input method service.");
}
}
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1279cf7..685bee4 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3026,7 +3026,8 @@
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLP(pkg, origPackage, realName, suid, destCodeFile,
- destResourceFile, pkg.applicationInfo.flags, true, false);
+ destResourceFile, pkg.applicationInfo.nativeLibraryDir,
+ pkg.applicationInfo.flags, true, false);
if (pkgSetting == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -3244,14 +3245,21 @@
}
}
+ pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
+
/*
* Set the data dir to the default "/data/data/<package name>/lib"
* if we got here without anyone telling us different (e.g., apps
* stored on SD card have their native libraries stored in the ASEC
* container with the APK).
+ *
+ * This happens during an upgrade from a package settings file that
+ * doesn't have a native library path attribute at all.
*/
- if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
- pkg.applicationInfo.nativeLibraryDir = new File(dataPath, LIB_DIR_NAME).getPath();
+ if (pkgSetting.nativeLibraryPathString == null && pkg.applicationInfo.dataDir != null) {
+ final String nativeLibraryPath = new File(dataPath, LIB_DIR_NAME).getPath();
+ pkg.applicationInfo.nativeLibraryDir = nativeLibraryPath;
+ pkgSetting.nativeLibraryPathString = nativeLibraryPath;
}
pkgSetting.uidError = uidError;
@@ -3273,7 +3281,6 @@
if ((!isSystemApp(pkg) || isUpdatedSystemApp(pkg)) && !isExternal(pkg)) {
Log.i(TAG, path + " changed; unpacking");
File sharedLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
- sharedLibraryDir.mkdir();
NativeLibraryHelper.copyNativeBinariesLI(scanFile, sharedLibraryDir);
}
pkg.mScanPath = path;
@@ -3591,40 +3598,6 @@
}
}
- // Convenience call for removeNativeBinariesLI(File)
- private void removeNativeBinariesLI(PackageParser.Package pkg) {
- File nativeLibraryDir = getNativeBinaryDirForPackage(pkg);
- removeNativeBinariesLI(nativeLibraryDir);
- }
-
- // Remove the native binaries of a given package. This simply
- // gets rid of the files in the 'lib' sub-directory.
- public void removeNativeBinariesLI(File binaryDir) {
- if (DEBUG_NATIVE) {
- Slog.w(TAG, "Deleting native binaries from: " + binaryDir.getPath());
- }
-
- // Just remove any file in the directory. Since the directory
- // is owned by the 'system' UID, the application is not supposed
- // to have written anything there.
- //
- if (binaryDir.exists()) {
- File[] binaries = binaryDir.listFiles();
- if (binaries != null) {
- for (int nn = 0; nn < binaries.length; nn++) {
- if (DEBUG_NATIVE) {
- Slog.d(TAG, " Deleting " + binaries[nn].getName());
- }
- if (!binaries[nn].delete()) {
- Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
- }
- }
- }
- // Do not delete 'lib' directory itself, or this will prevent
- // installation of future updates.
- }
- }
-
void removePackageLI(PackageParser.Package pkg, boolean chatty) {
if (chatty && Config.LOGD) Log.d(
TAG, "Removing package " + pkg.applicationInfo.packageName );
@@ -5128,7 +5101,7 @@
}
}
if (libraryPath != null) {
- removeNativeBinariesLI(new File(libraryPath));
+ NativeLibraryHelper.removeNativeBinariesLI(libraryPath);
}
}
@@ -6202,8 +6175,8 @@
synchronized (mPackages) {
// Reinstate the old system package
mSettings.enableSystemPackageLP(p.packageName);
- // Remove any native libraries. XXX needed?
- removeNativeBinariesLI(p);
+ // Remove any native libraries from the upgraded package.
+ NativeLibraryHelper.removeNativeBinariesLI(p.applicationInfo.nativeLibraryDir);
}
// Install the system package
PackageParser.Package newPkg = scanPackageLI(ps.codePath,
@@ -7583,18 +7556,20 @@
String installerPackageName;
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
- int pVersionCode, int pkgFlags) {
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
super(pkgFlags);
this.name = name;
this.realName = realName;
- init(codePath, resourcePath, pVersionCode);
+ init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
}
- void init(File codePath, File resourcePath, int pVersionCode) {
+ void init(File codePath, File resourcePath, String nativeLibraryPathString,
+ int pVersionCode) {
this.codePath = codePath;
this.codePathString = codePath.toString();
this.resourcePath = resourcePath;
this.resourcePathString = resourcePath.toString();
+ this.nativeLibraryPathString = nativeLibraryPathString;
this.versionCode = pVersionCode;
}
@@ -7687,8 +7662,9 @@
SharedUserSetting sharedUser;
PackageSetting(String name, String realName, File codePath, File resourcePath,
- int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags);
+ String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ pkgFlags);
}
@Override
@@ -7800,8 +7776,9 @@
final int sharedId;
PendingPackage(String name, String realName, File codePath, File resourcePath,
- int sharedId, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, pVersionCode, pkgFlags);
+ String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ pVersionCode, pkgFlags);
this.sharedId = sharedId;
}
}
@@ -7830,10 +7807,10 @@
PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int pkgFlags, boolean create, boolean add) {
final String name = pkg.packageName;
PackageSetting p = getPackageLP(name, origPackage, realName, sharedUser, codePath,
- resourcePath, pkg.mVersionCode, pkgFlags, create, add);
+ resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags, create, add);
return p;
}
@@ -7929,14 +7906,14 @@
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- PackageSetting ret = addPackageLP(name, p.realName, p.codePath,
- p.resourcePath, p.userId, p.versionCode, p.pkgFlags);
+ PackageSetting ret = addPackageLP(name, p.realName, p.codePath, p.resourcePath,
+ p.nativeLibraryPathString, p.userId, p.versionCode, p.pkgFlags);
mDisabledSysPackages.remove(name);
return ret;
}
- PackageSetting addPackageLP(String name, String realName, File codePath,
- File resourcePath, int uid, int vc, int pkgFlags) {
+ PackageSetting addPackageLP(String name, String realName, File codePath, File resourcePath,
+ String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.userId == uid) {
@@ -7946,7 +7923,8 @@
"Adding duplicate package, keeping first: " + name);
return null;
}
- p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags);
+ p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ vc, pkgFlags);
p.userId = uid;
if (addUserIdLP(uid, p, name)) {
mPackages.put(name, p);
@@ -8001,7 +7979,7 @@
private PackageSetting getPackageLP(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- int vc, int pkgFlags, boolean create, boolean add) {
+ String nativeLibraryPathString, int vc, int pkgFlags, boolean create, boolean add) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (!p.codePath.equals(codePath)) {
@@ -8044,8 +8022,8 @@
}
if (origPackage != null) {
// We are consuming the data from an existing package.
- p = new PackageSetting(origPackage.name, name, codePath,
- resourcePath, vc, pkgFlags);
+ p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
+ " is adopting original package " + origPackage.name);
// Note that we will retain the new package's signature so
@@ -8061,7 +8039,8 @@
// Update new package state.
p.setTimeStamp(codePath.lastModified());
} else {
- p = new PackageSetting(name, realName, codePath, resourcePath, vc, pkgFlags);
+ p = new PackageSetting(name, realName, codePath, resourcePath,
+ nativeLibraryPathString, vc, pkgFlags);
p.setTimeStamp(codePath.lastModified());
p.sharedUser = sharedUser;
if (sharedUser != null) {
@@ -8782,8 +8761,8 @@
Object idObj = getUserIdLP(pp.sharedId);
if (idObj != null && idObj instanceof SharedUserSetting) {
PackageSetting p = getPackageLP(pp.name, null, pp.realName,
- (SharedUserSetting)idObj, pp.codePath, pp.resourcePath,
- pp.versionCode, pp.pkgFlags, true, true);
+ (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
+ pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags, true, true);
if (p == null) {
Slog.w(TAG, "Unable to create application package for "
+ pp.name);
@@ -8888,6 +8867,7 @@
String realName = parser.getAttributeValue(null, "realName");
String codePathStr = parser.getAttributeValue(null, "codePath");
String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
+ String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
if (resourcePathStr == null) {
resourcePathStr = codePathStr;
}
@@ -8902,9 +8882,8 @@
int pkgFlags = 0;
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
- PackageSetting ps = new PackageSetting(name, realName,
- new File(codePathStr),
- new File(resourcePathStr), versionCode, pkgFlags);
+ PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
String timeStampStr = parser.getAttributeValue(null, "ts");
if (timeStampStr != null) {
try {
@@ -9023,9 +9002,9 @@
"Error in package manager settings: <package> has no codePath at "
+ parser.getPositionDescription());
} else if (userId > 0) {
- packageSetting = addPackageLP(name.intern(), realName,
- new File(codePathStr), new File(resourcePathStr),
- userId, versionCode, pkgFlags);
+ packageSetting = addPackageLP(name.intern(), realName, new File(codePathStr),
+ new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
+ pkgFlags);
if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
+ ": userId=" + userId + " pkg=" + packageSetting);
if (packageSetting == null) {
@@ -9042,7 +9021,7 @@
if (userId > 0) {
packageSetting = new PendingPackage(name.intern(), realName,
new File(codePathStr), new File(resourcePathStr),
- userId, versionCode, pkgFlags);
+ nativeLibraryPathStr, userId, versionCode, pkgFlags);
packageSetting.setTimeStamp(timeStamp, timeStampStr);
mPendingPackages.add((PendingPackage) packageSetting);
if (DEBUG_SETTINGS) Log.i(TAG, "Reading package " + name
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 0bc9b61..27d875e 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5097,8 +5097,7 @@
}
/* Notifies the window manager about an input channel that is not responding.
- * The method can either cause dispatching to be aborted by returning -2 or
- * return a new timeout in nanoseconds.
+ * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
*/
@@ -5107,7 +5106,7 @@
synchronized (mWindowMap) {
WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
if (windowState == null) {
- return -2; // irrelevant, abort dispatching (-2)
+ return 0; // window is unknown, abort dispatching
}
Slog.i(TAG, "Input event dispatching timed out sending to "
@@ -5130,8 +5129,7 @@
/* Notifies the window manager about an application that is not responding
* in general rather than with respect to a particular input channel.
- * The method can either cause dispatching to be aborted by returning -2 or
- * return a new timeout in nanoseconds.
+ * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
*/
@@ -5157,7 +5155,7 @@
} catch (RemoteException ex) {
}
}
- return -2; // abort dispatching
+ return 0; // abort dispatching
}
private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
@@ -5271,15 +5269,6 @@
}
}
- /* Notifies that an app switch key (BACK / HOME) has just been pressed.
- * This essentially starts a .5 second timeout for the application to process
- * subsequent input events while waiting for the app switch to occur. If it takes longer
- * than this, the pending events will be dropped.
- */
- public void notifyAppSwitchComing() {
- // TODO Not implemented yet. Should go in the native side.
- }
-
/* Notifies that the lid switch changed state. */
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java
index eee97c3..c3786f5 100644
--- a/services/java/com/android/server/sip/SipService.java
+++ b/services/java/com/android/server/sip/SipService.java
@@ -27,6 +27,7 @@
import android.net.sip.ISipService;
import android.net.sip.ISipSession;
import android.net.sip.ISipSessionListener;
+import android.net.sip.SipErrorCode;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipSessionAdapter;
@@ -528,6 +529,8 @@
private int mBackoff = 1;
private boolean mRegistered;
private long mExpiryTime;
+ private SipErrorCode mErrorCode;
+ private String mErrorMessage;
private String getAction() {
return toString();
@@ -551,15 +554,25 @@
}
public void stop() {
+ stop(false);
+ }
+
+ private void stopButKeepStates() {
+ stop(true);
+ }
+
+ private void stop(boolean keepStates) {
if (mSession == null) return;
- if (mConnected) mSession.unregister();
+ if (mConnected && mRegistered) mSession.unregister();
mTimer.cancel(this);
if (mKeepAliveProcess != null) {
mKeepAliveProcess.stop();
mKeepAliveProcess = null;
}
- mSession = null;
- mRegistered = false;
+ if (!keepStates) {
+ mSession = null;
+ mRegistered = false;
+ }
}
private boolean isStopped() {
@@ -568,20 +581,33 @@
public void setListener(ISipSessionListener listener) {
Log.v(TAG, "setListener(): " + listener);
- mProxy.setListener(listener);
- if (mSession == null) return;
+ synchronized (SipService.this) {
+ mProxy.setListener(listener);
+ if (mSession == null) return;
- try {
- if ((mSession != null) && SipSessionState.REGISTERING.equals(
- mSession.getState())) {
- mProxy.onRegistering(mSession);
- } else if (mRegistered) {
- int duration = (int)
- (mExpiryTime - SystemClock.elapsedRealtime());
- mProxy.onRegistrationDone(mSession, duration);
+ try {
+ SipSessionState state = (mSession == null)
+ ? SipSessionState.READY_TO_CALL
+ : Enum.valueOf(
+ SipSessionState.class, mSession.getState());
+ if ((state == SipSessionState.REGISTERING)
+ || (state == SipSessionState.DEREGISTERING)) {
+ mProxy.onRegistering(mSession);
+ } else if (mRegistered) {
+ int duration = (int)
+ (mExpiryTime - SystemClock.elapsedRealtime());
+ mProxy.onRegistrationDone(mSession, duration);
+ } else if (mErrorCode != null) {
+ if (mErrorCode == SipErrorCode.TIME_OUT) {
+ mProxy.onRegistrationTimeout(mSession);
+ } else {
+ mProxy.onRegistrationFailed(mSession,
+ mErrorCode.toString(), mErrorMessage);
+ }
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "setListener(): " + t);
}
- } catch (Throwable t) {
- Log.w(TAG, "setListener(): " + t);
}
}
@@ -590,6 +616,8 @@
}
public void run() {
+ mErrorCode = null;
+ mErrorMessage = null;
Log.v(TAG, " ~~~ registering");
synchronized (SipService.this) {
if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
@@ -634,11 +662,7 @@
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
mRegistered = false;
- try {
- mProxy.onRegistering(session);
- } catch (Throwable t) {
- Log.w(TAG, "onRegistering()", t);
- }
+ mProxy.onRegistering(session);
}
}
@@ -647,11 +671,9 @@
Log.v(TAG, "onRegistrationDone(): " + session + ": " + mSession);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
- try {
- mProxy.onRegistrationDone(session, duration);
- } catch (Throwable t) {
- Log.w(TAG, "onRegistrationDone()", t);
- }
+
+ mProxy.onRegistrationDone(session, duration);
+
if (isStopped()) return;
if (duration > 0) {
@@ -687,19 +709,25 @@
}
@Override
- public void onRegistrationFailed(ISipSession session, String className,
- String message) {
+ public void onRegistrationFailed(ISipSession session,
+ String errorCodeString, String message) {
+ SipErrorCode errorCode =
+ Enum.valueOf(SipErrorCode.class, errorCodeString);
Log.v(TAG, "onRegistrationFailed(): " + session + ": " + mSession
- + ": " + className + ": " + message);
+ + ": " + errorCode + ": " + message);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
- try {
- mProxy.onRegistrationFailed(session, className, message);
- } catch (Throwable t) {
- Log.w(TAG, "onRegistrationFailed(): " + t);
- }
+ mErrorCode = errorCode;
+ mErrorMessage = message;
+ mProxy.onRegistrationFailed(session, errorCode.toString(),
+ message);
- if (!isStopped()) onError();
+ if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
+ Log.d(TAG, " pause auto-registration");
+ stopButKeepStates();
+ } else if (!isStopped()) {
+ onError();
+ }
}
}
@@ -708,11 +736,8 @@
Log.v(TAG, "onRegistrationTimeout(): " + session + ": " + mSession);
synchronized (SipService.this) {
if (!isStopped() && (session != mSession)) return;
- try {
- mProxy.onRegistrationTimeout(session);
- } catch (Throwable t) {
- Log.w(TAG, "onRegistrationTimeout(): " + t);
- }
+ mErrorCode = SipErrorCode.TIME_OUT;
+ mProxy.onRegistrationTimeout(session);
if (!isStopped()) {
mRegistered = false;
diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java
index 2f7ddc4..da8e9b8 100644
--- a/services/java/com/android/server/sip/SipSessionGroup.java
+++ b/services/java/com/android/server/sip/SipSessionGroup.java
@@ -412,13 +412,7 @@
processCommand(command);
} catch (SipException e) {
Log.w(TAG, "command error: " + command, e);
- // TODO: find a better way to do this
- if ((command instanceof RegisterCommand)
- || (command == DEREGISTER)) {
- onRegistrationFailed(e);
- } else {
- onError(e);
- }
+ onError(e);
}
}
}).start();
@@ -480,7 +474,9 @@
private void processCommand(EventObject command) throws SipException {
if (!process(command)) {
- throw new SipException("wrong state to execute: " + command);
+ onError(SipErrorCode.IN_PROGRESS,
+ "cannot initiate a new transaction to execute: "
+ + command);
}
}
@@ -562,11 +558,8 @@
if (evt instanceof TimeoutEvent) {
processTimeout((TimeoutEvent) evt);
} else {
- Log.d(TAG, "Transaction terminated:" + this);
- if (!SipSessionState.IN_CALL.equals(mState)) {
- removeSipSession(this);
- }
- return true;
+ processTransactionTerminated(
+ (TransactionTerminatedEvent) evt);
}
return true;
} else if (evt instanceof DialogTerminatedEvent) {
@@ -585,6 +578,20 @@
}
}
+ private void processTransactionTerminated(
+ TransactionTerminatedEvent event) {
+ switch (mState) {
+ case IN_CALL:
+ case READY_TO_CALL:
+ Log.d(TAG, "Transaction terminated; do nothing");
+ break;
+ default:
+ Log.d(TAG, "Transaction terminated early: " + this);
+ onError(SipErrorCode.TRANSACTION_TERMINTED,
+ "transaction terminated");
+ }
+ }
+
private void processTimeout(TimeoutEvent event) {
Log.d(TAG, "processing Timeout..." + event);
Transaction current = event.isServerTransaction()
@@ -600,25 +607,26 @@
return;
}
switch (mState) {
- case REGISTERING:
- case DEREGISTERING:
- reset();
- mProxy.onRegistrationTimeout(this);
- break;
- case INCOMING_CALL:
- case INCOMING_CALL_ANSWERING:
- case OUTGOING_CALL_CANCELING:
- endCallOnError(SipErrorCode.TIME_OUT, event.toString());
- break;
- case PINGING:
- reset();
- mReRegisterFlag = true;
- mState = SipSessionState.READY_TO_CALL;
- break;
+ case REGISTERING:
+ case DEREGISTERING:
+ reset();
+ mProxy.onRegistrationTimeout(this);
+ break;
+ case INCOMING_CALL:
+ case INCOMING_CALL_ANSWERING:
+ case OUTGOING_CALL:
+ case OUTGOING_CALL_CANCELING:
+ onError(SipErrorCode.TIME_OUT, event.toString());
+ break;
+ case PINGING:
+ reset();
+ mReRegisterFlag = true;
+ mState = SipSessionState.READY_TO_CALL;
+ break;
- default:
- // do nothing
- break;
+ default:
+ Log.d(TAG, " do nothing");
+ break;
}
}
@@ -665,7 +673,7 @@
} else {
Log.w(TAG, "peer did not respect our rport request");
}
- mState = SipSessionState.READY_TO_CALL;
+ reset();
return true;
}
return false;
@@ -687,7 +695,6 @@
switch (statusCode) {
case Response.OK:
SipSessionState state = mState;
- reset();
onRegistrationDone((state == SipSessionState.REGISTERING)
? getExpiryTime(((ResponseEvent) evt).getResponse())
: -1);
@@ -698,15 +705,13 @@
case Response.PROXY_AUTHENTICATION_REQUIRED:
if (!handleAuthentication(event)) {
Log.v(TAG, "Incorrect username/password");
- reset();
onRegistrationFailed(SipErrorCode.INVALID_CREDENTIALS,
"incorrect username or password");
}
return true;
default:
if (statusCode >= 500) {
- reset();
- onRegistrationFailed(createCallbackException(response));
+ onRegistrationFailed(response);
return true;
}
}
@@ -720,7 +725,6 @@
String nonce = getNonceFromResponse(response);
if (((nonce != null) && nonce.equals(mLastNonce)) ||
(nonce == mLastNonce)) {
- Log.v(TAG, "Incorrect username/password");
return false;
} else {
mClientTransaction = mSipHelper.handleChallenge(
@@ -906,7 +910,7 @@
}
if (statusCode >= 400) {
- onError(createCallbackException(response));
+ onError(response);
return true;
}
} else if (evt instanceof TransactionTerminatedEvent) {
@@ -954,10 +958,6 @@
response.getReasonPhrase(), response.getStatusCode());
}
- private Exception createCallbackException(Response response) {
- return new SipException(createErrorMessage(response));
- }
-
private void establishCall() {
mState = SipSessionState.IN_CALL;
mInCall = true;
@@ -965,22 +965,22 @@
}
private void fallbackToPreviousInCall(Throwable exception) {
- mState = SipSessionState.IN_CALL;
exception = getRootCause(exception);
- mProxy.onCallChangeFailed(this, getErrorCode(exception).toString(),
+ fallbackToPreviousInCall(getErrorCode(exception),
exception.toString());
}
+ private void fallbackToPreviousInCall(SipErrorCode errorCode,
+ String message) {
+ mState = SipSessionState.IN_CALL;
+ mProxy.onCallChangeFailed(this, errorCode.toString(), message);
+ }
+
private void endCallNormally() {
reset();
mProxy.onCallEnded(this);
}
- private void endCallOnError(Throwable exception) {
- exception = getRootCause(exception);
- endCallOnError(getErrorCode(exception), exception.toString());
- }
-
private void endCallOnError(SipErrorCode errorCode, String message) {
reset();
mProxy.onError(this, errorCode.toString(), message);
@@ -991,26 +991,34 @@
mProxy.onCallBusy(this);
}
- private void onError(Throwable exception) {
- if (mInCall) {
- fallbackToPreviousInCall(exception);
- } else {
- endCallOnError(exception);
+ private void onError(SipErrorCode errorCode, String message) {
+ switch (mState) {
+ case REGISTERING:
+ case DEREGISTERING:
+ onRegistrationFailed(errorCode, message);
+ break;
+ default:
+ if (mInCall) {
+ fallbackToPreviousInCall(errorCode, message);
+ } else {
+ endCallOnError(errorCode, message);
+ }
}
}
+
+ private void onError(Throwable exception) {
+ exception = getRootCause(exception);
+ onError(getErrorCode(exception), exception.toString());
+ }
+
private void onError(Response response) {
- if (mInCall) {
- fallbackToPreviousInCall(createCallbackException(response));
+ int statusCode = response.getStatusCode();
+ if (!mInCall && ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
+ || (statusCode == Response.BUSY_HERE))) {
+ endCallOnBusy();
} else {
- int statusCode = response.getStatusCode();
- if ((statusCode == Response.TEMPORARILY_UNAVAILABLE)
- || (statusCode == Response.BUSY_HERE)) {
- endCallOnBusy();
- } else {
- endCallOnError(getErrorCode(statusCode),
- createErrorMessage(response));
- }
+ onError(getErrorCode(statusCode), createErrorMessage(response));
}
}
@@ -1053,19 +1061,29 @@
}
private void onRegistrationDone(int duration) {
+ reset();
mProxy.onRegistrationDone(this, duration);
}
private void onRegistrationFailed(SipErrorCode errorCode,
String message) {
+ reset();
mProxy.onRegistrationFailed(this, errorCode.toString(), message);
}
private void onRegistrationFailed(Throwable exception) {
+ reset();
exception = getRootCause(exception);
onRegistrationFailed(getErrorCode(exception),
exception.toString());
}
+
+ private void onRegistrationFailed(Response response) {
+ reset();
+ int statusCode = response.getStatusCode();
+ onRegistrationFailed(getErrorCode(statusCode),
+ createErrorMessage(response));
+ }
}
/**
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 7af5e95..6dd619c 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -24,9 +24,6 @@
// Log debug messages about InputDispatcherPolicy
#define DEBUG_INPUT_DISPATCHER_POLICY 0
-// Log debug messages about input focus tracking
-#define DEBUG_FOCUS 0
-
#include "JNIHelp.h"
#include "jni.h"
#include <limits.h>
@@ -44,81 +41,6 @@
namespace android {
-// Window flags from WindowManager.LayoutParams
-enum {
- FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
- FLAG_DIM_BEHIND = 0x00000002,
- FLAG_BLUR_BEHIND = 0x00000004,
- FLAG_NOT_FOCUSABLE = 0x00000008,
- FLAG_NOT_TOUCHABLE = 0x00000010,
- FLAG_NOT_TOUCH_MODAL = 0x00000020,
- FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
- FLAG_KEEP_SCREEN_ON = 0x00000080,
- FLAG_LAYOUT_IN_SCREEN = 0x00000100,
- FLAG_LAYOUT_NO_LIMITS = 0x00000200,
- FLAG_FULLSCREEN = 0x00000400,
- FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
- FLAG_DITHER = 0x00001000,
- FLAG_SECURE = 0x00002000,
- FLAG_SCALED = 0x00004000,
- FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
- FLAG_LAYOUT_INSET_DECOR = 0x00010000,
- FLAG_ALT_FOCUSABLE_IM = 0x00020000,
- FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
- FLAG_SHOW_WHEN_LOCKED = 0x00080000,
- FLAG_SHOW_WALLPAPER = 0x00100000,
- FLAG_TURN_SCREEN_ON = 0x00200000,
- FLAG_DISMISS_KEYGUARD = 0x00400000,
- FLAG_IMMERSIVE = 0x00800000,
- FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
- FLAG_COMPATIBLE_WINDOW = 0x20000000,
- FLAG_SYSTEM_ERROR = 0x40000000,
-};
-
-// Window types from WindowManager.LayoutParams
-enum {
- FIRST_APPLICATION_WINDOW = 1,
- TYPE_BASE_APPLICATION = 1,
- TYPE_APPLICATION = 2,
- TYPE_APPLICATION_STARTING = 3,
- LAST_APPLICATION_WINDOW = 99,
- FIRST_SUB_WINDOW = 1000,
- TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
- TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
- TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
- TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
- TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
- LAST_SUB_WINDOW = 1999,
- FIRST_SYSTEM_WINDOW = 2000,
- TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
- TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
- TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
- TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
- TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
- TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
- TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
- TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
- TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
- TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
- TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
- TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
- TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
- TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
- TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
- LAST_SYSTEM_WINDOW = 2999,
-};
-
-// Delay between reporting long touch events to the power manager.
-const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
-
-// Default input dispatching timeout if there is no focused application or paused window
-// from which to determine an appropriate dispatching timeout.
-const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
-
-// Minimum amount of time to provide to the input dispatcher for delivery of an event
-// regardless of how long the application window was paused.
-const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec
-
// ----------------------------------------------------------------------------
static struct {
@@ -134,7 +56,6 @@
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptKeyBeforeDispatching;
jmethodID checkInjectEventsPermission;
- jmethodID notifyAppSwitchComing;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions;
@@ -235,7 +156,7 @@
inline sp<InputManager> getInputManager() const { return mInputManager; }
- String8 dump();
+ void dump(String8& dump);
void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
void setDisplayOrientation(int32_t displayId, int32_t orientation);
@@ -270,72 +191,33 @@
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifyConfigurationChanged(nsecs_t when);
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle);
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
- virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
- nsecs_t& outNewTimeout);
+ virtual nsecs_t notifyInputChannelANR(const sp<InputChannel>& inputChannel);
virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
virtual nsecs_t getKeyRepeatTimeout();
virtual nsecs_t getKeyRepeatDelay();
- virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
- virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
virtual int32_t getMaxEventsPerSecond();
+ virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
+ const KeyEvent* keyEvent, uint32_t policyFlags);
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType);
+ virtual bool checkInjectEventsPermissionNonReentrant(
+ int32_t injectorPid, int32_t injectorUid);
private:
- struct InputWindow {
- sp<InputChannel> inputChannel;
- int32_t layoutParamsFlags;
- int32_t layoutParamsType;
- nsecs_t dispatchingTimeout;
- int32_t frameLeft;
- int32_t frameTop;
- int32_t frameRight;
- int32_t frameBottom;
- int32_t visibleFrameLeft;
- int32_t visibleFrameTop;
- int32_t visibleFrameRight;
- int32_t visibleFrameBottom;
- int32_t touchableAreaLeft;
- int32_t touchableAreaTop;
- int32_t touchableAreaRight;
- int32_t touchableAreaBottom;
- bool visible;
- bool hasFocus;
- bool hasWallpaper;
- bool paused;
- int32_t ownerPid;
- int32_t ownerUid;
-
- bool visibleFrameIntersects(const InputWindow* other) const;
- bool touchableAreaContainsPoint(int32_t x, int32_t y) const;
- };
-
- struct InputApplication {
- String8 name;
- nsecs_t dispatchingTimeout;
- jweak tokenObjWeak;
- };
-
- class ANRTimer {
- enum Budget {
- SYSTEM = 0,
- APPLICATION = 1
- };
-
- Budget mBudget;
- nsecs_t mStartTime;
- bool mFrozen;
- InputWindow* mPausedWindow;
+ class ApplicationToken : public InputApplicationHandle {
+ jweak mTokenObjWeak;
public:
- ANRTimer();
+ ApplicationToken(jweak tokenObjWeak) :
+ mTokenObjWeak(tokenObjWeak) { }
- void dispatchFrozenBySystem();
- void dispatchPausedByApplication(InputWindow* pausedWindow);
- bool waitForDispatchStateChangeLd(NativeInputManager* inputManager);
+ virtual ~ApplicationToken() {
+ JNIEnv* env = NativeInputManager::jniEnv();
+ env->DeleteWeakGlobalRef(mTokenObjWeak);
+ }
- nsecs_t getTimeSpentWaitingForApplication() const;
+ inline jweak getTokenObj() { return mTokenObjWeak; }
};
sp<InputManager> mInputManager;
@@ -364,80 +246,14 @@
jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
- // Input target and focus tracking. (lock mDispatchLock)
- Mutex mDispatchLock;
- Condition mDispatchStateChanged;
-
- bool mDispatchEnabled;
- bool mDispatchFrozen;
- bool mWindowsReady;
- Vector<InputWindow> mWindows;
- Vector<InputWindow*> mWallpaperWindows;
- Vector<sp<InputChannel> > mMonitoringChannels;
-
- // Focus tracking for keys, trackball, etc.
- 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;
- };
- Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
- Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
-
- // Focused application.
- InputApplication* mFocusedApplication;
- InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
-
- void dumpDeviceInfo(String8& dump);
- void dumpDispatchStateLd(String8& dump);
- void logDispatchStateLd();
-
- bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
- void releaseFocusedApplicationLd(JNIEnv* env);
-
- int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow);
- int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
- bool isWindowObscuredLocked(const InputWindow* window);
-
- void releaseTouchedWindowLd();
-
- int32_t waitForNonTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
- int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
-
- bool interceptKeyBeforeDispatching(const InputTarget& target,
- const KeyEvent* keyEvent, uint32_t policyFlags);
-
- void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType);
- void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
- bool checkInjectionPermission(const InputWindow* window,
- int32_t injectorPid, int32_t injectorUid);
-
static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
- static void addTarget(const InputWindow* window, int32_t targetFlags,
- nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
- void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
- void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
- void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+ static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
+ static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
return AndroidRuntime::getJNIEnv();
}
-
- static bool isAppSwitchKey(int32_t keyCode);
- static bool isPolicyKey(int32_t keyCode, bool isScreenOn);
- static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
};
// ----------------------------------------------------------------------------
@@ -445,10 +261,7 @@
NativeInputManager::NativeInputManager(jobject callbacksObj) :
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
mMaxEventsPerSecond(-1),
- mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
- mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
- mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
- mFocusedApplication(NULL) {
+ mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0) {
JNIEnv* env = jniEnv();
mCallbacksObj = env->NewGlobalRef(callbacksObj);
@@ -461,27 +274,16 @@
JNIEnv* env = jniEnv();
env->DeleteGlobalRef(mCallbacksObj);
-
- releaseFocusedApplicationLd(env);
}
-String8 NativeInputManager::dump() {
- String8 dump;
- { // acquire lock
- AutoMutex _l(mDisplayLock);
- dump.append("Native Input Dispatcher State:\n");
- dumpDispatchStateLd(dump);
- dump.append("\n");
- } // release lock
+void NativeInputManager::dump(String8& dump) {
+ dump.append("Input Reader State:\n");
+ mInputManager->getReader()->dump(dump);
+ dump.append("\n");
- dump.append("Input Devices:\n");
- dumpDeviceInfo(dump);
-
- return dump;
-}
-
-bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
- return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+ dump.append("Input Dispatcher State:\n");
+ mInputManager->getDispatcher()->dump(dump);
+ dump.append("\n");
}
bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) {
@@ -503,6 +305,7 @@
case AKEYCODE_MEDIA_PREVIOUS:
case AKEYCODE_MEDIA_REWIND:
case AKEYCODE_MEDIA_FAST_FORWARD:
+ // The policy always cares about these keys.
return true;
default:
// We need to pass all keys to the policy in the following cases:
@@ -565,12 +368,9 @@
mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak);
}
- status = mInputManager->registerInputChannel(inputChannel);
+ status = mInputManager->getDispatcher()->registerInputChannel(inputChannel, monitor);
if (! status) {
// Success.
- if (monitor) {
- registerMonitoringChannel(inputChannel);
- }
return OK;
}
@@ -604,9 +404,7 @@
env->DeleteWeakGlobalRef(inputChannelObjWeak);
- unregisterMonitoringChannel(inputChannel);
-
- return mInputManager->unregisterInputChannel(inputChannel);
+ return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
}
jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
@@ -710,20 +508,11 @@
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
- pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
+ android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
}
if (wmActions & WM_ACTION_PASS_TO_USER) {
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
-
- if (down && isAppSwitchKey(keyCode)) {
- JNIEnv* env = jniEnv();
-
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
- checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
-
- actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
- }
}
return actions;
@@ -906,13 +695,42 @@
JNIEnv* env = jniEnv();
InputConfiguration config;
- mInputManager->getInputConfiguration(& config);
+ mInputManager->getReader()->getInputConfiguration(& config);
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
when, config.touchScreen, config.keyboard, config.navigation);
checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
}
+nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyANR");
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ ApplicationToken* token = static_cast<ApplicationToken*>(inputApplicationHandle.get());
+ jweak tokenObjWeak = token->getTokenObj();
+
+ jlong newTimeout;
+ jobject tokenObjLocal = env->NewLocalRef(tokenObjWeak);
+ if (tokenObjLocal) {
+ newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyANR, tokenObjLocal);
+ if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+ newTimeout = 0; // abort dispatch
+ } else {
+ assert(newTimeout >= 0);
+ }
+
+ env->DeleteLocalRef(tokenObjLocal);
+ } else {
+ newTimeout = 0; // abort dispatch
+ }
+
+ return newTimeout;
+}
+
void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
@@ -928,12 +746,9 @@
env->DeleteLocalRef(inputChannelObjLocal);
}
-
- unregisterMonitoringChannel(inputChannel);
}
-bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
- nsecs_t& outNewTimeout) {
+nsecs_t NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyInputChannelANR - inputChannel='%s'",
inputChannel->getName().string());
@@ -947,20 +762,17 @@
newTimeout = env->CallLongMethod(mCallbacksObj,
gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
- newTimeout = -2;
+ newTimeout = 0; // abort dispatch
+ } else {
+ assert(newTimeout >= 0);
}
env->DeleteLocalRef(inputChannelObjLocal);
} else {
- newTimeout = -2;
+ newTimeout = 0; // abort dispatch
}
- if (newTimeout == -2) {
- return false; // abort
- }
-
- outNewTimeout = newTimeout;
- return true; // resume
+ return newTimeout;
}
void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
@@ -981,27 +793,6 @@
}
}
-bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("notifyANR");
-#endif
-
- JNIEnv* env = jniEnv();
-
- jlong newTimeout = env->CallLongMethod(mCallbacksObj,
- gCallbacksClassInfo.notifyANR, tokenObj);
- if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
- newTimeout = -2;
- }
-
- if (newTimeout == -2) {
- return false; // abort
- }
-
- outNewTimeout = newTimeout;
- return true; // resume
-}
-
nsecs_t NativeInputManager::getKeyRepeatTimeout() {
if (! isScreenOn()) {
// Disable key repeat when the screen is off.
@@ -1032,89 +823,26 @@
}
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
-#if DEBUG_FOCUS
- LOGD("setInputWindows");
-#endif
- { // acquire lock
- AutoMutex _l(mDispatchLock);
+ Vector<InputWindow> windows;
- 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();
+ jsize length = env->GetArrayLength(windowObjArray);
+ for (jsize i = 0; i < length; i++) {
+ jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
+ if (! inputTargetObj) {
+ break; // found null element indicating end of used portion of the array
}
- bool hadFocusedWindow = mFocusedWindow != NULL;
-
- mWindows.clear();
- mFocusedWindow = NULL;
- mWallpaperWindows.clear();
-
- if (windowObjArray) {
- mWindowsReady = true;
-
- jsize length = env->GetArrayLength(windowObjArray);
- for (jsize i = 0; i < length; i++) {
- jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
- if (! inputTargetObj) {
- break; // found null element indicating end of used portion of the array
- }
-
- mWindows.push();
- InputWindow& window = mWindows.editTop();
- bool valid = populateWindow(env, inputTargetObj, window);
- if (! valid) {
- mWindows.pop();
- }
-
- env->DeleteLocalRef(inputTargetObj);
- }
-
- size_t numWindows = mWindows.size();
- for (size_t i = 0; i < numWindows; i++) {
- InputWindow* window = & mWindows.editItemAt(i);
- if (window->hasFocus) {
- mFocusedWindow = window;
- }
-
- if (window->layoutParamsType == 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;
- }
- }
- } else {
- mWindowsReady = false;
+ windows.push();
+ InputWindow& window = windows.editTop();
+ bool valid = populateWindow(env, inputTargetObj, window);
+ if (! valid) {
+ windows.pop();
}
- mTempTouchedWallpaperChannels.clear();
+ env->DeleteLocalRef(inputTargetObj);
+ }
- if ((hadFocusedWindow && ! mFocusedWindow)
- || (mFocusedWindow && ! mFocusedWindow->visible)) {
- preemptInputDispatch();
- }
-
- mDispatchStateChanged.broadcast();
-
-#if DEBUG_FOCUS
- logDispatchStateLd();
-#endif
- } // release lock
+ mInputManager->getDispatcher()->setInputWindows(windows);
}
bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
@@ -1205,663 +933,60 @@
}
void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
-#if DEBUG_FOCUS
- LOGD("setFocusedApplication");
-#endif
- { // acquire lock
- AutoMutex _l(mDispatchLock);
+ if (applicationObj) {
+ jstring nameObj = jstring(env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.name));
+ jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
+ gInputApplicationClassInfo.dispatchingTimeoutNanos);
+ jobject tokenObj = env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.token);
+ jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
+ if (! tokenObjWeak) {
+ LOGE("Could not create weak reference for application token.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+ env->DeleteLocalRef(tokenObj);
- releaseFocusedApplicationLd(env);
-
- if (applicationObj) {
- jstring nameObj = jstring(env->GetObjectField(applicationObj,
- gInputApplicationClassInfo.name));
- jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
- gInputApplicationClassInfo.dispatchingTimeoutNanos);
- jobject tokenObj = env->GetObjectField(applicationObj,
- gInputApplicationClassInfo.token);
- jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
- if (! tokenObjWeak) {
- LOGE("Could not create weak reference for application token.");
- LOGE_EX(env);
- env->ExceptionClear();
- }
- env->DeleteLocalRef(tokenObj);
-
- mFocusedApplication = & mFocusedApplicationStorage;
-
- if (nameObj) {
- const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
- mFocusedApplication->name.setTo(nameStr);
- env->ReleaseStringUTFChars(nameObj, nameStr);
- env->DeleteLocalRef(nameObj);
- } else {
- LOGE("InputApplication.name should not be null.");
- mFocusedApplication->name.setTo("unknown");
- }
-
- mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos;
- mFocusedApplication->tokenObjWeak = tokenObjWeak;
+ String8 name;
+ if (nameObj) {
+ const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+ name.setTo(nameStr);
+ env->ReleaseStringUTFChars(nameObj, nameStr);
+ env->DeleteLocalRef(nameObj);
+ } else {
+ LOGE("InputApplication.name should not be null.");
+ name.setTo("unknown");
}
- mDispatchStateChanged.broadcast();
-
-#if DEBUG_FOCUS
- logDispatchStateLd();
-#endif
- } // release lock
-}
-
-void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) {
- if (mFocusedApplication) {
- env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak);
- mFocusedApplication = NULL;
+ InputApplication application;
+ application.name = name;
+ application.dispatchingTimeout = dispatchingTimeoutNanos;
+ application.handle = new ApplicationToken(tokenObjWeak);
+ mInputManager->getDispatcher()->setFocusedApplication(& application);
+ } else {
+ mInputManager->getDispatcher()->setFocusedApplication(NULL);
}
}
void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
-#if DEBUG_FOCUS
- LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
-#endif
-
- { // acquire lock
- AutoMutex _l(mDispatchLock);
-
- if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
- mDispatchEnabled = enabled;
- mDispatchFrozen = frozen;
-
- mDispatchStateChanged.broadcast();
- }
-
-#if DEBUG_FOCUS
- logDispatchStateLd();
-#endif
- } // release lock
+ mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
}
void NativeInputManager::preemptInputDispatch() {
-#if DEBUG_FOCUS
- LOGD("preemptInputDispatch");
-#endif
-
- mInputManager->preemptInputDispatch();
+ mInputManager->getDispatcher()->preemptInputDispatch();
}
-int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
- InputWindow*& outFocusedWindow) {
-
- int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- bool firstIteration = true;
- ANRTimer anrTimer;
- for (;;) {
- if (firstIteration) {
- firstIteration = false;
- } else {
- if (! anrTimer.waitForDispatchStateChangeLd(this)) {
- LOGW("Dropping event because the dispatcher timed out waiting to identify "
- "the window that should receive it.");
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
- break;
- }
- }
-
- // If dispatch is not enabled then fail.
- if (! mDispatchEnabled) {
- LOGI("Dropping event because input dispatch is disabled.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- break;
- }
-
- // If dispatch is frozen or we don't have valid window data yet then wait.
- if (mDispatchFrozen || ! mWindowsReady) {
-#if DEBUG_FOCUS
- LOGD("Waiting because dispatch is frozen or windows are not ready.");
-#endif
- anrTimer.dispatchFrozenBySystem();
- continue;
- }
-
- // If there is no currently focused window and no focused application
- // then drop the event.
- if (! mFocusedWindow) {
- if (mFocusedApplication) {
-#if DEBUG_FOCUS
- LOGD("Waiting because there is no focused window but there is a "
- "focused application that may yet introduce a new target: '%s'.",
- mFocusedApplication->name.string());
-#endif
- continue;
- }
-
- LOGI("Dropping event because there is no focused window or focused application.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- break;
- }
-
- // Check permissions.
- if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- break;
- }
-
- // If the currently focused window is paused then keep waiting.
- if (mFocusedWindow->paused) {
-#if DEBUG_FOCUS
- LOGD("Waiting because focused window is paused.");
-#endif
- anrTimer.dispatchPausedByApplication(mFocusedWindow);
- continue;
- }
-
- // Success!
- break; // done waiting, exit loop
- }
-
- // Output targets.
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
- addTarget(mFocusedWindow, InputTarget::FLAG_SYNC,
- anrTimer.getTimeSpentWaitingForApplication(), outTargets);
-
- outFocusedWindow = mFocusedWindow;
- } else {
- outFocusedWindow = NULL;
- }
-
-#if DEBUG_FOCUS
- LOGD("waitForFocusedWindow finished: injectionResult=%d",
- injectionResult);
- logDispatchStateLd();
-#endif
- return injectionResult;
-}
-
-enum InjectionPermission {
- INJECTION_PERMISSION_UNKNOWN,
- INJECTION_PERMISSION_GRANTED,
- INJECTION_PERMISSION_DENIED
-};
-
-int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
- InputWindow*& outTouchedWindow) {
- nsecs_t startTime = now();
-
- // For security reasons, we defer updating the touch state until we are sure that
- // event injection will be allowed.
- //
- // FIXME In the original code, screenWasOff could never be set to true.
- // The reason is that the POLICY_FLAG_WOKE_HERE
- // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
- // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
- // actually enqueued using the policyFlags that appeared in the final EV_SYN
- // events upon which no preprocessing took place. So policyFlags was always 0.
- // In the new native input dispatcher we're a bit more careful about event
- // preprocessing so the touches we receive can actually have non-zero policyFlags.
- // Unfortunately we obtain undesirable behavior.
- //
- // Here's what happens:
- //
- // When the device dims in anticipation of going to sleep, touches
- // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
- // the device to brighten and reset the user activity timer.
- // Touches on other windows (such as the launcher window)
- // are dropped. Then after a moment, the device goes to sleep. Oops.
- //
- // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
- // instead of POLICY_FLAG_WOKE_HERE...
- //
- bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
-
- int32_t action = motionEvent->getAction();
-
- bool firstIteration = true;
- ANRTimer anrTimer;
- int32_t injectionResult;
- InjectionPermission injectionPermission;
- for (;;) {
- if (firstIteration) {
- firstIteration = false;
- } else {
- if (! anrTimer.waitForDispatchStateChangeLd(this)) {
- LOGW("Dropping event because the dispatcher timed out waiting to identify "
- "the window that should receive it.");
- injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
- injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- break; // timed out, exit wait loop
- }
- }
-
- // If dispatch is not enabled then fail.
- if (! mDispatchEnabled) {
- LOGI("Dropping event because input dispatch is disabled.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- break; // failed, exit wait loop
- }
-
- // If dispatch is frozen or we don't have valid window data yet then wait.
- if (mDispatchFrozen || ! mWindowsReady) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("Waiting because dispatch is frozen or windows are not ready.");
-#endif
- anrTimer.dispatchFrozenBySystem();
- continue;
- }
-
- // Update the touch state as needed based on the properties of the touch event.
- if (action == AMOTION_EVENT_ACTION_DOWN) {
- /* Case 1: ACTION_DOWN */
-
- InputWindow* newTouchedWindow = NULL;
- mTempTouchedOutsideTargets.clear();
-
- int32_t x = int32_t(motionEvent->getX(0));
- int32_t y = int32_t(motionEvent->getY(0));
- InputWindow* topErrorWindow = NULL;
- bool obscured = false;
-
- // 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);
- int32_t flags = window->layoutParamsFlags;
-
- if (flags & FLAG_SYSTEM_ERROR) {
- if (! topErrorWindow) {
- topErrorWindow = window;
- }
- }
-
- if (window->visible) {
- if (! (flags & FLAG_NOT_TOUCHABLE)) {
- bool isTouchModal = (flags &
- (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0;
- if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
- if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
- newTouchedWindow = window;
- obscured = isWindowObscuredLocked(window);
- }
- break; // found touched window, exit window loop
- }
- }
-
- if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
- OutsideTarget outsideTarget;
- outsideTarget.window = window;
- outsideTarget.obscured = isWindowObscuredLocked(window);
- mTempTouchedOutsideTargets.push(outsideTarget);
- }
- }
- }
-
- // If there is an error window but it is not taking focus (typically because
- // it is invisible) then wait for it. Any other focused window may in
- // fact be in ANR state.
- if (topErrorWindow && newTouchedWindow != topErrorWindow) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("Waiting because system error window is pending.");
-#endif
- anrTimer.dispatchFrozenBySystem();
- continue; // wait some more
- }
-
- // If we did not find a touched window then fail.
- if (! newTouchedWindow) {
- if (mFocusedApplication) {
-#if DEBUG_FOCUS
- LOGD("Waiting because there is no focused window but there is a "
- "focused application that may yet introduce a new target: '%s'.",
- mFocusedApplication->name.string());
-#endif
- continue;
- }
-
- LOGI("Dropping event because there is no touched window or focused application.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_UNKNOWN;
- break; // failed, exit wait loop
- }
-
- // Check permissions.
- if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- injectionPermission = INJECTION_PERMISSION_DENIED;
- break; // failed, exit wait loop
- }
-
- // 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
- anrTimer.dispatchPausedByApplication(newTouchedWindow);
- continue; // wait some more
- }
-
- // Success! Update the touch dispatch state for real.
- releaseTouchedWindowLd();
-
- mTouchedWindow = newTouchedWindow;
- mTouchedWindowIsObscured = obscured;
-
- if (newTouchedWindow->hasWallpaper) {
- mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
- }
-
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- break; // done
- } else {
- /* Case 2: Everything but ACTION_DOWN */
-
- // Check permissions.
- if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
- injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- injectionPermission = INJECTION_PERMISSION_DENIED;
- break; // failed, exit wait loop
- }
-
- // If the pointer is not currently down, then ignore the event.
- if (! mTouchDown) {
- LOGI("Dropping event because the pointer is not down.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- break; // failed, exit wait loop
- }
-
- // If there is no currently touched window then fail.
- if (! mTouchedWindow) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("Dropping event because there is no touched window to receive it.");
-#endif
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- break; // failed, exit wait loop
- }
-
- // 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
- anrTimer.dispatchPausedByApplication(mTouchedWindow);
- continue; // wait some more
- }
-
- // Success!
- injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- break; // done
- }
- }
-
- // Output targets.
- if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
- size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
- for (size_t i = 0; i < numWallpaperWindows; i++) {
- addTarget(mTouchedWallpaperWindows[i],
- InputTarget::FLAG_WINDOW_IS_OBSCURED, 0, outTargets);
- }
-
- 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;
- }
- addTarget(outsideTarget.window, outsideTargetFlags, 0, outTargets);
- }
-
- int32_t targetFlags = InputTarget::FLAG_SYNC;
- if (mTouchedWindowIsObscured) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- }
- addTarget(mTouchedWindow, targetFlags,
- anrTimer.getTimeSpentWaitingForApplication(), outTargets);
- outTouchedWindow = mTouchedWindow;
- } else {
- outTouchedWindow = NULL;
- }
- mTempTouchedOutsideTargets.clear();
-
- // Check injection permission once and for all.
- if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
- if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
- injectorPid, injectorUid)) {
- injectionPermission = INJECTION_PERMISSION_GRANTED;
- } else {
- injectionPermission = INJECTION_PERMISSION_DENIED;
- }
- }
-
- // 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!
- LOGW("Pointer down received while already down.");
- } else {
- mTouchDown = true;
- }
-
- 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.
- releaseTouchedWindowLd();
- }
- } else if (action == AMOTION_EVENT_ACTION_UP) {
- mTouchDown = false;
- releaseTouchedWindowLd();
- }
- } else {
- LOGW("Not updating touch focus because injection was denied.");
- }
-
-#if DEBUG_FOCUS
- LOGD("waitForTouchedWindow finished: injectionResult=%d",
- injectionResult);
- logDispatchStateLd();
-#endif
- return injectionResult;
-}
-
-void NativeInputManager::releaseTouchedWindowLd() {
- mTouchedWindow = NULL;
- mTouchedWindowIsObscured = false;
- mTouchedWallpaperWindows.clear();
-}
-
-void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags,
- nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets) {
- nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication;
- if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) {
- timeout = MIN_INPUT_DISPATCHING_TIMEOUT;
- }
-
- outTargets.push();
-
- InputTarget& target = outTargets.editTop();
- target.inputChannel = window->inputChannel;
- target.flags = targetFlags;
- target.timeout = timeout;
- target.xOffset = - window->frameLeft;
- target.yOffset = - window->frameTop;
-}
-
-bool NativeInputManager::checkInjectionPermission(const InputWindow* window,
- int32_t injectorPid, int32_t injectorUid) {
- if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
- JNIEnv* env = jniEnv();
- jboolean result = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
- checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
-
- 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(),
- window->ownerUid);
- } else {
- LOGW("Permission denied: injecting event from pid %d uid %d",
- injectorPid, injectorUid);
- }
- return false;
- }
- }
-
- return true;
-}
-
-bool NativeInputManager::isWindowObscuredLocked(const InputWindow* window) {
- 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)) {
- return true;
- }
- }
- return false;
-}
-
-int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
-#endif
-
- int32_t windowType;
- { // acquire lock
- AutoMutex _l(mDispatchLock);
-
- InputWindow* focusedWindow;
- int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
- injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- return injectionResult;
- }
-
- windowType = focusedWindow->layoutParamsType;
- } // release lock
-
- if (isPolicyKey(keyEvent->getKeyCode(), isScreenOn())) {
- const InputTarget& target = outTargets.top();
- bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
- if (consumed) {
- outTargets.clear();
- return INPUT_EVENT_INJECTION_SUCCEEDED;
- }
-
- addMonitoringTargetsLd(outTargets);
- }
-
- pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
- return INPUT_EVENT_INJECTION_SUCCEEDED;
-}
-
-int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent,
- uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
-#endif
-
- int32_t source = motionEvent->getSource();
- if (source & AINPUT_SOURCE_CLASS_POINTER) {
- return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
- outTargets);
- } else {
- return waitForNonTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
- outTargets);
- }
-}
-
-int32_t NativeInputManager::waitForNonTouchEventTargets(MotionEvent* motionEvent,
- uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("waitForNonTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
-#endif
-
- int32_t windowType;
- { // acquire lock
- AutoMutex _l(mDispatchLock);
-
- InputWindow* focusedWindow;
- int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
- injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- return injectionResult;
- }
-
- windowType = focusedWindow->layoutParamsType;
-
- addMonitoringTargetsLd(outTargets);
- } // release lock
-
- pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
- return INPUT_EVENT_INJECTION_SUCCEEDED;
-}
-
-int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent,
- uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
- Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("waitForTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
-#endif
-
- int32_t windowType;
- { // acquire lock
- AutoMutex _l(mDispatchLock);
-
- InputWindow* touchedWindow;
- int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags,
- injectorPid, injectorUid, outTargets, /*out*/ touchedWindow);
- if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
- return injectionResult;
- }
-
- windowType = touchedWindow->layoutParamsType;
-
- addMonitoringTargetsLd(outTargets);
- } // release lock
-
- int32_t eventType;
- switch (motionEvent->getAction()) {
- 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 (motionEvent->getEventTime() - motionEvent->getDownTime()
- >= EVENT_IGNORE_DURATION) {
- eventType = POWER_MANAGER_TOUCH_EVENT;
- } else {
- eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
- }
- break;
- }
- pokeUserActivityIfNeeded(windowType, eventType);
- return INPUT_EVENT_INJECTION_SUCCEEDED;
-}
-
-bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target,
+bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) {
+ bool isScreenOn = this->isScreenOn();
+ if (! isPolicyKey(keyEvent->getKeyCode(), isScreenOn)) {
+ return false;
+ }
+
JNIEnv* env = jniEnv();
- jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel);
+ jobject inputChannelObj = getInputChannelObjLocal(env, inputChannel);
if (inputChannelObj) {
jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
gCallbacksClassInfo.interceptKeyBeforeDispatching,
@@ -1875,253 +1000,27 @@
return consumed && ! error;
} else {
LOGW("Could not apply key dispatch policy because input channel '%s' is "
- "no longer valid.", target.inputChannel->getName().string());
+ "no longer valid.", inputChannel->getName().string());
return false;
}
}
-void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) {
- if (windowType != TYPE_KEYGUARD) {
- nsecs_t eventTime = now();
- pokeUserActivity(eventTime, eventType);
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) {
+ if (windowType != InputWindow::TYPE_KEYGUARD) {
+ android_server_PowerManagerService_userActivity(eventTime, eventType);
}
}
-void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
- android_server_PowerManagerService_userActivity(eventTime, eventType);
-}
-void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
- { // acquire lock
- AutoMutex _l(mDispatchLock);
- mMonitoringChannels.push(inputChannel);
- } // release lock
-}
-
-void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
- { // acquire lock
- AutoMutex _l(mDispatchLock);
-
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- if (mMonitoringChannels[i] == inputChannel) {
- mMonitoringChannels.removeAt(i);
- break;
- }
- }
- } // release lock
-}
-
-void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- outTargets.push();
-
- InputTarget& target = outTargets.editTop();
- target.inputChannel = mMonitoringChannels[i];
- target.flags = 0;
- target.timeout = -1;
- target.xOffset = 0;
- target.yOffset = 0;
- }
-}
-
-static void dumpMotionRange(String8& dump,
- const char* name, const InputDeviceInfo::MotionRange* range) {
- if (range) {
- dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
- name, range->min, range->max, range->flat, range->fuzz);
- }
-}
-
-#define DUMP_MOTION_RANGE(range) \
- dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
-
-void NativeInputManager::dumpDeviceInfo(String8& dump) {
- Vector<int32_t> deviceIds;
- mInputManager->getInputDeviceIds(deviceIds);
-
- InputDeviceInfo deviceInfo;
- for (size_t i = 0; i < deviceIds.size(); i++) {
- int32_t deviceId = deviceIds[i];
-
- status_t result = mInputManager->getInputDeviceInfo(deviceId, & deviceInfo);
- if (result == NAME_NOT_FOUND) {
- continue;
- } else if (result != OK) {
- dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n",
- result);
- continue;
- }
-
- dump.appendFormat(" Device %d: '%s'\n",
- deviceInfo.getId(), deviceInfo.getName().string());
- dump.appendFormat(" sources = 0x%08x\n",
- deviceInfo.getSources());
- dump.appendFormat(" keyboardType = %d\n",
- deviceInfo.getKeyboardType());
-
- dump.append(" motion ranges:\n");
- DUMP_MOTION_RANGE(X);
- DUMP_MOTION_RANGE(Y);
- DUMP_MOTION_RANGE(PRESSURE);
- DUMP_MOTION_RANGE(SIZE);
- DUMP_MOTION_RANGE(TOUCH_MAJOR);
- DUMP_MOTION_RANGE(TOUCH_MINOR);
- DUMP_MOTION_RANGE(TOOL_MAJOR);
- DUMP_MOTION_RANGE(TOOL_MINOR);
- DUMP_MOTION_RANGE(ORIENTATION);
- }
-}
-
-#undef DUMP_MOTION_RANGE
-
-void NativeInputManager::logDispatchStateLd() {
- String8 dump;
- dumpDispatchStateLd(dump);
- LOGD("%s", dump.string());
-}
-
-void NativeInputManager::dumpDispatchStateLd(String8& dump) {
- dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled);
- dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen);
- dump.appendFormat(" windowsReady: %d\n", mWindowsReady);
-
- if (mFocusedApplication) {
- dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
- mFocusedApplication->name.string(),
- mFocusedApplication->dispatchingTimeout / 1000000.0);
- } else {
- dump.append(" focusedApplication: <null>\n");
- }
- dump.appendFormat(" focusedWindow: '%s'\n",
- mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
- dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n",
- mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
- mTouchDown);
- for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
- dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n",
- i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
- }
- for (size_t i = 0; i < mWindows.size(); i++) {
- dump.appendFormat(" windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, "
- "visible=%d, flags=0x%08x, type=0x%08x, "
- "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].inputChannel->getName().string(),
- mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
- mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
- 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);
- }
-
- for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
- dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
- i, mMonitoringChannels[i]->getName().string());
- }
-}
-
-// ----------------------------------------------------------------------------
-
-bool NativeInputManager::InputWindow::visibleFrameIntersects(const InputWindow* other) const {
- return visibleFrameRight > other->visibleFrameLeft
- && visibleFrameLeft < other->visibleFrameRight
- && visibleFrameBottom > other->visibleFrameTop
- && visibleFrameTop < other->visibleFrameBottom;
-}
-
-bool NativeInputManager::InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
- return x >= touchableAreaLeft && x <= touchableAreaRight
- && y >= touchableAreaTop && y <= touchableAreaBottom;
-}
-
-// ----------------------------------------------------------------------------
-
-NativeInputManager::ANRTimer::ANRTimer() :
- mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
-}
-
-void NativeInputManager::ANRTimer::dispatchFrozenBySystem() {
- mFrozen = true;
-}
-
-void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) {
- mPausedWindow = pausedWindow;
-}
-
-bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) {
- nsecs_t currentTime = now();
-
- Budget newBudget;
- nsecs_t dispatchingTimeout;
- sp<InputChannel> pausedChannel = NULL;
- jobject tokenObj = NULL;
- if (mFrozen) {
- newBudget = SYSTEM;
- dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
- mFrozen = false;
- } else if (mPausedWindow) {
- newBudget = APPLICATION;
- dispatchingTimeout = mPausedWindow->dispatchingTimeout;
- pausedChannel = mPausedWindow->inputChannel;
- mPausedWindow = NULL;
- } else if (inputManager->mFocusedApplication) {
- newBudget = APPLICATION;
- dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout;
- tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak);
- } else {
- newBudget = APPLICATION;
- dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
- }
-
- if (mBudget != newBudget) {
- mBudget = newBudget;
- mStartTime = currentTime;
- }
-
- bool result = false;
- nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime;
- if (timeoutRemaining > 0
- && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock,
- timeoutRemaining) == OK) {
- result = true;
- } else {
- if (pausedChannel != NULL || tokenObj != NULL) {
- bool resumed;
- nsecs_t newTimeout = 0;
-
- inputManager->mDispatchLock.unlock(); // release lock
- if (pausedChannel != NULL) {
- resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout);
- } else {
- resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout);
- }
- inputManager->mDispatchLock.lock(); // re-acquire lock
-
- if (resumed) {
- mStartTime = now() - dispatchingTimeout + newTimeout;
- result = true;
- }
- }
- }
-
- if (tokenObj) {
- jniEnv()->DeleteLocalRef(tokenObj);
- }
-
+bool NativeInputManager::checkInjectEventsPermissionNonReentrant(
+ int32_t injectorPid, int32_t injectorUid) {
+ JNIEnv* env = jniEnv();
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
+ checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
return result;
}
-nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const {
- return mBudget == APPLICATION ? now() - mStartTime : 0;
-}
-
// ----------------------------------------------------------------------------
static sp<NativeInputManager> gNativeInputManager;
@@ -2184,7 +1083,7 @@
return AKEY_STATE_UNKNOWN;
}
- return gNativeInputManager->getInputManager()->getScanCodeState(
+ return gNativeInputManager->getInputManager()->getReader()->getScanCodeState(
deviceId, uint32_t(sourceMask), scanCode);
}
@@ -2194,7 +1093,7 @@
return AKEY_STATE_UNKNOWN;
}
- return gNativeInputManager->getInputManager()->getKeyCodeState(
+ return gNativeInputManager->getInputManager()->getReader()->getKeyCodeState(
deviceId, uint32_t(sourceMask), keyCode);
}
@@ -2204,7 +1103,7 @@
return AKEY_STATE_UNKNOWN;
}
- return gNativeInputManager->getInputManager()->getSwitchState(
+ return gNativeInputManager->getInputManager()->getReader()->getSwitchState(
deviceId, uint32_t(sourceMask), sw);
}
@@ -2219,7 +1118,7 @@
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(keyCodes)) {
- result = gNativeInputManager->getInputManager()->hasKeys(
+ result = gNativeInputManager->getInputManager()->getReader()->hasKeys(
deviceId, uint32_t(sourceMask), numCodes, codes, flags);
} else {
result = JNI_FALSE;
@@ -2306,14 +1205,14 @@
KeyEvent keyEvent;
android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent);
- return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
- injectorPid, injectorUid, syncMode, timeoutMillis);
+ return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
+ & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
MotionEvent motionEvent;
android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent);
- return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
- injectorPid, injectorUid, syncMode, timeoutMillis);
+ return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
+ & motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
@@ -2363,7 +1262,7 @@
}
InputDeviceInfo deviceInfo;
- status_t status = gNativeInputManager->getInputManager()->getInputDeviceInfo(
+ status_t status = gNativeInputManager->getInputManager()->getReader()->getInputDeviceInfo(
deviceId, & deviceInfo);
if (status) {
return NULL;
@@ -2405,7 +1304,7 @@
}
Vector<int> deviceIds;
- gNativeInputManager->getInputManager()->getInputDeviceIds(deviceIds);
+ gNativeInputManager->getInputManager()->getReader()->getInputDeviceIds(deviceIds);
jintArray deviceIdsObj = env->NewIntArray(deviceIds.size());
if (! deviceIdsObj) {
@@ -2421,7 +1320,8 @@
return NULL;
}
- String8 dump(gNativeInputManager->dump());
+ String8 dump;
+ gNativeInputManager->dump(dump);
return env->NewStringUTF(dump.string());
}
@@ -2519,9 +1419,6 @@
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
"checkInjectEventsPermission", "(II)Z");
- GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
- "notifyAppSwitchComing", "()V");
-
GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz,
"filterTouchEvents", "()Z");
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index 7c329b2..af10711 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -20,20 +20,10 @@
#include "JNIHelp.h"
#include "jni.h"
+#include <ui/PowerManager.h>
+
namespace android {
-enum {
- POWER_MANAGER_OTHER_EVENT = 0,
- POWER_MANAGER_CHEEK_EVENT = 1,
- POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
- // up events or LONG_TOUCH events.
- POWER_MANAGER_LONG_TOUCH_EVENT = 3,
- POWER_MANAGER_TOUCH_UP_EVENT = 4,
- POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
-
- POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
-};
-
extern bool android_server_PowerManagerService_isScreenOn();
extern bool android_server_PowerManagerService_isScreenBright();
extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType);
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index e5456e4..c20c200 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -40,6 +40,7 @@
MMI, /* not presently used; dial() returns null */
INVALID_NUMBER, /* invalid dial string */
INVALID_CREDENTIALS, /* invalid credentials */
+ TIMED_OUT, /* client timed out */
LOST_SIGNAL,
LIMIT_EXCEEDED, /* eg GSM ACM limit exceeded */
INCOMING_REJECTED, /* an incoming call that was rejected */
diff --git a/telephony/java/com/android/internal/telephony/sip/SipCallBase.java b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
index e7eda4f..7e407b2 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipCallBase.java
@@ -26,7 +26,6 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import javax.sip.SipException;
abstract class SipCallBase extends Call {
private static final int MAX_CONNECTIONS_PER_CALL = 5;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index bf1b939..2c99145 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -24,6 +24,7 @@
import android.net.rtp.AudioStream;
import android.net.sip.SipAudioCall;
import android.net.sip.SipErrorCode;
+import android.net.sip.SipException;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipSessionState;
@@ -67,8 +68,6 @@
import java.util.ArrayList;
import java.util.List;
-import javax.sip.SipException;
-
/**
* {@hide}
*/
@@ -827,7 +826,8 @@
onError(Connection.DisconnectCause.INVALID_NUMBER);
break;
case TIME_OUT:
- onError(Connection.DisconnectCause.CONGESTION);
+ case TRANSACTION_TERMINTED:
+ onError(Connection.DisconnectCause.TIMED_OUT);
break;
case INVALID_CREDENTIALS:
onError(Connection.DisconnectCause.INVALID_CREDENTIALS);
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 02f82b3..39083a5 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -20,8 +20,6 @@
import android.net.rtp.AudioStream;
import android.os.Message;
-import javax.sip.SipException;
-
/**
* Interface for making audio calls over SIP.
* @hide
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 2e2ca5b..67ba97f 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -43,7 +43,6 @@
import java.util.List;
import java.util.Map;
import javax.sdp.SdpException;
-import javax.sip.SipException;
/**
* Class that handles an audio call over SIP.
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
index 2eb67e8..8624811 100644
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ b/voip/java/android/net/sip/SipErrorCode.java
@@ -31,6 +31,9 @@
/** When server responds with an error. */
SERVER_ERROR,
+ /** When transaction is terminated unexpectedly. */
+ TRANSACTION_TERMINTED,
+
/** When some error occurs on the device, possibly due to a bug. */
CLIENT_ERROR,
@@ -41,5 +44,8 @@
INVALID_REMOTE_URI,
/** When invalid credentials are provided. */
- INVALID_CREDENTIALS;
+ INVALID_CREDENTIALS,
+
+ /** The client is in a transaction and cannot initiate a new one. */
+ IN_PROGRESS;
}
diff --git a/voip/java/android/net/sip/SipException.java b/voip/java/android/net/sip/SipException.java
new file mode 100644
index 0000000..d615342
--- /dev/null
+++ b/voip/java/android/net/sip/SipException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.net.sip;
+
+/**
+ * @hide
+ */
+public class SipException extends Exception {
+ public SipException() {
+ }
+
+ public SipException(String message) {
+ super(message);
+ }
+
+ public SipException(String message, Throwable cause) {
+ // we want to eliminate the dependency on javax.sip.SipException
+ super(message, ((cause instanceof javax.sip.SipException)
+ && (cause.getCause() != null))
+ ? cause.getCause()
+ : cause);
+ }
+}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index beec8fe..ccae7f9 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -25,7 +25,6 @@
import android.os.ServiceManager;
import java.text.ParseException;
-import javax.sip.SipException;
/**
* The class provides API for various SIP related tasks. Specifically, the API
@@ -501,15 +500,15 @@
}
@Override
- public void onRegistrationFailed(ISipSession session, String className,
+ public void onRegistrationFailed(ISipSession session, String errorCode,
String message) {
- mListener.onRegistrationFailed(getUri(session), className, message);
+ mListener.onRegistrationFailed(getUri(session), errorCode, message);
}
@Override
public void onRegistrationTimeout(ISipSession session) {
mListener.onRegistrationFailed(getUri(session),
- SipException.class.getName(), "registration timed out");
+ SipErrorCode.TIME_OUT.toString(), "registration timed out");
}
}
}