Merge "Move native netd calls to varargs."
diff --git a/api/current.txt b/api/current.txt
index 86b6d8f..5dc32ae 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14788,6 +14788,7 @@
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 34c0c2a..db72585 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -184,6 +184,7 @@
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 @@
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 @@
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 c5872b8..173cabf 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -105,6 +105,11 @@
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 1128fce..7cb9b37 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -34,6 +34,16 @@
#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, 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 @@
<< "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 3099b83..a53a93c 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -109,7 +109,7 @@
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 @@
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 @@
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 81defd6..0586d9e 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -128,6 +128,19 @@
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/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index a7d8cac..94fbbc8 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -145,7 +145,12 @@
private final ArrayList<String> mUuidIntentTracker;
private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
- private final HashMap<Integer, Pair<Integer, IBinder>> mServiceRecordToPid;
+ private static class ServiceRecordClient {
+ int pid;
+ IBinder binder;
+ IBinder.DeathRecipient death;
+ }
+ private final HashMap<Integer, ServiceRecordClient> mServiceRecordToPid;
private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
private final BluetoothProfileState mA2dpProfileState;
@@ -221,7 +226,7 @@
mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
mUuidIntentTracker = new ArrayList<String>();
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
- mServiceRecordToPid = new HashMap<Integer, Pair<Integer, IBinder>>();
+ mServiceRecordToPid = new HashMap<Integer, ServiceRecordClient>();
mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
@@ -1528,11 +1533,17 @@
return -1;
}
- int pid = Binder.getCallingPid();
- mServiceRecordToPid.put(new Integer(handle), new Pair<Integer, IBinder>(pid, b));
+ ServiceRecordClient client = new ServiceRecordClient();
+ client.pid = Binder.getCallingPid();
+ client.binder = b;
+ client.death = new Reaper(handle, client.pid, RFCOMM_RECORD_REAPER);
+ mServiceRecordToPid.put(new Integer(handle), client);
try {
- b.linkToDeath(new Reaper(handle, pid, RFCOMM_RECORD_REAPER), 0);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ b.linkToDeath(client.death, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ client.death = null;
+ }
return handle;
}
@@ -1547,10 +1558,15 @@
}
private synchronized void checkAndRemoveRecord(int handle, int pid) {
- Pair<Integer, IBinder> pidPair = mServiceRecordToPid.get(handle);
- if (pidPair != null && pid == pidPair.first) {
+ ServiceRecordClient client = mServiceRecordToPid.get(handle);
+ if (client != null && pid == client.pid) {
if (DBG) Log.d(TAG, "Removing service record " +
Integer.toHexString(handle) + " for pid " + pid);
+
+ if (client.death != null) {
+ client.binder.unlinkToDeath(client.death, 0);
+ }
+
mServiceRecordToPid.remove(handle);
removeServiceRecordNative(handle);
}
@@ -1880,7 +1896,7 @@
private void dumpApplicationServiceRecords(PrintWriter pw) {
pw.println("\n--Application Service Records--");
for (Integer handle : mServiceRecordToPid.keySet()) {
- Integer pid = mServiceRecordToPid.get(handle).first;
+ Integer pid = mServiceRecordToPid.get(handle).pid;
pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 18167b6..7ce96c0 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -18,7 +18,6 @@
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.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 @@
}
};
-
- 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 @@
}
Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
mCaller.sendMessage(msg);
+ } else {
+ event.recycle();
}
}
@@ -599,8 +605,8 @@
}
mCreated = true;
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
+ mInputEventReceiver = new WallpaperInputEventReceiver(
+ mInputChannel, Looper.myLooper());
}
mSurfaceHolder.mSurfaceLock.lock();
@@ -902,8 +908,9 @@
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 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 ccb6489..443acf6 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -837,10 +837,37 @@
(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 @@
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 01ddcc9..c42bbdc 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* Common base class for input events.
*/
@@ -27,8 +29,21 @@
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 @@
* 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 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 9b081b2..fafe416 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -58,7 +58,7 @@
// 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 @@
* 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 @@
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 0000000..abb5281
--- /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 14ce14c..0000000
--- 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 5735b63..909a3b2 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 @@
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 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 f53e42c..9a46aab 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1225,7 +1225,6 @@
private static KeyEvent gRecyclerTop;
private KeyEvent mNext;
- private boolean mRecycled;
private int mDeviceId;
private int mSource;
@@ -1535,8 +1534,8 @@
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
- ev.mRecycled = false;
ev.mNext = null;
+ ev.prepareForReuse();
return ev;
}
@@ -1598,10 +1597,7 @@
* @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 8e0ab1a..e49193e 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -167,7 +167,6 @@
*/
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 @@
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 @@
gRecyclerTop = ev.mNext;
gRecyclerUsed -= 1;
}
- ev.mRecycledLocation = null;
- ev.mRecycled = false;
ev.mNext = null;
+ ev.prepareForReuse();
return ev;
}
@@ -1647,19 +1643,7 @@
* 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 65e72c9..c1db572 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -127,16 +127,19 @@
* 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 5060f83..f23c312 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -154,9 +154,6 @@
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 @@
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 @@
mInputQueue = new InputQueue(mInputChannel);
mInputQueueCallback.onInputQueueCreated(mInputQueue);
} else {
- InputQueue.registerInputChannel(mInputChannel, mInputHandler,
- Looper.myQueue());
+ mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
+ Looper.myLooper());
}
}
@@ -841,23 +847,11 @@
}
}
- 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 @@
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 @@
EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
}
}
-
} finally {
- surface.unlockCanvasAndPost(canvas);
- }
- }
+ final long unlockCanvasAndPostStartTime;
+ if (ViewDebug.DEBUG_LATENCY) {
+ unlockCanvasAndPostStartTime = System.nanoTime();
+ }
- if (LOCAL_LOGV) {
- Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
+ surface.unlockCanvasAndPost(canvas);
+
+ 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 @@
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 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 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 @@
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 @@
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 @@
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 @@
mProfile = false;
}
break;
- case FINISHED_EVENT:
- handleFinishedEvent(msg.arg1, msg.arg2 != 0);
+ case IME_FINISHED_EVENT:
+ handleImeFinishedEvent(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);
- 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 @@
//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 @@
}
}
- 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 @@
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 @@
// 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 @@
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);
+ finishInputEvent(q, 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!");
- }
- }
- }
-
- 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 @@
// touch mode here.
ensureTouchMode(false);
- finishMotionEvent(event, sendDone, true);
+ finishInputEvent(q, true);
mLastTrackballTime = Integer.MIN_VALUE;
return;
}
@@ -2962,18 +2907,18 @@
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 @@
+ 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 @@
if (isJoystick) {
updateJoystickDirection(event, false);
}
- finishMotionEvent(event, sendDone, false);
+ finishInputEvent(q, false);
return;
}
@@ -3077,16 +3019,16 @@
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 @@
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 @@
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 @@
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 @@
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);
-
- // Perform predispatching before the IME.
- if (mView.dispatchKeyEventPreIme(event)) {
- finishKeyEvent(event, sendDone, true);
- return;
- }
-
- // 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);
+ 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;
}
+
+ if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
+
+ // 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 @@
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
- finishKeyEvent(event, sendDone, true);
+ finishInputEvent(q, true);
return;
}
@@ -3334,14 +3266,14 @@
&& 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 @@
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 @@
}
// 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 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 @@
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;
- }
-
- /**
- * 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);
- }
-
- /**
- * Return the message to the pool.
- */
- public void recycle() {
- mMessage.recycle();
- synchronized (sPoolSync) {
- if (sPoolSize < MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- sPoolSize++;
- }
- }
-
- }
+ // Used for latency calculations.
+ public long mReceiveTimeNanos;
+ public long mDeliverTimeNanos;
+ public long mDeliverPostImeTimeNanos;
}
- /**
- * 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;
+ private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
+ InputEventReceiver receiver, int flags) {
+ QueuedInputEvent q = mQueuedInputEventPool;
+ if (q != null) {
+ mQueuedInputEventPoolSize -= 1;
+ mQueuedInputEventPool = q.mNext;
+ q.mNext = null;
} else {
- InputEventMessage currMessage = mPendingInputEvents;
- while (currMessage.mNext != null) {
- currMessage = currMessage.mNext;
- }
- currMessage.mNext = inputMessage;
+ q = new QueuedInputEvent();
}
- sendEmptyMessageAtTime(PROCESS_INPUT_EVENTS, when);
+
+ 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;
+ }
+ }
+
+ 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 {
+ while (last.mNext != null) {
+ last = last.mNext;
+ }
+ last.mNext = q;
+ }
+
+ scheduleProcessInputEvents();
+ }
+
+ private void scheduleProcessInputEvents() {
+ if (!mProcessInputEventsPending) {
+ mProcessInputEventsPending = true;
+ sendEmptyMessage(DO_PROCESS_INPUT_EVENTS);
+ }
+ }
+
+ void processInputEvents() {
+ while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) {
+ QueuedInputEvent q = mFirstPendingInputEvent;
+ mFirstPendingInputEvent = q.mNext;
+ q.mNext = null;
+ mCurrentInputEvent = q;
+ deliverInputEvent(q);
+ }
+
+ // 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 (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());
+ }
+
+ 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();
+ }
+
+ recycleQueuedInputEvent(q);
+
+ mCurrentInputEvent = null;
+ if (mFirstPendingInputEvent != null) {
+ scheduleProcessInputEvents();
+ }
+ }
+
+ final class WindowInputEventReceiver extends InputEventReceiver {
+ public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ enqueueInputEvent(event, this, 0);
+ }
+ }
+ WindowInputEventReceiver mInputEventReceiver;
+
public void dispatchKey(KeyEvent event) {
- dispatchKey(event, false);
- }
-
- 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:");
-
- debug();
-
- if (DBG) Log.d("keydisp", "===================================================");
- }
- }
-
- Message msg = obtainMessage(DISPATCH_KEY);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
-
- if (LOCAL_LOGV) Log.v(
- TAG, "sending key " + event + " to " + mView);
-
- 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);
- }
- }
-
- private void dispatchPointer(MotionEvent event, boolean sendDone) {
- Message msg = obtainMessage(DISPATCH_POINTER);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
- enqueueInputEvent(msg, event.getEventTime());
- }
-
- private void dispatchTrackball(MotionEvent event, boolean sendDone) {
- Message msg = obtainMessage(DISPATCH_TRACKBALL);
- msg.obj = event;
- msg.arg1 = sendDone ? 1 : 0;
- enqueueInputEvent(msg, event.getEventTime());
- }
-
- 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());
+ enqueueInputEvent(event, null, 0);
}
public void dispatchAppVisibility(boolean visible) {
@@ -4099,7 +4059,7 @@
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 2e19bf6..7d729c6 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -340,7 +340,8 @@
* 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 787bcdd..1fab1ca 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9384,42 +9384,59 @@
mPositionY = mTempCoords[1];
}
- public boolean isVisible(int positionX, int positionY) {
- final TextView textView = TextView.this;
-
- 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;
- }
-
- int posX = mPositionX + positionX;
- int posY = mPositionY + positionY;
-
- // 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;
- }
-
- 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());
- }
-
public void onScrollChanged() {
mScrollHasChanged = true;
}
}
+ private boolean isPositionVisible(int positionX, int positionY) {
+ synchronized (sTmpPosition) {
+ final float[] position = sTmpPosition;
+ position[0] = positionX;
+ position[1] = positionY;
+ View view = this;
+
+ while (view != null) {
+ if (view != this) {
+ // Local scroll is already taken into account in positionX/Y
+ position[0] -= view.getScrollX();
+ position[1] -= view.getScrollY();
+ }
+
+ if (position[0] < 0 || position[1] < 0 ||
+ position[0] > view.getWidth() || position[1] > view.getHeight()) {
+ return false;
+ }
+
+ if (!view.getMatrix().isIdentity()) {
+ view.getMatrix().mapPoints(position);
+ }
+
+ 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
protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
@@ -9518,7 +9535,7 @@
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 @@
return false;
}
- return getPositionListener().isVisible(mPositionX + mHotspotX, mPositionY);
+ return TextView.this.isPositionVisible(mPositionX + mHotspotX, mPositionY);
}
public abstract int getCurrentCursorOffset();
@@ -11518,6 +11535,7 @@
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 74b4b06..0000000
--- 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 71c5d26..f20fbbb 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -50,7 +50,7 @@
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 6d1410c..c6447e1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -168,7 +168,7 @@
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 @@
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 0000000..9ae63dd
--- /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 300c04a..0000000
--- 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/docs/html/guide/practices/ui_guidelines/widget_design.jd b/docs/html/guide/practices/ui_guidelines/widget_design.jd
index de20e00..f63f3c4 100644
--- a/docs/html/guide/practices/ui_guidelines/widget_design.jd
+++ b/docs/html/guide/practices/ui_guidelines/widget_design.jd
@@ -250,13 +250,15 @@
sizes, widget layouts must adapt to different Home screen grid cell sizes.</p>
<p>Below is an example layout that a music widget showing text information and two buttons can use.
-It builds upon the previous discussion of adding margins depending on OS version.</p>
+It builds upon the previous discussion of adding margins depending on OS version. Note that the
+most robust and resilient way to add margins to the widget is to wrap the widget frame and contents
+in a padded {@link android.widget.FrameLayout}.</p>
<pre>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_margin="@dimen/widget_margin">
+ android:padding="@dimen/widget_margin">
<LinearLayout
android:layout_width="match_parent"
@@ -295,16 +297,16 @@
<p>When a user adds the widget to their home screen, on an example Android 4.0 device where each
-grid cell is 80dp × 100dp in size and 16dp of margins are automatically applied on all sizes,
+grid cell is 80dp × 100dp in size and 8dp of margins are automatically applied on all sizes,
the widget will be stretched, like so:</p>
<img src="{@docRoot}images/widget_design/music_example_stretched.png"
- alt="Music widget sitting on an example 80dp x 100dp grid with 16dp of automatic margins
+ alt="Music widget sitting on an example 80dp x 100dp grid with 8dp of automatic margins
added by the system" id="music_example_stretched">
<p class="img-caption"><strong>Figure 7.</strong> Music widget sitting on an example 80dp x 100dp
-grid with 16dp of automatic margins added by the system.</p>
+grid with 8dp of automatic margins added by the system.</p>
<h2 id="templates">Using the App Widget Templates Pack</h2>
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index 61337b7..2cb23c1 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -346,7 +346,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- <strong>android:layout_margin="@dimen/widget_margin"></strong>
+ <strong>android:padding="@dimen/widget_margin"></strong>
<LinearLayout
android:layout_width="match_parent"
@@ -363,7 +363,7 @@
<li>Create two dimensions resources, one in <code>res/values/</code> to provide the pre-Android 4.0 custom margins, and one in <code>res/values-v14/</code> to provide no extra padding for Android 4.0 widgets:
<p><strong>res/values/dimens.xml</strong>:<br>
- <pre><dimen name="widget_margin">15dp</dimen></pre></p>
+ <pre><dimen name="widget_margin">8dp</dimen></pre></p>
<p><strong>res/values-v14/dimens.xml</strong>:<br>
<pre><dimen name="widget_margin">0dp</dimen></pre></p>
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 6965702..641134a 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -773,6 +773,7 @@
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 3372d1c..ae7a3b5 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -477,8 +477,9 @@
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 @@
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::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 ab475bf..ab483fb 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -290,7 +290,7 @@
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 3c838fc..a60ac08 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2063,7 +2063,7 @@
}
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 @@
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 2fc88e1..cd9ff93 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -123,7 +123,7 @@
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/AudioManager.java b/media/java/android/media/AudioManager.java
index a0881a7..936ec0f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -22,8 +22,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -37,7 +35,6 @@
import android.view.KeyEvent;
import android.view.VolumePanel;
-import java.util.Iterator;
import java.util.HashMap;
/**
@@ -49,11 +46,9 @@
public class AudioManager {
private final Context mContext;
- private final Handler mHandler;
private long mVolumeKeyUpTime;
private int mVolumeControlStream = -1;
private static String TAG = "AudioManager";
- private static boolean localLOGV = false;
/**
* Broadcast intent, a hint for applications that audio is about to become
@@ -359,7 +354,6 @@
*/
public AudioManager(Context context) {
mContext = context;
- mHandler = new Handler(context.getMainLooper());
}
private static IAudioService getService()
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index f13a6a2..687e2f6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1572,49 +1572,83 @@
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;
+ btDevice = deviceList.get(0);
+ handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
}
- // 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);
+ 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;
+ }
+ // 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);
}
}
- 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 @@
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 @@
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 @@
}
}
+ // 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 @@
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 @@
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 @@
}
+ // 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 @@
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 @@
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), "");
- }
- } 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), "");
+ 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 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), "");
+ synchronized (mConnectedDevices) {
+ 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 @@
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 @@
updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
}
- /**
+ /**
* see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
* precondition: mediaIntent != null, target != null
*/
@@ -3417,7 +3497,7 @@
}
}
- /**
+ /**
* see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
* precondition: mediaIntent != null, eventReceiver != null
*/
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 98617d2..19db1c0 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -51,7 +51,15 @@
private final IContentProvider mMediaProvider;
private final String mVolumeName;
private final Uri mObjectsUri;
- private final String mMediaStoragePath; // path to primary storage
+ // path to primary storage
+ private final String mMediaStoragePath;
+ // if not null, restrict all queries to these subdirectories
+ private final String[] mSubDirectories;
+ // where clause for restricting queries to files in mSubDirectories
+ private String mSubDirectoriesWhere;
+ // where arguments for restricting queries to files in mSubDirectories
+ private String[] mSubDirectoriesWhereArgs;
+
private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
// cached property groups for single properties
@@ -112,7 +120,8 @@
System.loadLibrary("media_jni");
}
- public MtpDatabase(Context context, String volumeName, String storagePath) {
+ public MtpDatabase(Context context, String volumeName, String storagePath,
+ String[] subDirectories) {
native_setup();
mContext = context;
@@ -122,6 +131,31 @@
mObjectsUri = Files.getMtpObjectsUri(volumeName);
mMediaScanner = new MediaScanner(context);
+ mSubDirectories = subDirectories;
+ if (subDirectories != null) {
+ // Compute "where" string for restricting queries to subdirectories
+ StringBuilder builder = new StringBuilder();
+ builder.append("(");
+ int count = subDirectories.length;
+ for (int i = 0; i < count; i++) {
+ builder.append(Files.FileColumns.DATA + "=? OR "
+ + Files.FileColumns.DATA + " LIKE ?");
+ if (i != count - 1) {
+ builder.append(" OR ");
+ }
+ }
+ builder.append(")");
+ mSubDirectoriesWhere = builder.toString();
+
+ // Compute "where" arguments for restricting queries to subdirectories
+ mSubDirectoriesWhereArgs = new String[count * 2];
+ for (int i = 0, j = 0; i < count; i++) {
+ String path = subDirectories[i];
+ mSubDirectoriesWhereArgs[j++] = path;
+ mSubDirectoriesWhereArgs[j++] = path + "/%";
+ }
+ }
+
// Set locale to MediaScanner.
Locale locale = context.getResources().getConfiguration().locale;
if (locale != null) {
@@ -190,9 +224,44 @@
}
}
+ // check to see if the path is contained in one of our storage subdirectories
+ // returns true if we have no special subdirectories
+ private boolean inStorageSubDirectory(String path) {
+ if (mSubDirectories == null) return true;
+ if (path == null) return false;
+
+ boolean allowed = false;
+ int pathLength = path.length();
+ for (int i = 0; i < mSubDirectories.length && !allowed; i++) {
+ String subdir = mSubDirectories[i];
+ int subdirLength = subdir.length();
+ if (subdirLength < pathLength &&
+ path.charAt(subdirLength) == '/' &&
+ path.startsWith(subdir)) {
+ allowed = true;
+ }
+ }
+ return allowed;
+ }
+
+ // check to see if the path matches one of our storage subdirectories
+ // returns true if we have no special subdirectories
+ private boolean isStorageSubDirectory(String path) {
+ if (mSubDirectories == null) return false;
+ for (int i = 0; i < mSubDirectories.length; i++) {
+ if (path.equals(mSubDirectories[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private int beginSendObject(String path, int format, int parent,
int storageId, long size, long modified) {
- // first make sure the object does not exist
+ // if mSubDirectories is not null, do not allow copying files to any other locations
+ if (!inStorageSubDirectory(path)) return -1;
+
+ // make sure the object does not exist
if (path != null) {
Cursor c = null;
try {
@@ -269,33 +338,40 @@
}
private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
+ String where;
+ String[] whereArgs;
+
if (storageID == 0xFFFFFFFF) {
// query all stores
if (format == 0) {
// query all formats
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, null, null, null);
+ where = null;
+ whereArgs = null;
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(parent) };
}
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- }
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, PARENT_WHERE,
- new String[] { Integer.toString(parent) }, null);
} else {
// query specific format
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_WHERE,
- new String[] { Integer.toString(format) }, null);
+ where = FORMAT_WHERE;
+ whereArgs = new String[] { Integer.toString(format) };
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = FORMAT_PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(format),
+ Integer.toString(parent) };
}
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- }
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_PARENT_WHERE,
- new String[] { Integer.toString(format), Integer.toString(parent) }, null);
}
} else {
// query specific store
@@ -303,35 +379,61 @@
// query all formats
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_WHERE,
- new String[] { Integer.toString(storageID) }, null);
+ where = STORAGE_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID) };
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = STORAGE_PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID),
+ Integer.toString(parent) };
}
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- }
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_PARENT_WHERE,
- new String[] { Integer.toString(storageID), Integer.toString(parent) },
- null);
} else {
// query specific format
if (parent == 0) {
// query all objects
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_WHERE,
- new String[] { Integer.toString(storageID), Integer.toString(format) },
- null);
+ where = STORAGE_FORMAT_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID),
+ Integer.toString(format) };
+ } else {
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ where = STORAGE_FORMAT_PARENT_WHERE;
+ whereArgs = new String[] { Integer.toString(storageID),
+ Integer.toString(format),
+ Integer.toString(parent) };
}
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- }
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_PARENT_WHERE,
- new String[] { Integer.toString(storageID),
- Integer.toString(format),
- Integer.toString(parent) },
- null);
}
}
+
+ // if we are restricting queries to mSubDirectories, we need to add the restriction
+ // onto our "where" arguments
+ if (mSubDirectoriesWhere != null) {
+ if (where == null) {
+ where = mSubDirectoriesWhere;
+ whereArgs = mSubDirectoriesWhereArgs;
+ } else {
+ where = where + " AND " + mSubDirectoriesWhere;
+
+ // create new array to hold whereArgs and mSubDirectoriesWhereArgs
+ String[] newWhereArgs =
+ new String[whereArgs.length + mSubDirectoriesWhereArgs.length];
+ int i, j;
+ for (i = 0; i < whereArgs.length; i++) {
+ newWhereArgs[i] = whereArgs[i];
+ }
+ for (j = 0; j < mSubDirectoriesWhereArgs.length; i++, j++) {
+ newWhereArgs[i] = mSubDirectoriesWhereArgs[j];
+ }
+ whereArgs = newWhereArgs;
+ }
+ }
+
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where, whereArgs, null);
}
private int[] getObjectList(int storageID, int format, int parent) {
@@ -613,6 +715,11 @@
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
+ // do not allow renaming any of the special subdirectories
+ if (isStorageSubDirectory(path)) {
+ return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+ }
+
// now rename the file. make sure this succeeds before updating database
File oldFile = new File(path);
int lastSlash = path.lastIndexOf('/');
@@ -794,6 +901,11 @@
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
+ // do not allow deleting any of the special subdirectories
+ if (isStorageSubDirectory(path)) {
+ return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+ }
+
if (format == MtpConstants.FORMAT_ASSOCIATION) {
// recursive case - delete all children first
Uri uri = Files.getMtpObjectsUri(mVolumeName);
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 20225ba..cfea7e8 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -25,10 +25,12 @@
#include "MtpDataPacket.h"
#include "MtpStringBuffer.h"
+#define MTP_BUFFER_SIZE 16384
+
namespace android {
MtpDataPacket::MtpDataPacket()
- : MtpPacket(16384), // MAX_USBFS_BUFFER_SIZE
+ : MtpPacket(MTP_BUFFER_SIZE), // MAX_USBFS_BUFFER_SIZE
mOffset(MTP_CONTAINER_HEADER_SIZE)
{
}
@@ -345,7 +347,7 @@
#ifdef MTP_DEVICE
int MtpDataPacket::read(int fd) {
- int ret = ::read(fd, mBuffer, mBufferSize);
+ int ret = ::read(fd, mBuffer, MTP_BUFFER_SIZE);
if (ret < MTP_CONTAINER_HEADER_SIZE)
return -1;
mPacketSize = ret;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 51eb97f..1334e6c 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -1053,11 +1053,14 @@
int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
if (result == MTP_RESPONSE_OK) {
ALOGV("deleting %s", (const char *)filePath);
- deletePath((const char *)filePath);
- return mDatabase->deleteFile(handle);
- } else {
- return result;
+ result = mDatabase->deleteFile(handle);
+ // Don't delete the actual files unless the database deletion is allowed
+ if (result == MTP_RESPONSE_OK) {
+ deletePath((const char *)filePath);
+ }
}
+
+ return result;
}
MtpResponseCode MtpServer::doGetObjectPropDesc() {
diff --git a/media/tests/README.txt b/media/tests/README.txt
new file mode 100644
index 0000000..e3e1639
--- /dev/null
+++ b/media/tests/README.txt
@@ -0,0 +1,10 @@
+MediaFrameworkTest/
+ Uses instrumentation and so can be run with runtest.
+ It assumes /sdcard/media_api/ has been populated.
+
+contents/media_api/
+ Push to /sdcard/media_api/ for use with MediaFrameworkTest:
+ adb shell mkdir /sdcard/media_api
+ adb push contents/media_api/ /sdcard/media_api/
+
+All other subdirectories are manual tests or sample apps.
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 2dcd80d..2232995 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.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.WindowManager;
import android.view.animation.Interpolator;
import android.widget.ImageView;
+
import com.android.systemui.R;
import java.io.File;
@@ -254,6 +256,8 @@
private float mBgPadding;
private float mBgPaddingScale;
+ private CameraSound mCameraSound;
+
/**
* @param context everything needs a context :(
@@ -303,6 +307,9 @@
// 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 @@
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 7684c34..e6b86fc 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.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.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.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 @@
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 @@
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 @@
}
}
if (removeView != null) {
+ if (mPointerLocationInputEventReceiver != null) {
+ mPointerLocationInputEventReceiver.dispose();
+ mPointerLocationInputEventReceiver = null;
+ }
if (mPointerLocationInputChannel != null) {
- InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
mPointerLocationInputChannel.dispose();
mPointerLocationInputChannel = null;
}
@@ -1836,13 +1847,19 @@
* 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 @@
}
}
} 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 @@
}
} 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 d34087f..16eeb7ba 100644
--- a/services/java/com/android/server/DeviceStorageMonitorService.java
+++ b/services/java/com/android/server/DeviceStorageMonitorService.java
@@ -163,7 +163,6 @@
} 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 73cd64e..a19035a 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.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 @@
float mCurrentX, mCurrentY;
float mThumbOffsetX, mThumbOffsetY;
InputChannel mServerChannel, mClientChannel;
+ DragInputEventReceiver mInputEventReceiver;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
WindowState mTargetWindow;
@@ -90,8 +91,8 @@
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 @@
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 0e72f7d..121ce18 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.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 @@
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 @@
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 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 f5c2de9..75ace4f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -36,7 +36,6 @@
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.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 @@
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 @@
} 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 @@
}
@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()) {