summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--cmds/installd/commands.c23
-rw-r--r--cmds/installd/installd.h5
-rw-r--r--cmds/installd/tests/installd_utils_test.cpp56
-rw-r--r--cmds/installd/utils.c18
-rw-r--r--core/java/android/os/IBinder.java13
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java43
-rw-r--r--core/java/android/view/HardwareRenderer.java43
-rwxr-xr-xcore/java/android/view/InputEvent.java57
-rw-r--r--core/java/android/view/InputEventConsistencyVerifier.java11
-rw-r--r--core/java/android/view/InputEventReceiver.java151
-rw-r--r--core/java/android/view/InputHandler.java43
-rw-r--r--core/java/android/view/InputQueue.java131
-rwxr-xr-xcore/java/android/view/KeyEvent.java8
-rw-r--r--core/java/android/view/MotionEvent.java20
-rw-r--r--core/java/android/view/ViewDebug.java13
-rw-r--r--core/java/android/view/ViewRootImpl.java676
-rw-r--r--core/java/android/view/WindowManagerPolicy.java3
-rw-r--r--core/java/android/widget/TextView.java78
-rw-r--r--core/java/com/android/internal/view/BaseInputHandler.java36
-rw-r--r--core/jni/Android.mk2
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp307
-rw-r--r--core/jni/android_view_InputQueue.cpp530
-rw-r--r--libs/binder/IPCThreadState.cpp1
-rw-r--r--libs/hwui/DisplayListRenderer.cpp15
-rw-r--r--libs/hwui/DisplayListRenderer.h2
-rw-r--r--libs/hwui/OpenGLRenderer.cpp14
-rw-r--r--libs/hwui/OpenGLRenderer.h2
-rw-r--r--media/java/android/media/AudioService.java398
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java10
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java65
-rw-r--r--services/java/com/android/server/DeviceStorageMonitorService.java1
-rw-r--r--services/java/com/android/server/wm/DragState.java10
-rw-r--r--services/java/com/android/server/wm/FakeWindowImpl.java12
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java33
36 files changed, 1417 insertions, 1418 deletions
diff --git a/api/current.txt b/api/current.txt
index 86b6d8fc39bc..5dc32aed7761 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14788,6 +14788,7 @@ package android.os {
field public static final int FLAG_ONEWAY = 1; // 0x1
field public static final int INTERFACE_TRANSACTION = 1598968902; // 0x5f4e5446
field public static final int LAST_CALL_TRANSACTION = 16777215; // 0xffffff
+ field public static final int LIKE_TRANSACTION = 1598835019; // 0x5f4c494b
field public static final int PING_TRANSACTION = 1599098439; // 0x5f504e47
field public static final int TWEET_TRANSACTION = 1599362900; // 0x5f545754
}
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 34c0c2a97761..db7258517379 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -184,6 +184,7 @@ int free_cache(int64_t free_size)
DIR *d;
struct dirent *de;
int64_t avail;
+ char datadir[PKG_PATH_MAX];
avail = disk_free();
if (avail < 0) return -1;
@@ -191,9 +192,14 @@ int free_cache(int64_t free_size)
LOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
if (avail >= free_size) return 0;
- d = opendir(android_data_dir.path);
+ if (create_persona_path(datadir, 0)) {
+ LOGE("couldn't get directory for persona 0");
+ return -1;
+ }
+
+ d = opendir(datadir);
if (d == NULL) {
- LOGE("cannot open %s: %s\n", android_data_dir.path, strerror(errno));
+ LOGE("cannot open %s: %s\n", datadir, strerror(errno));
return -1;
}
dfd = dirfd(d);
@@ -578,19 +584,6 @@ fail:
return -1;
}
-int create_move_path(char path[PKG_PATH_MAX],
- const char* pkgname,
- const char* leaf,
- uid_t persona)
-{
- if ((android_data_dir.len + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) {
- return -1;
- }
-
- sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf);
- return 0;
-}
-
void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
struct stat* statbuf)
{
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index c5872b8efa09..173cabfb77a7 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -105,6 +105,11 @@ int create_pkg_path(char path[PKG_PATH_MAX],
int create_persona_path(char path[PKG_PATH_MAX],
uid_t persona);
+int create_move_path(char path[PKG_PATH_MAX],
+ const char* pkgname,
+ const char* leaf,
+ uid_t persona);
+
int is_valid_package_name(const char* pkgname);
int create_cache_path(char path[PKG_PATH_MAX], const char *src);
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 1128fceca0af..7cb9b37fe038 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -34,6 +34,16 @@ extern "C" {
#define TEST_SYSTEM_DIR1 "/system/app/"
#define TEST_SYSTEM_DIR2 "/vendor/app/"
+#define REALLY_LONG_APP_NAME "com.example." \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+#define REALLY_LONG_LEAF_NAME "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
+ "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
+ "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
+ "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_"
+
namespace android {
class UtilsTest : public testing::Test {
@@ -210,7 +220,7 @@ TEST_F(UtilsTest, CheckSystemApp_BadPathEscapeFail) {
TEST_F(UtilsTest, GetPathFromString_NullPathFail) {
dir_rec_t test1;
- EXPECT_EQ(-1, get_path_from_string(&test1, NULL))
+ EXPECT_EQ(-1, get_path_from_string(&test1, (const char *) NULL))
<< "Should not allow NULL as a path.";
}
@@ -327,6 +337,50 @@ TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) {
<< "Package path should be in /data/app-private/";
}
+TEST_F(UtilsTest, CreatePersonaPath_Primary) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_persona_path(path, 0))
+ << "Should successfully build primary user path.";
+
+ EXPECT_STREQ("/data/data/", path)
+ << "Primary user should have correct path";
+}
+
+TEST_F(UtilsTest, CreatePersonaPath_Secondary) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_persona_path(path, 1))
+ << "Should successfully build primary user path.";
+
+ EXPECT_STREQ("/data/user/1/", path)
+ << "Primary user should have correct path";
+}
+
+TEST_F(UtilsTest, CreateMovePath_Primary) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_move_path(path, "com.android.test", "shared_prefs", 0))
+ << "Should be able to create move path for primary user";
+
+ EXPECT_STREQ("/data/data/com.android.test/shared_prefs", path)
+ << "Primary user package directory should be created correctly";
+}
+
+TEST_F(UtilsTest, CreateMovePath_Fail_AppTooLong) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(-1, create_move_path(path, REALLY_LONG_APP_NAME, "shared_prefs", 0))
+ << "Should fail to create move path for primary user";
+}
+
+TEST_F(UtilsTest, CreateMovePath_Fail_LeafTooLong) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(-1, create_move_path(path, "com.android.test", REALLY_LONG_LEAF_NAME, 0))
+ << "Should fail to create move path for primary user";
+}
+
TEST_F(UtilsTest, CopyAndAppend_Normal) {
//int copy_and_append(dir_rec_t* dst, dir_rec_t* src, char* suffix)
dir_rec_t dst;
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 3099b8341f6e..a53a93ce1073 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -109,7 +109,7 @@ int create_persona_path(char path[PKG_PATH_MAX],
uid_len = 0;
} else {
persona_prefix = SECONDARY_USER_PREFIX;
- uid_len = snprintf(NULL, 0, "%d", persona);
+ uid_len = snprintf(NULL, 0, "%d/", persona);
}
char *dst = path;
@@ -126,7 +126,7 @@ int create_persona_path(char path[PKG_PATH_MAX],
LOGE("Error building user path");
return -1;
}
- int ret = snprintf(dst, dst_size, "%d", persona);
+ int ret = snprintf(dst, dst_size, "%d/", persona);
if (ret < 0 || (size_t) ret != uid_len) {
LOGE("Error appending persona id to path");
return -1;
@@ -135,6 +135,20 @@ int create_persona_path(char path[PKG_PATH_MAX],
return 0;
}
+int create_move_path(char path[PKG_PATH_MAX],
+ const char* pkgname,
+ const char* leaf,
+ uid_t persona)
+{
+ if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1)
+ >= PKG_PATH_MAX) {
+ return -1;
+ }
+
+ sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf);
+ return 0;
+}
+
/**
* Checks whether the package name is valid. Returns -1 on error and
* 0 on success.
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 81defd690af8..0586d9ed1eeb 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -128,6 +128,19 @@ public interface IBinder {
int TWEET_TRANSACTION = ('_'<<24)|('T'<<16)|('W'<<8)|'T';
/**
+ * IBinder protocol transaction code: tell an app asynchronously that the
+ * caller likes it. The app is responsible for incrementing and maintaining
+ * its own like counter, and may display this value to the user to indicate the
+ * quality of the app. This is an optional command that applications do not
+ * need to handle, so the default implementation is to do nothing.
+ *
+ * <p>There is no response returned and nothing about the
+ * system will be functionally affected by it, but it will improve the
+ * app's self-esteem.
+ */
+ int LIKE_TRANSACTION = ('_'<<24)|('L'<<16)|('I'<<8)|'K';
+
+ /**
* Flag to {@link #transact}: this is a one-way call, meaning that the
* caller returns immediately, without waiting for a result from the
* callee. Applies only if the caller and callee are in different
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 18167b601d32..7ce96c0e31bd 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -18,7 +18,6 @@ package android.service.wallpaper;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
-import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.BaseSurfaceHolder;
import android.annotation.SdkConstant;
@@ -45,8 +44,8 @@ import android.view.Gravity;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.InputHandler;
-import android.view.InputQueue;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
@@ -228,24 +227,29 @@ public abstract class WallpaperService extends Service {
}
};
-
- final InputHandler mInputHandler = new BaseInputHandler() {
+
+ final class WallpaperInputEventReceiver extends InputEventReceiver {
+ public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleMotion(MotionEvent event,
- InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
- int source = event.getSource();
- if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- dispatchPointer(event);
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
+ dispatchPointer(dup);
handled = true;
}
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
-
+ }
+ WallpaperInputEventReceiver mInputEventReceiver;
+
final BaseIWindow mWindow = new BaseIWindow() {
@Override
public void resized(int w, int h, Rect coveredInsets,
@@ -534,6 +538,8 @@ public abstract class WallpaperService extends Service {
}
Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
mCaller.sendMessage(msg);
+ } else {
+ event.recycle();
}
}
@@ -599,8 +605,8 @@ public abstract class WallpaperService extends Service {
}
mCreated = true;
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
+ mInputEventReceiver = new WallpaperInputEventReceiver(
+ mInputChannel, Looper.myLooper());
}
mSurfaceHolder.mSurfaceLock.lock();
@@ -902,8 +908,9 @@ public abstract class WallpaperService extends Service {
if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
+ mSurfaceHolder.getSurface() + " of: " + this);
- if (mInputChannel != null) {
- InputQueue.unregisterInputChannel(mInputChannel);
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
}
mSession.remove(mWindow);
@@ -970,6 +977,8 @@ public abstract class WallpaperService extends Service {
public void dispatchPointer(MotionEvent event) {
if (mEngine != null) {
mEngine.dispatchPointer(event);
+ } else {
+ event.recycle();
}
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ccb64895703c..443acf68411c 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -837,10 +837,37 @@ public abstract class HardwareRenderer {
(view.mPrivateFlags & View.INVALIDATED) == View.INVALIDATED;
view.mPrivateFlags &= ~View.INVALIDATED;
+ final long getDisplayListStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ getDisplayListStartTime = System.nanoTime();
+ }
+
DisplayList displayList = view.getDisplayList();
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- getDisplayList() took "
+ + ((now - getDisplayListStartTime) * 0.000001f) + "ms");
+ }
+
if (displayList != null) {
- if (canvas.drawDisplayList(displayList, view.getWidth(),
- view.getHeight(), mRedrawClip)) {
+ final long drawDisplayListStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ drawDisplayListStartTime = System.nanoTime();
+ }
+
+ boolean invalidateNeeded = canvas.drawDisplayList(
+ displayList, view.getWidth(),
+ view.getHeight(), mRedrawClip);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took "
+ + ((now - drawDisplayListStartTime) * 0.000001f)
+ + "ms, invalidateNeeded=" + invalidateNeeded + ".");
+ }
+
+ if (invalidateNeeded) {
if (mRedrawClip.isEmpty() || view.getParent() == null) {
view.invalidate();
} else {
@@ -872,7 +899,19 @@ public abstract class HardwareRenderer {
attachInfo.mIgnoreDirtyState = false;
+ final long eglSwapBuffersStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ eglSwapBuffersStartTime = System.nanoTime();
+ }
+
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- eglSwapBuffers() took "
+ + ((now - eglSwapBuffersStartTime) * 0.000001f) + "ms");
+ }
+
checkEglErrors();
return dirty == null;
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 01ddcc9bf1a4..c42bbdcadf04 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -19,6 +19,8 @@ package android.view;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* Common base class for input events.
*/
@@ -27,8 +29,21 @@ public abstract class InputEvent implements Parcelable {
protected static final int PARCEL_TOKEN_MOTION_EVENT = 1;
/** @hide */
protected static final int PARCEL_TOKEN_KEY_EVENT = 2;
-
+
+ // Next sequence number.
+ private static final AtomicInteger mNextSeq = new AtomicInteger();
+
+ /** @hide */
+ protected int mSeq;
+
+ /** @hide */
+ protected boolean mRecycled;
+
+ private static final boolean TRACK_RECYCLED_LOCATION = false;
+ private RuntimeException mRecycledLocation;
+
/*package*/ InputEvent() {
+ mSeq = mNextSeq.getAndIncrement();
}
/**
@@ -82,7 +97,29 @@ public abstract class InputEvent implements Parcelable {
* objects are fine. See {@link KeyEvent#recycle()} for details.
* @hide
*/
- public abstract void recycle();
+ public void recycle() {
+ if (TRACK_RECYCLED_LOCATION) {
+ if (mRecycledLocation != null) {
+ throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+ }
+ mRecycledLocation = new RuntimeException("Last recycled here");
+ } else {
+ if (mRecycled) {
+ throw new RuntimeException(toString() + " recycled twice!");
+ }
+ mRecycled = true;
+ }
+ }
+
+ /**
+ * Reinitializes the event on reuse (after recycling).
+ * @hide
+ */
+ protected void prepareForReuse() {
+ mRecycled = false;
+ mRecycledLocation = null;
+ mSeq = mNextSeq.getAndIncrement();
+ }
/**
* Gets a private flag that indicates when the system has detected that this input event
@@ -113,6 +150,22 @@ public abstract class InputEvent implements Parcelable {
*/
public abstract long getEventTimeNano();
+ /**
+ * Gets the unique sequence number of this event.
+ * Every input event that is created or received by a process has a
+ * unique sequence number. Moreover, a new sequence number is obtained
+ * each time an event object is recycled.
+ *
+ * Sequence numbers are only guaranteed to be locally unique within a process.
+ * Sequence numbers are not preserved when events are parceled.
+ *
+ * @return The unique sequence number of this event.
+ * @hide
+ */
+ public int getSequenceNumber() {
+ return mSeq;
+ }
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index 9b081b207dff..fafe416defdf 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -58,7 +58,7 @@ public final class InputEventConsistencyVerifier {
// so that the verifier can detect when it has been asked to verify the same event twice.
// It does not make sense to examine the contents of the last event since it may have
// been recycled.
- private InputEvent mLastEvent;
+ private int mLastEventSeq;
private String mLastEventType;
private int mLastNestingLevel;
@@ -140,7 +140,7 @@ public final class InputEventConsistencyVerifier {
* Resets the state of the input event consistency verifier.
*/
public void reset() {
- mLastEvent = null;
+ mLastEventSeq = -1;
mLastNestingLevel = 0;
mTrackballDown = false;
mTrackballUnhandled = false;
@@ -573,17 +573,18 @@ public final class InputEventConsistencyVerifier {
private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
// Ignore the event if we already checked it at a higher nesting level.
- if (event == mLastEvent && nestingLevel < mLastNestingLevel
+ final int seq = event.getSequenceNumber();
+ if (seq == mLastEventSeq && nestingLevel < mLastNestingLevel
&& eventType == mLastEventType) {
return false;
}
if (nestingLevel > 0) {
- mLastEvent = event;
+ mLastEventSeq = seq;
mLastEventType = eventType;
mLastNestingLevel = nestingLevel;
} else {
- mLastEvent = null;
+ mLastEventSeq = -1;
mLastEventType = null;
mLastNestingLevel = 0;
}
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
new file mode 100644
index 000000000000..abb52810c446
--- /dev/null
+++ b/core/java/android/view/InputEventReceiver.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 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.view;
+
+import dalvik.system.CloseGuard;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.util.Log;
+
+/**
+ * Provides a low-level mechanism for an application to receive input events.
+ * @hide
+ */
+public abstract class InputEventReceiver {
+ private static final String TAG = "InputEventReceiver";
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ private int mReceiverPtr;
+
+ // We keep references to the input channel and message queue objects here so that
+ // they are not GC'd while the native peer of the receiver is using them.
+ private InputChannel mInputChannel;
+ private MessageQueue mMessageQueue;
+
+ // The sequence number of the event that is in progress.
+ private int mEventSequenceNumberInProgress = -1;
+
+ private static native int nativeInit(InputEventReceiver receiver,
+ InputChannel inputChannel, MessageQueue messageQueue);
+ private static native void nativeDispose(int receiverPtr);
+ private static native void nativeFinishInputEvent(int receiverPtr, boolean handled);
+
+ /**
+ * Creates an input event receiver bound to the specified input channel.
+ *
+ * @param inputChannel The input channel.
+ * @param looper The looper to use when invoking callbacks.
+ */
+ public InputEventReceiver(InputChannel inputChannel, Looper looper) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null");
+ }
+ if (looper == null) {
+ throw new IllegalArgumentException("looper must not be null");
+ }
+
+ mInputChannel = inputChannel;
+ mMessageQueue = looper.getQueue();
+ mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue);
+
+ mCloseGuard.open("dispose");
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ dispose();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Disposes the receiver.
+ */
+ public void dispose() {
+ if (mCloseGuard != null) {
+ mCloseGuard.close();
+ }
+ if (mReceiverPtr != 0) {
+ nativeDispose(mReceiverPtr);
+ mReceiverPtr = 0;
+ }
+ mInputChannel = null;
+ mMessageQueue = null;
+ }
+
+ /**
+ * Called when an input event is received.
+ * The recipient should process the input event and then call {@link #finishInputEvent}
+ * to indicate whether the event was handled. No new input events will be received
+ * until {@link #finishInputEvent} is called.
+ *
+ * @param event The input event that was received.
+ */
+ public void onInputEvent(InputEvent event) {
+ finishInputEvent(event, false);
+ }
+
+ /**
+ * Finishes an input event and indicates whether it was handled.
+ *
+ * @param event The input event that was finished.
+ * @param handled True if the event was handled.
+ */
+ public void finishInputEvent(InputEvent event, boolean handled) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+ if (mReceiverPtr == 0) {
+ Log.w(TAG, "Attempted to finish an input event but the input event "
+ + "receiver has already been disposed.");
+ } else {
+ if (event.getSequenceNumber() != mEventSequenceNumberInProgress) {
+ Log.w(TAG, "Attempted to finish an input event that is not in progress.");
+ } else {
+ mEventSequenceNumberInProgress = -1;
+ nativeFinishInputEvent(mReceiverPtr, handled);
+ }
+ }
+ recycleInputEvent(event);
+ }
+
+ // Called from native code.
+ @SuppressWarnings("unused")
+ private void dispatchInputEvent(InputEvent event) {
+ mEventSequenceNumberInProgress = event.getSequenceNumber();
+ onInputEvent(event);
+ }
+
+ private static void recycleInputEvent(InputEvent event) {
+ if (event instanceof MotionEvent) {
+ // Event though key events are also recyclable, we only recycle motion events.
+ // Historically, key events were not recyclable and applications expect
+ // them to be immutable. We only ever recycle key events behind the
+ // scenes where an application never sees them (so, not here).
+ event.recycle();
+ }
+ }
+
+ public static interface Factory {
+ public InputEventReceiver createInputEventReceiver(
+ InputChannel inputChannel, Looper looper);
+ }
+}
diff --git a/core/java/android/view/InputHandler.java b/core/java/android/view/InputHandler.java
deleted file mode 100644
index 14ce14c4b87a..000000000000
--- a/core/java/android/view/InputHandler.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.view;
-
-/**
- * Handles input messages that arrive on an input channel.
- * @hide
- */
-public interface InputHandler {
- /**
- * Handle a key event.
- * It is the responsibility of the callee to ensure that the finished callback is
- * eventually invoked when the event processing is finished and the input system
- * can send the next event.
- * @param event The key event data.
- * @param finishedCallback The callback to invoke when event processing is finished.
- */
- public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback);
-
- /**
- * Handle a motion event.
- * It is the responsibility of the callee to ensure that the finished callback is
- * eventually invoked when the event processing is finished and the input system
- * can send the next event.
- * @param event The motion event data.
- * @param finishedCallback The callback to invoke when event processing is finished.
- */
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback);
-}
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 5735b6397cf0..909a3b2b9d24 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -16,18 +16,11 @@
package android.view;
-import android.os.MessageQueue;
-import android.util.Slog;
-
/**
* An input queue provides a mechanism for an application to receive incoming
* input events. Currently only usable from native code.
*/
public final class InputQueue {
- private static final String TAG = "InputQueue";
-
- private static final boolean DEBUG = false;
-
/**
* Interface to receive notification of when an InputQueue is associated
* and dissociated with a thread.
@@ -48,13 +41,6 @@ public final class InputQueue {
final InputChannel mChannel;
- private static final Object sLock = new Object();
-
- private static native void nativeRegisterInputChannel(InputChannel inputChannel,
- InputHandler inputHandler, MessageQueue messageQueue);
- private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
- private static native void nativeFinished(long finishedToken, boolean handled);
-
/** @hide */
public InputQueue(InputChannel channel) {
mChannel = channel;
@@ -64,121 +50,4 @@ public final class InputQueue {
public InputChannel getInputChannel() {
return mChannel;
}
-
- /**
- * Registers an input channel and handler.
- * @param inputChannel The input channel to register.
- * @param inputHandler The input handler to input events send to the target.
- * @param messageQueue The message queue on whose thread the handler should be invoked.
- * @hide
- */
- public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,
- MessageQueue messageQueue) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null");
- }
- if (inputHandler == null) {
- throw new IllegalArgumentException("inputHandler must not be null");
- }
- if (messageQueue == null) {
- throw new IllegalArgumentException("messageQueue must not be null");
- }
-
- synchronized (sLock) {
- if (DEBUG) {
- Slog.d(TAG, "Registering input channel '" + inputChannel + "'");
- }
-
- nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);
- }
- }
-
- /**
- * Unregisters an input channel.
- * Does nothing if the channel is not currently registered.
- * @param inputChannel The input channel to unregister.
- * @hide
- */
- public static void unregisterInputChannel(InputChannel inputChannel) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null");
- }
-
- synchronized (sLock) {
- if (DEBUG) {
- Slog.d(TAG, "Unregistering input channel '" + inputChannel + "'");
- }
-
- nativeUnregisterInputChannel(inputChannel);
- }
- }
-
- @SuppressWarnings("unused")
- private static void dispatchKeyEvent(InputHandler inputHandler,
- KeyEvent event, long finishedToken) {
- FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
- inputHandler.handleKey(event, finishedCallback);
- }
-
- @SuppressWarnings("unused")
- private static void dispatchMotionEvent(InputHandler inputHandler,
- MotionEvent event, long finishedToken) {
- FinishedCallback finishedCallback = FinishedCallback.obtain(finishedToken);
- inputHandler.handleMotion(event, finishedCallback);
- }
-
- /**
- * A callback that must be invoked to when finished processing an event.
- * @hide
- */
- public static final class FinishedCallback {
- private static final boolean DEBUG_RECYCLING = false;
-
- private static final int RECYCLE_MAX_COUNT = 4;
-
- private static FinishedCallback sRecycleHead;
- private static int sRecycleCount;
-
- private FinishedCallback mRecycleNext;
- private long mFinishedToken;
-
- private FinishedCallback() {
- }
-
- public static FinishedCallback obtain(long finishedToken) {
- synchronized (sLock) {
- FinishedCallback callback = sRecycleHead;
- if (callback != null) {
- sRecycleHead = callback.mRecycleNext;
- sRecycleCount -= 1;
- callback.mRecycleNext = null;
- } else {
- callback = new FinishedCallback();
- }
- callback.mFinishedToken = finishedToken;
- return callback;
- }
- }
-
- public void finished(boolean handled) {
- synchronized (sLock) {
- if (mFinishedToken == -1) {
- throw new IllegalStateException("Event finished callback already invoked.");
- }
-
- nativeFinished(mFinishedToken, handled);
- mFinishedToken = -1;
-
- if (sRecycleCount < RECYCLE_MAX_COUNT) {
- mRecycleNext = sRecycleHead;
- sRecycleHead = this;
- sRecycleCount += 1;
-
- if (DEBUG_RECYCLING) {
- Slog.d(TAG, "Recycled finished callbacks: " + sRecycleCount);
- }
- }
- }
- }
- }
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index f53e42cb4603..9a46aab76922 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1225,7 +1225,6 @@ public class KeyEvent extends InputEvent implements Parcelable {
private static KeyEvent gRecyclerTop;
private KeyEvent mNext;
- private boolean mRecycled;
private int mDeviceId;
private int mSource;
@@ -1535,8 +1534,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
- ev.mRecycled = false;
ev.mNext = null;
+ ev.prepareForReuse();
return ev;
}
@@ -1598,10 +1597,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @hide
*/
public final void recycle() {
- if (mRecycled) {
- throw new RuntimeException(toString() + " recycled twice!");
- }
- mRecycled = true;
+ super.recycle();
mCharacters = null;
synchronized (gRecyclerLock) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 8e0ab1a12502..e49193e2c69c 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -167,7 +167,6 @@ import android.util.SparseArray;
*/
public final class MotionEvent extends InputEvent implements Parcelable {
private static final long NS_PER_MS = 1000000;
- private static final boolean TRACK_RECYCLED_LOCATION = false;
/**
* An invalid pointer id.
@@ -1315,8 +1314,6 @@ public final class MotionEvent extends InputEvent implements Parcelable {
private int mNativePtr;
private MotionEvent mNext;
- private RuntimeException mRecycledLocation;
- private boolean mRecycled;
private static native int nativeInitialize(int nativePtr,
int deviceId, int source, int action, int flags, int edgeFlags,
@@ -1397,9 +1394,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
- ev.mRecycledLocation = null;
- ev.mRecycled = false;
ev.mNext = null;
+ ev.prepareForReuse();
return ev;
}
@@ -1647,19 +1643,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
* this function you must not ever touch the event again.
*/
public final void recycle() {
- // Ensure recycle is only called once!
- if (TRACK_RECYCLED_LOCATION) {
- if (mRecycledLocation != null) {
- throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
- }
- mRecycledLocation = new RuntimeException("Last recycled here");
- //Log.w("MotionEvent", "Recycling event " + this, mRecycledLocation);
- } else {
- if (mRecycled) {
- throw new RuntimeException(toString() + " recycled twice!");
- }
- mRecycled = true;
- }
+ super.recycle();
synchronized (gRecyclerLock) {
if (gRecyclerUsed < MAX_RECYCLED) {
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 65e72c9c8123..c1db5720e2f1 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -127,16 +127,19 @@ public class ViewDebug {
* Logs the relative difference between the time an event was created and the time it
* was delivered.
*
- * Logs the time spent waiting for Surface.lockCanvas() or eglSwapBuffers().
- * This is time that the event loop spends blocked and unresponsive. Ideally, drawing
- * and animations should be perfectly synchronized with VSYNC so that swap buffers
- * is instantaneous.
+ * Logs the time spent waiting for Surface.lockCanvas(), Surface.unlockCanvasAndPost()
+ * or eglSwapBuffers(). This is time that the event loop spends blocked and unresponsive.
+ * Ideally, drawing and animations should be perfectly synchronized with VSYNC so that
+ * dequeuing and queueing buffers is instantaneous.
*
- * Logs the time spent in ViewRoot.performTraversals() or ViewRoot.draw().
+ * Logs the time spent in ViewRoot.performTraversals() and ViewRoot.performDraw().
* @hide
*/
public static final boolean DEBUG_LATENCY = false;
+ /** @hide */
+ public static final String DEBUG_LATENCY_TAG = "ViewLatency";
+
/**
* <p>Enables or disables views consistency check. Even when this property is enabled,
* view consistency checks happen only if {@link false} is set
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5060f83e7a99..f23c3127e3a9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -154,9 +154,6 @@ public final class ViewRootImpl extends Handler implements ViewParent,
final TypedValue mTmpValue = new TypedValue();
final InputMethodCallback mInputMethodCallback;
- final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
- int mPendingEventSeq = 0;
-
final Thread mThread;
final WindowLeaked mLocation;
@@ -219,7 +216,16 @@ public final class ViewRootImpl extends Handler implements ViewParent,
boolean mNewSurfaceNeeded;
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
- InputEventMessage mPendingInputEvents = null;
+
+ // Pool of queued input events.
+ private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
+ private QueuedInputEvent mQueuedInputEventPool;
+ private int mQueuedInputEventPoolSize;
+
+ // Input event queue.
+ QueuedInputEvent mFirstPendingInputEvent;
+ QueuedInputEvent mCurrentInputEvent;
+ boolean mProcessInputEventsPending;
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -552,8 +558,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
mInputQueue = new InputQueue(mInputChannel);
mInputQueueCallback.onInputQueueCreated(mInputQueue);
} else {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
+ mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
+ Looper.myLooper());
}
}
@@ -841,23 +847,11 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
}
- private void processInputEvents(boolean outOfOrder) {
- while (mPendingInputEvents != null) {
- handleMessage(mPendingInputEvents.mMessage);
- InputEventMessage tmpMessage = mPendingInputEvents;
- mPendingInputEvents = mPendingInputEvents.mNext;
- tmpMessage.recycle();
- if (outOfOrder) {
- removeMessages(PROCESS_INPUT_EVENTS);
- }
- }
- }
-
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
- processInputEvents(true);
+ processInputEvents();
if (DBG) {
System.out.println("======================================");
@@ -2024,7 +2018,19 @@ public final class ViewRootImpl extends Handler implements ViewParent,
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
mAttachInfo.mSetIgnoreDirtyState = false;
+
+ final long drawStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ drawStartTime = System.nanoTime();
+ }
+
mView.draw(canvas);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- draw() took "
+ + ((now - drawStartTime) * 0.000001f) + "ms");
+ }
} finally {
if (!mAttachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
@@ -2040,14 +2046,24 @@ public final class ViewRootImpl extends Handler implements ViewParent,
EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
}
}
-
} finally {
+ final long unlockCanvasAndPostStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ unlockCanvasAndPostStartTime = System.nanoTime();
+ }
+
surface.unlockCanvasAndPost(canvas);
- }
- }
- if (LOCAL_LOGV) {
- Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+ if (ViewDebug.DEBUG_LATENCY) {
+ long now = System.nanoTime();
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- unlockCanvasAndPost() took "
+ + ((now - unlockCanvasAndPostStartTime) * 0.000001f) + "ms");
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+ }
+ }
}
if (animating) {
@@ -2266,8 +2282,9 @@ public final class ViewRootImpl extends Handler implements ViewParent,
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueueCallback = null;
mInputQueue = null;
- } else if (mInputChannel != null) {
- InputQueue.unregisterInputChannel(mInputChannel);
+ } else if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
}
try {
sWindowSession.remove(mWindow);
@@ -2344,7 +2361,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
public final static int DISPATCH_TRACKBALL = 1007;
public final static int DISPATCH_APP_VISIBILITY = 1008;
public final static int DISPATCH_GET_NEW_SURFACE = 1009;
- public final static int FINISHED_EVENT = 1010;
+ public final static int IME_FINISHED_EVENT = 1010;
public final static int DISPATCH_KEY_FROM_IME = 1011;
public final static int FINISH_INPUT_CONNECTION = 1012;
public final static int CHECK_FOCUS = 1013;
@@ -2358,7 +2375,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021;
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022;
public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 1023;
- public final static int PROCESS_INPUT_EVENTS = 1024;
+ public final static int DO_PROCESS_INPUT_EVENTS = 1024;
@Override
public String getMessageName(Message message) {
@@ -2383,8 +2400,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
return "DISPATCH_APP_VISIBILITY";
case DISPATCH_GET_NEW_SURFACE:
return "DISPATCH_GET_NEW_SURFACE";
- case FINISHED_EVENT:
- return "FINISHED_EVENT";
+ case IME_FINISHED_EVENT:
+ return "IME_FINISHED_EVENT";
case DISPATCH_KEY_FROM_IME:
return "DISPATCH_KEY_FROM_IME";
case FINISH_INPUT_CONNECTION:
@@ -2411,8 +2428,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
- case PROCESS_INPUT_EVENTS:
- return "PROCESS_INPUT_EVENTS";
+ case DO_PROCESS_INPUT_EVENTS:
+ return "DO_PROCESS_INPUT_EVENTS";
}
return super.getMessageName(message);
}
@@ -2437,17 +2454,22 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (ViewDebug.DEBUG_LATENCY) {
traversalStartTime = System.nanoTime();
mLastDrawDurationNanos = 0;
+ if (mLastTraversalFinishedTimeNanos != 0) {
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals(); it has been "
+ + ((traversalStartTime - mLastTraversalFinishedTimeNanos) * 0.000001f)
+ + "ms since the last traversals finished.");
+ } else {
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "Starting performTraversals().");
+ }
}
performTraversals();
if (ViewDebug.DEBUG_LATENCY) {
long now = System.nanoTime();
- Log.d(TAG, "Latency: Spent "
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, "performTraversals() took "
+ ((now - traversalStartTime) * 0.000001f)
- + "ms in performTraversals(), with "
- + (mLastDrawDurationNanos * 0.000001f)
- + "ms of that time in draw()");
+ + "ms.");
mLastTraversalFinishedTimeNanos = now;
}
@@ -2456,23 +2478,12 @@ public final class ViewRootImpl extends Handler implements ViewParent,
mProfile = false;
}
break;
- case FINISHED_EVENT:
- handleFinishedEvent(msg.arg1, msg.arg2 != 0);
- break;
- case DISPATCH_KEY:
- deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
- break;
- case DISPATCH_POINTER:
- deliverPointerEvent((MotionEvent) msg.obj, msg.arg1 != 0);
+ case IME_FINISHED_EVENT:
+ handleImeFinishedEvent(msg.arg1, msg.arg2 != 0);
break;
- case DISPATCH_TRACKBALL:
- deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0);
- break;
- case DISPATCH_GENERIC_MOTION:
- deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0);
- break;
- case PROCESS_INPUT_EVENTS:
- processInputEvents(false);
+ case DO_PROCESS_INPUT_EVENTS:
+ mProcessInputEventsPending = false;
+ processInputEvents();
break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
@@ -2594,7 +2605,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
//noinspection UnusedAssignment
event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
}
- deliverKeyEventPostIme((KeyEvent)msg.obj, false);
+ enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME);
} break;
case FINISH_INPUT_CONNECTION: {
InputMethodManager imm = InputMethodManager.peekInstance();
@@ -2656,70 +2667,6 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
}
- private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
- if (mFinishedCallback != null) {
- Slog.w(TAG, "Received a new input event from the input queue but there is "
- + "already an unfinished input event in progress.");
- }
-
- if (ViewDebug.DEBUG_LATENCY) {
- mInputEventReceiveTimeNanos = System.nanoTime();
- mInputEventDeliverTimeNanos = 0;
- mInputEventDeliverPostImeTimeNanos = 0;
- }
-
- mFinishedCallback = finishedCallback;
- }
-
- private void finishInputEvent(InputEvent event, boolean handled) {
- if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished");
-
- if (mFinishedCallback == null) {
- Slog.w(TAG, "Attempted to tell the input queue that the current input event "
- + "is finished but there is no input event actually in progress.");
- return;
- }
-
- if (ViewDebug.DEBUG_LATENCY) {
- final long now = System.nanoTime();
- final long eventTime = event.getEventTimeNano();
- final StringBuilder msg = new StringBuilder();
- msg.append("Latency: Spent ");
- msg.append((now - mInputEventReceiveTimeNanos) * 0.000001f);
- msg.append("ms processing ");
- if (event instanceof KeyEvent) {
- final KeyEvent keyEvent = (KeyEvent)event;
- msg.append("key event, action=");
- msg.append(KeyEvent.actionToString(keyEvent.getAction()));
- } else {
- final MotionEvent motionEvent = (MotionEvent)event;
- msg.append("motion event, action=");
- msg.append(MotionEvent.actionToString(motionEvent.getAction()));
- msg.append(", historySize=");
- msg.append(motionEvent.getHistorySize());
- }
- msg.append(", handled=");
- msg.append(handled);
- msg.append(", received at +");
- msg.append((mInputEventReceiveTimeNanos - eventTime) * 0.000001f);
- if (mInputEventDeliverTimeNanos != 0) {
- msg.append("ms, delivered at +");
- msg.append((mInputEventDeliverTimeNanos - eventTime) * 0.000001f);
- }
- if (mInputEventDeliverPostImeTimeNanos != 0) {
- msg.append("ms, delivered post IME at +");
- msg.append((mInputEventDeliverPostImeTimeNanos - eventTime) * 0.000001f);
- }
- msg.append("ms, finished at +");
- msg.append((now - eventTime) * 0.000001f);
- msg.append("ms.");
- Log.d(TAG, msg.toString());
- }
-
- mFinishedCallback.finished(handled);
- mFinishedCallback = null;
- }
-
/**
* Something in the current window tells us we need to change the touch mode. For
* example, we are not in touch mode, and the user touches the screen.
@@ -2841,11 +2788,27 @@ public final class ViewRootImpl extends Handler implements ViewParent,
return false;
}
- private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+ private void deliverInputEvent(QueuedInputEvent q) {
if (ViewDebug.DEBUG_LATENCY) {
- mInputEventDeliverTimeNanos = System.nanoTime();
+ q.mDeliverTimeNanos = System.nanoTime();
}
+ if (q.mEvent instanceof KeyEvent) {
+ deliverKeyEvent(q);
+ } else {
+ final int source = q.mEvent.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ deliverPointerEvent(q);
+ } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ deliverTrackballEvent(q);
+ } else {
+ deliverGenericMotionEvent(q);
+ }
+ }
+ }
+
+ private void deliverPointerEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
final boolean isTouchEvent = event.isTouchEvent();
if (mInputEventConsistencyVerifier != null) {
if (isTouchEvent) {
@@ -2857,7 +2820,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
- finishMotionEvent(event, sendDone, false);
+ finishInputEvent(q, false);
return;
}
@@ -2892,41 +2855,23 @@ public final class ViewRootImpl extends Handler implements ViewParent,
lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
}
if (handled) {
- finishMotionEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
// Pointer event was unhandled.
- finishMotionEvent(event, sendDone, false);
- }
-
- private void finishMotionEvent(MotionEvent event, boolean sendDone, boolean handled) {
- event.recycle();
- if (sendDone) {
- finishInputEvent(event, handled);
- }
- //noinspection ConstantConditions
- if (LOCAL_LOGV || WATCH_POINTER) {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- Log.i(TAG, "Done dispatching!");
- }
- }
+ finishInputEvent(q, false);
}
- private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
- if (ViewDebug.DEBUG_LATENCY) {
- mInputEventDeliverTimeNanos = System.nanoTime();
- }
-
- if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
-
+ private void deliverTrackballEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
}
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
- finishMotionEvent(event, sendDone, false);
+ finishInputEvent(q, false);
return;
}
@@ -2938,7 +2883,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
// touch mode here.
ensureTouchMode(false);
- finishMotionEvent(event, sendDone, true);
+ finishInputEvent(q, true);
mLastTrackballTime = Integer.MIN_VALUE;
return;
}
@@ -2962,18 +2907,18 @@ public final class ViewRootImpl extends Handler implements ViewParent,
case MotionEvent.ACTION_DOWN:
x.reset(2);
y.reset(2);
- deliverKeyEvent(new KeyEvent(curTime, curTime,
+ dispatchKey(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD), false);
+ InputDevice.SOURCE_KEYBOARD));
break;
case MotionEvent.ACTION_UP:
x.reset(2);
y.reset(2);
- deliverKeyEvent(new KeyEvent(curTime, curTime,
+ dispatchKey(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD), false);
+ InputDevice.SOURCE_KEYBOARD));
break;
}
@@ -3024,38 +2969,35 @@ public final class ViewRootImpl extends Handler implements ViewParent,
+ keycode);
movement--;
int repeatCount = accelMovement - movement;
- deliverKeyEvent(new KeyEvent(curTime, curTime,
+ dispatchKey(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD), false);
+ InputDevice.SOURCE_KEYBOARD));
}
while (movement > 0) {
if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+ keycode);
movement--;
curTime = SystemClock.uptimeMillis();
- deliverKeyEvent(new KeyEvent(curTime, curTime,
+ dispatchKey(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_DOWN, keycode, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD), false);
- deliverKeyEvent(new KeyEvent(curTime, curTime,
+ InputDevice.SOURCE_KEYBOARD));
+ dispatchKey(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_UP, keycode, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD), false);
- }
+ InputDevice.SOURCE_KEYBOARD));
+ }
mLastTrackballTime = curTime;
}
// Unfortunately we can't tell whether the application consumed the keys, so
// we always consider the trackball event handled.
- finishMotionEvent(event, sendDone, true);
+ finishInputEvent(q, true);
}
- private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
- if (ViewDebug.DEBUG_LATENCY) {
- mInputEventDeliverTimeNanos = System.nanoTime();
- }
-
+ private void deliverGenericMotionEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
}
@@ -3068,7 +3010,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (isJoystick) {
updateJoystickDirection(event, false);
}
- finishMotionEvent(event, sendDone, false);
+ finishInputEvent(q, false);
return;
}
@@ -3077,16 +3019,16 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (isJoystick) {
updateJoystickDirection(event, false);
}
- finishMotionEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
if (isJoystick) {
// Translate the joystick event into DPAD keys and try to deliver those.
updateJoystickDirection(event, true);
- finishMotionEvent(event, sendDone, true);
+ finishInputEvent(q, true);
} else {
- finishMotionEvent(event, sendDone, false);
+ finishInputEvent(q, false);
}
}
@@ -3108,9 +3050,9 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (xDirection != mLastJoystickXDirection) {
if (mLastJoystickXKeyCode != 0) {
- deliverKeyEvent(new KeyEvent(time, time,
+ dispatchKey(new KeyEvent(time, time,
KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
mLastJoystickXKeyCode = 0;
}
@@ -3119,17 +3061,17 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (xDirection != 0 && synthesizeNewKeys) {
mLastJoystickXKeyCode = xDirection > 0
? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
- deliverKeyEvent(new KeyEvent(time, time,
+ dispatchKey(new KeyEvent(time, time,
KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
}
}
if (yDirection != mLastJoystickYDirection) {
if (mLastJoystickYKeyCode != 0) {
- deliverKeyEvent(new KeyEvent(time, time,
+ dispatchKey(new KeyEvent(time, time,
KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
mLastJoystickYKeyCode = 0;
}
@@ -3138,9 +3080,9 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (yDirection != 0 && synthesizeNewKeys) {
mLastJoystickYKeyCode = yDirection > 0
? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
- deliverKeyEvent(new KeyEvent(time, time,
+ dispatchKey(new KeyEvent(time, time,
KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false);
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
}
}
}
@@ -3231,91 +3173,81 @@ public final class ViewRootImpl extends Handler implements ViewParent,
return false;
}
- int enqueuePendingEvent(Object event, boolean sendDone) {
- int seq = mPendingEventSeq+1;
- if (seq < 0) seq = 0;
- mPendingEventSeq = seq;
- mPendingEvents.put(seq, event);
- return sendDone ? seq : -seq;
- }
-
- Object retrievePendingEvent(int seq) {
- if (seq < 0) seq = -seq;
- Object event = mPendingEvents.get(seq);
- if (event != null) {
- mPendingEvents.remove(seq);
- }
- return event;
- }
-
- private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
- if (ViewDebug.DEBUG_LATENCY) {
- mInputEventDeliverTimeNanos = System.nanoTime();
- }
-
+ private void deliverKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
}
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- finishKeyEvent(event, sendDone, false);
- return;
- }
-
- if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
+ if ((q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
+ // If there is no view, then the event will not be handled.
+ if (mView == null || !mAdded) {
+ finishInputEvent(q, false);
+ return;
+ }
- // Perform predispatching before the IME.
- if (mView.dispatchKeyEventPreIme(event)) {
- finishKeyEvent(event, sendDone, true);
- return;
- }
+ if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
- // Dispatch to the IME before propagating down the view hierarchy.
- // The IME will eventually call back into handleFinishedEvent.
- if (mLastWasImTarget) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- int seq = enqueuePendingEvent(event, sendDone);
- if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
- + seq + " event=" + event);
- imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
+ // Perform predispatching before the IME.
+ if (mView.dispatchKeyEventPreIme(event)) {
+ finishInputEvent(q, true);
return;
}
+
+ // Dispatch to the IME before propagating down the view hierarchy.
+ // The IME will eventually call back into handleImeFinishedEvent.
+ if (mLastWasImTarget) {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ final int seq = event.getSequenceNumber();
+ if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+ + seq + " event=" + event);
+ imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
+ return;
+ }
+ }
}
// Not dispatching to IME, continue with post IME actions.
- deliverKeyEventPostIme(event, sendDone);
+ deliverKeyEventPostIme(q);
}
- private void handleFinishedEvent(int seq, boolean handled) {
- final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
- if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + " event=" + event);
- if (event != null) {
- final boolean sendDone = seq >= 0;
+ void handleImeFinishedEvent(int seq, boolean handled) {
+ final QueuedInputEvent q = mCurrentInputEvent;
+ if (q != null && q.mEvent.getSequenceNumber() == seq) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+ if (DEBUG_IMF) {
+ Log.v(TAG, "IME finished event: seq=" + seq
+ + " handled=" + handled + " event=" + event);
+ }
if (handled) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
} else {
- deliverKeyEventPostIme(event, sendDone);
+ deliverKeyEventPostIme(q);
+ }
+ } else {
+ if (DEBUG_IMF) {
+ Log.v(TAG, "IME finished event: seq=" + seq
+ + " handled=" + handled + ", event not found!");
}
}
}
- private void deliverKeyEventPostIme(KeyEvent event, boolean sendDone) {
+ private void deliverKeyEventPostIme(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
if (ViewDebug.DEBUG_LATENCY) {
- mInputEventDeliverPostImeTimeNanos = System.nanoTime();
+ q.mDeliverPostImeTimeNanos = System.nanoTime();
}
// If the view went away, then the event will not be handled.
if (mView == null || !mAdded) {
- finishKeyEvent(event, sendDone, false);
+ finishInputEvent(q, false);
return;
}
// If the key's purpose is to exit touch mode then we consume it and consider it handled.
if (checkForLeavingTouchModeAndConsume(event)) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
@@ -3325,7 +3257,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
@@ -3334,14 +3266,14 @@ public final class ViewRootImpl extends Handler implements ViewParent,
&& event.isCtrlPressed()
&& !KeyEvent.isModifierKey(event.getKeyCode())) {
if (mView.dispatchKeyShortcutEvent(event)) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
}
// Apply the fallback event policy.
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
@@ -3396,14 +3328,14 @@ public final class ViewRootImpl extends Handler implements ViewParent,
if (v.requestFocus(direction, mTempRect)) {
playSoundEffect(
SoundEffectConstants.getContantForFocusDirection(direction));
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
}
// Give the focused view a last chance to handle the dpad key.
if (mView.dispatchUnhandledMove(focused, direction)) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
}
@@ -3411,13 +3343,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
// Key was unhandled.
- finishKeyEvent(event, sendDone, false);
- }
-
- private void finishKeyEvent(KeyEvent event, boolean sendDone, boolean handled) {
- if (sendDone) {
- finishInputEvent(event, handled);
- }
+ finishInputEvent(q, false);
}
/* drag/drop */
@@ -3742,8 +3668,8 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
}
- public void dispatchFinishedEvent(int seq, boolean handled) {
- Message msg = obtainMessage(FINISHED_EVENT);
+ void dispatchImeFinishedEvent(int seq, boolean handled) {
+ Message msg = obtainMessage(IME_FINISHED_EVENT);
msg.arg1 = seq;
msg.arg2 = handled ? 1 : 0;
sendMessage(msg);
@@ -3772,152 +3698,186 @@ public final class ViewRootImpl extends Handler implements ViewParent,
sendMessage(msg);
}
- private long mInputEventReceiveTimeNanos;
- private long mInputEventDeliverTimeNanos;
- private long mInputEventDeliverPostImeTimeNanos;
- private InputQueue.FinishedCallback mFinishedCallback;
-
- private final InputHandler mInputHandler = new InputHandler() {
- public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(finishedCallback);
- dispatchKey(event, true);
- }
-
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
- startInputEvent(finishedCallback);
- dispatchMotion(event, true);
- }
- };
-
/**
- * Utility class used to queue up input events which are then handled during
- * performTraversals(). Doing it this way allows us to ensure that we are up to date with
- * all input events just prior to drawing, instead of placing those events on the regular
- * handler queue, potentially behind a drawing event.
+ * Represents a pending input event that is waiting in a queue.
+ *
+ * Input events are processed in serial order by the timestamp specified by
+ * {@link InputEvent#getEventTime()}. In general, the input dispatcher delivers
+ * one input event to the application at a time and waits for the application
+ * to finish handling it before delivering the next one.
+ *
+ * However, because the application or IME can synthesize and inject multiple
+ * key events at a time without going through the input dispatcher, we end up
+ * needing a queue on the application's side.
*/
- static class InputEventMessage {
- Message mMessage;
- InputEventMessage mNext;
+ private static final class QueuedInputEvent {
+ public static final int FLAG_DELIVER_POST_IME = 1 << 0;
- private static final Object sPoolSync = new Object();
- private static InputEventMessage sPool;
- private static int sPoolSize = 0;
+ public QueuedInputEvent mNext;
- private static final int MAX_POOL_SIZE = 10;
+ public InputEvent mEvent;
+ public InputEventReceiver mReceiver;
+ public int mFlags;
- private InputEventMessage(Message m) {
- mMessage = m;
- mNext = null;
- }
+ // Used for latency calculations.
+ public long mReceiveTimeNanos;
+ public long mDeliverTimeNanos;
+ public long mDeliverPostImeTimeNanos;
+ }
- /**
- * Return a new Message instance from the global pool. Allows us to
- * avoid allocating new objects in many cases.
- */
- public static InputEventMessage obtain(Message msg) {
- synchronized (sPoolSync) {
- if (sPool != null) {
- InputEventMessage m = sPool;
- sPool = m.mNext;
- m.mNext = null;
- sPoolSize--;
- m.mMessage = msg;
- return m;
- }
- }
- return new InputEventMessage(msg);
+ private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
+ InputEventReceiver receiver, int flags) {
+ QueuedInputEvent q = mQueuedInputEventPool;
+ if (q != null) {
+ mQueuedInputEventPoolSize -= 1;
+ mQueuedInputEventPool = q.mNext;
+ q.mNext = null;
+ } else {
+ q = new QueuedInputEvent();
}
- /**
- * Return the message to the pool.
- */
- public void recycle() {
- mMessage.recycle();
- synchronized (sPoolSync) {
- if (sPoolSize < MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- sPoolSize++;
- }
- }
+ q.mEvent = event;
+ q.mReceiver = receiver;
+ q.mFlags = flags;
+ return q;
+ }
+
+ private void recycleQueuedInputEvent(QueuedInputEvent q) {
+ q.mEvent = null;
+ q.mReceiver = null;
+ if (mQueuedInputEventPoolSize < MAX_QUEUED_INPUT_EVENT_POOL_SIZE) {
+ mQueuedInputEventPoolSize += 1;
+ q.mNext = mQueuedInputEventPool;
+ mQueuedInputEventPool = q;
}
}
- /**
- * Place the input event message at the end of the current pending list
- */
- private void enqueueInputEvent(Message msg, long when) {
- InputEventMessage inputMessage = InputEventMessage.obtain(msg);
- if (mPendingInputEvents == null) {
- mPendingInputEvents = inputMessage;
+ void enqueueInputEvent(InputEvent event,
+ InputEventReceiver receiver, int flags) {
+ QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
+
+ if (ViewDebug.DEBUG_LATENCY) {
+ q.mReceiveTimeNanos = System.nanoTime();
+ q.mDeliverTimeNanos = 0;
+ q.mDeliverPostImeTimeNanos = 0;
+ }
+
+ // Always enqueue the input event in order, regardless of its time stamp.
+ // We do this because the application or the IME may inject key events
+ // in response to touch events and we want to ensure that the injected keys
+ // are processed in the order they were received and we cannot trust that
+ // the time stamp of injected events are monotonic.
+ QueuedInputEvent last = mFirstPendingInputEvent;
+ if (last == null) {
+ mFirstPendingInputEvent = q;
} else {
- InputEventMessage currMessage = mPendingInputEvents;
- while (currMessage.mNext != null) {
- currMessage = currMessage.mNext;
+ while (last.mNext != null) {
+ last = last.mNext;
}
- currMessage.mNext = inputMessage;
+ last.mNext = q;
}
- sendEmptyMessageAtTime(PROCESS_INPUT_EVENTS, when);
+
+ scheduleProcessInputEvents();
}
- public void dispatchKey(KeyEvent event) {
- dispatchKey(event, false);
+ private void scheduleProcessInputEvents() {
+ if (!mProcessInputEventsPending) {
+ mProcessInputEventsPending = true;
+ sendEmptyMessage(DO_PROCESS_INPUT_EVENTS);
+ }
}
- private void dispatchKey(KeyEvent event, boolean sendDone) {
- //noinspection ConstantConditions
- if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
- if (DBG) Log.d("keydisp", "===================================================");
- if (DBG) Log.d("keydisp", "Focused view Hierarchy is:");
+ void processInputEvents() {
+ while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) {
+ QueuedInputEvent q = mFirstPendingInputEvent;
+ mFirstPendingInputEvent = q.mNext;
+ q.mNext = null;
+ mCurrentInputEvent = q;
+ deliverInputEvent(q);
+ }
- debug();
+ // We are done processing all input events that we can process right now
+ // so we can clear the pending flag immediately.
+ if (mProcessInputEventsPending) {
+ mProcessInputEventsPending = false;
+ removeMessages(DO_PROCESS_INPUT_EVENTS);
+ }
+ }
+
+ private void finishInputEvent(QueuedInputEvent q, boolean handled) {
+ if (q != mCurrentInputEvent) {
+ throw new IllegalStateException("finished input event out of order");
+ }
- if (DBG) Log.d("keydisp", "===================================================");
+ if (ViewDebug.DEBUG_LATENCY) {
+ final long now = System.nanoTime();
+ final long eventTime = q.mEvent.getEventTimeNano();
+ final StringBuilder msg = new StringBuilder();
+ msg.append("Spent ");
+ msg.append((now - q.mReceiveTimeNanos) * 0.000001f);
+ msg.append("ms processing ");
+ if (q.mEvent instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)q.mEvent;
+ msg.append("key event, action=");
+ msg.append(KeyEvent.actionToString(keyEvent.getAction()));
+ } else {
+ final MotionEvent motionEvent = (MotionEvent)q.mEvent;
+ msg.append("motion event, action=");
+ msg.append(MotionEvent.actionToString(motionEvent.getAction()));
+ msg.append(", historySize=");
+ msg.append(motionEvent.getHistorySize());
}
+ msg.append(", handled=");
+ msg.append(handled);
+ msg.append(", received at +");
+ msg.append((q.mReceiveTimeNanos - eventTime) * 0.000001f);
+ if (q.mDeliverTimeNanos != 0) {
+ msg.append("ms, delivered at +");
+ msg.append((q.mDeliverTimeNanos - eventTime) * 0.000001f);
+ }
+ if (q.mDeliverPostImeTimeNanos != 0) {
+ msg.append("ms, delivered post IME at +");
+ msg.append((q.mDeliverPostImeTimeNanos - eventTime) * 0.000001f);
+ }
+ msg.append("ms, finished at +");
+ msg.append((now - eventTime) * 0.000001f);
+ msg.append("ms.");
+ Log.d(ViewDebug.DEBUG_LATENCY_TAG, msg.toString());
}
- Message msg = obtainMessage(DISPATCH_KEY);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
+ if (q.mReceiver != null) {
+ q.mReceiver.finishInputEvent(q.mEvent, handled);
+ } else if (q.mEvent instanceof MotionEvent) {
+ // Event though key events are also recyclable, we only recycle motion events.
+ // Historically, key events were not recyclable and applications expect
+ // them to be immutable. We only ever recycle key events behind the
+ // scenes where an application never sees them (so, not here).
+ q.mEvent.recycle();
+ }
- if (LOCAL_LOGV) Log.v(
- TAG, "sending key " + event + " to " + mView);
+ recycleQueuedInputEvent(q);
- enqueueInputEvent(msg, event.getEventTime());
- }
-
- private void dispatchMotion(MotionEvent event, boolean sendDone) {
- int source = event.getSource();
- if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- dispatchPointer(event, sendDone);
- } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- dispatchTrackball(event, sendDone);
- } else {
- dispatchGenericMotion(event, sendDone);
+ mCurrentInputEvent = null;
+ if (mFirstPendingInputEvent != null) {
+ scheduleProcessInputEvents();
}
}
- private void dispatchPointer(MotionEvent event, boolean sendDone) {
- Message msg = obtainMessage(DISPATCH_POINTER);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
- enqueueInputEvent(msg, event.getEventTime());
- }
+ final class WindowInputEventReceiver extends InputEventReceiver {
+ public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
- private void dispatchTrackball(MotionEvent event, boolean sendDone) {
- Message msg = obtainMessage(DISPATCH_TRACKBALL);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
- enqueueInputEvent(msg, event.getEventTime());
+ @Override
+ public void onInputEvent(InputEvent event) {
+ enqueueInputEvent(event, this, 0);
+ }
}
+ WindowInputEventReceiver mInputEventReceiver;
- private void dispatchGenericMotion(MotionEvent event, boolean sendDone) {
- Message msg = obtainMessage(DISPATCH_GENERIC_MOTION);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
- enqueueInputEvent(msg, event.getEventTime());
+ public void dispatchKey(KeyEvent event) {
+ enqueueInputEvent(event, null, 0);
}
public void dispatchAppVisibility(boolean visible) {
@@ -4099,7 +4059,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
public void finishedEvent(int seq, boolean handled) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
- viewAncestor.dispatchFinishedEvent(seq, handled);
+ viewAncestor.dispatchImeFinishedEvent(seq, handled);
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 2e19bf64d3e1..7d729c68ab4d 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -340,7 +340,8 @@ public interface WindowManagerPolicy {
* Add a fake window to the window manager. This window sits
* at the top of the other windows and consumes events.
*/
- public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+ public FakeWindow addFakeWindow(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 787bcdd2c7f3..1fab1cab5e49 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9384,40 +9384,57 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mPositionY = mTempCoords[1];
}
- public boolean isVisible(int positionX, int positionY) {
- final TextView textView = TextView.this;
+ public void onScrollChanged() {
+ mScrollHasChanged = true;
+ }
+ }
- if (mTempRect == null) mTempRect = new Rect();
- final Rect clip = mTempRect;
- clip.left = getCompoundPaddingLeft();
- clip.top = getExtendedPaddingTop();
- clip.right = textView.getWidth() - getCompoundPaddingRight();
- clip.bottom = textView.getHeight() - getExtendedPaddingBottom();
-
- final ViewParent parent = textView.getParent();
- if (parent == null || !parent.getChildVisibleRect(textView, clip, null)) {
- return false;
- }
+ private boolean isPositionVisible(int positionX, int positionY) {
+ synchronized (sTmpPosition) {
+ final float[] position = sTmpPosition;
+ position[0] = positionX;
+ position[1] = positionY;
+ View view = this;
- int posX = mPositionX + positionX;
- int posY = mPositionY + positionY;
+ while (view != null) {
+ if (view != this) {
+ // Local scroll is already taken into account in positionX/Y
+ position[0] -= view.getScrollX();
+ position[1] -= view.getScrollY();
+ }
- // Offset by 1 to take into account 0.5 and int rounding around getPrimaryHorizontal.
- return posX >= clip.left - 1 && posX <= clip.right + 1 &&
- posY >= clip.top && posY <= clip.bottom;
- }
+ if (position[0] < 0 || position[1] < 0 ||
+ position[0] > view.getWidth() || position[1] > view.getHeight()) {
+ return false;
+ }
- public boolean isOffsetVisible(int offset) {
- final int line = mLayout.getLineForOffset(offset);
- final int lineBottom = mLayout.getLineBottom(line);
- final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset);
- return isVisible(primaryHorizontal + viewportToContentHorizontalOffset(),
- lineBottom + viewportToContentVerticalOffset());
- }
+ if (!view.getMatrix().isIdentity()) {
+ view.getMatrix().mapPoints(position);
+ }
- public void onScrollChanged() {
- mScrollHasChanged = true;
+ position[0] += view.getLeft();
+ position[1] += view.getTop();
+
+ final ViewParent parent = view.getParent();
+ if (parent instanceof View) {
+ view = (View) parent;
+ } else {
+ // We've reached the ViewRoot, stop iterating
+ view = null;
+ }
+ }
}
+
+ // We've been able to walk up the view hierarchy and the position was never clipped
+ return true;
+ }
+
+ private boolean isOffsetVisible(int offset) {
+ final int line = mLayout.getLineForOffset(offset);
+ final int lineBottom = mLayout.getLineBottom(line);
+ final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset);
+ return isPositionVisible(primaryHorizontal + viewportToContentHorizontalOffset(),
+ lineBottom + viewportToContentVerticalOffset());
}
@Override
@@ -9518,7 +9535,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void updatePosition(int parentPositionX, int parentPositionY,
boolean parentPositionChanged, boolean parentScrolled) {
// Either parentPositionChanged or parentScrolled is true, check if still visible
- if (isShowing() && getPositionListener().isOffsetVisible(getTextOffset())) {
+ if (isShowing() && isOffsetVisible(getTextOffset())) {
if (parentScrolled) computeLocalPosition();
updatePosition(parentPositionX, parentPositionY);
} else {
@@ -10542,7 +10559,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- return getPositionListener().isVisible(mPositionX + mHotspotX, mPositionY);
+ return TextView.this.isPositionVisible(mPositionX + mHotspotX, mPositionY);
}
public abstract int getCurrentCursorOffset();
@@ -11518,6 +11535,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private Path mHighlightPath;
private boolean mHighlightPathBogus = true;
private static final RectF sTempRect = new RectF();
+ private static final float[] sTmpPosition = new float[2];
// XXX should be much larger
private static final int VERY_WIDE = 1024*1024;
diff --git a/core/java/com/android/internal/view/BaseInputHandler.java b/core/java/com/android/internal/view/BaseInputHandler.java
deleted file mode 100644
index 74b4b06eaa25..000000000000
--- a/core/java/com/android/internal/view/BaseInputHandler.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 com.android.internal.view;
-
-import android.view.InputHandler;
-import android.view.InputQueue;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-/**
- * Base do-nothing implementation of an input handler.
- * @hide
- */
-public abstract class BaseInputHandler implements InputHandler {
- public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
- finishedCallback.finished(false);
- }
-
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
- finishedCallback.finished(false);
- }
-}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 71c5d2662292..f20fbbb6019e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -50,7 +50,7 @@ LOCAL_SRC_FILES:= \
android_view_Surface.cpp \
android_view_TextureView.cpp \
android_view_InputChannel.cpp \
- android_view_InputQueue.cpp \
+ android_view_InputEventReceiver.cpp \
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_HardwareRenderer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6d1410cc84f6..c6447e198b17 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -168,7 +168,7 @@ extern int register_android_app_backup_FullBackup(JNIEnv *env);
extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_view_InputChannel(JNIEnv* env);
-extern int register_android_view_InputQueue(JNIEnv* env);
+extern int register_android_view_InputEventReceiver(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_view_PointerIcon(JNIEnv* env);
@@ -1192,7 +1192,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
- REG_JNI(register_android_view_InputQueue),
+ REG_JNI(register_android_view_InputEventReceiver),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_view_PointerIcon),
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
new file mode 100644
index 000000000000..9ae63dd61db2
--- /dev/null
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#define LOG_TAG "InputEventReceiver"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about the dispatch cycle.
+#define DEBUG_DISPATCH_CYCLE 0
+
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <ui/InputTransport.h>
+#include "android_os_MessageQueue.h"
+#include "android_view_InputChannel.h"
+#include "android_view_KeyEvent.h"
+#include "android_view_MotionEvent.h"
+
+namespace android {
+
+static struct {
+ jclass clazz;
+
+ jmethodID dispatchInputEvent;
+} gInputEventReceiverClassInfo;
+
+
+class NativeInputEventReceiver : public RefBase {
+public:
+ NativeInputEventReceiver(JNIEnv* env,
+ jobject receiverObj, const sp<InputChannel>& inputChannel,
+ const sp<Looper>& looper);
+
+ status_t initialize();
+ status_t finishInputEvent(bool handled);
+ static int handleReceiveCallback(int receiveFd, int events, void* data);
+
+protected:
+ virtual ~NativeInputEventReceiver();
+
+private:
+ jobject mReceiverObjGlobal;
+ InputConsumer mInputConsumer;
+ sp<Looper> mLooper;
+ bool mEventInProgress;
+ PreallocatedInputEventFactory mInputEventFactory;
+
+ const char* getInputChannelName() {
+ return mInputConsumer.getChannel()->getName().string();
+ }
+};
+
+
+NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
+ jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
+ mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
+ mInputConsumer(inputChannel), mLooper(looper), mEventInProgress(false) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
+#endif
+}
+
+NativeInputEventReceiver::~NativeInputEventReceiver() {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
+#endif
+
+ mLooper->removeFd(mInputConsumer.getChannel()->getReceivePipeFd());
+ if (mEventInProgress) {
+ mInputConsumer.sendFinishedSignal(false); // ignoring result
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mReceiverObjGlobal);
+}
+
+status_t NativeInputEventReceiver::initialize() {
+ status_t result = mInputConsumer.initialize();
+ if (result) {
+ LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
+ getInputChannelName(), result);
+ return result;
+ }
+
+ int32_t receiveFd = mInputConsumer.getChannel()->getReceivePipeFd();
+ mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+ return OK;
+}
+
+status_t NativeInputEventReceiver::finishInputEvent(bool handled) {
+ if (mEventInProgress) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Finished input event.", getInputChannelName());
+#endif
+ mEventInProgress = false;
+
+ status_t status = mInputConsumer.sendFinishedSignal(handled);
+ if (status) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ getInputChannelName(), status);
+ }
+ return status;
+ } else {
+ LOGW("Ignoring attempt to finish input event while no event is in progress.");
+ return OK;
+ }
+}
+
+int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
+ sp<NativeInputEventReceiver> r = static_cast<NativeInputEventReceiver*>(data);
+
+ if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+ LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. "
+ "events=0x%x", r->getInputChannelName(), events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & ALOOPER_EVENT_INPUT)) {
+ LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x", r->getInputChannelName(), events);
+ return 1;
+ }
+
+ status_t status = r->mInputConsumer.receiveDispatchSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
+ r->getInputChannelName(), status);
+ return 0; // remove the callback
+ }
+
+ if (r->mEventInProgress) {
+ LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
+ r->getInputChannelName());
+ return 1;
+ }
+
+ InputEvent* inputEvent;
+ status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
+ if (status) {
+ LOGW("channel '%s' ~ Failed to consume input event. status=%d",
+ r->getInputChannelName(), status);
+ r->mInputConsumer.sendFinishedSignal(false);
+ return 1;
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject inputEventObj;
+ switch (inputEvent->getType()) {
+ case AINPUT_EVENT_TYPE_KEY:
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Received key event.",
+ r->getInputChannelName());
+#endif
+ inputEventObj = android_view_KeyEvent_fromNative(env,
+ static_cast<KeyEvent*>(inputEvent));
+ break;
+
+ case AINPUT_EVENT_TYPE_MOTION:
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Received motion event.",
+ r->getInputChannelName());
+#endif
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+ static_cast<MotionEvent*>(inputEvent));
+ break;
+
+ default:
+ assert(false); // InputConsumer should prevent this from ever happening
+ inputEventObj = NULL;
+ }
+
+ if (!inputEventObj) {
+ LOGW("channel '%s' ~ Failed to obtain event object.",
+ r->getInputChannelName());
+ r->mInputConsumer.sendFinishedSignal(false);
+ return 1;
+ }
+
+ r->mEventInProgress = true;
+
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Invoking input handler.", r->getInputChannelName());
+#endif
+ env->CallVoidMethod(r->mReceiverObjGlobal,
+ gInputEventReceiverClassInfo.dispatchInputEvent, inputEventObj);
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Returned from input handler.", r->getInputChannelName());
+#endif
+
+ if (env->ExceptionCheck()) {
+ LOGE("channel '%s' ~ An exception occurred while dispatching an event.",
+ r->getInputChannelName());
+ LOGE_EX(env);
+ env->ExceptionClear();
+
+ if (r->mEventInProgress) {
+ r->mInputConsumer.sendFinishedSignal(false);
+ r->mEventInProgress = false;
+ }
+ }
+
+ env->DeleteLocalRef(inputEventObj);
+ return 1;
+}
+
+
+static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
+ jobject inputChannelObj, jobject messageQueueObj) {
+ sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+ inputChannelObj);
+ if (inputChannel == NULL) {
+ jniThrowRuntimeException(env, "InputChannel is not initialized.");
+ return 0;
+ }
+
+ sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
+ if (looper == NULL) {
+ jniThrowRuntimeException(env, "MessageQueue is not initialized.");
+ return 0;
+ }
+
+ sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
+ receiverObj, inputChannel, looper);
+ status_t status = receiver->initialize();
+ if (status) {
+ String8 message;
+ message.appendFormat("Failed to initialize input event receiver. status=%d", status);
+ jniThrowRuntimeException(env, message.string());
+ return 0;
+ }
+
+ receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
+ return reinterpret_cast<jint>(receiver.get());
+}
+
+static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
+ sp<NativeInputEventReceiver> receiver =
+ reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+ receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
+}
+
+static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jboolean handled) {
+ sp<NativeInputEventReceiver> receiver =
+ reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+ status_t status = receiver->finishInputEvent(handled);
+ if (status) {
+ String8 message;
+ message.appendFormat("Failed to finish input event. status=%d", status);
+ jniThrowRuntimeException(env, message.string());
+ }
+}
+
+
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeInit",
+ "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
+ (void*)nativeInit },
+ { "nativeDispose",
+ "(I)V",
+ (void*)nativeDispose },
+ { "nativeFinishInputEvent", "(IZ)V",
+ (void*)nativeFinishInputEvent }
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+int register_android_view_InputEventReceiver(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
+ gMethods, NELEM(gMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
+
+ GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
+ gInputEventReceiverClassInfo.clazz,
+ "dispatchInputEvent", "(Landroid/view/InputEvent;)V");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
deleted file mode 100644
index 300c04a03a03..000000000000
--- a/core/jni/android_view_InputQueue.cpp
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "InputQueue-JNI"
-
-//#define LOG_NDEBUG 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 0
-
-
-#include "JNIHelp.h"
-
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/Log.h>
-#include <utils/Looper.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <ui/InputTransport.h>
-#include "android_os_MessageQueue.h"
-#include "android_view_InputChannel.h"
-#include "android_view_KeyEvent.h"
-#include "android_view_MotionEvent.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct {
- jclass clazz;
-
- jmethodID dispatchKeyEvent;
- jmethodID dispatchMotionEvent;
-} gInputQueueClassInfo;
-
-// ----------------------------------------------------------------------------
-
-class NativeInputQueue {
-public:
- NativeInputQueue();
- ~NativeInputQueue();
-
- status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj,
- jobject inputHandlerObj, jobject messageQueueObj);
-
- status_t unregisterInputChannel(JNIEnv* env, jobject inputChannelObj);
-
- status_t finished(JNIEnv* env, jlong finishedToken, bool handled, bool ignoreSpuriousFinish);
-
-private:
- class Connection : public RefBase {
- protected:
- virtual ~Connection();
-
- public:
- enum Status {
- // Everything is peachy.
- STATUS_NORMAL,
- // The input channel has been unregistered.
- STATUS_ZOMBIE
- };
-
- Connection(uint16_t id,
- const sp<InputChannel>& inputChannel, const sp<Looper>& looper);
-
- inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
-
- // A unique id for this connection.
- uint16_t id;
-
- Status status;
-
- sp<InputChannel> inputChannel;
- InputConsumer inputConsumer;
- sp<Looper> looper;
- jobject inputHandlerObjGlobal;
- PreallocatedInputEventFactory inputEventFactory;
-
- // The sequence number of the current event being dispatched.
- // This is used as part of the finished token as a way to determine whether the finished
- // token is still valid before sending a finished signal back to the publisher.
- uint16_t messageSeqNum;
-
- // True if a message has been received from the publisher but not yet finished.
- bool messageInProgress;
- };
-
- Mutex mLock;
- uint16_t mNextConnectionId;
- KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd;
-
- ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
-
- static void handleInputChannelDisposed(JNIEnv* env,
- jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data);
-
- static int handleReceiveCallback(int receiveFd, int events, void* data);
-
- static jlong generateFinishedToken(int32_t receiveFd,
- uint16_t connectionId, uint16_t messageSeqNum);
-
- static void parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex);
-};
-
-// ----------------------------------------------------------------------------
-
-NativeInputQueue::NativeInputQueue() :
- mNextConnectionId(0) {
-}
-
-NativeInputQueue::~NativeInputQueue() {
-}
-
-status_t NativeInputQueue::registerInputChannel(JNIEnv* env, jobject inputChannelObj,
- jobject inputHandlerObj, jobject messageQueueObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- if (inputChannel == NULL) {
- LOGW("Input channel is not initialized.");
- return BAD_VALUE;
- }
-
-#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Registered", inputChannel->getName().string());
-#endif
-
- sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- if (getConnectionIndex(inputChannel) >= 0) {
- LOGW("Attempted to register already registered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
-
- uint16_t connectionId = mNextConnectionId++;
- sp<Connection> connection = new Connection(connectionId, inputChannel, looper);
- status_t result = connection->inputConsumer.initialize();
- if (result) {
- LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
- inputChannel->getName().string(), result);
- return result;
- }
-
- connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
-
- int32_t receiveFd = inputChannel->getReceivePipeFd();
- mConnectionsByReceiveFd.add(receiveFd, connection);
-
- looper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
- } // release lock
-
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
- handleInputChannelDisposed, this);
- return OK;
-}
-
-status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) {
- sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- if (inputChannel == NULL) {
- LOGW("Input channel is not initialized.");
- return BAD_VALUE;
- }
-
-#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
-#endif
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- ssize_t connectionIndex = getConnectionIndex(inputChannel);
- if (connectionIndex < 0) {
- LOGW("Attempted to unregister already unregistered input channel '%s'",
- inputChannel->getName().string());
- return BAD_VALUE;
- }
-
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
-
- connection->status = Connection::STATUS_ZOMBIE;
-
- connection->looper->removeFd(inputChannel->getReceivePipeFd());
-
- env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
- connection->inputHandlerObjGlobal = NULL;
-
- if (connection->messageInProgress) {
- LOGI("Sending finished signal for input channel '%s' since it is being unregistered "
- "while an input message is still in progress.",
- connection->getInputChannelName());
- connection->messageInProgress = false;
- connection->inputConsumer.sendFinishedSignal(false); // ignoring result
- }
- } // release lock
-
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
- return OK;
-}
-
-ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChannel) {
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
- if (connectionIndex >= 0) {
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- if (connection->inputChannel.get() == inputChannel.get()) {
- return connectionIndex;
- }
- }
-
- return -1;
-}
-
-status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken,
- bool handled, bool ignoreSpuriousFinish) {
- int32_t receiveFd;
- uint16_t connectionId;
- uint16_t messageSeqNum;
- parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
-
- { // acquire lock
- AutoMutex _l(mLock);
-
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
- if (connectionIndex < 0) {
- if (! ignoreSpuriousFinish) {
- LOGI("Ignoring finish signal on channel that is no longer registered.");
- }
- return DEAD_OBJECT;
- }
-
- sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
- if (connectionId != connection->id) {
- if (! ignoreSpuriousFinish) {
- LOGI("Ignoring finish signal on channel that is no longer registered.");
- }
- return DEAD_OBJECT;
- }
-
- if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) {
- if (! ignoreSpuriousFinish) {
- LOGW("Attempted to finish input twice on channel '%s'. "
- "finished messageSeqNum=%d, current messageSeqNum=%d, messageInProgress=%d",
- connection->getInputChannelName(),
- messageSeqNum, connection->messageSeqNum, connection->messageInProgress);
- }
- return INVALID_OPERATION;
- }
-
- connection->messageInProgress = false;
-
- status_t status = connection->inputConsumer.sendFinishedSignal(handled);
- if (status) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- connection->getInputChannelName(), status);
- return status;
- }
-
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Finished event.",
- connection->getInputChannelName());
-#endif
- } // release lock
-
- return OK;
-}
-
-void NativeInputQueue::handleInputChannelDisposed(JNIEnv* env,
- jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) {
- LOGW("Input channel object '%s' was disposed without first being unregistered with "
- "the input queue!", inputChannel->getName().string());
-
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- q->unregisterInputChannel(env, inputChannelObj);
-}
-
-int NativeInputQueue::handleReceiveCallback(int receiveFd, int events, void* data) {
- NativeInputQueue* q = static_cast<NativeInputQueue*>(data);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
-
- sp<Connection> connection;
- InputEvent* inputEvent;
- jobject inputHandlerObjLocal;
- jlong finishedToken;
- { // acquire lock
- AutoMutex _l(q->mLock);
-
- ssize_t connectionIndex = q->mConnectionsByReceiveFd.indexOfKey(receiveFd);
- if (connectionIndex < 0) {
- LOGE("Received spurious receive callback for unknown input channel. "
- "fd=%d, events=0x%x", receiveFd, events);
- return 0; // remove the callback
- }
-
- connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
- if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
- LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. "
- "events=0x%x", connection->getInputChannelName(), events);
- return 0; // remove the callback
- }
-
- if (! (events & ALOOPER_EVENT_INPUT)) {
- LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
- "events=0x%x", connection->getInputChannelName(), events);
- return 1;
- }
-
- status_t status = connection->inputConsumer.receiveDispatchSignal();
- if (status) {
- LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
- connection->getInputChannelName(), status);
- return 0; // remove the callback
- }
-
- if (connection->messageInProgress) {
- LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
- connection->getInputChannelName());
- return 1;
- }
-
- status = connection->inputConsumer.consume(& connection->inputEventFactory, & inputEvent);
- if (status) {
- LOGW("channel '%s' ~ Failed to consume input event. status=%d",
- connection->getInputChannelName(), status);
- connection->inputConsumer.sendFinishedSignal(false);
- return 1;
- }
-
- connection->messageInProgress = true;
- connection->messageSeqNum += 1;
-
- finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
-
- inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
- } // release lock
-
- // Invoke the handler outside of the lock.
- //
- // Note: inputEvent is stored in a field of the connection object which could potentially
- // become disposed due to the input channel being unregistered concurrently.
- // For this reason, we explicitly keep the connection object alive by holding
- // a strong pointer to it within this scope. We also grabbed a local reference to
- // the input handler object itself for the same reason.
-
- int32_t inputEventType = inputEvent->getType();
-
- jobject inputEventObj;
- jmethodID dispatchMethodId;
- switch (inputEventType) {
- case AINPUT_EVENT_TYPE_KEY:
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Received key event.", connection->getInputChannelName());
-#endif
- inputEventObj = android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent*>(inputEvent));
- dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
- break;
-
- case AINPUT_EVENT_TYPE_MOTION:
-#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName());
-#endif
- inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
- static_cast<MotionEvent*>(inputEvent));
- dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;
- break;
-
- default:
- assert(false); // InputConsumer should prevent this from ever happening
- inputEventObj = NULL;
- }
-
- if (! inputEventObj) {
- LOGW("channel '%s' ~ Failed to obtain DVM event object.",
- connection->getInputChannelName());
- env->DeleteLocalRef(inputHandlerObjLocal);
- q->finished(env, finishedToken, false, false);
- return 1;
- }
-
-#if DEBUG_DISPATCH_CYCLE
- LOGD("Invoking input handler.");
-#endif
- env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
- dispatchMethodId, inputHandlerObjLocal, inputEventObj,
- jlong(finishedToken));
-#if DEBUG_DISPATCH_CYCLE
- LOGD("Returned from input handler.");
-#endif
-
- if (env->ExceptionCheck()) {
- LOGE("An exception occurred while invoking the input handler for an event.");
- LOGE_EX(env);
- env->ExceptionClear();
-
- q->finished(env, finishedToken, false, true /*ignoreSpuriousFinish*/);
- }
-
- env->DeleteLocalRef(inputEventObj);
- env->DeleteLocalRef(inputHandlerObjLocal);
- return 1;
-}
-
-jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,
- uint16_t messageSeqNum) {
- return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);
-}
-
-void NativeInputQueue::parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {
- *outReceiveFd = int32_t(finishedToken >> 32);
- *outConnectionId = uint16_t(finishedToken >> 16);
- *outMessageIndex = uint16_t(finishedToken);
-}
-
-// ----------------------------------------------------------------------------
-
-NativeInputQueue::Connection::Connection(uint16_t id,
- const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
- id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
- looper(looper), inputHandlerObjGlobal(NULL),
- messageSeqNum(0), messageInProgress(false) {
-}
-
-NativeInputQueue::Connection::~Connection() {
-}
-
-// ----------------------------------------------------------------------------
-
-static NativeInputQueue gNativeInputQueue;
-
-static void android_view_InputQueue_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj, jobject inputHandlerObj, jobject messageQueueObj) {
- status_t status = gNativeInputQueue.registerInputChannel(
- env, inputChannelObj, inputHandlerObj, messageQueueObj);
-
- if (status) {
- String8 message;
- message.appendFormat("Failed to register input channel. status=%d", status);
- jniThrowRuntimeException(env, message.string());
- }
-}
-
-static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj) {
- status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);
-
- if (status) {
- String8 message;
- message.appendFormat("Failed to unregister input channel. status=%d", status);
- jniThrowRuntimeException(env, message.string());
- }
-}
-
-static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,
- jlong finishedToken, bool handled) {
- status_t status = gNativeInputQueue.finished(
- env, finishedToken, handled, false /*ignoreSpuriousFinish*/);
-
- // We ignore the case where an event could not be finished because the input channel
- // was no longer registered (DEAD_OBJECT) since it is a common race that can occur
- // during application shutdown. The input dispatcher recovers gracefully anyways.
- if (status != OK && status != DEAD_OBJECT) {
- String8 message;
- message.appendFormat("Failed to finish input event. status=%d", status);
- jniThrowRuntimeException(env, message.string());
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static JNINativeMethod gInputQueueMethods[] = {
- /* name, signature, funcPtr */
- { "nativeRegisterInputChannel",
- "(Landroid/view/InputChannel;Landroid/view/InputHandler;Landroid/os/MessageQueue;)V",
- (void*)android_view_InputQueue_nativeRegisterInputChannel },
- { "nativeUnregisterInputChannel",
- "(Landroid/view/InputChannel;)V",
- (void*)android_view_InputQueue_nativeUnregisterInputChannel },
- { "nativeFinished", "(JZ)V",
- (void*)android_view_InputQueue_nativeFinished }
-};
-
-#define FIND_CLASS(var, className) \
- var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
-
-#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
- var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find static method " methodName);
-
-int register_android_view_InputQueue(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, "android/view/InputQueue",
- gInputQueueMethods, NELEM(gInputQueueMethods));
- LOG_FATAL_IF(res < 0, "Unable to register native methods.");
-
- FIND_CLASS(gInputQueueClassInfo.clazz, "android/view/InputQueue");
-
- GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchKeyEvent, gInputQueueClassInfo.clazz,
- "dispatchKeyEvent",
- "(Landroid/view/InputHandler;Landroid/view/KeyEvent;J)V");
-
- GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchMotionEvent, gInputQueueClassInfo.clazz,
- "dispatchMotionEvent",
- "(Landroid/view/InputHandler;Landroid/view/MotionEvent;J)V");
- return 0;
-}
-
-} // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6965702ab098..641134a5d2e1 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -773,6 +773,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive)
bwr.read_buffer = (long unsigned int)mIn.data();
} else {
bwr.read_size = 0;
+ bwr.read_buffer = 0;
}
IF_LOG_COMMANDS() {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 3372d1c249bf..ae7a3b5b27b4 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -477,8 +477,9 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
float x = getFloat();
float y = getFloat();
SkPaint* paint = getPaint();
- LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, x, y, paint);
+ float length = getFloat();
+ LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent, OP_NAMES[op],
+ text.text(), text.length(), count, x, y, paint, length);
}
break;
case ResetShader: {
@@ -837,9 +838,10 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level)
float x = getFloat();
float y = getFloat();
SkPaint* paint = getPaint();
- DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, x, y, paint);
- renderer.drawText(text.text(), text.length(), count, x, y, paint);
+ float length = getFloat();
+ DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent,
+ OP_NAMES[op], text.text(), text.length(), count, x, y, paint, length);
+ renderer.drawText(text.text(), text.length(), count, x, y, paint, length);
}
break;
case ResetShader: {
@@ -1196,13 +1198,14 @@ void DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) {
}
void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, SkPaint* paint) {
+ float x, float y, SkPaint* paint, float length) {
if (count <= 0) return;
addOp(DisplayList::DrawText);
addText(text, bytesCount);
addInt(count);
addPoint(x, y);
addPaint(paint);
+ addFloat(length < 0.0f ? paint->measureText(text, bytesCount) : length);
}
void DisplayListRenderer::resetShader() {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index ab475bf53beb..ab483fbc2ff5 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -290,7 +290,7 @@ public:
virtual void drawLines(float* points, int count, SkPaint* paint);
virtual void drawPoints(float* points, int count, SkPaint* paint);
virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
- SkPaint* paint);
+ SkPaint* paint, float length);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3c838fcf8a61..a60ac0804af4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2063,7 +2063,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
}
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, SkPaint* paint) {
+ float x, float y, SkPaint* paint, float length) {
if (text == NULL || count == 0) {
return;
}
@@ -2080,20 +2080,26 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
#endif
- float length = -1.0f;
switch (paint->getTextAlign()) {
case SkPaint::kCenter_Align:
- length = paint->measureText(text, bytesCount);
+ if (length < 0.0f) length = paint->measureText(text, bytesCount);
x -= length / 2.0f;
break;
case SkPaint::kRight_Align:
- length = paint->measureText(text, bytesCount);
+ if (length < 0.0f) length = paint->measureText(text, bytesCount);
x -= length;
break;
default:
break;
}
+ SkPaint::FontMetrics metrics;
+ paint->getFontMetrics(&metrics, 0.0f);
+ if (quickReject(x, y + metrics.fTop,
+ x + (length >= 0.0f ? length : INT_MAX / 2), y + metrics.fBottom)) {
+ return;
+ }
+
const float oldX = x;
const float oldY = y;
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 2fc88e1d9674..cd9ff936f4f1 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -123,7 +123,7 @@ public:
virtual void drawLines(float* points, int count, SkPaint* paint);
virtual void drawPoints(float* points, int count, SkPaint* paint);
virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
- SkPaint* paint);
+ SkPaint* paint, float length = -1.0f);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index f13a6a2b8887..687e2f6ce8c6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1572,49 +1572,83 @@ public class AudioService extends IAudioService.Stub {
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- synchronized (mScoClients) {
- // Discard timeout message
- mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
- mBluetoothHeadset = (BluetoothHeadset) proxy;
- List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+ BluetoothDevice btDevice;
+ List<BluetoothDevice> deviceList;
+ switch(profile) {
+ case BluetoothProfile.A2DP:
+ BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
+ deviceList = a2dp.getConnectedDevices();
if (deviceList.size() > 0) {
- mBluetoothHeadsetDevice = deviceList.get(0);
- } else {
- mBluetoothHeadsetDevice = null;
- }
- // Refresh SCO audio state
- checkScoAudioState();
- // Continue pending action if any
- if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
- mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
- mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
- boolean status = false;
- if (mBluetoothHeadsetDevice != null) {
- switch (mScoAudioState) {
- case SCO_STATE_ACTIVATE_REQ:
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- break;
- case SCO_STATE_DEACTIVATE_REQ:
- status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- break;
- case SCO_STATE_DEACTIVATE_EXT_REQ:
- status = mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
- }
+ btDevice = deviceList.get(0);
+ handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
+ }
+ break;
+
+ case BluetoothProfile.HEADSET:
+ synchronized (mScoClients) {
+ // Discard timeout message
+ mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
+ mBluetoothHeadset = (BluetoothHeadset) proxy;
+ deviceList = mBluetoothHeadset.getConnectedDevices();
+ if (deviceList.size() > 0) {
+ mBluetoothHeadsetDevice = deviceList.get(0);
+ } else {
+ mBluetoothHeadsetDevice = null;
}
- if (!status) {
- sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
- SENDMSG_REPLACE, 0, 0, null, 0);
+ // Refresh SCO audio state
+ checkScoAudioState();
+ // Continue pending action if any
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+ boolean status = false;
+ if (mBluetoothHeadsetDevice != null) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_EXT_REQ:
+ status = mBluetoothHeadset.stopVoiceRecognition(
+ mBluetoothHeadsetDevice);
+ }
+ }
+ if (!status) {
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, 0);
+ }
}
}
+ break;
+
+ default:
+ break;
}
}
public void onServiceDisconnected(int profile) {
- synchronized (mScoClients) {
- mBluetoothHeadset = null;
+ switch(profile) {
+ case BluetoothProfile.A2DP:
+ synchronized (mConnectedDevices) {
+ if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
+ makeA2dpDeviceUnavailableNow(
+ mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+ }
+ }
+ break;
+
+ case BluetoothProfile.HEADSET:
+ synchronized (mScoClients) {
+ mBluetoothHeadset = null;
+ }
+ break;
+
+ default:
+ break;
}
}
};
@@ -2191,15 +2225,17 @@ public class AudioService extends IAudioService.Stub {
AudioSystem.setParameters("restarting=true");
// Restore device connection states
- Set set = mConnectedDevices.entrySet();
- Iterator i = set.iterator();
- while(i.hasNext()){
- Map.Entry device = (Map.Entry)i.next();
- AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
- AudioSystem.DEVICE_STATE_AVAILABLE,
- (String)device.getValue());
+ synchronized (mConnectedDevices) {
+ Set set = mConnectedDevices.entrySet();
+ Iterator i = set.iterator();
+ while(i.hasNext()){
+ Map.Entry device = (Map.Entry)i.next();
+ AudioSystem.setDeviceConnectionState(
+ ((Integer)device.getKey()).intValue(),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ (String)device.getValue());
+ }
}
-
// Restore call state
AudioSystem.setPhoneState(mMode);
@@ -2238,7 +2274,9 @@ public class AudioService extends IAudioService.Stub {
case MSG_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
- makeA2dpDeviceUnavailableNow( (String) msg.obj );
+ synchronized (mConnectedDevices) {
+ makeA2dpDeviceUnavailableNow( (String) msg.obj );
+ }
break;
case MSG_SET_FORCE_USE:
@@ -2298,6 +2336,7 @@ public class AudioService extends IAudioService.Stub {
}
}
+ // must be called synchronized on mConnectedDevices
private void makeA2dpDeviceAvailable(String address) {
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -2308,6 +2347,7 @@ public class AudioService extends IAudioService.Stub {
address);
}
+ // must be called synchronized on mConnectedDevices
private void makeA2dpDeviceUnavailableNow(String address) {
Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
mContext.sendBroadcast(noisyIntent);
@@ -2317,6 +2357,7 @@ public class AudioService extends IAudioService.Stub {
mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
}
+ // must be called synchronized on mConnectedDevices
private void makeA2dpDeviceUnavailableLater(String address) {
// prevent any activity on the A2DP audio output to avoid unwanted
// reconnection of the sink.
@@ -2329,14 +2370,60 @@ public class AudioService extends IAudioService.Stub {
}
+ // must be called synchronized on mConnectedDevices
private void cancelA2dpDeviceTimeout() {
mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
}
+ // must be called synchronized on mConnectedDevices
private boolean hasScheduledA2dpDockTimeout() {
return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
}
+ private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
+ {
+ if (btDevice == null) {
+ return;
+ }
+ String address = btDevice.getAddress();
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ address = "";
+ }
+ synchronized (mConnectedDevices) {
+ boolean isConnected =
+ (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
+ mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
+
+ if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
+ if (btDevice.isBluetoothDock()) {
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ // introduction of a delay for transient disconnections of docks when
+ // power is rapidly turned off/on, this message will be canceled if
+ // we reconnect the dock under a preset delay
+ makeA2dpDeviceUnavailableLater(address);
+ // the next time isConnected is evaluated, it will be false for the dock
+ }
+ } else {
+ makeA2dpDeviceUnavailableNow(address);
+ }
+ } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
+ if (btDevice.isBluetoothDock()) {
+ // this could be a reconnection after a transient disconnection
+ cancelA2dpDeviceTimeout();
+ mDockAddress = address;
+ } else {
+ // this could be a connection of another A2DP device before the timeout of
+ // a dock: cancel the dock timeout, and make the dock unavailable now
+ if(hasScheduledA2dpDockTimeout()) {
+ cancelA2dpDeviceTimeout();
+ makeA2dpDeviceUnavailableNow(mDockAddress);
+ }
+ }
+ makeA2dpDeviceAvailable(address);
+ }
+ }
+ }
+
/* cache of the address of the last dock the device was connected to */
private String mDockAddress;
@@ -2374,44 +2461,8 @@ public class AudioService extends IAudioService.Stub {
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (btDevice == null) {
- return;
- }
- String address = btDevice.getAddress();
- if (!BluetoothAdapter.checkBluetoothAddress(address)) {
- address = "";
- }
- boolean isConnected =
- (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
- mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
-
- if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
- if (btDevice.isBluetoothDock()) {
- if (state == BluetoothProfile.STATE_DISCONNECTED) {
- // introduction of a delay for transient disconnections of docks when
- // power is rapidly turned off/on, this message will be canceled if
- // we reconnect the dock under a preset delay
- makeA2dpDeviceUnavailableLater(address);
- // the next time isConnected is evaluated, it will be false for the dock
- }
- } else {
- makeA2dpDeviceUnavailableNow(address);
- }
- } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
- if (btDevice.isBluetoothDock()) {
- // this could be a reconnection after a transient disconnection
- cancelA2dpDeviceTimeout();
- mDockAddress = address;
- } else {
- // this could be a connection of another A2DP device before the timeout of
- // a dock: cancel the dock timeout, and make the dock unavailable now
- if(hasScheduledA2dpDockTimeout()) {
- cancelA2dpDeviceTimeout();
- makeA2dpDeviceUnavailableNow(mDockAddress);
- }
- }
- makeA2dpDeviceAvailable(address);
- }
+
+ handleA2dpConnectionStateChange(btDevice, state);
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
@@ -2440,103 +2491,126 @@ public class AudioService extends IAudioService.Stub {
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
- boolean isConnected = (mConnectedDevices.containsKey(device) &&
- mConnectedDevices.get(device).equals(address));
- synchronized (mScoClients) {
- if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
- AudioSystem.setDeviceConnectionState(device,
+ synchronized (mConnectedDevices) {
+ boolean isConnected = (mConnectedDevices.containsKey(device) &&
+ mConnectedDevices.get(device).equals(address));
+
+ synchronized (mScoClients) {
+ if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
+ AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
address);
- mConnectedDevices.remove(device);
- mBluetoothHeadsetDevice = null;
- resetBluetoothSco();
- } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
- AudioSystem.setDeviceConnectionState(device,
- AudioSystem.DEVICE_STATE_AVAILABLE,
- address);
- mConnectedDevices.put(new Integer(device), address);
- mBluetoothHeadsetDevice = btDevice;
+ mConnectedDevices.remove(device);
+ mBluetoothHeadsetDevice = null;
+ resetBluetoothSco();
+ } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
+ AudioSystem.setDeviceConnectionState(device,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ address);
+ mConnectedDevices.put(new Integer(device), address);
+ mBluetoothHeadsetDevice = btDevice;
+ }
}
}
} else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", 0);
int microphone = intent.getIntExtra("microphone", 0);
- if (microphone != 0) {
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_STATE_UNAVAILABLE,
- "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioSystem.DEVICE_STATE_AVAILABLE,
- "");
- mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
+ synchronized (mConnectedDevices) {
+ if (microphone != 0) {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put(
+ new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
+ }
+ } else {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put(
+ new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
+ }
}
- } else {
+ }
+ } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
+ int state = intent.getIntExtra("state", 0);
+ Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
+ synchronized (mConnectedDevices) {
boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
- AudioSystem.DEVICE_STATE_UNAVAILABLE,
- "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
} else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
- AudioSystem.DEVICE_STATE_AVAILABLE,
- "");
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
+ new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
}
}
- } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
- int state = intent.getIntExtra("state", 0);
- Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_AVAILABLE, "");
- mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
- }
} else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
int state = intent.getIntExtra("state", 0);
Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
- AudioSystem.DEVICE_STATE_AVAILABLE, "");
- mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
+ synchronized (mConnectedDevices) {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
+ }
}
} else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", 0);
Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
- boolean isConnected =
- mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
- if (state == 0 && isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
- mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
- } else if (state == 1 && !isConnected) {
- AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
- AudioSystem.DEVICE_STATE_AVAILABLE, "");
- mConnectedDevices.put(
- new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+ synchronized (mConnectedDevices) {
+ boolean isConnected =
+ mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+ if (state == 0 && isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+ } else if (state == 1 && !isConnected) {
+ AudioSystem.setDeviceConnectionState(
+ AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put(
+ new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+ }
}
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
@@ -2600,6 +2674,12 @@ public class AudioService extends IAudioService.Stub {
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
mContext.sendStickyBroadcast(newIntent);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.A2DP);
+ }
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
@@ -3401,7 +3481,7 @@ public class AudioService extends IAudioService.Stub {
updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
}
- /**
+ /**
* see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
* precondition: mediaIntent != null, target != null
*/
@@ -3417,7 +3497,7 @@ public class AudioService extends IAudioService.Stub {
}
}
- /**
+ /**
* see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
* precondition: mediaIntent != null, eventReceiver != null
*/
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 2dcd80d74d1b..2232995c5c64 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -34,6 +34,7 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.PointF;
+import android.hardware.CameraSound;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
@@ -49,6 +50,7 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.widget.ImageView;
+
import com.android.systemui.R;
import java.io.File;
@@ -254,6 +256,8 @@ class GlobalScreenshot {
private float mBgPadding;
private float mBgPaddingScale;
+ private CameraSound mCameraSound;
+
/**
* @param context everything needs a context :(
@@ -303,6 +307,9 @@ class GlobalScreenshot {
// Scale has to account for both sides of the bg
mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
+
+ // Setup the Camera shutter sound
+ mCameraSound = new CameraSound();
}
/**
@@ -413,6 +420,9 @@ class GlobalScreenshot {
mScreenshotLayout.post(new Runnable() {
@Override
public void run() {
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.playSound(CameraSound.SHUTTER_CLICK);
+
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mScreenshotView.buildLayer();
mScreenshotAnimation.start();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 7684c34fc167..e6b86fcf7c5f 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -45,6 +45,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.LocalPowerManager;
+import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
@@ -61,7 +62,6 @@ import com.android.internal.app.ShutdownThread;
import com.android.internal.policy.PolicyManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
-import com.android.internal.view.BaseInputHandler;
import com.android.internal.widget.PointerLocationView;
import android.util.DisplayMetrics;
@@ -75,8 +75,8 @@ import android.view.IApplicationToken;
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.InputQueue;
-import android.view.InputHandler;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -345,25 +345,32 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mFocusedWindow;
IApplicationToken mFocusedApp;
- private final InputHandler mPointerLocationInputHandler = new BaseInputHandler() {
+ final class PointerLocationInputEventReceiver extends InputEventReceiver {
+ public PointerLocationInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ final MotionEvent motionEvent = (MotionEvent)event;
synchronized (mLock) {
if (mPointerLocationView != null) {
- mPointerLocationView.addPointerEvent(event);
+ mPointerLocationView.addPointerEvent(motionEvent);
handled = true;
}
}
}
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
-
+ }
+ PointerLocationInputEventReceiver mPointerLocationInputEventReceiver;
+
// The current size of the screen; really; (ir)regardless of whether the status
// bar can be hidden or not
int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
@@ -1000,9 +1007,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mPointerLocationInputChannel == null) {
try {
mPointerLocationInputChannel =
- mWindowManager.monitorInput("PointerLocationView");
- InputQueue.registerInputChannel(mPointerLocationInputChannel,
- mPointerLocationInputHandler, mHandler.getLooper().getQueue());
+ mWindowManager.monitorInput("PointerLocationView");
+ mPointerLocationInputEventReceiver =
+ new PointerLocationInputEventReceiver(
+ mPointerLocationInputChannel, mHandler.getLooper());
} catch (RemoteException ex) {
Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
ex);
@@ -1010,8 +1018,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
if (removeView != null) {
+ if (mPointerLocationInputEventReceiver != null) {
+ mPointerLocationInputEventReceiver.dispose();
+ mPointerLocationInputEventReceiver = null;
+ }
if (mPointerLocationInputChannel != null) {
- InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
mPointerLocationInputChannel.dispose();
mPointerLocationInputChannel = null;
}
@@ -1836,13 +1847,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* to determine when the nav bar should be shown and prevent applications from
* receiving those touches.
*/
- final InputHandler mHideNavInputHandler = new BaseInputHandler() {
+ final class HideNavInputEventReceiver extends InputEventReceiver {
+ public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
// When the user taps down, we re-show the nav bar.
boolean changed = false;
synchronized (mLock) {
@@ -1879,9 +1896,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
+ }
+ final InputEventReceiver.Factory mHideNavInputEventReceiverFactory =
+ new InputEventReceiver.Factory() {
+ @Override
+ public InputEventReceiver createInputEventReceiver(
+ InputChannel inputChannel, Looper looper) {
+ return new HideNavInputEventReceiver(inputChannel, looper);
+ }
};
@Override
@@ -1945,7 +1970,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
} else if (mHideNavFakeWindow == null) {
mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow(
- mHandler.getLooper(), mHideNavInputHandler,
+ mHandler.getLooper(), mHideNavInputEventReceiverFactory,
"hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER,
0, false, false, true);
}
diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/DeviceStorageMonitorService.java
index d34087f959d0..16eeb7babb35 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -163,7 +163,6 @@ public class DeviceStorageMonitorService extends Binder {
} catch (IllegalArgumentException e) {
// ignore; report -1
}
- mCacheFileStats.restat(CACHE_PATH);
EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
mFreeMem, mFreeSystem, mFreeCache);
}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 73cd64e9f735..a19035afabae 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import com.android.server.wm.WindowManagerService.DragInputEventReceiver;
import com.android.server.wm.WindowManagerService.H;
import android.content.ClipData;
@@ -28,7 +29,6 @@ import android.os.RemoteException;
import android.util.Slog;
import android.view.DragEvent;
import android.view.InputChannel;
-import android.view.InputQueue;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
@@ -50,6 +50,7 @@ class DragState {
float mCurrentX, mCurrentY;
float mThumbOffsetX, mThumbOffsetY;
InputChannel mServerChannel, mClientChannel;
+ DragInputEventReceiver mInputEventReceiver;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
WindowState mTargetWindow;
@@ -90,8 +91,8 @@ class DragState {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, mService.mDragInputHandler,
- mService.mH.getLooper().getQueue());
+ mInputEventReceiver = mService.new DragInputEventReceiver(mClientChannel,
+ mService.mH.getLooper());
mDragApplicationHandle = new InputApplicationHandle(null);
mDragApplicationHandle.name = "drag";
@@ -139,7 +140,8 @@ class DragState {
Slog.e(WindowManagerService.TAG, "Unregister of nonexistent drag input channel");
} else {
mService.mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
mClientChannel.dispose();
mServerChannel.dispose();
mClientChannel = null;
diff --git a/services/java/com/android/server/wm/FakeWindowImpl.java b/services/java/com/android/server/wm/FakeWindowImpl.java
index 0e72f7d989a2..121ce1861bd4 100644
--- a/services/java/com/android/server/wm/FakeWindowImpl.java
+++ b/services/java/com/android/server/wm/FakeWindowImpl.java
@@ -20,7 +20,7 @@ import android.os.Looper;
import android.os.Process;
import android.util.Slog;
import android.view.InputChannel;
-import android.view.InputHandler;
+import android.view.InputEventReceiver;
import android.view.InputQueue;
import android.view.WindowManagerPolicy;
@@ -29,11 +29,13 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
+ final InputEventReceiver mInputEventReceiver;
final int mWindowLayer;
boolean mTouchFullscreen;
- public FakeWindowImpl(WindowManagerService service, Looper looper, InputHandler inputHandler,
+ public FakeWindowImpl(WindowManagerService service,
+ Looper looper, InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen) {
mService = service;
@@ -42,7 +44,9 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
- InputQueue.registerInputChannel(mClientChannel, inputHandler, looper.getQueue());
+
+ mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
+ mClientChannel, looper);
mApplicationHandle = new InputApplicationHandle(null);
mApplicationHandle.name = name;
@@ -87,8 +91,8 @@ public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
public void dismiss() {
synchronized (mService.mWindowMap) {
if (mService.removeFakeWindowLocked(this)) {
+ mInputEventReceiver.dispose();
mService.mInputManager.unregisterInputChannel(mServerChannel);
- InputQueue.unregisterInputChannel(mClientChannel);
mClientChannel.dispose();
mServerChannel.dispose();
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index f5c2de912710..75ace4f3082f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -36,7 +36,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.PhoneWindowManager;
-import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
@@ -107,8 +106,7 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.InputHandler;
-import android.view.InputQueue;
+import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
@@ -571,18 +569,25 @@ public class WindowManagerService extends IWindowManager.Stub
boolean mTurnOnScreen;
DragState mDragState = null;
- final InputHandler mDragInputHandler = new BaseInputHandler() {
+
+ final class DragInputEventReceiver extends InputEventReceiver {
+ public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
@Override
- public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
+ public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
+ if (event instanceof MotionEvent
+ && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
&& mDragState != null) {
+ final MotionEvent motionEvent = (MotionEvent)event;
boolean endDrag = false;
- final float newX = event.getRawX();
- final float newY = event.getRawY();
+ final float newX = motionEvent.getRawX();
+ final float newY = motionEvent.getRawY();
- switch (event.getAction()) {
+ switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (DEBUG_DRAG) {
Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer");
@@ -623,10 +628,10 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (Exception e) {
Slog.e(TAG, "Exception caught by drag handleMotion", e);
} finally {
- finishedCallback.finished(handled);
+ finishInputEvent(event, handled);
}
}
- };
+ }
/**
* Whether the UI is currently running in touch mode (not showing
@@ -9378,11 +9383,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public FakeWindow addFakeWindow(Looper looper, InputHandler inputHandler,
+ public FakeWindow addFakeWindow(Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory,
String name, int windowType, int layoutParamsFlags, boolean canReceiveKeys,
boolean hasFocus, boolean touchFullscreen) {
synchronized (mWindowMap) {
- FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputHandler, name, windowType,
+ FakeWindowImpl fw = new FakeWindowImpl(this, looper, inputEventReceiverFactory,
+ name, windowType,
layoutParamsFlags, canReceiveKeys, hasFocus, touchFullscreen);
int i=0;
while (i<mFakeWindows.size()) {