Merge "Added support for the GL_TEXTURE_EXTERNAL target" into gingerbread
diff --git a/common/Android.mk b/common/Android.mk
deleted file mode 100644
index 1f78840..0000000
--- a/common/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2009 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Note: the source code is in java/, not src/, because this code is also part of
-# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-common
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
-     $(call all-java-files-under, java) \
-     $(call all-logtags-files-under, java)
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Include this library in the build server's output directory
-$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):android-common.jar)
-
-# Build the test package
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/common/java/com/android/common/ArrayListCursor.java b/common/java/com/android/common/ArrayListCursor.java
deleted file mode 100644
index 9ad5c36..0000000
--- a/common/java/com/android/common/ArrayListCursor.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2006 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.common;
-
-import android.database.AbstractCursor;
-import android.database.CursorWindow;
-
-import java.lang.System;
-import java.util.ArrayList;
-
-/**
- * A convenience class that presents a two-dimensional ArrayList
- * as a Cursor.
- * @deprecated This is has been replaced by MatrixCursor.
-*/
-public class ArrayListCursor extends AbstractCursor {
-    private String[] mColumnNames;
-    private ArrayList<Object>[] mRows;
-
-    @SuppressWarnings({"unchecked"})
-    public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) {
-        int colCount = columnNames.length;
-        boolean foundID = false;
-        // Add an _id column if not in columnNames
-        for (int i = 0; i < colCount; ++i) {
-            if (columnNames[i].compareToIgnoreCase("_id") == 0) {
-                mColumnNames = columnNames;
-                foundID = true;
-                break;
-            }
-        }
-
-        if (!foundID) {
-            mColumnNames = new String[colCount + 1];
-            System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length);
-            mColumnNames[colCount] = "_id";
-        }
-
-        int rowCount = rows.size();
-        mRows = new ArrayList[rowCount];
-
-        for (int i = 0; i < rowCount; ++i) {
-            mRows[i] = rows.get(i);
-            if (!foundID) {
-                mRows[i].add(i);
-            }
-        }
-    }
-
-    @Override
-    public void fillWindow(int position, CursorWindow window) {
-        if (position < 0 || position > getCount()) {
-            return;
-        }
-
-        window.acquireReference();
-        try {
-            int oldpos = mPos;
-            mPos = position - 1;
-            window.clear();
-            window.setStartPosition(position);
-            int columnNum = getColumnCount();
-            window.setNumColumns(columnNum);
-            while (moveToNext() && window.allocRow()) {
-                for (int i = 0; i < columnNum; i++) {
-                    final Object data = mRows[mPos].get(i);
-                    if (data != null) {
-                        if (data instanceof byte[]) {
-                            byte[] field = (byte[]) data;
-                            if (!window.putBlob(field, mPos, i)) {
-                                window.freeLastRow();
-                                break;
-                            }
-                        } else {
-                            String field = data.toString();
-                            if (!window.putString(field, mPos, i)) {
-                                window.freeLastRow();
-                                break;
-                            }
-                        }
-                    } else {
-                        if (!window.putNull(mPos, i)) {
-                            window.freeLastRow();
-                            break;
-                        }
-                    }
-                }
-            }
-
-            mPos = oldpos;
-        } catch (IllegalStateException e){
-            // simply ignore it
-        } finally {
-            window.releaseReference();
-        }
-    }
-
-    @Override
-    public int getCount() {
-        return mRows.length;
-    }
-
-    @Override
-    public String[] getColumnNames() {
-        return mColumnNames;
-    }
-
-    @Override
-    public byte[] getBlob(int columnIndex) {
-        return (byte[]) mRows[mPos].get(columnIndex);
-    }
-
-    @Override
-    public String getString(int columnIndex) {
-        Object cell = mRows[mPos].get(columnIndex);
-        return (cell == null) ? null : cell.toString();
-    }
-
-    @Override
-    public short getShort(int columnIndex) {
-        Number num = (Number) mRows[mPos].get(columnIndex);
-        return num.shortValue();
-    }
-
-    @Override
-    public int getInt(int columnIndex) {
-        Number num = (Number) mRows[mPos].get(columnIndex);
-        return num.intValue();
-    }
-
-    @Override
-    public long getLong(int columnIndex) {
-        Number num = (Number) mRows[mPos].get(columnIndex);
-        return num.longValue();
-    }
-
-    @Override
-    public float getFloat(int columnIndex) {
-        Number num = (Number) mRows[mPos].get(columnIndex);
-        return num.floatValue();
-    }
-
-    @Override
-    public double getDouble(int columnIndex) {
-        Number num = (Number) mRows[mPos].get(columnIndex);
-        return num.doubleValue();
-    }
-
-    @Override
-    public boolean isNull(int columnIndex) {
-        return mRows[mPos].get(columnIndex) == null;
-    }
-}
diff --git a/common/java/com/android/common/GoogleLogTags.logtags b/common/java/com/android/common/GoogleLogTags.logtags
deleted file mode 100644
index f848ddf..0000000
--- a/common/java/com/android/common/GoogleLogTags.logtags
+++ /dev/null
@@ -1,100 +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.
-
-option java_package com.android.common
-
-#####
-# This file contains definitions for event log (android.util.EventLog) tags
-# used by Google Mobile Services applications.  These definitions are part of
-# the platform even when the Google applications are not.
-#
-# See system/core/logcat/event.logtags for a description of the file format.
-#
-# These event log tags must be assigned specific numbers (no "?") in the range
-# 200000-300000.  If new tags are added, be aware that older platforms will be
-# missing the tag definitions, and may not be able to show them in their logs.
-
-#####
-# System Update (OTA)
-
-# System update status bits
-# [31- 9] Reserved for future use
-# [ 8- 7] package verified (0=not attempted, 1=succeeded, 2=failed)
-# [    6] install approved
-# [    5] download approved
-# [ 4- 0] status
-201001 system_update (status|1|5),(download_result|1|5),(bytes|2|2),(url|3)
-201002 system_update_user (action|3)
-
-#####
-# Android Market
-
-# @param changes Number of changes made to database in reconstruct
-202001 vending_reconstruct (changes|1)
-
-#####
-# Google Services Framework
-
-203001 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3)
-
-203002 google_http_request (elapsed|2|3),(status|1),(appname|3),(reused|1)
-
-#####
-# Google Talk Service
-
-# This event is logged when GTalkService encounters important events
-204001 gtalkservice (eventType|1)
-# This event is logged for GTalk connection state changes. The status field is an int, but
-# it really contains 4 separate values, each taking up a byte
-#     (eventType << 24) + (connection state << 16) + (connection error << 8) + network state
-204002 gtalk_connection (status|1)
-
-# This event is logged when GTalk connection is closed.
-# The status field is an int, but contains 2 different values, it's represented as
-#
-#     (networkType << 8) + connection error
-#
-# the possible error values are
-#
-# no_error=0, no_network=1, connection_failed=2, unknown_host=3, auth_failed=4,
-# auth_expired=5, heart_beat_timeout=6, server_error=7, server_reject_rate_limiting=8, unknown=10
-#
-# duration is the connection duration.
-204003 gtalk_conn_close (status|1),(duration|1)
-
-# This event is logged for GTalk heartbeat resets
-# interval_and_nt contains both the heartbeat interval and the network type, It's represented as
-#     (networkType << 16) + interval
-# interval is in seconds; network type can be 0 (mobile) or 1 (wifi); ip is the host ip addr.
-204004 gtalk_heartbeat_reset (interval_and_nt|1),(ip|3)
-
-# This event is logged when an Rmq v2 packet is sent or received.
-204005 c2dm (packet_type|1),(persistent_id|3),(stream_id|1),(last_stream_id|1)
-
-#####
-# Google Login Service and Setup Wizard
-
-# This event is for when communicating to the server times out during account setup
-205001 setup_server_timeout
-205002 setup_required_captcha (action|3)
-205003 setup_io_error (status|3)
-205004 setup_server_error
-205005 setup_retries_exhausted
-205006 setup_no_data_network
-205007 setup_completed
-
-205008 gls_account_tried (status|1)
-205009 gls_account_saved (status|1)
-205010 gls_authenticate (status|1),(service|3)
-205011 google_mail_switch (direction|1)
diff --git a/common/java/com/android/common/NetworkConnectivityListener.java b/common/java/com/android/common/NetworkConnectivityListener.java
deleted file mode 100644
index b49b80d..0000000
--- a/common/java/com/android/common/NetworkConnectivityListener.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2006 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.common;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Iterator;
-
-/**
- * A wrapper for a broadcast receiver that provides network connectivity
- * state information, independent of network type (mobile, Wi-Fi, etc.).
- * @deprecated Code tempted to use this class should simply listen for connectivity intents
- * (or poll ConnectivityManager) directly.
- * {@hide}
- */
-public class NetworkConnectivityListener {
-    private static final String TAG = "NetworkConnectivityListener";
-    private static final boolean DBG = false;
-
-    private Context mContext;
-    private HashMap<Handler, Integer> mHandlers = new HashMap<Handler, Integer>();
-    private State mState;
-    private boolean mListening;
-    private String mReason;
-    private boolean mIsFailover;
-
-    /** Network connectivity information */
-    private NetworkInfo mNetworkInfo;
-
-    /**
-     * In case of a Disconnect, the connectivity manager may have
-     * already established, or may be attempting to establish, connectivity
-     * with another network. If so, {@code mOtherNetworkInfo} will be non-null.
-     */
-    private NetworkInfo mOtherNetworkInfo;
-
-    private ConnectivityBroadcastReceiver mReceiver;
-
-    private class ConnectivityBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-
-            if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
-                mListening == false) {
-                Log.w(TAG, "onReceived() called with " + mState.toString() + " and " + intent);
-                return;
-            }
-
-            boolean noConnectivity =
-                intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-
-            if (noConnectivity) {
-                mState = State.NOT_CONNECTED;
-            } else {
-                mState = State.CONNECTED;
-            }
-
-            mNetworkInfo = (NetworkInfo)
-                intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
-            mOtherNetworkInfo = (NetworkInfo)
-                intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
-
-            mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
-            mIsFailover =
-                intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
-
-            if (DBG) {
-                Log.d(TAG, "onReceive(): mNetworkInfo=" + mNetworkInfo +  " mOtherNetworkInfo = "
-                        + (mOtherNetworkInfo == null ? "[none]" : mOtherNetworkInfo +
-                        " noConn=" + noConnectivity) + " mState=" + mState.toString());
-            }
-
-            // Notifiy any handlers.
-            Iterator<Handler> it = mHandlers.keySet().iterator();
-            while (it.hasNext()) {
-                Handler target = it.next();
-                Message message = Message.obtain(target, mHandlers.get(target));
-                target.sendMessage(message);
-            }
-        }
-    };
-
-    public enum State {
-        UNKNOWN,
-
-        /** This state is returned if there is connectivity to any network **/
-        CONNECTED,
-        /**
-         * This state is returned if there is no connectivity to any network. This is set
-         * to true under two circumstances:
-         * <ul>
-         * <li>When connectivity is lost to one network, and there is no other available
-         * network to attempt to switch to.</li>
-         * <li>When connectivity is lost to one network, and the attempt to switch to
-         * another network fails.</li>
-         */
-        NOT_CONNECTED
-    }
-
-    /**
-     * Create a new NetworkConnectivityListener.
-     */
-    public NetworkConnectivityListener() {
-        mState = State.UNKNOWN;
-        mReceiver = new ConnectivityBroadcastReceiver();
-    }
-
-    /**
-     * This method starts listening for network connectivity state changes.
-     * @param context
-     */
-    public synchronized void startListening(Context context) {
-        if (!mListening) {
-            mContext = context;
-
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-            context.registerReceiver(mReceiver, filter);
-            mListening = true;
-        }
-    }
-
-    /**
-     * This method stops this class from listening for network changes.
-     */
-    public synchronized void stopListening() {
-        if (mListening) {
-            mContext.unregisterReceiver(mReceiver);
-            mContext = null;
-            mNetworkInfo = null;
-            mOtherNetworkInfo = null;
-            mIsFailover = false;
-            mReason = null;
-            mListening = false;
-        }
-    }
-
-    /**
-     * This methods registers a Handler to be called back onto with the specified what code when
-     * the network connectivity state changes.
-     *
-     * @param target The target handler.
-     * @param what The what code to be used when posting a message to the handler.
-     */
-    public void registerHandler(Handler target, int what) {
-        mHandlers.put(target, what);
-    }
-
-    /**
-     * This methods unregisters the specified Handler.
-     * @param target
-     */
-    public void unregisterHandler(Handler target) {
-        mHandlers.remove(target);
-    }
-
-    public State getState() {
-        return mState;
-    }
-
-    /**
-     * Return the NetworkInfo associated with the most recent connectivity event.
-     * @return {@code NetworkInfo} for the network that had the most recent connectivity event.
-     */
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
-
-    /**
-     * If the most recent connectivity event was a DISCONNECT, return
-     * any information supplied in the broadcast about an alternate
-     * network that might be available. If this returns a non-null
-     * value, then another broadcast should follow shortly indicating
-     * whether connection to the other network succeeded.
-     *
-     * @return NetworkInfo
-     */
-    public NetworkInfo getOtherNetworkInfo() {
-        return mOtherNetworkInfo;
-    }
-
-    /**
-     * Returns true if the most recent event was for an attempt to switch over to
-     * a new network following loss of connectivity on another network.
-     * @return {@code true} if this was a failover attempt, {@code false} otherwise.
-     */
-    public boolean isFailover() {
-        return mIsFailover;
-    }
-
-    /**
-     * An optional reason for the connectivity state change may have been supplied.
-     * This returns it.
-     * @return the reason for the state change, if available, or {@code null}
-     * otherwise.
-     */
-    public String getReason() {
-        return mReason;
-    }
-}
diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java
deleted file mode 100644
index 1786957..0000000
--- a/common/java/com/android/common/OperationScheduler.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2009 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.common;
-
-import android.content.SharedPreferences;
-import android.net.http.AndroidHttpClient;
-import android.text.format.Time;
-
-import java.util.Map;
-import java.util.TreeSet;
-
-/**
- * Tracks the success/failure history of a particular network operation in
- * persistent storage and computes retry strategy accordingly.  Handles
- * exponential backoff, periodic rescheduling, event-driven triggering,
- * retry-after moratorium intervals, etc. based on caller-specified parameters.
- *
- * <p>This class does not directly perform or invoke any operations,
- * it only keeps track of the schedule.  Somebody else needs to call
- * {@link #getNextTimeMillis()} as appropriate and do the actual work.
- */
-public class OperationScheduler {
-    /** Tunable parameter options for {@link #getNextTimeMillis}. */
-    public static class Options {
-        /** Wait this long after every error before retrying. */
-        public long backoffFixedMillis = 0;
-
-        /** Wait this long times the number of consecutive errors so far before retrying. */
-        public long backoffIncrementalMillis = 5000;
-
-        /** Maximum duration of moratorium to honor.  Mostly an issue for clock rollbacks. */
-        public long maxMoratoriumMillis = 24 * 3600 * 1000;
-
-        /** Minimum duration after success to wait before allowing another trigger. */
-        public long minTriggerMillis = 0;
-
-        /** Automatically trigger this long after the last success. */
-        public long periodicIntervalMillis = 0;
-
-        @Override
-        public String toString() {
-            return String.format(
-                    "OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]",
-                    backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0,
-                    maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0,
-                    periodicIntervalMillis / 1000.0);
-        }
-    }
-
-    private static final String PREFIX = "OperationScheduler_";
-    private final SharedPreferences mStorage;
-
-    /**
-     * Initialize the scheduler state.
-     * @param storage to use for recording the state of operations across restarts/reboots
-     */
-    public OperationScheduler(SharedPreferences storage) {
-        mStorage = storage;
-    }
-
-    /**
-     * Parse scheduler options supplied in this string form:
-     *
-     * <pre>
-     * backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval)
-     * </pre>
-     *
-     * All values are times in (possibly fractional) <em>seconds</em> (not milliseconds).
-     * Omitted settings are left at whatever existing default value was passed in.
-     *
-     * <p>
-     * The default options: <code>backoff=0+5 max=86400 min=0 period=0</code><br>
-     * Fractions are OK: <code>backoff=+2.5 period=10.0</code><br>
-     * The "period=" can be omitted: <code>3600</code><br>
-     *
-     * @param spec describing some or all scheduler options.
-     * @param options to update with parsed values.
-     * @return the options passed in (for convenience)
-     * @throws IllegalArgumentException if the syntax is invalid
-     */
-    public static Options parseOptions(String spec, Options options)
-            throws IllegalArgumentException {
-        for (String param : spec.split(" +")) {
-            if (param.length() == 0) continue;
-            if (param.startsWith("backoff=")) {
-                int plus = param.indexOf('+', 8);
-                if (plus < 0) {
-                    options.backoffFixedMillis = parseSeconds(param.substring(8));
-                } else {
-                    if (plus > 8) {
-                        options.backoffFixedMillis = parseSeconds(param.substring(8, plus));
-                    }
-                    options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1));
-                }
-            } else if (param.startsWith("max=")) {
-                options.maxMoratoriumMillis = parseSeconds(param.substring(4));
-            } else if (param.startsWith("min=")) {
-                options.minTriggerMillis = parseSeconds(param.substring(4));
-            } else if (param.startsWith("period=")) {
-                options.periodicIntervalMillis = parseSeconds(param.substring(7));
-            } else {
-                options.periodicIntervalMillis = parseSeconds(param);
-            }
-        }
-        return options;
-    }
-
-    private static long parseSeconds(String param) throws NumberFormatException {
-        return (long) (Float.parseFloat(param) * 1000);
-    }
-
-    /**
-     * Compute the time of the next operation.  Does not modify any state
-     * (unless the clock rolls backwards, in which case timers are reset).
-     *
-     * @param options to use for this computation.
-     * @return the wall clock time ({@link System#currentTimeMillis()}) when the
-     * next operation should be attempted -- immediately, if the return value is
-     * before the current time.
-     */
-    public long getNextTimeMillis(Options options) {
-        boolean enabledState = mStorage.getBoolean(PREFIX + "enabledState", true);
-        if (!enabledState) return Long.MAX_VALUE;
-
-        boolean permanentError = mStorage.getBoolean(PREFIX + "permanentError", false);
-        if (permanentError) return Long.MAX_VALUE;
-
-        // We do quite a bit of limiting to prevent a clock rollback from totally
-        // hosing the scheduler.  Times which are supposed to be in the past are
-        // clipped to the current time so we don't languish forever.
-
-        int errorCount = mStorage.getInt(PREFIX + "errorCount", 0);
-        long now = currentTimeMillis();
-        long lastSuccessTimeMillis = getTimeBefore(PREFIX + "lastSuccessTimeMillis", now);
-        long lastErrorTimeMillis = getTimeBefore(PREFIX + "lastErrorTimeMillis", now);
-        long triggerTimeMillis = mStorage.getLong(PREFIX + "triggerTimeMillis", Long.MAX_VALUE);
-        long moratoriumSetMillis = getTimeBefore(PREFIX + "moratoriumSetTimeMillis", now);
-        long moratoriumTimeMillis = getTimeBefore(PREFIX + "moratoriumTimeMillis",
-                moratoriumSetMillis + options.maxMoratoriumMillis);
-
-        long time = triggerTimeMillis;
-        if (options.periodicIntervalMillis > 0) {
-            time = Math.min(time, lastSuccessTimeMillis + options.periodicIntervalMillis);
-        }
-
-        time = Math.max(time, moratoriumTimeMillis);
-        time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis);
-        if (errorCount > 0) {
-            time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis +
-                    options.backoffIncrementalMillis * errorCount);
-        }
-        return time;
-    }
-
-    /**
-     * Return the last time the operation completed.  Does not modify any state.
-     *
-     * @return the wall clock time when {@link #onSuccess()} was last called.
-     */
-    public long getLastSuccessTimeMillis() {
-        return mStorage.getLong(PREFIX + "lastSuccessTimeMillis", 0);
-    }
-
-    /**
-     * Return the last time the operation was attempted.  Does not modify any state.
-     *
-     * @return the wall clock time when {@link #onSuccess()} or {@link
-     * #onTransientError()} was last called.
-     */
-    public long getLastAttemptTimeMillis() {
-        return Math.max(
-                mStorage.getLong(PREFIX + "lastSuccessTimeMillis", 0),
-                mStorage.getLong(PREFIX + "lastErrorTimeMillis", 0));
-    }
-
-    /**
-     * Fetch a {@link SharedPreferences} property, but force it to be before
-     * a certain time, updating the value if necessary.  This is to recover
-     * gracefully from clock rollbacks which could otherwise strand our timers.
-     *
-     * @param name of SharedPreferences key
-     * @param max time to allow in result
-     * @return current value attached to key (default 0), limited by max
-     */
-    private long getTimeBefore(String name, long max) {
-        long time = mStorage.getLong(name, 0);
-        if (time > max) mStorage.edit().putLong(name, (time = max)).commit();
-        return time;
-    }
-
-    /**
-     * Request an operation to be performed at a certain time.  The actual
-     * scheduled time may be affected by error backoff logic and defined
-     * minimum intervals.  Use {@link Long#MAX_VALUE} to disable triggering.
-     *
-     * @param millis wall clock time ({@link System#currentTimeMillis()}) to
-     * trigger another operation; 0 to trigger immediately
-     */
-    public void setTriggerTimeMillis(long millis) {
-        mStorage.edit().putLong(PREFIX + "triggerTimeMillis", millis).commit();
-    }
-
-    /**
-     * Forbid any operations until after a certain (absolute) time.
-     * Limited by {@link #Options.maxMoratoriumMillis}.
-     *
-     * @param millis wall clock time ({@link System#currentTimeMillis()})
-     * when operations should be allowed again; 0 to remove moratorium
-     */
-    public void setMoratoriumTimeMillis(long millis) {
-        mStorage.edit()
-                .putLong(PREFIX + "moratoriumTimeMillis", millis)
-                .putLong(PREFIX + "moratoriumSetTimeMillis", currentTimeMillis())
-                .commit();
-    }
-
-    /**
-     * Forbid any operations until after a certain time, as specified in
-     * the format used by the HTTP "Retry-After" header.
-     * Limited by {@link #Options.maxMoratoriumMillis}.
-     *
-     * @param retryAfter moratorium time in HTTP format
-     * @return true if a time was successfully parsed
-     */
-    public boolean setMoratoriumTimeHttp(String retryAfter) {
-        try {
-            long ms = Long.valueOf(retryAfter) * 1000;
-            setMoratoriumTimeMillis(ms + currentTimeMillis());
-            return true;
-        } catch (NumberFormatException nfe) {
-            try {
-                setMoratoriumTimeMillis(AndroidHttpClient.parseDate(retryAfter));
-                return true;
-            } catch (IllegalArgumentException iae) {
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Enable or disable all operations.  When disabled, all calls to
-     * {@link #getNextTimeMillis()} return {@link Long#MAX_VALUE}.
-     * Commonly used when data network availability goes up and down.
-     *
-     * @param enabled if operations can be performed
-     */
-    public void setEnabledState(boolean enabled) {
-        mStorage.edit().putBoolean(PREFIX + "enabledState", enabled).commit();
-    }
-
-    /**
-     * Report successful completion of an operation.  Resets all error
-     * counters, clears any trigger directives, and records the success.
-     */
-    public void onSuccess() {
-        resetTransientError();
-        resetPermanentError();
-        mStorage.edit()
-                .remove(PREFIX + "errorCount")
-                .remove(PREFIX + "lastErrorTimeMillis")
-                .remove(PREFIX + "permanentError")
-                .remove(PREFIX + "triggerTimeMillis")
-                .putLong(PREFIX + "lastSuccessTimeMillis", currentTimeMillis()).commit();
-    }
-
-    /**
-     * Report a transient error (usually a network failure).  Increments
-     * the error count and records the time of the latest error for backoff
-     * purposes.
-     */
-    public void onTransientError() {
-        mStorage.edit().putLong(PREFIX + "lastErrorTimeMillis", currentTimeMillis()).commit();
-        mStorage.edit().putInt(PREFIX + "errorCount",
-                mStorage.getInt(PREFIX + "errorCount", 0) + 1).commit();
-    }
-
-    /**
-     * Reset all transient error counts, allowing the next operation to proceed
-     * immediately without backoff.  Commonly used on network state changes, when
-     * partial progress occurs (some data received), and in other circumstances
-     * where there is reason to hope things might start working better.
-     */
-    public void resetTransientError() {
-        mStorage.edit().remove(PREFIX + "errorCount").commit();
-    }
-
-    /**
-     * Report a permanent error that will not go away until further notice.
-     * No operation will be scheduled until {@link #resetPermanentError()}
-     * is called.  Commonly used for authentication failures (which are reset
-     * when the accounts database is updated).
-     */
-    public void onPermanentError() {
-        mStorage.edit().putBoolean(PREFIX + "permanentError", true).commit();
-    }
-
-    /**
-     * Reset any permanent error status set by {@link #onPermanentError},
-     * allowing operations to be scheduled as normal.
-     */
-    public void resetPermanentError() {
-        mStorage.edit().remove(PREFIX + "permanentError").commit();
-    }
-
-    /**
-     * Return a string description of the scheduler state for debugging.
-     */
-    public String toString() {
-        StringBuilder out = new StringBuilder("[OperationScheduler:");
-        for (String key : new TreeSet<String>(mStorage.getAll().keySet())) {  // Sort keys
-            if (key.startsWith(PREFIX)) {
-                if (key.endsWith("TimeMillis")) {
-                    Time time = new Time();
-                    time.set(mStorage.getLong(key, 0));
-                    out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10));
-                    out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S"));
-                } else {
-                    out.append(" ").append(key.substring(PREFIX.length()));
-                    out.append("=").append(mStorage.getAll().get(key).toString());
-                }
-            }
-        }
-        return out.append("]").toString();
-    }
-
-    /**
-     * Gets the current time.  Can be overridden for unit testing.
-     *
-     * @return {@link System#currentTimeMillis()}
-     */
-    protected long currentTimeMillis() {
-        return System.currentTimeMillis();
-    }
-}
diff --git a/common/java/com/android/common/Rfc822InputFilter.java b/common/java/com/android/common/Rfc822InputFilter.java
deleted file mode 100644
index 6dfdc7b..0000000
--- a/common/java/com/android/common/Rfc822InputFilter.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2008 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.common;
-
-import android.text.InputFilter;
-import android.text.Spanned;
-import android.text.SpannableStringBuilder;
-
-/**
- * Implements special address cleanup rules:
- * The first space key entry following an "@" symbol that is followed by any combination
- * of letters and symbols, including one+ dots and zero commas, should insert an extra
- * comma (followed by the space).
- *
- * @hide
- */
-public class Rfc822InputFilter implements InputFilter {
-
-    public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
-        int dstart, int dend) {
-
-        // quick check - did they enter a single space?
-        if (end-start != 1 || source.charAt(start) != ' ') {
-            return null;
-        }
-
-        // determine if the characters before the new space fit the pattern
-        // follow backwards and see if we find a comma, dot, or @
-        int scanBack = dstart;
-        boolean dotFound = false;
-        while (scanBack > 0) {
-            char c = dest.charAt(--scanBack);
-            switch (c) {
-                case '.':
-                    dotFound = true;    // one or more dots are req'd
-                    break;
-                case ',':
-                    return null;
-                case '@':
-                    if (!dotFound) {
-                        return null;
-                    }
-                    // we have found a comma-insert case.  now just do it
-                    // in the least expensive way we can.
-                    if (source instanceof Spanned) {
-                        SpannableStringBuilder sb = new SpannableStringBuilder(",");
-                        sb.append(source);
-                        return sb;
-                    } else {
-                        return ", ";
-                    }
-                default:
-                    // just keep going
-            }
-        }
-
-        // no termination cases were found, so don't edit the input
-        return null;
-    }
-}
diff --git a/common/java/com/android/common/Rfc822Validator.java b/common/java/com/android/common/Rfc822Validator.java
deleted file mode 100644
index 087e425..0000000
--- a/common/java/com/android/common/Rfc822Validator.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2008 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.common;
-
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
-import android.widget.AutoCompleteTextView;
-
-import java.util.regex.Pattern;
-
-/**
- * This class works as a Validator for AutoCompleteTextView for
- * email addresses.  If a token does not appear to be a valid address,
- * it is trimmed of characters that cannot legitimately appear in one
- * and has the specified domain name added.  It is meant for use with
- * {@link Rfc822Token} and {@link Rfc822Tokenizer}.
- *
- * @deprecated In the future make sure we don't quietly alter the user's
- *             text in ways they did not intend.  Meanwhile, hide this
- *             class from the public API because it does not even have
- *             a full understanding of the syntax it claims to correct.
- * @hide
- */
-public class Rfc822Validator implements AutoCompleteTextView.Validator {
-    /*
-     * Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we
-     * want to make sure we will keep accepting email addresses with TLD's
-     * that don't exist at the time of this writing, so this regexp relaxes
-     * that constraint by accepting any kind of top level domain, not just
-     * ".com", ".fr", etc...
-     */
-    private static final Pattern EMAIL_ADDRESS_PATTERN =
-            Pattern.compile("[^\\s@]+@[^\\s@]+\\.[a-zA-z][a-zA-Z][a-zA-Z]*");
-
-    private String mDomain;
-
-    /**
-     * Constructs a new validator that uses the specified domain name as
-     * the default when none is specified.
-     */
-    public Rfc822Validator(String domain) {
-        mDomain = domain;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isValid(CharSequence text) {
-        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
-
-        return tokens.length == 1 &&
-               EMAIL_ADDRESS_PATTERN.
-                   matcher(tokens[0].getAddress()).matches();
-    }
-
-    /**
-     * @return a string in which all the characters that are illegal for the username
-     * or the domain name part of the email address have been removed.
-     */
-    private String removeIllegalCharacters(String s) {
-        StringBuilder result = new StringBuilder();
-        int length = s.length();
-        for (int i = 0; i < length; i++) {
-            char c = s.charAt(i);
-
-            /*
-             * An RFC822 atom can contain any ASCII printing character
-             * except for periods and any of the following punctuation.
-             * A local-part can contain multiple atoms, concatenated by
-             * periods, so do allow periods here.
-             */
-
-            if (c <= ' ' || c > '~') {
-                continue;
-            }
-
-            if (c == '(' || c == ')' || c == '<' || c == '>' ||
-                c == '@' || c == ',' || c == ';' || c == ':' ||
-                c == '\\' || c == '"' || c == '[' || c == ']') {
-                continue;
-            }
-
-            result.append(c);
-        }
-        return result.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public CharSequence fixText(CharSequence cs) {
-        // Return an empty string if the email address only contains spaces, \n or \t
-        if (TextUtils.getTrimmedLength(cs) == 0) return "";
-
-        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
-        StringBuilder sb = new StringBuilder();
-
-        for (int i = 0; i < tokens.length; i++) {
-            String text = tokens[i].getAddress();
-            int index = text.indexOf('@');
-            if (index < 0) {
-                // If there is no @, just append the domain of the account
-                tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
-            } else {
-                // Otherwise, remove the illegal characters on both sides of the '@'
-                String fix = removeIllegalCharacters(text.substring(0, index));
-                String domain = removeIllegalCharacters(text.substring(index + 1));
-                tokens[i].setAddress(fix + "@" + (domain.length() != 0 ? domain : mDomain));
-            }
-
-            sb.append(tokens[i].toString());
-            if (i + 1 < tokens.length) {
-                sb.append(", ");
-            }
-        }
-
-        return sb;
-    }
-}
diff --git a/common/java/com/android/common/Search.java b/common/java/com/android/common/Search.java
deleted file mode 100644
index 55fa6f5..0000000
--- a/common/java/com/android/common/Search.java
+++ /dev/null
@@ -1,38 +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.common;
-
-/**
- * Utilities for search implementations.
- *
- * @see android.app.SearchManager
- */
-public class Search {
-
-    /**
-     * Key for the source identifier set by the application that launched a search intent.
-     * The identifier is search-source specific string. It can be used
-     * by the search provider to keep statistics of where searches are started from.
-     *
-     * The source identifier is stored in the {@link android.app.SearchManager#APP_DATA}
-     * Bundle in {@link android.content.Intent#ACTION_SEARCH} and
-     * {@link android.content.Intent#ACTION_WEB_SEARCH} intents.
-     */
-    public final static String SOURCE = "source";
-
-    private Search() { }   // don't instantiate
-}
diff --git a/common/java/com/android/common/speech/LoggingEvents.java b/common/java/com/android/common/speech/LoggingEvents.java
deleted file mode 100644
index 1f3c6ef..0000000
--- a/common/java/com/android/common/speech/LoggingEvents.java
+++ /dev/null
@@ -1,136 +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.common.speech;
-
-/**
- * Logging event constants used for Voice Search and VoiceIME. These are the
- * keys and values of extras to be specified in logging broadcast intents.
- * This class is used by clients of the android.speech APIs to log how the
- * user interacts with the IME settings and speech recognition result.
- */
-public class LoggingEvents {
-    // The name of the broadcast intent for logging.
-    public static final String ACTION_LOG_EVENT = "com.android.common.speech.LOG_EVENT";
-
-    // The extra key used for the name of the app being logged.
-    public static final String EXTRA_APP_NAME = "app_name";
-
-    // The extra key used for the name of the app issuing the VoiceSearch
-    // or VoiceIME request
-    public static final String EXTRA_CALLING_APP_NAME = "";
-
-    // The extra key used for the event value. The possible event values depend
-    // on the app being logged for, and are defined in the subclasses below.
-    public static final String EXTRA_EVENT = "extra_event";
-
-    // The extra key used to log the time in milliseconds at which the EXTRA_EVENT
-    // occurred in the client.
-    public static final String EXTRA_TIMESTAMP = "timestamp";
-
-    // The extra key used (with a boolean value of 'true') as a way to trigger a
-    // flush of the log events to the server.
-    public static final String EXTRA_FLUSH = "flush";
-
-    /**
-     * Logging event constants for voice search. Below are the extra values for
-     * {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
-     * extras for some events that need to be included as additional fields in
-     * the event. Note that this is not representative of *all* voice search
-     * events - only the ones that need to be reported from outside the voice
-     * search app, such as from Browser.
-     */
-    public class VoiceSearch {
-        // The app name to be used for logging VoiceSearch events.
-        public static final String APP_NAME = "googlemobile";
-
-        public static final int RETRY = 0;
-
-        public static final int N_BEST_REVEAL = 1;
-
-        public static final int N_BEST_CHOOSE = 2;
-        public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index";  // value should be int
-
-        public static final int QUERY_UPDATED = 3;
-        public static final String EXTRA_QUERY_UPDATED_VALUE = "value";  // value should be String
-    }
-
-    /**
-     * Logging event constants for VoiceIME. Below are the extra values for
-     * {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
-     * extras for some events that need to be included as additional fields in
-     * the event.
-     */
-    public class VoiceIme {
-        // The app name to be used for logging VoiceIME events.
-        public static final String APP_NAME = "voiceime";
-
-        public static final int KEYBOARD_WARNING_DIALOG_SHOWN = 0;
-
-        public static final int KEYBOARD_WARNING_DIALOG_DISMISSED = 1;
-
-        public static final int KEYBOARD_WARNING_DIALOG_OK = 2;
-
-        public static final int KEYBOARD_WARNING_DIALOG_CANCEL = 3;
-
-        public static final int SETTINGS_WARNING_DIALOG_SHOWN = 4;
-
-        public static final int SETTINGS_WARNING_DIALOG_DISMISSED = 5;
-
-        public static final int SETTINGS_WARNING_DIALOG_OK = 6;
-
-        public static final int SETTINGS_WARNING_DIALOG_CANCEL = 7;
-
-        public static final int SWIPE_HINT_DISPLAYED = 8;
-
-        public static final int PUNCTUATION_HINT_DISPLAYED = 9;
-
-        public static final int CANCEL_DURING_LISTENING = 10;
-
-        public static final int CANCEL_DURING_WORKING = 11;
-
-        public static final int CANCEL_DURING_ERROR = 12;
-
-        public static final int ERROR = 13;
-        public static final String EXTRA_ERROR_CODE = "code";  // value should be int
-
-        public static final int START = 14;
-        public static final String EXTRA_START_LOCALE = "locale";  // value should be String
-        public static final String EXTRA_START_SWIPE = "swipe";  // value should be boolean
-
-        public static final int VOICE_INPUT_DELIVERED = 15;
-
-        public static final int N_BEST_CHOOSE = 16;
-        public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index";  // value should be int
-
-        public static final int TEXT_MODIFIED = 17;
-        public static final String EXTRA_TEXT_MODIFIED_LENGTH = "length";  // value should be int
-        public static final String EXTRA_TEXT_MODIFIED_TYPE = "type";  // value should be int below
-        public static final int TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION = 1;
-        public static final int TEXT_MODIFIED_TYPE_TYPING_DELETION = 2;
-        public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION = 3;
-        public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION_PUNCTUATION = 4;
-
-        public static final int INPUT_ENDED = 18;
-
-        public static final int VOICE_INPUT_SETTING_ENABLED = 19;
-
-        public static final int VOICE_INPUT_SETTING_DISABLED = 20;
-
-        public static final int IME_TEXT_ACCEPTED = 21;
-    }
-
-}
diff --git a/common/java/com/android/common/speech/Recognition.java b/common/java/com/android/common/speech/Recognition.java
deleted file mode 100644
index 1970179..0000000
--- a/common/java/com/android/common/speech/Recognition.java
+++ /dev/null
@@ -1,61 +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.common.speech;
-
-/**
- * Utilities for voice recognition implementations.
- *
- * @see android.speech.RecognitionService
- * @see android.speech.RecognizerIntent
- */
-public class Recognition {
-    
-    /**
-     * The key to the extra in the Bundle returned by
-     * android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS
-     * which is an ArrayList of CharSequences which are hints that can be shown to
-     * the user for voice actions currently supported by voice search for the user's current
-     * language preference for voice search (i.e., the one defined in the extra
-     * android.speech.RecognizerIntent#EXTRA_LANGUAGE_PREFERENCE).
-     *
-     * If this is paired with EXTRA_HINT_CONTEXT, should return a set of hints that are
-     * appropriate for the provided context.
-     *
-     * The CharSequences are SpannedStrings and will contain segments wrapped in
-     * <annotation action="true"></annotation>. This is to indicate the section of the text
-     * which represents the voice action, to be highlighted in the UI if so desired.
-     */
-    public static final String EXTRA_HINT_STRINGS = "android.speech.extra.HINT_STRINGS";
-    
-    /**
-     * The key to an extra to be included in the request intent for
-     * android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS.
-     * Should be an int of one of the values defined below. If an
-     * unknown int value is provided, it should be ignored.
-     */
-    public static final String EXTRA_HINT_CONTEXT = "android.speech.extra.HINT_CONTEXT";
-    
-    /**
-     * A set of values for EXTRA_HINT_CONTEXT.
-     */
-    public static final int HINT_CONTEXT_UNKNOWN = 0;
-    public static final int HINT_CONTEXT_VOICE_SEARCH_HELP = 1;
-    public static final int HINT_CONTEXT_CAR_HOME = 2;
-    public static final int HINT_CONTEXT_LAUNCHER = 3;
-
-    private Recognition() { }   // don't instantiate
-}
diff --git a/common/java/com/android/common/userhappiness/UserHappinessSignals.java b/common/java/com/android/common/userhappiness/UserHappinessSignals.java
deleted file mode 100644
index 347bdaa..0000000
--- a/common/java/com/android/common/userhappiness/UserHappinessSignals.java
+++ /dev/null
@@ -1,45 +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.common.userhappiness;
-
-import android.content.Intent;
-import android.content.Context;
-import com.android.common.speech.LoggingEvents;
-
-/**
- * Metrics for User Happiness are recorded here. Each app can define when to
- * call these User Happiness metrics.
- */
-public class UserHappinessSignals {
-
-    /**
-     *  Log when a user "accepted" IME text. Each application can define what
-     *  it means to "accept" text. In the case of Gmail, pressing the "Send"
-     *  button indicates text acceptance. We broadcast this information to
-     *  VoiceSearch LoggingEvents and use it to aggregate VoiceIME Happiness Metrics
-     */
-    public static void userAcceptedImeText(Context context) {
-        // Create a Voice IME Logging intent.
-        Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
-        i.putExtra(LoggingEvents.EXTRA_APP_NAME, LoggingEvents.VoiceIme.APP_NAME);
-        i.putExtra(LoggingEvents.EXTRA_EVENT, LoggingEvents.VoiceIme.IME_TEXT_ACCEPTED);
-        i.putExtra(LoggingEvents.EXTRA_CALLING_APP_NAME, context.getPackageName());
-        i.putExtra(LoggingEvents.EXTRA_TIMESTAMP, System.currentTimeMillis());
-        context.sendBroadcast(i);
-    }
-
-}
diff --git a/common/tests/Android.mk b/common/tests/Android.mk
deleted file mode 100644
index 7425552..0000000
--- a/common/tests/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2009 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := AndroidCommonTests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-common
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/common/tests/AndroidManifest.xml b/common/tests/AndroidManifest.xml
deleted file mode 100644
index 151ec20..0000000
--- a/common/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.common.tests"
-        android:sharedUserId="com.android.uid.test">
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <!-- Run tests with "runtest common" -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="com.android.common.tests"
-            android:label="Android Common Library Tests" />
-
-</manifest>
diff --git a/common/tests/src/com/android/common/OperationSchedulerTest.java b/common/tests/src/com/android/common/OperationSchedulerTest.java
deleted file mode 100644
index 955508f..0000000
--- a/common/tests/src/com/android/common/OperationSchedulerTest.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2009 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.common;
-
-import android.content.SharedPreferences;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-public class OperationSchedulerTest extends AndroidTestCase {
-    /**
-     * OperationScheduler subclass which uses an artificial time.
-     * Set {@link #timeMillis} to whatever value you like.
-     */
-    private class TimeTravelScheduler extends OperationScheduler {
-        static final long DEFAULT_TIME = 1250146800000L;  // 13-Aug-2009, 12:00:00 am
-        public long timeMillis = DEFAULT_TIME;
-
-        @Override
-        protected long currentTimeMillis() { return timeMillis; }
-        public TimeTravelScheduler() { super(getFreshStorage()); }
-    }
-
-    private SharedPreferences getFreshStorage() {
-        SharedPreferences sp = getContext().getSharedPreferences("OperationSchedulerTest", 0);
-        sp.edit().clear().commit();
-        return sp;
-    }
-
-    @MediumTest
-    public void testScheduler() throws Exception {
-        TimeTravelScheduler scheduler = new TimeTravelScheduler();
-        OperationScheduler.Options options = new OperationScheduler.Options();
-        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
-        assertEquals(0, scheduler.getLastSuccessTimeMillis());
-        assertEquals(0, scheduler.getLastAttemptTimeMillis());
-
-        long beforeTrigger = scheduler.timeMillis;
-        scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
-        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
-
-        // It will schedule for the later of the trigger and the moratorium...
-        scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
-        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
-        scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
-        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
-
-        // Test enable/disable toggle
-        scheduler.setEnabledState(false);
-        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
-        scheduler.setEnabledState(true);
-        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
-
-        // Backoff interval after an error
-        long beforeError = (scheduler.timeMillis += 100);
-        scheduler.onTransientError();
-        assertEquals(0, scheduler.getLastSuccessTimeMillis());
-        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
-        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
-        options.backoffFixedMillis = 1000000;
-        options.backoffIncrementalMillis = 500000;
-        assertEquals(beforeError + 1500000, scheduler.getNextTimeMillis(options));
-
-        // Two errors: backoff interval increases
-        beforeError = (scheduler.timeMillis += 100);
-        scheduler.onTransientError();
-        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
-        assertEquals(beforeError + 2000000, scheduler.getNextTimeMillis(options));
-
-        // Reset transient error: no backoff interval
-        scheduler.resetTransientError();
-        assertEquals(0, scheduler.getLastSuccessTimeMillis());
-        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
-        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
-
-        // Permanent error holds true even if transient errors are reset
-        // However, we remember that the transient error was reset...
-        scheduler.onPermanentError();
-        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
-        scheduler.resetTransientError();
-        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
-        scheduler.resetPermanentError();
-        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
-
-        // Success resets the trigger
-        long beforeSuccess = (scheduler.timeMillis += 100);
-        scheduler.onSuccess();
-        assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
-        assertEquals(beforeSuccess, scheduler.getLastSuccessTimeMillis());
-        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
-
-        // The moratorium is not reset by success!
-        scheduler.setTriggerTimeMillis(0);
-        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
-        scheduler.setMoratoriumTimeMillis(0);
-        assertEquals(beforeSuccess, scheduler.getNextTimeMillis(options));
-
-        // Periodic interval after success
-        options.periodicIntervalMillis = 250000;
-        scheduler.setTriggerTimeMillis(Long.MAX_VALUE);
-        assertEquals(beforeSuccess + 250000, scheduler.getNextTimeMillis(options));
-
-        // Trigger minimum is also since the last success
-        options.minTriggerMillis = 1000000;
-        assertEquals(beforeSuccess + 1000000, scheduler.getNextTimeMillis(options));
-    }
-
-    @SmallTest
-    public void testParseOptions() throws Exception {
-         OperationScheduler.Options options = new OperationScheduler.Options();
-         assertEquals(
-                 "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
-                 OperationScheduler.parseOptions("3600", options).toString());
-
-         assertEquals(
-                 "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
-                 OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
-
-         assertEquals(
-                 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
-                 OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
-                         options).toString());
-
-         assertEquals(
-                "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
-                 OperationScheduler.parseOptions("", options).toString());
-    }
-
-    @SmallTest
-    public void testMoratoriumWithHttpDate() throws Exception {
-        TimeTravelScheduler scheduler = new TimeTravelScheduler();
-        OperationScheduler.Options options = new OperationScheduler.Options();
-
-        long beforeTrigger = scheduler.timeMillis;
-        scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
-        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
-
-        scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000);
-        assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options));
-
-        long beforeMoratorium = scheduler.timeMillis;
-        assertTrue(scheduler.setMoratoriumTimeHttp("3000"));
-        long afterMoratorium = scheduler.timeMillis;
-        assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options));
-        assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options));
-
-        options.maxMoratoriumMillis = Long.MAX_VALUE / 2;
-        assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT"));
-        assertEquals(1924991999000L, scheduler.getNextTimeMillis(options));
-
-        assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date"));
-    }
-
-    @SmallTest
-    public void testClockRollbackScenario() throws Exception {
-        TimeTravelScheduler scheduler = new TimeTravelScheduler();
-        OperationScheduler.Options options = new OperationScheduler.Options();
-        options.minTriggerMillis = 2000;
-
-        // First, set up a scheduler with reasons to wait: a transient
-        // error with backoff and a moratorium for a few minutes.
-
-        long beforeTrigger = scheduler.timeMillis;
-        long triggerTime = beforeTrigger - 10000000;
-        scheduler.setTriggerTimeMillis(triggerTime);
-        assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
-        assertEquals(0, scheduler.getLastAttemptTimeMillis());
-
-        long beforeSuccess = (scheduler.timeMillis += 100);
-        scheduler.onSuccess();
-        scheduler.setTriggerTimeMillis(triggerTime);
-        assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
-        assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options));
-
-        long beforeError = (scheduler.timeMillis += 100);
-        scheduler.onTransientError();
-        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
-        assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options));
-
-        long beforeMoratorium = (scheduler.timeMillis += 100);
-        scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000);
-        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
-
-        // Now set the time back a few seconds.
-        // The moratorium time should still be honored.
-        long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000);
-        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
-
-        // The rollback also moved the last-attempt clock back to the rollback time.
-        assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis());
-
-        // But if we set the time back more than a day, the moratorium
-        // resets to the maximum moratorium (a day, by default), exposing
-        // the original trigger time.
-        beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000);
-        assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
-        assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
-
-        // If we roll forward until after the re-set moratorium, then it expires.
-        scheduler.timeMillis = triggerTime + 5000000;
-        assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
-        assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
-        assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis());
-    }
-}
diff --git a/common/tools/make-iana-tld-pattern.py b/common/tools/make-iana-tld-pattern.py
deleted file mode 100755
index de81c58..0000000
--- a/common/tools/make-iana-tld-pattern.py
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/env python
-
-from urllib2 import urlopen
-
-TLD_PREFIX = r"""
-    /**
-     *  Regular expression to match all IANA top-level domains.
-     *  List accurate as of 2010/02/05.  List taken from:
-     *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
-     */
-    public static final String TOP_LEVEL_DOMAIN_STR =
-"""
-TLD_SUFFIX = '";'
-
-URL_PREFIX = r"""
-    /**
-     *  Regular expression to match all IANA top-level domains for WEB_URL.
-     *  List accurate as of 2010/02/05.  List taken from:
-     *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-     *  This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
-     */
-    public static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL =
-        "(?:"
-"""
-
-URL_SUFFIX = ';'
-
-class Bucket:
-    def __init__(self, baseLetter):
-        self.base=baseLetter
-        self.words=[]
-        self.letters=[]
-
-    def dump(self, isWebUrl=False, isFirst=False, isLast=False):
-        if (len(self.words) == 0) and (len(self.letters) == 0):
-            return ''
-
-        self.words.sort()
-        self.letters.sort()
-
-        output = '        ';
-
-        if isFirst:
-            if isWebUrl:
-                output += '+ "'
-            else:
-                output += '"('
-        else:
-            output += '+ "|'
-
-        if len(self.words) != 0:
-            output += '('
-
-            if isWebUrl:
-                output += '?:'
-
-        firstWord = 1
-        for word in self.words:
-            if firstWord == 0:
-                output += '|'
-            firstWord = 0
-            for letter in word:
-                if letter == '-':
-                    output += '\\\\'  # escape the '-' character.
-                output += letter
-
-        if len(self.words) > 0 and len(self.letters) > 0:
-            output += '|'
-
-        if len(self.letters) == 1:
-            output += '%c%c' % (self.base, self.letters[0])
-        elif len(self.letters) > 0:
-            output += '%c[' % self.base
-
-            for letter in self.letters:
-                output += letter
-
-            output += ']'
-
-        if len(self.words) != 0:
-            output += ')'
-
-        if not isLast:
-            output += '"'
-            output += '\n'
-
-        return output;
-
-    def add(self, line):
-        length = len(line)
-
-        if line.startswith('#') or (length == 0):
-            return;
-
-        if length == 2:
-            self.letters.append(line[1:2])
-        else:
-            self.words.append(line)
-
-def getBucket(buckets, line):
-    letter = line[0]
-    bucket = buckets.get(letter)
-
-    if bucket is None:
-        bucket = Bucket(letter)
-        buckets[letter] = bucket
-
-    return bucket
-
-def makePattern(prefix, suffix, buckets, isWebUrl=False):
-    output = prefix
-
-    output += getBucket(buckets, 'a').dump(isFirst=True, isWebUrl=isWebUrl)
-
-    for letter in range(ord('b'), ord('z')):
-        output += getBucket(buckets, chr(letter)).dump(isWebUrl=isWebUrl)
-
-    output += getBucket(buckets, 'z').dump(isLast=True, isWebUrl=isWebUrl)
-
-    if isWebUrl:
-        output += '))"'
-    else:
-        output += ')'
-
-    output += suffix
-
-    print output
-
-if __name__ == "__main__":
-    f = urlopen('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')
-    domains = f.readlines()
-    f.close()
-
-    buckets = {}
-
-    for domain in domains:
-        domain = domain.lower()
-
-        if len(domain) > 0:
-            getBucket(buckets, domain[0]).add(domain.strip())
-
-    makePattern(TLD_PREFIX, TLD_SUFFIX, buckets, isWebUrl=False)
-    makePattern(URL_PREFIX, URL_SUFFIX, buckets, isWebUrl=True)
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 6a5c8a8..979d6e8 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -58,8 +58,6 @@
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
- *
- * TODO This enumeration should probably be split up or relabeled for clarity.
  */
 enum {
     /* These flags originate in RawEvents and are generally set in the key map. */
@@ -73,7 +71,9 @@
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
 
-    /* These flags are set by the input dispatch policy as it intercepts each event. */
+    POLICY_FLAG_RAW_MASK = 0x0000ffff,
+
+    /* These flags are set by the input reader policy as it intercepts each event. */
 
     // Indicates that the screen was off when the event was received and the event
     // should wake the device.
@@ -85,6 +85,37 @@
 };
 
 /*
+ * Describes the basic configuration of input devices that are present.
+ */
+struct InputConfiguration {
+    enum {
+        TOUCHSCREEN_UNDEFINED = 0,
+        TOUCHSCREEN_NOTOUCH = 1,
+        TOUCHSCREEN_STYLUS = 2,
+        TOUCHSCREEN_FINGER = 3
+    };
+
+    enum {
+        KEYBOARD_UNDEFINED = 0,
+        KEYBOARD_NOKEYS = 1,
+        KEYBOARD_QWERTY = 2,
+        KEYBOARD_12KEY = 3
+    };
+
+    enum {
+        NAVIGATION_UNDEFINED = 0,
+        NAVIGATION_NONAV = 1,
+        NAVIGATION_DPAD = 2,
+        NAVIGATION_TRACKBALL = 3,
+        NAVIGATION_WHEEL = 4
+    };
+
+    int32_t touchScreen;
+    int32_t keyboard;
+    int32_t navigation;
+};
+
+/*
  * Pointer coordinate data.
  */
 struct PointerCoords {
@@ -117,6 +148,9 @@
     int32_t mNature;
 };
 
+/*
+ * Key events.
+ */
 class KeyEvent : public InputEvent {
 public:
     virtual ~KeyEvent() { }
@@ -162,6 +196,9 @@
     nsecs_t mEventTime;
 };
 
+/*
+ * Motion events.
+ */
 class MotionEvent : public InputEvent {
 public:
     virtual ~MotionEvent() { }
@@ -174,6 +211,10 @@
 
     inline int32_t getMetaState() const { return mMetaState; }
 
+    inline float getXOffset() const { return mXOffset; }
+
+    inline float getYOffset() const { return mYOffset; }
+
     inline float getXPrecision() const { return mXPrecision; }
 
     inline float getYPrecision() const { return mYPrecision; }
@@ -186,18 +227,22 @@
 
     inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
 
-    inline float getRawX() const { return mRawX; }
-
-    inline float getRawY() const { return mRawY; }
-
-    inline float getX(size_t pointerIndex) const {
+    inline float getRawX(size_t pointerIndex) const {
         return getCurrentPointerCoords(pointerIndex).x;
     }
 
-    inline float getY(size_t pointerIndex) const {
+    inline float getRawY(size_t pointerIndex) const {
         return getCurrentPointerCoords(pointerIndex).y;
     }
 
+    inline float getX(size_t pointerIndex) const {
+        return getRawX(pointerIndex) + mXOffset;
+    }
+
+    inline float getY(size_t pointerIndex) const {
+        return getRawY(pointerIndex) + mYOffset;
+    }
+
     inline float getPressure(size_t pointerIndex) const {
         return getCurrentPointerCoords(pointerIndex).pressure;
     }
@@ -212,14 +257,22 @@
         return mSampleEventTimes[historicalIndex];
     }
 
-    inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
+    inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
         return getHistoricalPointerCoords(pointerIndex, historicalIndex).x;
     }
 
-    inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
+    inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
         return getHistoricalPointerCoords(pointerIndex, historicalIndex).y;
     }
 
+    inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
+        return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset;
+    }
+
+    inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
+        return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset;
+    }
+
     inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
         return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure;
     }
@@ -234,8 +287,8 @@
             int32_t action,
             int32_t edgeFlags,
             int32_t metaState,
-            float rawX,
-            float rawY,
+            float xOffset,
+            float yOffset,
             float xPrecision,
             float yPrecision,
             nsecs_t downTime,
@@ -250,12 +303,19 @@
 
     void offsetLocation(float xOffset, float yOffset);
 
+    // Low-level accessors.
+    inline const int32_t* getPointerIds() const { return mPointerIds.array(); }
+    inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
+    inline const PointerCoords* getSamplePointerCoords() const {
+            return mSamplePointerCoords.array();
+    }
+
 private:
     int32_t mAction;
     int32_t mEdgeFlags;
     int32_t mMetaState;
-    float mRawX;
-    float mRawY;
+    float mXOffset;
+    float mYOffset;
     float mXPrecision;
     float mYPrecision;
     nsecs_t mDownTime;
diff --git a/include/ui/InputDispatchPolicy.h b/include/ui/InputDispatchPolicy.h
deleted file mode 100644
index 3546813..0000000
--- a/include/ui/InputDispatchPolicy.h
+++ /dev/null
@@ -1,198 +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.
- */
-
-#ifndef _UI_INPUT_DISPATCH_POLICY_H
-#define _UI_INPUT_DISPATCH_POLICY_H
-
-/**
- * Native input dispatch policy.
- */
-
-#include <ui/Input.h>
-#include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-
-namespace android {
-
-class InputChannel;
-
-/*
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- */
-struct InputTarget {
-    enum {
-        /* This flag indicates that subsequent event delivery should be held until the
-         * current event is delivered to this target or a timeout occurs. */
-        FLAG_SYNC = 0x01,
-
-        /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
-         * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */
-        FLAG_OUTSIDE = 0x02,
-
-        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
-         * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
-         * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */
-        FLAG_CANCEL = 0x04
-    };
-
-    // The input channel to be targeted.
-    sp<InputChannel> inputChannel;
-
-    // Flags for the input target.
-    int32_t flags;
-
-    // The timeout for event delivery to this target in nanoseconds.  Or -1 if none.
-    nsecs_t timeout;
-
-    // The x and y offset to add to a MotionEvent as it is delivered.
-    // (ignored for KeyEvents)
-    float xOffset, yOffset;
-};
-
-/*
- * Input dispatch policy interface.
- *
- * The input dispatch policy is used by the input dispatcher to interact with the
- * Window Manager and other system components.  This separation of concerns keeps
- * the input dispatcher relatively free of special case logic such as is required
- * to determine the target of iput events, when to wake the device, how to interact
- * with key guard, and when to transition to the home screen.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI.  This class is also mocked in the input dispatcher unit tests since
- * it is an ideal test seam.
- */
-class InputDispatchPolicyInterface : public virtual RefBase {
-protected:
-    InputDispatchPolicyInterface() { }
-    virtual ~InputDispatchPolicyInterface() { }
-
-public:
-    enum {
-        ROTATION_0 = 0,
-        ROTATION_90 = 1,
-        ROTATION_180 = 2,
-        ROTATION_270 = 3
-    };
-
-    enum {
-        // The input dispatcher should do nothing and discard the input unless other
-        // flags are set.
-        ACTION_NONE = 0,
-
-        // The input dispatcher should dispatch the input to the application.
-        ACTION_DISPATCH = 0x00000001,
-
-        // The input dispatcher should perform special filtering in preparation for
-        // a pending app switch.
-        ACTION_APP_SWITCH_COMING = 0x00000002,
-
-        // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it
-        // passes through the dispatch pipeline.
-        ACTION_WOKE_HERE = 0x00000004,
-
-        // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
-        // passes through the dispatch pipeline.
-        ACTION_BRIGHT_HERE = 0x00000008
-    };
-
-    enum {
-        TOUCHSCREEN_UNDEFINED = 0,
-        TOUCHSCREEN_NOTOUCH = 1,
-        TOUCHSCREEN_STYLUS = 2,
-        TOUCHSCREEN_FINGER = 3
-    };
-
-    enum {
-        KEYBOARD_UNDEFINED = 0,
-        KEYBOARD_NOKEYS = 1,
-        KEYBOARD_QWERTY = 2,
-        KEYBOARD_12KEY = 3
-    };
-
-    enum {
-        NAVIGATION_UNDEFINED = 0,
-        NAVIGATION_NONAV = 1,
-        NAVIGATION_DPAD = 2,
-        NAVIGATION_TRACKBALL = 3,
-        NAVIGATION_WHEEL = 4
-    };
-
-    struct VirtualKeyDefinition {
-        int32_t scanCode;
-
-        // configured position data, specified in display coords
-        int32_t centerX;
-        int32_t centerY;
-        int32_t width;
-        int32_t height;
-    };
-
-    /* Gets information about the display with the specified id.
-     * Returns true if the display info is available, false otherwise.
-     */
-    virtual bool getDisplayInfo(int32_t displayId,
-            int32_t* width, int32_t* height, int32_t* orientation) = 0;
-
-    virtual void notifyConfigurationChanged(nsecs_t when,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0;
-
-    virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen) = 0;
-
-    virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
-            int32_t action, int32_t flags, int32_t keyCode,
-            int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
-
-    virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
-            bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0;
-
-    virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
-            bool rolled) = 0;
-
-    virtual int32_t interceptTouch(nsecs_t when) = 0;
-
-    virtual bool allowKeyRepeat() = 0;
-    virtual nsecs_t getKeyRepeatTimeout() = 0;
-
-    virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets) = 0;
-    virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets) = 0;
-
-    /* Determine whether to turn on some hacks we have to improve the touch interaction with a
-     * certain device whose screen currently is not all that good.
-     */
-    virtual bool filterTouchEvents() = 0;
-
-    /* Determine whether to turn on some hacks to improve touch interaction with another device
-     * where touch coordinate data can get corrupted.
-     */
-    virtual bool filterJumpyTouchEvents() = 0;
-
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DISPATCH_POLICY_H
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index bde07f2..511ad20 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -18,7 +18,6 @@
 #define _UI_INPUT_DISPATCHER_H
 
 #include <ui/Input.h>
-#include <ui/InputDispatchPolicy.h>
 #include <ui/InputTransport.h>
 #include <utils/KeyedVector.h>
 #include <utils/Vector.h>
@@ -35,6 +34,118 @@
 
 namespace android {
 
+/*
+ * Constants used to report the outcome of input event injection.
+ */
+enum {
+    /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+    INPUT_EVENT_INJECTION_PENDING = -1,
+
+    /* Injection succeeded. */
+    INPUT_EVENT_INJECTION_SUCCEEDED = 0,
+
+    /* Injection failed because the injector did not have permission to inject
+     * into the application with input focus. */
+    INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
+
+    /* Injection failed because there were no available input targets. */
+    INPUT_EVENT_INJECTION_FAILED = 2,
+
+    /* Injection failed due to a timeout. */
+    INPUT_EVENT_INJECTION_TIMED_OUT = 3
+};
+
+
+/*
+ * An input target specifies how an input event is to be dispatched to a particular window
+ * including the window's input channel, control flags, a timeout, and an X / Y offset to
+ * be added to input event coordinates to compensate for the absolute position of the
+ * window area.
+ */
+struct InputTarget {
+    enum {
+        /* This flag indicates that subsequent event delivery should be held until the
+         * current event is delivered to this target or a timeout occurs. */
+        FLAG_SYNC = 0x01,
+
+        /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
+         * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */
+        FLAG_OUTSIDE = 0x02,
+
+        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
+         * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
+         * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */
+        FLAG_CANCEL = 0x04
+    };
+
+    // The input channel to be targeted.
+    sp<InputChannel> inputChannel;
+
+    // Flags for the input target.
+    int32_t flags;
+
+    // The timeout for event delivery to this target in nanoseconds.  Or -1 if none.
+    nsecs_t timeout;
+
+    // The x and y offset to add to a MotionEvent as it is delivered.
+    // (ignored for KeyEvents)
+    float xOffset, yOffset;
+};
+
+
+/*
+ * Input dispatcher policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class InputDispatcherPolicyInterface : public virtual RefBase {
+protected:
+    InputDispatcherPolicyInterface() { }
+    virtual ~InputDispatcherPolicyInterface() { }
+
+public:
+    /* Notifies the system that a configuration change has occurred. */
+    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+
+    /* Notifies the system that an input channel is unrecoverably broken. */
+    virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
+
+    /* Notifies the system that an input channel is not responding.
+     * Returns true and a new timeout value if the dispatcher should keep waiting.
+     * Otherwise returns false. */
+    virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+            nsecs_t& outNewTimeout) = 0;
+
+    /* Notifies the system that an input channel recovered from ANR. */
+    virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
+
+    /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
+    virtual nsecs_t getKeyRepeatTimeout() = 0;
+
+    /* Gets the input targets for a key event.
+     * If the event is being injected, injectorPid and injectorUid should specify the
+     * process id and used id of the injecting application, otherwise they should both
+     * be -1.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
+    virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid,
+            Vector<InputTarget>& outTargets) = 0;
+
+    /* Gets the input targets for a motion event.
+     * If the event is being injected, injectorPid and injectorUid should specify the
+     * process id and used id of the injecting application, otherwise they should both
+     * be -1.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
+    virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid,
+            Vector<InputTarget>& outTargets) = 0;
+};
+
+
 /* Notifies the system about input events generated by the input reader.
  * The dispatcher is expected to be mostly asynchronous. */
 class InputDispatcherInterface : public virtual RefBase {
@@ -51,14 +162,10 @@
     virtual void dispatchOnce() = 0;
 
     /* Notifies the dispatcher about new events.
-     * The dispatcher will process most of these events asynchronously although some
-     * policy processing may occur synchronously.
      *
      * These methods should only be called on the input reader thread.
      */
-    virtual void notifyConfigurationChanged(nsecs_t eventTime,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0;
-    virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) = 0;
+    virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
     virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
@@ -68,6 +175,17 @@
             uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime) = 0;
 
+    /* Injects an input event and optionally waits for sync.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+
     /* Registers or unregister input channels that may be used as targets for input events.
      *
      * These methods may be called on any thread (usually by the input manager).
@@ -76,19 +194,33 @@
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 };
 
-/* Dispatches events. */
+/* Dispatches events to input targets.  Some functions of the input dispatcher, such as
+ * identifying input targets, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ *     Because the policy can potentially block or cause re-entrance into the input dispatcher,
+ *     the input dispatcher never calls into the policy while holding its internal locks.
+ *     The implementation is also carefully designed to recover from scenarios such as an
+ *     input channel becoming unregistered while identifying input targets or processing timeouts.
+ *
+ *     Methods marked 'Locked' must be called with the lock acquired.
+ *
+ *     Methods marked 'LockedInterruptible' must be called with the lock acquired but
+ *     may during the course of their execution release the lock, call into the policy, and
+ *     then reacquire the lock.  The caller is responsible for recovering gracefully.
+ *
+ *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
+ */
 class InputDispatcher : public InputDispatcherInterface {
 protected:
     virtual ~InputDispatcher();
 
 public:
-    explicit InputDispatcher(const sp<InputDispatchPolicyInterface>& policy);
+    explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
 
     virtual void dispatchOnce();
 
-    virtual void notifyConfigurationChanged(nsecs_t eventTime,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig);
-    virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen);
+    virtual void notifyConfigurationChanged(nsecs_t eventTime);
     virtual void notifyAppSwitchComing(nsecs_t eventTime);
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
@@ -98,6 +230,9 @@
             uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime);
 
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
@@ -119,12 +254,17 @@
         int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
+
+        int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+        int32_t injectorPid;     // -1 if not injected
+        int32_t injectorUid;     // -1 if not injected
+
+        bool dispatchInProgress; // initially false, set to true while dispatching
+
+        inline bool isInjected() { return injectorPid >= 0; }
     };
 
     struct ConfigurationChangedEntry : EventEntry {
-        int32_t touchScreenConfig;
-        int32_t keyboardConfig;
-        int32_t navigationConfig;
     };
 
     struct KeyEntry : EventEntry {
@@ -165,6 +305,7 @@
         MotionSample* lastSample;
     };
 
+    // Tracks the progress of dispatching a particular event to a particular connection.
     struct DispatchEntry : Link<DispatchEntry> {
         EventEntry* eventEntry; // the event to dispatch
         int32_t targetFlags;
@@ -189,6 +330,37 @@
         MotionSample* tailMotionSample;
     };
 
+    // A command entry captures state and behavior for an action to be performed in the
+    // dispatch loop after the initial processing has taken place.  It is essentially
+    // a kind of continuation used to postpone sensitive policy interactions to a point
+    // in the dispatch loop where it is safe to release the lock (generally after finishing
+    // the critical parts of the dispatch cycle).
+    //
+    // The special thing about commands is that they can voluntarily release and reacquire
+    // the dispatcher lock at will.  Initially when the command starts running, the
+    // dispatcher lock is held.  However, if the command needs to call into the policy to
+    // do some work, it can release the lock, do the work, then reacquire the lock again
+    // before returning.
+    //
+    // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+    // never calls into the policy while holding its lock.
+    //
+    // Commands are implicitly 'LockedInterruptible'.
+    struct CommandEntry;
+    typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
+
+    class Connection;
+    struct CommandEntry : Link<CommandEntry> {
+        CommandEntry();
+        ~CommandEntry();
+
+        Command command;
+
+        // parameters for the command (usage varies by command)
+        sp<Connection> connection;
+    };
+
+    // Generic queue implementation.
     template <typename T>
     struct Queue {
         T head;
@@ -238,21 +410,28 @@
     public:
         Allocator();
 
-        ConfigurationChangedEntry* obtainConfigurationChangedEntry();
-        KeyEntry* obtainKeyEntry();
-        MotionEntry* obtainMotionEntry();
+        ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
+        KeyEntry* obtainKeyEntry(nsecs_t eventTime,
+                int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+                int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+                int32_t repeatCount, nsecs_t downTime);
+        MotionEntry* obtainMotionEntry(nsecs_t eventTime,
+                int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+                int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+                nsecs_t downTime, uint32_t pointerCount,
+                const int32_t* pointerIds, const PointerCoords* pointerCoords);
         DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
+        CommandEntry* obtainCommandEntry(Command command);
 
         void releaseEventEntry(EventEntry* entry);
         void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
         void releaseKeyEntry(KeyEntry* entry);
         void releaseMotionEntry(MotionEntry* entry);
         void releaseDispatchEntry(DispatchEntry* entry);
+        void releaseCommandEntry(CommandEntry* entry);
 
         void appendMotionSample(MotionEntry* motionEntry,
-                nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords);
-        void freeMotionSample(MotionSample* sample);
-        void freeMotionSampleList(MotionSample* head);
+                nsecs_t eventTime, const PointerCoords* pointerCoords);
 
     private:
         Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
@@ -260,6 +439,9 @@
         Pool<MotionEntry> mMotionEntryPool;
         Pool<MotionSample> mMotionSamplePool;
         Pool<DispatchEntry> mDispatchEntryPool;
+        Pool<CommandEntry> mCommandEntryPool;
+
+        void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
     };
 
     /* Manages the dispatch state associated with a single input channel. */
@@ -291,7 +473,9 @@
 
         explicit Connection(const sp<InputChannel>& inputChannel);
 
-        inline const char* getInputChannelName() { return inputChannel->getName().string(); }
+        inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
+
+        const char* getStatusLabel() const;
 
         // Finds a DispatchEntry in the outbound queue associated with the specified event.
         // Returns NULL if not found.
@@ -321,24 +505,36 @@
         }
 
         status_t initialize();
+
+        void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
     };
 
-    sp<InputDispatchPolicyInterface> mPolicy;
+    sp<InputDispatcherPolicyInterface> mPolicy;
 
     Mutex mLock;
 
-    Queue<EventEntry> mInboundQueue;
     Allocator mAllocator;
-
     sp<PollLoop> mPollLoop;
 
+    Queue<EventEntry> mInboundQueue;
+    Queue<CommandEntry> mCommandQueue;
+
     // All registered connections mapped by receive pipe file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
 
     // Active connections are connections that have a non-empty outbound queue.
+    // We don't use a ref-counted pointer here because we explicitly abort connections
+    // during unregistration which causes the connection's outbound queue to be cleared
+    // and the connection itself to be deactivated.
     Vector<Connection*> mActiveConnections;
 
-    // Pool of key and motion event objects used only to ask the input dispatch policy
+    // List of connections that have timed out.  Only used by dispatchOnce()
+    // We don't use a ref-counted pointer here because it is not possible for a connection
+    // to be unregistered while processing timed out connections since we hold the lock for
+    // the duration.
+    Vector<Connection*> mTimedOutConnections;
+
+    // Preallocated key and motion event objects used only to ask the input dispatcher policy
     // for the targets of an event that is to be dispatched.
     KeyEvent mReusableKeyEvent;
     MotionEvent mReusableMotionEvent;
@@ -347,6 +543,14 @@
     // If there is a synchronous event dispatch in progress, the current input targets will
     // remain unchanged until the dispatch has completed or been aborted.
     Vector<InputTarget> mCurrentInputTargets;
+    bool mCurrentInputTargetsValid; // false while targets are being recomputed
+
+    // Event injection and synchronization.
+    Condition mInjectionResultAvailableCondition;
+    Condition mFullySynchronizedCondition;
+    bool isFullySynchronizedLocked();
+    EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
+    void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
@@ -357,26 +561,41 @@
 
     void resetKeyRepeatLocked();
 
+    // Deferred command processing.
+    bool runCommandsLockedInterruptible();
+    CommandEntry* postCommandLocked(Command command);
+
     // Process events that have just been dequeued from the head of the input queue.
-    void processConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry);
-    void processKeyLocked(nsecs_t currentTime, KeyEntry* entry);
-    void processKeyRepeatLocked(nsecs_t currentTime);
-    void processMotionLocked(nsecs_t currentTime, MotionEntry* entry);
+    void processConfigurationChangedLockedInterruptible(
+            nsecs_t currentTime, ConfigurationChangedEntry* entry);
+    void processKeyLockedInterruptible(
+            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout);
+    void processKeyRepeatLockedInterruptible(
+            nsecs_t currentTime, nsecs_t keyRepeatTimeout);
+    void processMotionLockedInterruptible(
+            nsecs_t currentTime, MotionEntry* entry);
 
     // Identify input targets for an event and dispatch to them.
-    void identifyInputTargetsAndDispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry);
-    void identifyInputTargetsAndDispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry);
-    void dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, EventEntry* entry,
-            bool resumeWithAppendedMotionSample);
+    void identifyInputTargetsAndDispatchKeyLockedInterruptible(
+            nsecs_t currentTime, KeyEntry* entry);
+    void identifyInputTargetsAndDispatchMotionLockedInterruptible(
+            nsecs_t currentTime, MotionEntry* entry);
+    void dispatchEventToCurrentInputTargetsLocked(
+            nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
 
     // Manage the dispatch cycle for a single connection.
-    void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
+    // These methods are deliberately not Interruptible because doing all of the work
+    // with the mutex held makes it easier to ensure that connection invariants are maintained.
+    // If needed, the methods post commands to run later once the critical bits are done.
+    void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             EventEntry* eventEntry, const InputTarget* inputTarget,
             bool resumeWithAppendedMotionSample);
-    void startDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
-    void finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
-    bool timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
-    bool abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
+    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+            const sp<Connection>& connection, nsecs_t newTimeout);
+    void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool broken);
     static bool handleReceiveCallback(int receiveFd, int events, void* data);
 
@@ -385,11 +604,19 @@
     void deactivateConnectionLocked(Connection* connection);
 
     // Interesting events that we might like to log or tell the framework about.
-    void onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection);
-    void onDispatchCycleFinishedLocked(nsecs_t currentTime, Connection* connection,
-            bool recoveredFromANR);
-    void onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection);
-    void onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection);
+    void onDispatchCycleStartedLocked(
+            nsecs_t currentTime, const sp<Connection>& connection);
+    void onDispatchCycleFinishedLocked(
+            nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
+    void onDispatchCycleANRLocked(
+            nsecs_t currentTime, const sp<Connection>& connection);
+    void onDispatchCycleBrokenLocked(
+            nsecs_t currentTime, const sp<Connection>& connection);
+
+    // Outbound policy interactions.
+    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
 };
 
 /* Enqueues and dispatches input events, endlessly. */
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index eb27513..7509dd2 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -23,7 +23,6 @@
 
 #include <ui/EventHub.h>
 #include <ui/Input.h>
-#include <ui/InputDispatchPolicy.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 #include <utils/Timers.h>
@@ -32,9 +31,14 @@
 
 namespace android {
 
-class InputReader;
-class InputDispatcher;
+class InputChannel;
+
+class InputReaderInterface;
+class InputReaderPolicyInterface;
 class InputReaderThread;
+
+class InputDispatcherInterface;
+class InputDispatcherPolicyInterface;
 class InputDispatcherThread;
 
 /*
@@ -74,8 +78,20 @@
     /* Unregisters an input channel. */
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 
+    /* Injects an input event and optionally waits for sync.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+     */
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+
+    /* Gets input device configuration. */
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+
     /*
-     * Query current input state.
+     * Queries current input state.
      *   deviceId may be -1 to search for the device automatically, filtered by class.
      *   deviceClasses may be -1 to ignore device class while searching.
      */
@@ -86,7 +102,7 @@
     virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
             int32_t sw) const = 0;
 
-    /* Determine whether physical keys exist for the given framework-domain key codes. */
+    /* Determines whether physical keys exist for the given framework-domain key codes. */
     virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
 };
 
@@ -95,12 +111,15 @@
     virtual ~InputManager();
 
 public:
-    /*
-     * Creates an input manager that reads events from the given
-     * event hub and applies the given input dispatch policy.
-     */
-    InputManager(const sp<EventHubInterface>& eventHub,
-            const sp<InputDispatchPolicyInterface>& policy);
+    InputManager(
+            const sp<EventHubInterface>& eventHub,
+            const sp<InputReaderPolicyInterface>& readerPolicy,
+            const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
+
+    // (used for testing purposes)
+    InputManager(
+            const sp<InputReaderInterface>& reader,
+            const sp<InputDispatcherInterface>& dispatcher);
 
     virtual status_t start();
     virtual status_t stop();
@@ -108,6 +127,10 @@
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
             int32_t scanCode) const;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
@@ -117,16 +140,13 @@
     virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
 
 private:
-    sp<EventHubInterface> mEventHub;
-    sp<InputDispatchPolicyInterface> mPolicy;
-
-    sp<InputDispatcher> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
-
-    sp<InputReader> mReader;
+    sp<InputReaderInterface> mReader;
     sp<InputReaderThread> mReaderThread;
 
-    void configureExcludedDevices();
+    sp<InputDispatcherInterface> mDispatcher;
+    sp<InputDispatcherThread> mDispatcherThread;
+
+    void initialize();
 };
 
 } // namespace android
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 7e7a64c..d76b8fe 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -19,7 +19,6 @@
 
 #include <ui/EventHub.h>
 #include <ui/Input.h>
-#include <ui/InputDispatchPolicy.h>
 #include <ui/InputDispatcher.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
@@ -35,21 +34,6 @@
  * (This is limited by our use of BitSet32 to track pointer assignments.) */
 #define MAX_POINTER_ID 32
 
-/** Amount that trackball needs to move in order to generate a key event. */
-#define TRACKBALL_MOVEMENT_THRESHOLD 6
-
-/* Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value. */
-#define JUMPY_EPSILON_DIVISOR 212
-
-/* Number of jumpy points to drop for touchscreens that need it. */
-#define JUMPY_TRANSITION_DROPS 3
-#define JUMPY_DROP_LIMIT 3
-
-/* Maximum squared distance for averaging.
- * If moving farther than this, turn of averaging to avoid lag in response. */
-#define AVERAGING_DISTANCE_LIMIT (75 * 75)
-
 /* Maximum number of historical samples to average. */
 #define AVERAGING_HISTORY_SIZE 5
 
@@ -335,8 +319,129 @@
 };
 
 
-/* Processes raw input events and sends cooked event data to an input dispatcher
- * in accordance with the input dispatch policy. */
+/*
+ * Input reader policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class InputReaderPolicyInterface : public virtual RefBase {
+protected:
+    InputReaderPolicyInterface() { }
+    virtual ~InputReaderPolicyInterface() { }
+
+public:
+    /* Display orientations. */
+    enum {
+        ROTATION_0 = 0,
+        ROTATION_90 = 1,
+        ROTATION_180 = 2,
+        ROTATION_270 = 3
+    };
+
+    /* Actions returned by interceptXXX methods. */
+    enum {
+        // The input dispatcher should do nothing and discard the input unless other
+        // flags are set.
+        ACTION_NONE = 0,
+
+        // The input dispatcher should dispatch the input to the application.
+        ACTION_DISPATCH = 0x00000001,
+
+        // The input dispatcher should perform special filtering in preparation for
+        // a pending app switch.
+        ACTION_APP_SWITCH_COMING = 0x00000002,
+
+        // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it
+        // passes through the dispatch pipeline.
+        ACTION_WOKE_HERE = 0x00000004,
+
+        // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
+        // passes through the dispatch pipeline.
+        ACTION_BRIGHT_HERE = 0x00000008
+    };
+
+    /* Describes a virtual key. */
+    struct VirtualKeyDefinition {
+        int32_t scanCode;
+
+        // configured position data, specified in display coords
+        int32_t centerX;
+        int32_t centerY;
+        int32_t width;
+        int32_t height;
+    };
+
+    /* Gets information about the display with the specified id.
+     * Returns true if the display info is available, false otherwise.
+     */
+    virtual bool getDisplayInfo(int32_t displayId,
+            int32_t* width, int32_t* height, int32_t* orientation) = 0;
+
+    /* Provides feedback for a virtual key.
+     */
+    virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
+            int32_t action, int32_t flags, int32_t keyCode,
+            int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
+
+    /* Intercepts a key event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
+            bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0;
+
+    /* Intercepts a trackball event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
+            bool rolled) = 0;
+
+    /* Intercepts a touch event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptTouch(nsecs_t when) = 0;
+
+    /* Intercepts a switch event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Switches are not dispatched to applications so this method should
+     * usually return ACTION_NONE.
+     */
+    virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) = 0;
+
+    /* Determines whether to turn on some hacks we have to improve the touch interaction with a
+     * certain device whose screen currently is not all that good.
+     */
+    virtual bool filterTouchEvents() = 0;
+
+    /* Determines whether to turn on some hacks to improve touch interaction with another device
+     * where touch coordinate data can get corrupted.
+     */
+    virtual bool filterJumpyTouchEvents() = 0;
+
+    /* Gets the configured virtual key definitions for an input device. */
+    virtual void getVirtualKeyDefinitions(const String8& deviceName,
+            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
+
+    /* Gets the excluded device names for the platform. */
+    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
+};
+
+
+/* Processes raw input events and sends cooked event data to an input dispatcher. */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -355,16 +460,42 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const = 0;
+
+    /* Gets the current input device configuration.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+
+    /*
+     * Query current input state.
+     *   deviceId may be -1 to search for the device automatically, filtered by class.
+     *   deviceClasses may be -1 to ignore device class while searching.
+     */
+    virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t scanCode) const = 0;
+    virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t keyCode) const = 0;
+    virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
+            int32_t sw) const = 0;
+
+    /* Determine whether physical keys exist for the given framework-domain key codes. */
+    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
 };
 
+
 /* The input reader reads raw event data from the event hub and processes it into input events
- * that it sends to the input dispatcher.  Some functions of the input reader are controlled
- * by the input dispatch policy, such as early event filtering in low power states.
+ * that it sends to the input dispatcher.  Some functions of the input reader, such as early
+ * event filtering in low power states, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ *     Because the policy can potentially block or cause re-entrance into the input reader,
+ *     the input reader never calls into the policy while holding its internal locks.
  */
 class InputReader : public InputReaderInterface {
 public:
     InputReader(const sp<EventHubInterface>& eventHub,
-            const sp<InputDispatchPolicyInterface>& policy,
+            const sp<InputReaderPolicyInterface>& policy,
             const sp<InputDispatcherInterface>& dispatcher);
     virtual ~InputReader();
 
@@ -372,6 +503,17 @@
 
     virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const;
 
+    virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const;
+
+    virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t scanCode) const;
+    virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t keyCode) const;
+    virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
+            int32_t sw) const;
+
+    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+
 private:
     // Lock that must be acquired while manipulating state that may be concurrently accessed
     // from other threads by input state query methods.  It should be held for as short a
@@ -383,15 +525,18 @@
     //     (but not other internal device state)
     mutable Mutex mExportedStateLock;
 
-    // current virtual key information
-    int32_t mGlobalVirtualKeyCode;
-    int32_t mGlobalVirtualScanCode;
+    // current virtual key information (lock mExportedStateLock)
+    int32_t mExportedVirtualKeyCode;
+    int32_t mExportedVirtualScanCode;
+
+    // current input configuration (lock mExportedStateLock)
+    InputConfiguration mExportedInputConfiguration;
 
     // combined key meta state
     int32_t mGlobalMetaState;
 
     sp<EventHubInterface> mEventHub;
-    sp<InputDispatchPolicyInterface> mPolicy;
+    sp<InputReaderPolicyInterface> mPolicy;
     sp<InputDispatcherInterface> mDispatcher;
 
     KeyedVector<int32_t, InputDevice*> mDevices;
@@ -414,7 +559,7 @@
     // input policy processing and dispatch
     void onKey(nsecs_t when, InputDevice* device, bool down,
             int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
-    void onSwitch(nsecs_t when, InputDevice* device, bool down, int32_t code);
+    void onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, int32_t switchValue);
     void onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device);
     void onMultiTouchScreenStateChanged(nsecs_t when, InputDevice* device);
     void onTouchScreenChanged(nsecs_t when, InputDevice* device, bool havePointerIds);
@@ -445,13 +590,17 @@
     void configureVirtualKeys(InputDevice* device);
     void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name,
             InputDevice::AbsoluteAxisInfo* out);
+    void configureExcludedDevices();
 
     // global meta state management for all devices
     void resetGlobalMetaState();
     int32_t globalMetaState();
 
     // virtual key management
-    void updateGlobalVirtualKeyState();
+    void updateExportedVirtualKeyState();
+
+    // input configuration management
+    void updateExportedInputConfiguration();
 };
 
 
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 9537523..7b182f3 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -62,7 +62,7 @@
      * Returns OK on success.
      */
     static status_t openInputChannelPair(const String8& name,
-            InputChannel** outServerChannel, InputChannel** outClientChannel);
+            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
 
     inline String8 getName() const { return mName; }
     inline int32_t getAshmemFd() const { return mAshmemFd; }
@@ -72,7 +72,8 @@
     /* Sends a signal to the other endpoint.
      *
      * Returns OK on success.
-     * Errors probably indicate that the channel is broken.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
      */
     status_t sendSignal(char signal);
 
@@ -81,6 +82,7 @@
      *
      * Returns OK on success.
      * Returns WOULD_BLOCK if there is no signal present.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
     status_t receiveSignal(char* outSignal);
@@ -298,7 +300,7 @@
      * Returns INVALID_OPERATION if there is no currently published event.
      * Returns NO_MEMORY if the event could not be created.
      */
-    status_t consume(InputEventFactoryInterface* factory, InputEvent** event);
+    status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);
 
     /* Sends a finished signal to the publisher to inform it that the current message is
      * finished processing.
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index c9d951f..a95fb17 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -114,8 +114,10 @@
     };
 
     Mutex mLock;
-    Condition mAwake;
     bool mPolling;
+    uint32_t mWaiters;
+    Condition mAwake;
+    Condition mResume;
 
     int mWakeReadPipeFd;
     int mWakeWritePipeFd;
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index d40ae16..ec851bd 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -115,10 +115,10 @@
 
 
     //! insert an array at a given index
-            ssize_t         insertArrayAt(const TYPE* array, size_t index, size_t numItems);
+            ssize_t         insertArrayAt(const TYPE* array, size_t index, size_t length);
 
     //! append an array at the end of this vector
-            ssize_t         appendArray(const TYPE* array, size_t numItems);
+            ssize_t         appendArray(const TYPE* array, size_t length);
 
             /*! 
              * add/insert/replace items
@@ -126,7 +126,7 @@
              
     //! insert one or several items initialized with their default constructor
     inline  ssize_t         insertAt(size_t index, size_t numItems = 1);
-    //! insert on onr several items initialized from a prototype item
+    //! insert one or several items initialized from a prototype item
             ssize_t         insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
     //! pop the top of the stack (removes the last element). No-op if the stack's empty
     inline  void            pop();
@@ -265,13 +265,13 @@
 }
 
 template<class TYPE> inline
-ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t numItems) {
-    return VectorImpl::insertAt(array, index, numItems);
+ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
+    return VectorImpl::insertArrayAt(array, index, length);
 }
 
 template<class TYPE> inline
-ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t numItems) {
-    return VectorImpl::add(array, numItems);
+ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
+    return VectorImpl::appendArray(array, length);
 }
 
 template<class TYPE> inline
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
index 46a7bc2..c4ec2ff 100644
--- a/include/utils/VectorImpl.h
+++ b/include/utils/VectorImpl.h
@@ -65,9 +65,11 @@
             size_t          capacity() const;
             ssize_t         setCapacity(size_t size);
 
-            /*! append/insert another vector */
+            /*! append/insert another vector or array */
             ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
             ssize_t         appendVector(const VectorImpl& vector);
+            ssize_t         insertArrayAt(const void* array, size_t index, size_t length);
+            ssize_t         appendArray(const void* array, size_t length);
             
             /*! add/insert/replace items */
             ssize_t         insertAt(size_t where, size_t numItems = 1);
@@ -76,7 +78,7 @@
             void            push();
             void            push(const void* item);
             ssize_t         add();
-            ssize_t         add(const void* item, size_t numItems = 1);
+            ssize_t         add(const void* item);
             ssize_t         replaceAt(size_t index);
             ssize_t         replaceAt(const void* item, size_t index);
 
@@ -184,8 +186,8 @@
             void            push(const void* item);
             ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
             ssize_t         appendVector(const VectorImpl& vector);
-            ssize_t         insertArrayAt(const void* array, size_t index, size_t numItems);
-            ssize_t         appendArray(const void* array, size_t numItems);
+            ssize_t         insertArrayAt(const void* array, size_t index, size_t length);
+            ssize_t         appendArray(const void* array, size_t length);
             ssize_t         insertAt(size_t where, size_t numItems = 1);
             ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);
             ssize_t         replaceAt(size_t index);
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index d367708..0e6f2f5 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -50,8 +50,8 @@
         int32_t action,
         int32_t edgeFlags,
         int32_t metaState,
-        float rawX,
-        float rawY,
+        float xOffset,
+        float yOffset,
         float xPrecision,
         float yPrecision,
         nsecs_t downTime,
@@ -63,8 +63,8 @@
     mAction = action;
     mEdgeFlags = edgeFlags;
     mMetaState = metaState;
-    mRawX = rawX;
-    mRawY = rawY;
+    mXOffset = xOffset;
+    mYOffset = yOffset;
     mXPrecision = xPrecision;
     mYPrecision = yPrecision;
     mDownTime = downTime;
@@ -83,13 +83,8 @@
 }
 
 void MotionEvent::offsetLocation(float xOffset, float yOffset) {
-    if (xOffset != 0 || yOffset != 0) {
-        for (size_t i = 0; i < mSamplePointerCoords.size(); i++) {
-            PointerCoords& pointerCoords = mSamplePointerCoords.editItemAt(i);
-            pointerCoords.x += xOffset;
-            pointerCoords.y += yOffset;
-        }
-    }
+    mXOffset += xOffset;
+    mYOffset += yOffset;
 }
 
 } // namespace android
@@ -163,6 +158,14 @@
     return reinterpret_cast<const MotionEvent*>(motion_event)->getEventTime();
 }
 
+float motion_event_get_x_offset(const input_event_t* motion_event) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getXOffset();
+}
+
+float motion_event_get_y_offset(const input_event_t* motion_event) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getYOffset();
+}
+
 float motion_event_get_x_precision(const input_event_t* motion_event) {
     return reinterpret_cast<const MotionEvent*>(motion_event)->getXPrecision();
 }
@@ -179,12 +182,12 @@
     return reinterpret_cast<const MotionEvent*>(motion_event)->getPointerId(pointer_index);
 }
 
-float motion_event_get_raw_x(const input_event_t* motion_event) {
-    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawX();
+float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawX(pointer_index);
 }
 
-float motion_event_get_raw_y(const input_event_t* motion_event) {
-    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawY();
+float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawY(pointer_index);
 }
 
 float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index) {
@@ -213,6 +216,18 @@
             history_index);
 }
 
+float motion_event_get_historical_raw_x(input_event_t* motion_event, size_t pointer_index,
+        size_t history_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalRawX(
+            pointer_index, history_index);
+}
+
+float motion_event_get_historical_raw_y(input_event_t* motion_event, size_t pointer_index,
+        size_t history_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalRawY(
+            pointer_index, history_index);
+}
+
 float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index,
         size_t history_index) {
     return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalX(
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 0ccde0d..2ad3382 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -19,9 +19,15 @@
 // Log debug messages about the dispatch cycle.
 #define DEBUG_DISPATCH_CYCLE 1
 
+// Log debug messages about registrations.
+#define DEBUG_REGISTRATION 1
+
 // Log debug messages about performance statistics.
 #define DEBUG_PERFORMANCE_STATISTICS 1
 
+// Log debug messages about input event injection.
+#define DEBUG_INJECTION 1
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
 
@@ -40,9 +46,13 @@
             || keyCode == KEYCODE_DPAD_RIGHT;
 }
 
+static inline nsecs_t now() {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
 // --- InputDispatcher ---
 
-InputDispatcher::InputDispatcher(const sp<InputDispatchPolicyInterface>& policy) :
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
     mPolicy(policy) {
     mPollLoop = new PollLoop();
 
@@ -55,6 +65,8 @@
     mInboundQueue.tail.eventTime = LONG_LONG_MAX;
 
     mKeyRepeatState.lastKeyEntry = NULL;
+
+    mCurrentInputTargetsValid = false;
 }
 
 InputDispatcher::~InputDispatcher() {
@@ -72,57 +84,58 @@
 }
 
 void InputDispatcher::dispatchOnce() {
-    bool allowKeyRepeat = mPolicy->allowKeyRepeat();
+    nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
 
+    bool skipPoll = false;
     nsecs_t currentTime;
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
         AutoMutex _l(mLock);
-        currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        currentTime = now();
 
         // Reset the key repeat timer whenever we disallow key events, even if the next event
         // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
         // out of sleep.
         // XXX we should handle resetting input state coming out of sleep more generally elsewhere
-        if (! allowKeyRepeat) {
+        if (keyRepeatTimeout < 0) {
             resetKeyRepeatLocked();
         }
 
-        // Process timeouts for all connections and determine if there are any synchronous
-        // event dispatches pending.
+        // Detect and process timeouts for all connections and determine if there are any
+        // synchronous event dispatches pending.  This step is entirely non-interruptible.
         bool hasPendingSyncTarget = false;
-        for (size_t i = 0; i < mActiveConnections.size(); ) {
+        size_t activeConnectionCount = mActiveConnections.size();
+        for (size_t i = 0; i < activeConnectionCount; i++) {
             Connection* connection = mActiveConnections.itemAt(i);
 
-            nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
-            if (connectionTimeoutTime <= currentTime) {
-                bool deactivated = timeoutDispatchCycleLocked(currentTime, connection);
-                if (deactivated) {
-                    // Don't increment i because the connection has been removed
-                    // from mActiveConnections (hence, deactivated).
-                    continue;
-                }
-            }
-
-            if (connectionTimeoutTime < nextWakeupTime) {
-                nextWakeupTime = connectionTimeoutTime;
-            }
-
             if (connection->hasPendingSyncTarget()) {
                 hasPendingSyncTarget = true;
             }
 
-            i += 1;
+            nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
+            if (connectionTimeoutTime <= currentTime) {
+                mTimedOutConnections.add(connection);
+            } else if (connectionTimeoutTime < nextWakeupTime) {
+                nextWakeupTime = connectionTimeoutTime;
+            }
         }
 
+        size_t timedOutConnectionCount = mTimedOutConnections.size();
+        for (size_t i = 0; i < timedOutConnectionCount; i++) {
+            Connection* connection = mTimedOutConnections.itemAt(i);
+            timeoutDispatchCycleLocked(currentTime, connection);
+            skipPoll = true;
+        }
+        mTimedOutConnections.clear();
+
         // If we don't have a pending sync target, then we can begin delivering a new event.
         // (Otherwise we wait for dispatch to complete for that target.)
         if (! hasPendingSyncTarget) {
             if (mInboundQueue.isEmpty()) {
                 if (mKeyRepeatState.lastKeyEntry) {
                     if (currentTime >= mKeyRepeatState.nextRepeatTime) {
-                        processKeyRepeatLocked(currentTime);
-                        return; // dispatched once
+                        processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout);
+                        skipPoll = true;
                     } else {
                         if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
                             nextWakeupTime = mKeyRepeatState.nextRepeatTime;
@@ -130,31 +143,30 @@
                     }
                 }
             } else {
-                // Inbound queue has at least one entry.  Dequeue it and begin dispatching.
-                // Note that we do not hold the lock for this process because dispatching may
-                // involve making many callbacks.
-                EventEntry* entry = mInboundQueue.dequeueAtHead();
+                // Inbound queue has at least one entry.
+                // Start processing it but leave it on the queue until later so that the
+                // input reader can keep appending samples onto a motion event between the
+                // time we started processing it and the time we finally enqueue dispatch
+                // entries for it.
+                EventEntry* entry = mInboundQueue.head.next;
 
                 switch (entry->type) {
                 case EventEntry::TYPE_CONFIGURATION_CHANGED: {
                     ConfigurationChangedEntry* typedEntry =
                             static_cast<ConfigurationChangedEntry*>(entry);
-                    processConfigurationChangedLocked(currentTime, typedEntry);
-                    mAllocator.releaseConfigurationChangedEntry(typedEntry);
+                    processConfigurationChangedLockedInterruptible(currentTime, typedEntry);
                     break;
                 }
 
                 case EventEntry::TYPE_KEY: {
                     KeyEntry* typedEntry = static_cast<KeyEntry*>(entry);
-                    processKeyLocked(currentTime, typedEntry);
-                    mAllocator.releaseKeyEntry(typedEntry);
+                    processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout);
                     break;
                 }
 
                 case EventEntry::TYPE_MOTION: {
                     MotionEntry* typedEntry = static_cast<MotionEntry*>(entry);
-                    processMotionLocked(currentTime, typedEntry);
-                    mAllocator.releaseMotionEntry(typedEntry);
+                    processMotionLockedInterruptible(currentTime, typedEntry);
                     break;
                 }
 
@@ -162,30 +174,73 @@
                     assert(false);
                     break;
                 }
-                return; // dispatched once
+
+                // Dequeue and release the event entry that we just processed.
+                mInboundQueue.dequeue(entry);
+                mAllocator.releaseEventEntry(entry);
+                skipPoll = true;
             }
         }
+
+        // Run any deferred commands.
+        skipPoll |= runCommandsLockedInterruptible();
+
+        // Wake up synchronization waiters, if needed.
+        if (isFullySynchronizedLocked()) {
+            mFullySynchronizedCondition.broadcast();
+        }
     } // release lock
 
+    // If we dispatched anything, don't poll just now.  Wait for the next iteration.
+    // Contents may have shifted during flight.
+    if (skipPoll) {
+        return;
+    }
+
     // Wait for callback or timeout or wake.
     nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
     int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
     mPollLoop->pollOnce(timeoutMillis);
 }
 
-void InputDispatcher::processConfigurationChangedLocked(nsecs_t currentTime,
-        ConfigurationChangedEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, "
-            "keyboardConfig=%d, navigationConfig=%d", entry->eventTime,
-            entry->touchScreenConfig, entry->keyboardConfig, entry->navigationConfig);
-#endif
+bool InputDispatcher::runCommandsLockedInterruptible() {
+    if (mCommandQueue.isEmpty()) {
+        return false;
+    }
 
-    mPolicy->notifyConfigurationChanged(entry->eventTime, entry->touchScreenConfig,
-            entry->keyboardConfig, entry->navigationConfig);
+    do {
+        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
+
+        Command command = commandEntry->command;
+        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+
+        commandEntry->connection.clear();
+        mAllocator.releaseCommandEntry(commandEntry);
+    } while (! mCommandQueue.isEmpty());
+    return true;
 }
 
-void InputDispatcher::processKeyLocked(nsecs_t currentTime, KeyEntry* entry) {
+InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
+    CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command);
+    mCommandQueue.enqueueAtTail(commandEntry);
+    return commandEntry;
+}
+
+void InputDispatcher::processConfigurationChangedLockedInterruptible(
+        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+    mLock.unlock();
+
+    mPolicy->notifyConfigurationChanged(entry->eventTime);
+
+    mLock.lock();
+}
+
+void InputDispatcher::processKeyLockedInterruptible(
+        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processKey - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, "
             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
@@ -209,7 +264,7 @@
         } else {
             // Not a repeat.  Save key down state in case we do see a repeat later.
             resetKeyRepeatLocked();
-            mKeyRepeatState.nextRepeatTime = entry->eventTime + mPolicy->getKeyRepeatTimeout();
+            mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
         }
         mKeyRepeatState.lastKeyEntry = entry;
         entry->refCount += 1;
@@ -217,10 +272,11 @@
         resetKeyRepeatLocked();
     }
 
-    identifyInputTargetsAndDispatchKeyLocked(currentTime, entry);
+    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::processKeyRepeatLocked(nsecs_t currentTime) {
+void InputDispatcher::processKeyRepeatLockedInterruptible(
+        nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
     // TODO Old WindowManagerServer code sniffs the input queue for following key up
     //      events and drops the repeat if one is found.  We should do something similar.
     //      One good place to do it is in notifyKey as soon as the key up enters the
@@ -229,30 +285,25 @@
     // Synthesize a key repeat after the repeat timeout expired.
     // We reuse the previous key entry if otherwise unreferenced.
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+    uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
+        entry->eventTime = currentTime;
+        entry->downTime = currentTime;
+        entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
     } else {
-        KeyEntry* newEntry = mAllocator.obtainKeyEntry();
-        newEntry->deviceId = entry->deviceId;
-        newEntry->nature = entry->nature;
-        newEntry->policyFlags = entry->policyFlags;
-        newEntry->action = entry->action;
-        newEntry->flags = entry->flags;
-        newEntry->keyCode = entry->keyCode;
-        newEntry->scanCode = entry->scanCode;
-        newEntry->metaState = entry->metaState;
-        newEntry->repeatCount = entry->repeatCount + 1;
+        KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime,
+                entry->deviceId, entry->nature, policyFlags,
+                entry->action, entry->flags, entry->keyCode, entry->scanCode,
+                entry->metaState, entry->repeatCount + 1, currentTime);
 
         mKeyRepeatState.lastKeyEntry = newEntry;
         mAllocator.releaseKeyEntry(entry);
 
         entry = newEntry;
     }
-    entry->eventTime = currentTime;
-    entry->downTime = currentTime;
-    entry->policyFlags = 0;
 
-    mKeyRepeatState.nextRepeatTime = currentTime + mPolicy->getKeyRepeatTimeout();
+    mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
 
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, "
@@ -263,10 +314,11 @@
             entry->repeatCount, entry->downTime);
 #endif
 
-    identifyInputTargetsAndDispatchKeyLocked(currentTime, entry);
+    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::processMotionLocked(nsecs_t currentTime, MotionEntry* entry) {
+void InputDispatcher::processMotionLockedInterruptible(
+        nsecs_t currentTime, MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processMotion - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, "
             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
@@ -296,54 +348,75 @@
     }
 #endif
 
-    identifyInputTargetsAndDispatchMotionLocked(currentTime, entry);
+    identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::identifyInputTargetsAndDispatchKeyLocked(
+void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
         nsecs_t currentTime, KeyEntry* entry) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("identifyInputTargetsAndDispatchKey");
 #endif
 
+    entry->dispatchInProgress = true;
+    mCurrentInputTargetsValid = false;
+    mLock.unlock();
+
     mReusableKeyEvent.initialize(entry->deviceId, entry->nature, entry->action, entry->flags,
             entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
             entry->downTime, entry->eventTime);
 
     mCurrentInputTargets.clear();
-    mPolicy->getKeyEventTargets(& mReusableKeyEvent, entry->policyFlags,
+    int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
+            entry->policyFlags, entry->injectorPid, entry->injectorUid,
             mCurrentInputTargets);
 
+    mLock.lock();
+    mCurrentInputTargetsValid = true;
+
+    setInjectionResultLocked(entry, injectionResult);
+
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 }
 
-void InputDispatcher::identifyInputTargetsAndDispatchMotionLocked(
+void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
         nsecs_t currentTime, MotionEntry* entry) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("identifyInputTargetsAndDispatchMotion");
 #endif
 
+    entry->dispatchInProgress = true;
+    mCurrentInputTargetsValid = false;
+    mLock.unlock();
+
     mReusableMotionEvent.initialize(entry->deviceId, entry->nature, entry->action,
             entry->edgeFlags, entry->metaState,
-            entry->firstSample.pointerCoords[0].x, entry->firstSample.pointerCoords[0].y,
-            entry->xPrecision, entry->yPrecision,
+            0, 0, entry->xPrecision, entry->yPrecision,
             entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
             entry->firstSample.pointerCoords);
 
     mCurrentInputTargets.clear();
-    mPolicy->getMotionEventTargets(& mReusableMotionEvent, entry->policyFlags,
+    int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
+            entry->policyFlags, entry->injectorPid, entry->injectorUid,
             mCurrentInputTargets);
 
+    mLock.lock();
+    mCurrentInputTargetsValid = true;
+
+    setInjectionResultLocked(entry, injectionResult);
+
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 }
 
 void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
         EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("dispatchEventToCurrentInputTargets, "
+    LOGD("dispatchEventToCurrentInputTargets - "
             "resumeWithAppendedMotionSample=%s",
             resumeWithAppendedMotionSample ? "true" : "false");
 #endif
 
+    assert(eventEntry->dispatchInProgress); // should already have been set to true
+
     for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
 
@@ -351,7 +424,7 @@
                 inputTarget.inputChannel->getReceivePipeFd());
         if (connectionIndex >= 0) {
             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-            prepareDispatchCycleLocked(currentTime, connection.get(), eventEntry, & inputTarget,
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
                     resumeWithAppendedMotionSample);
         } else {
             LOGW("Framework requested delivery of an input event to channel '%s' but it "
@@ -361,11 +434,11 @@
     }
 }
 
-void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
-        EventEntry* eventEntry, const InputTarget* inputTarget,
+void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ prepareDispatchCycle, flags=%d, timeout=%lldns, "
+    LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
             "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
             inputTarget->xOffset, inputTarget->yOffset,
@@ -377,7 +450,7 @@
     // not responding.
     if (connection->status != Connection::STATUS_NORMAL) {
         LOGV("channel '%s' ~ Dropping event because the channel status is %s",
-                connection->status == Connection::STATUS_BROKEN ? "BROKEN" : "NOT RESPONDING");
+                connection->getStatusLabel());
         return;
     }
 
@@ -488,12 +561,13 @@
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty) {
-        activateConnectionLocked(connection);
+        activateConnectionLocked(connection.get());
         startDispatchCycleLocked(currentTime, connection);
     }
 }
 
-void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ startDispatchCycle",
             connection->getInputChannelName());
@@ -623,22 +697,24 @@
     connection->lastDispatchTime = currentTime;
 
     nsecs_t timeout = dispatchEntry->timeout;
-    connection->nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
+    connection->setNextTimeoutTime(currentTime, timeout);
 
     // Notify other system components.
     onDispatchCycleStartedLocked(currentTime, connection);
 }
 
-void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ finishDispatchCycle: %01.1fms since event, "
+    LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
             "%01.1fms since dispatch",
             connection->getInputChannelName(),
             connection->getEventLatencyMillis(currentTime),
             connection->getDispatchLatencyMillis(currentTime));
 #endif
 
-    if (connection->status == Connection::STATUS_BROKEN) {
+    if (connection->status == Connection::STATUS_BROKEN
+            || connection->status == Connection::STATUS_ZOMBIE) {
         return;
     }
 
@@ -696,60 +772,77 @@
     }
 
     // Outbound queue is empty, deactivate the connection.
-    deactivateConnectionLocked(connection);
+    deactivateConnectionLocked(connection.get());
 }
 
-bool InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ timeoutDispatchCycle",
             connection->getInputChannelName());
 #endif
 
     if (connection->status != Connection::STATUS_NORMAL) {
-        return false;
+        return;
     }
 
     // Enter the not responding state.
     connection->status = Connection::STATUS_NOT_RESPONDING;
     connection->lastANRTime = currentTime;
-    bool deactivated = abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
 
     // Notify other system components.
+    // This enqueues a command which will eventually either call
+    // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
     onDispatchCycleANRLocked(currentTime, connection);
-    return deactivated;
 }
 
-bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
-        bool broken) {
+void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection, nsecs_t newTimeout) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ abortDispatchCycle, broken=%s",
-            connection->getInputChannelName(), broken ? "true" : "false");
+    LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked",
+            connection->getInputChannelName());
 #endif
 
-    if (connection->status == Connection::STATUS_BROKEN) {
-        return false;
+    if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+        return;
     }
 
+    // Resume normal dispatch.
+    connection->status = Connection::STATUS_NORMAL;
+    connection->setNextTimeoutTime(currentTime, newTimeout);
+}
+
+void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection, bool broken) {
+#if DEBUG_DISPATCH_CYCLE
+    LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
+            connection->getInputChannelName(), broken ? "true" : "false");
+#endif
+
     // Clear the pending timeout.
     connection->nextTimeoutTime = LONG_LONG_MAX;
 
     // Clear the outbound queue.
-    while (! connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
-        mAllocator.releaseDispatchEntry(dispatchEntry);
-    }
+    if (! connection->outboundQueue.isEmpty()) {
+        do {
+            DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+            mAllocator.releaseDispatchEntry(dispatchEntry);
+        } while (! connection->outboundQueue.isEmpty());
 
-    // Outbound queue is empty, deactivate the connection.
-    deactivateConnectionLocked(connection);
+        deactivateConnectionLocked(connection.get());
+    }
 
     // Handle the case where the connection appears to be unrecoverably broken.
+    // Ignore already broken or zombie connections.
     if (broken) {
-        connection->status = Connection::STATUS_BROKEN;
+        if (connection->status == Connection::STATUS_NORMAL
+                || connection->status == Connection::STATUS_NOT_RESPONDING) {
+            connection->status = Connection::STATUS_BROKEN;
 
-        // Notify other system components.
-        onDispatchCycleBrokenLocked(currentTime, connection);
+            // Notify other system components.
+            onDispatchCycleBrokenLocked(currentTime, connection);
+        }
     }
-    return true; /*deactivated*/
 }
 
 bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -765,13 +858,14 @@
             return false; // remove the callback
         }
 
-        nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        nsecs_t currentTime = now();
 
         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
         if (events & (POLLERR | POLLHUP | POLLNVAL)) {
             LOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                     "events=0x%x", connection->getInputChannelName(), events);
-            d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+            d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+            d->runCommandsLockedInterruptible();
             return false; // remove the callback
         }
 
@@ -785,32 +879,27 @@
         if (status) {
             LOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                     connection->getInputChannelName(), status);
-            d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+            d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+            d->runCommandsLockedInterruptible();
             return false; // remove the callback
         }
 
-        d->finishDispatchCycleLocked(currentTime, connection.get());
+        d->finishDispatchCycleLocked(currentTime, connection);
+        d->runCommandsLockedInterruptible();
         return true;
     } // release lock
 }
 
-void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime, int32_t touchScreenConfig,
-        int32_t keyboardConfig, int32_t navigationConfig) {
+void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, "
-            "keyboardConfig=%d, navigationConfig=%d", eventTime,
-            touchScreenConfig, keyboardConfig, navigationConfig);
+    LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
 #endif
 
     bool wasEmpty;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry();
-        newEntry->eventTime = eventTime;
-        newEntry->touchScreenConfig = touchScreenConfig;
-        newEntry->keyboardConfig = keyboardConfig;
-        newEntry->navigationConfig = navigationConfig;
+        ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -821,16 +910,6 @@
     }
 }
 
-void InputDispatcher::notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyLidSwitchChanged - eventTime=%lld, open=%s", eventTime,
-            lidOpen ? "true" : "false");
-#endif
-
-    // Send lid switch notification immediately and synchronously.
-    mPolicy->notifyLidSwitchChanged(eventTime, lidOpen);
-}
-
 void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime);
@@ -852,6 +931,9 @@
                     LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
                             keyEntry->keyCode, keyEntry->action);
                     mInboundQueue.dequeue(keyEntry);
+
+                    setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+
                     mAllocator.releaseKeyEntry(keyEntry);
                 } else {
                     // stop at last non-movement key
@@ -878,18 +960,10 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        KeyEntry* newEntry = mAllocator.obtainKeyEntry();
-        newEntry->eventTime = eventTime;
-        newEntry->deviceId = deviceId;
-        newEntry->nature = nature;
-        newEntry->policyFlags = policyFlags;
-        newEntry->action = action;
-        newEntry->flags = flags;
-        newEntry->keyCode = keyCode;
-        newEntry->scanCode = scanCode;
-        newEntry->metaState = metaState;
-        newEntry->repeatCount = 0;
-        newEntry->downTime = downTime;
+        int32_t repeatCount = 0;
+        KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
+                deviceId, nature, policyFlags, action, flags, keyCode, scanCode,
+                metaState, repeatCount, downTime);
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -942,20 +1016,33 @@
                 }
 
                 if (motionEntry->action != MOTION_EVENT_ACTION_MOVE
-                        || motionEntry->pointerCount != pointerCount) {
+                        || motionEntry->pointerCount != pointerCount
+                        || motionEntry->isInjected()) {
                     // Last motion event in the queue for this device is not compatible for
                     // appending new samples.  Stop here.
                     goto NoBatchingOrStreaming;
                 }
 
                 // The last motion event is a move and is compatible for appending.
-                // Do the batching magic and exit.
-                mAllocator.appendMotionSample(motionEntry, eventTime, pointerCount, pointerCoords);
+                // Do the batching magic.
+                mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
 #if DEBUG_BATCHING
                 LOGD("Appended motion sample onto batch for most recent "
                         "motion event for this device in the inbound queue.");
 #endif
-                return; // done
+
+                // Sanity check for special case because dispatch is interruptible.
+                // The dispatch logic is partially interruptible and releases its lock while
+                // identifying targets.  However, as soon as the targets have been identified,
+                // the dispatcher proceeds to write a dispatch entry into all relevant outbound
+                // queues and then promptly removes the motion entry from the queue.
+                //
+                // Consequently, we should never observe the case where the inbound queue contains
+                // an in-progress motion entry unless the current input targets are invalid
+                // (currently being computed).  Check for this!
+                assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid));
+
+                return; // done!
             }
 
             // STREAMING CASE
@@ -977,34 +1064,39 @@
             // Note: This code crucially depends on the invariant that an outbound queue always
             //       contains at most one synchronous event and it is always last (but it might
             //       not be first!).
-            for (size_t i = 0; i < mActiveConnections.size(); i++) {
-                Connection* connection = mActiveConnections.itemAt(i);
-                if (! connection->outboundQueue.isEmpty()) {
-                    DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
-                    if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
-                        if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
-                            goto NoBatchingOrStreaming;
-                        }
+            if (mCurrentInputTargetsValid) {
+                for (size_t i = 0; i < mActiveConnections.size(); i++) {
+                    Connection* connection = mActiveConnections.itemAt(i);
+                    if (! connection->outboundQueue.isEmpty()) {
+                        DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
+                        if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
+                            if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+                                goto NoBatchingOrStreaming;
+                            }
 
-                        MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
-                                dispatchEntry->eventEntry);
-                        if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE
-                                || syncedMotionEntry->deviceId != deviceId
-                                || syncedMotionEntry->pointerCount != pointerCount) {
-                            goto NoBatchingOrStreaming;
-                        }
+                            MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
+                                    dispatchEntry->eventEntry);
+                            if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE
+                                    || syncedMotionEntry->deviceId != deviceId
+                                    || syncedMotionEntry->pointerCount != pointerCount
+                                    || syncedMotionEntry->isInjected()) {
+                                goto NoBatchingOrStreaming;
+                            }
 
-                        // Found synced move entry.  Append sample and resume dispatch.
-                        mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
-                                pointerCount, pointerCoords);
-#if DEBUG_BATCHING
-                        LOGD("Appended motion sample onto batch for most recent synchronously "
-                                "dispatched motion event for this device in the outbound queues.");
-#endif
-                        nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
-                                true /*resumeWithAppendedMotionSample*/);
-                        return; // done!
+                            // Found synced move entry.  Append sample and resume dispatch.
+                            mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
+                                    pointerCoords);
+    #if DEBUG_BATCHING
+                            LOGD("Appended motion sample onto batch for most recent synchronously "
+                                    "dispatched motion event for this device in the outbound queues.");
+    #endif
+                            nsecs_t currentTime = now();
+                            dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
+                                    true /*resumeWithAppendedMotionSample*/);
+
+                            runCommandsLockedInterruptible();
+                            return; // done!
+                        }
                     }
                 }
             }
@@ -1013,24 +1105,10 @@
         }
 
         // Just enqueue a new motion event.
-        MotionEntry* newEntry = mAllocator.obtainMotionEntry();
-        newEntry->eventTime = eventTime;
-        newEntry->deviceId = deviceId;
-        newEntry->nature = nature;
-        newEntry->policyFlags = policyFlags;
-        newEntry->action = action;
-        newEntry->metaState = metaState;
-        newEntry->edgeFlags = edgeFlags;
-        newEntry->xPrecision = xPrecision;
-        newEntry->yPrecision = yPrecision;
-        newEntry->downTime = downTime;
-        newEntry->pointerCount = pointerCount;
-        newEntry->firstSample.eventTime = eventTime;
-        newEntry->lastSample = & newEntry->firstSample;
-        for (uint32_t i = 0; i < pointerCount; i++) {
-            newEntry->pointerIds[i] = pointerIds[i];
-            newEntry->firstSample.pointerCoords[i] = pointerCoords[i];
-        }
+        MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
+                deviceId, nature, policyFlags, action, metaState, edgeFlags,
+                xPrecision, yPrecision, downTime,
+                pointerCount, pointerIds, pointerCoords);
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -1041,6 +1119,133 @@
     }
 }
 
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
+        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+    LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+            "sync=%d, timeoutMillis=%d",
+            event->getType(), injectorPid, injectorUid, sync, timeoutMillis);
+#endif
+
+    nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
+
+    EventEntry* injectedEntry;
+    bool wasEmpty;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        injectedEntry = createEntryFromInputEventLocked(event);
+        injectedEntry->refCount += 1;
+        injectedEntry->injectorPid = injectorPid;
+        injectedEntry->injectorUid = injectorUid;
+
+        wasEmpty = mInboundQueue.isEmpty();
+        mInboundQueue.enqueueAtTail(injectedEntry);
+
+    } // release lock
+
+    if (wasEmpty) {
+        mPollLoop->wake();
+    }
+
+    int32_t injectionResult;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (;;) {
+            injectionResult = injectedEntry->injectionResult;
+            if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+                break;
+            }
+
+            nsecs_t remainingTimeout = endTime - now();
+            if (remainingTimeout <= 0) {
+                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                sync = false;
+                break;
+            }
+
+            mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+        }
+
+        if (sync) {
+            while (! isFullySynchronizedLocked()) {
+                nsecs_t remainingTimeout = endTime - now();
+                if (remainingTimeout <= 0) {
+                    injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                    break;
+                }
+
+                mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout);
+            }
+        }
+
+        mAllocator.releaseEventEntry(injectedEntry);
+    } // release lock
+
+    return injectionResult;
+}
+
+void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+    if (entry->isInjected()) {
+#if DEBUG_INJECTION
+        LOGD("Setting input event injection result to %d.  "
+                "injectorPid=%d, injectorUid=%d",
+                 injectionResult, entry->injectorPid, entry->injectorUid);
+#endif
+
+        entry->injectionResult = injectionResult;
+        mInjectionResultAvailableCondition.broadcast();
+    }
+}
+
+bool InputDispatcher::isFullySynchronizedLocked() {
+    return mInboundQueue.isEmpty() && mActiveConnections.isEmpty();
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
+        const InputEvent* event) {
+    switch (event->getType()) {
+    case INPUT_EVENT_TYPE_KEY: {
+        const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
+        uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+
+        KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
+                keyEvent->getDeviceId(), keyEvent->getNature(), policyFlags,
+                keyEvent->getAction(), keyEvent->getFlags(),
+                keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
+                keyEvent->getRepeatCount(), keyEvent->getDownTime());
+        return keyEntry;
+    }
+
+    case INPUT_EVENT_TYPE_MOTION: {
+        const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+        uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+
+        const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+        const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+        size_t pointerCount = motionEvent->getPointerCount();
+
+        MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
+                motionEvent->getDeviceId(), motionEvent->getNature(), policyFlags,
+                motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
+                motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+                motionEvent->getDownTime(), uint32_t(pointerCount),
+                motionEvent->getPointerIds(), samplePointerCoords);
+        for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+            sampleEventTimes += 1;
+            samplePointerCoords += pointerCount;
+            mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords);
+        }
+        return motionEntry;
+    }
+
+    default:
+        assert(false);
+        return NULL;
+    }
+}
+
 void InputDispatcher::resetKeyRepeatLocked() {
     if (mKeyRepeatState.lastKeyEntry) {
         mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
@@ -1049,6 +1254,10 @@
 }
 
 status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+    LOGD("channel '%s' - Registered", inputChannel->getName().string());
+#endif
+
     int receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1069,6 +1278,8 @@
         }
 
         mConnectionsByReceiveFd.add(receiveFd, connection);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
@@ -1076,6 +1287,10 @@
 }
 
 status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+    LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
+#endif
+
     int32_t receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1093,8 +1308,10 @@
 
         connection->status = Connection::STATUS_ZOMBIE;
 
-        nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+        nsecs_t currentTime = now();
+        abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->removeCallback(receiveFd);
@@ -1123,11 +1340,12 @@
     }
 }
 
-void InputDispatcher::onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleStartedLocked(
+        nsecs_t currentTime, const sp<Connection>& connection) {
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
-        Connection* connection, bool recoveredFromANR) {
+void InputDispatcher::onDispatchCycleFinishedLocked(
+        nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
     if (recoveredFromANR) {
         LOGI("channel '%s' ~ Recovered from ANR.  %01.1fms since event, "
                 "%01.1fms since dispatch, %01.1fms since ANR",
@@ -1136,51 +1354,150 @@
                 connection->getDispatchLatencyMillis(currentTime),
                 connection->getANRLatencyMillis(currentTime));
 
-        // TODO tell framework
+        CommandEntry* commandEntry = postCommandLocked(
+                & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
+        commandEntry->connection = connection;
     }
 }
 
-void InputDispatcher::onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleANRLocked(
+        nsecs_t currentTime, const sp<Connection>& connection) {
     LOGI("channel '%s' ~ Not responding!  %01.1fms since event, %01.1fms since dispatch",
             connection->getInputChannelName(),
             connection->getEventLatencyMillis(currentTime),
             connection->getDispatchLatencyMillis(currentTime));
 
-    // TODO tell framework
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
+    commandEntry->connection = connection;
 }
 
-void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleBrokenLocked(
+        nsecs_t currentTime, const sp<Connection>& connection) {
     LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
             connection->getInputChannelName());
 
-    // TODO tell framework
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+    commandEntry->connection = connection;
 }
 
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<Connection> connection = commandEntry->connection;
+
+    if (connection->status != Connection::STATUS_ZOMBIE) {
+        mLock.unlock();
+
+        mPolicy->notifyInputChannelBroken(connection->inputChannel);
+
+        mLock.lock();
+    }
+}
+
+void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<Connection> connection = commandEntry->connection;
+
+    if (connection->status != Connection::STATUS_ZOMBIE) {
+        mLock.unlock();
+
+        nsecs_t newTimeout;
+        bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
+
+        mLock.lock();
+
+        nsecs_t currentTime = now();
+        if (resume) {
+            resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
+        } else {
+            abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
+        }
+    }
+}
+
+void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<Connection> connection = commandEntry->connection;
+
+    if (connection->status != Connection::STATUS_ZOMBIE) {
+        mLock.unlock();
+
+        mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
+
+        mLock.lock();
+    }
+}
+
+
 // --- InputDispatcher::Allocator ---
 
 InputDispatcher::Allocator::Allocator() {
 }
 
+void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
+        nsecs_t eventTime) {
+    entry->type = type;
+    entry->refCount = 1;
+    entry->dispatchInProgress = false;
+    entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    entry->injectorPid = -1;
+    entry->injectorUid = -1;
+}
+
 InputDispatcher::ConfigurationChangedEntry*
-InputDispatcher::Allocator::obtainConfigurationChangedEntry() {
+InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) {
     ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
-    entry->refCount = 1;
-    entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED;
+    initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime);
     return entry;
 }
 
-InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry() {
+InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime,
+        int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+        int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+        int32_t repeatCount, nsecs_t downTime) {
     KeyEntry* entry = mKeyEntryPool.alloc();
-    entry->refCount = 1;
-    entry->type = EventEntry::TYPE_KEY;
+    initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime);
+
+    entry->deviceId = deviceId;
+    entry->nature = nature;
+    entry->policyFlags = policyFlags;
+    entry->action = action;
+    entry->flags = flags;
+    entry->keyCode = keyCode;
+    entry->scanCode = scanCode;
+    entry->metaState = metaState;
+    entry->repeatCount = repeatCount;
+    entry->downTime = downTime;
     return entry;
 }
 
-InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry() {
+InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
+        int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+        int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+        nsecs_t downTime, uint32_t pointerCount,
+        const int32_t* pointerIds, const PointerCoords* pointerCoords) {
     MotionEntry* entry = mMotionEntryPool.alloc();
-    entry->refCount = 1;
-    entry->type = EventEntry::TYPE_MOTION;
+    initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime);
+
+    entry->eventTime = eventTime;
+    entry->deviceId = deviceId;
+    entry->nature = nature;
+    entry->policyFlags = policyFlags;
+    entry->action = action;
+    entry->metaState = metaState;
+    entry->edgeFlags = edgeFlags;
+    entry->xPrecision = xPrecision;
+    entry->yPrecision = yPrecision;
+    entry->downTime = downTime;
+    entry->pointerCount = pointerCount;
+    entry->firstSample.eventTime = eventTime;
     entry->firstSample.next = NULL;
+    entry->lastSample = & entry->firstSample;
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        entry->pointerIds[i] = pointerIds[i];
+        entry->firstSample.pointerCoords[i] = pointerCoords[i];
+    }
     return entry;
 }
 
@@ -1192,6 +1509,12 @@
     return entry;
 }
 
+InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) {
+    CommandEntry* entry = mCommandEntryPool.alloc();
+    entry->command = command;
+    return entry;
+}
+
 void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
     switch (entry->type) {
     case EventEntry::TYPE_CONFIGURATION_CHANGED:
@@ -1231,7 +1554,11 @@
 void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
-        freeMotionSampleList(entry->firstSample.next);
+        for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
+            MotionSample* next = sample->next;
+            mMotionSamplePool.free(sample);
+            sample = next;
+        }
         mMotionEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -1243,11 +1570,16 @@
     mDispatchEntryPool.free(entry);
 }
 
+void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) {
+    mCommandEntryPool.free(entry);
+}
+
 void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
-        nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords) {
+        nsecs_t eventTime, const PointerCoords* pointerCoords) {
     MotionSample* sample = mMotionSamplePool.alloc();
     sample->eventTime = eventTime;
-    for (int32_t i = 0; i < pointerCount; i++) {
+    uint32_t pointerCount = motionEntry->pointerCount;
+    for (uint32_t i = 0; i < pointerCount; i++) {
         sample->pointerCoords[i] = pointerCoords[i];
     }
 
@@ -1256,18 +1588,6 @@
     motionEntry->lastSample = sample;
 }
 
-void InputDispatcher::Allocator::freeMotionSample(MotionSample* sample) {
-    mMotionSamplePool.free(sample);
-}
-
-void InputDispatcher::Allocator::freeMotionSampleList(MotionSample* head) {
-    while (head) {
-        MotionSample* next = head->next;
-        mMotionSamplePool.free(head);
-        head = next;
-    }
-}
-
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
@@ -1284,6 +1604,29 @@
     return inputPublisher.initialize();
 }
 
+void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
+    nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
+}
+
+const char* InputDispatcher::Connection::getStatusLabel() const {
+    switch (status) {
+    case STATUS_NORMAL:
+        return "NORMAL";
+
+    case STATUS_BROKEN:
+        return "BROKEN";
+
+    case STATUS_NOT_RESPONDING:
+        return "NOT_RESPONDING";
+
+    case STATUS_ZOMBIE:
+        return "ZOMBIE";
+
+    default:
+        return "UNKNOWN";
+    }
+}
+
 InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
         const EventEntry* eventEntry) const {
     for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev;
@@ -1295,6 +1638,14 @@
     return NULL;
 }
 
+// --- InputDispatcher::CommandEntry ---
+
+InputDispatcher::CommandEntry::CommandEntry() {
+}
+
+InputDispatcher::CommandEntry::~CommandEntry() {
+}
+
 
 // --- InputDispatcherThread ---
 
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index ab354a5..32c58b4 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -14,29 +14,30 @@
 
 namespace android {
 
-InputManager::InputManager(const sp<EventHubInterface>& eventHub,
-        const sp<InputDispatchPolicyInterface>& policy) :
-        mEventHub(eventHub), mPolicy(policy) {
-    mDispatcher = new InputDispatcher(policy);
-    mReader = new InputReader(eventHub, policy, mDispatcher);
+InputManager::InputManager(
+        const sp<EventHubInterface>& eventHub,
+        const sp<InputReaderPolicyInterface>& readerPolicy,
+        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
+    mDispatcher = new InputDispatcher(dispatcherPolicy);
+    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
+    initialize();
+}
 
-    mDispatcherThread = new InputDispatcherThread(mDispatcher);
-    mReaderThread = new InputReaderThread(mReader);
-
-    configureExcludedDevices();
+InputManager::InputManager(
+        const sp<InputReaderInterface>& reader,
+        const sp<InputDispatcherInterface>& dispatcher) :
+        mReader(reader),
+        mDispatcher(dispatcher) {
+    initialize();
 }
 
 InputManager::~InputManager() {
     stop();
 }
 
-void InputManager::configureExcludedDevices() {
-    Vector<String8> excludedDeviceNames;
-    mPolicy->getExcludedDeviceNames(excludedDeviceNames);
-
-    for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
-        mEventHub->addExcludedDevice(excludedDeviceNames[i]);
-    }
+void InputManager::initialize() {
+    mReaderThread = new InputReaderThread(mReader);
+    mDispatcherThread = new InputDispatcherThread(mDispatcher);
 }
 
 status_t InputManager::start() {
@@ -79,36 +80,31 @@
     return mDispatcher->unregisterInputChannel(inputChannel);
 }
 
-int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode)
-    const {
-    int32_t vkKeyCode, vkScanCode;
-    if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) {
-        if (vkScanCode == scanCode) {
-            return KEY_STATE_VIRTUAL;
-        }
-    }
-
-    return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode);
+int32_t InputManager::injectInputEvent(const InputEvent* event,
+        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
+    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
 }
 
-int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, int32_t keyCode)
-    const {
-    int32_t vkKeyCode, vkScanCode;
-    if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) {
-        if (vkKeyCode == keyCode) {
-            return KEY_STATE_VIRTUAL;
-        }
-    }
+void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
+    mReader->getCurrentInputConfiguration(outConfiguration);
+}
 
-    return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
+int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t scanCode) const {
+    return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode);
+}
+
+int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t keyCode) const {
+    return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode);
 }
 
 int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
-    return mEventHub->getSwitchState(deviceId, deviceClasses, sw);
+    return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw);
 }
 
 bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
-    return mEventHub->hasKeys(numCodes, keyCodes, outFlags);
+    return mReader->hasKeys(numCodes, keyCodes, outFlags);
 }
 
 } // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 796abd0..1824054 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -19,6 +19,9 @@
 // Log debug messages about pointers.
 #define DEBUG_POINTERS 1
 
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
 #include <cutils/log.h>
 #include <ui/InputReader.h>
 
@@ -27,6 +30,22 @@
 #include <errno.h>
 #include <limits.h>
 
+/** Amount that trackball needs to move in order to generate a key event. */
+#define TRACKBALL_MOVEMENT_THRESHOLD 6
+
+/* Slop distance for jumpy pointer detection.
+ * The vertical range of the screen divided by this is our epsilon value. */
+#define JUMPY_EPSILON_DIVISOR 212
+
+/* Number of jumpy points to drop for touchscreens that need it. */
+#define JUMPY_TRANSITION_DROPS 3
+#define JUMPY_DROP_LIMIT 3
+
+/* Maximum squared distance for averaging.
+ * If moving farther than this, turn of averaging to avoid lag in response. */
+#define AVERAGING_DISTANCE_LIMIT (75 * 75)
+
+
 namespace android {
 
 // --- Static Functions ---
@@ -41,6 +60,14 @@
     return a < b ? a : b;
 }
 
+template<typename T>
+inline static void swap(T& a, T& b) {
+    T temp = a;
+    a = b;
+    b = temp;
+}
+
+
 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
     int32_t mask;
     switch (keyCode) {
@@ -89,7 +116,7 @@
         sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
 
 int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
-    if (orientation != InputDispatchPolicyInterface::ROTATION_0) {
+    if (orientation != InputReaderPolicyInterface::ROTATION_0) {
         for (int i = 0; i < keyCodeRotationMapSize; i++) {
             if (keyCode == keyCodeRotationMap[i][0]) {
                 return keyCodeRotationMap[i][orientation];
@@ -172,6 +199,12 @@
     jumpyTouchFilter.jumpyPointsDropped = 0;
 }
 
+struct PointerDistanceHeapElement {
+    uint32_t currentPointerIndex : 8;
+    uint32_t lastPointerIndex : 8;
+    uint64_t distance : 48; // squared distance
+};
+
 void InputDevice::TouchScreenState::calculatePointerIds() {
     uint32_t currentPointerCount = currentTouch.pointerCount;
     uint32_t lastPointerCount = lastTouch.pointerCount;
@@ -198,11 +231,7 @@
         // We build a heap of squared euclidean distances between current and last pointers
         // associated with the current and last pointer indices.  Then, we find the best
         // match (by distance) for each current pointer.
-        struct {
-            uint32_t currentPointerIndex : 8;
-            uint32_t lastPointerIndex : 8;
-            uint64_t distance : 48; // squared distance
-        } heap[MAX_POINTERS * MAX_POINTERS];
+        PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
 
         uint32_t heapSize = 0;
         for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
@@ -217,23 +246,45 @@
                 uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
 
                 // Insert new element into the heap (sift up).
+                heap[heapSize].currentPointerIndex = currentPointerIndex;
+                heap[heapSize].lastPointerIndex = lastPointerIndex;
+                heap[heapSize].distance = distance;
                 heapSize += 1;
-                uint32_t insertionIndex = heapSize;
-                while (insertionIndex > 1) {
-                    uint32_t parentIndex = (insertionIndex - 1) / 2;
-                    if (distance < heap[parentIndex].distance) {
-                        heap[insertionIndex] = heap[parentIndex];
-                        insertionIndex = parentIndex;
-                    } else {
-                        break;
-                    }
-                }
-                heap[insertionIndex].currentPointerIndex = currentPointerIndex;
-                heap[insertionIndex].lastPointerIndex = lastPointerIndex;
-                heap[insertionIndex].distance = distance;
             }
         }
 
+        // Heapify
+        for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+            startIndex -= 1;
+            for (uint32_t parentIndex = startIndex; ;) {
+                uint32_t childIndex = parentIndex * 2 + 1;
+                if (childIndex >= heapSize) {
+                    break;
+                }
+
+                if (childIndex + 1 < heapSize
+                        && heap[childIndex + 1].distance < heap[childIndex].distance) {
+                    childIndex += 1;
+                }
+
+                if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                    break;
+                }
+
+                swap(heap[parentIndex], heap[childIndex]);
+                parentIndex = childIndex;
+            }
+        }
+
+#if DEBUG_POINTER_ASSIGNMENT
+        LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+        for (size_t i = 0; i < heapSize; i++) {
+            LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                    i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                    heap[i].distance);
+        }
+#endif
+
         // Pull matches out by increasing order of distance.
         // To avoid reassigning pointers that have already been matched, the loop keeps track
         // of which last and current pointers have been matched using the matchedXXXBits variables.
@@ -246,7 +297,7 @@
             for (;;) {
                 if (first) {
                     // The first time through the loop, we just consume the root element of
-                    // the heap (the one with smalled distance).
+                    // the heap (the one with smallest distance).
                     first = false;
                 } else {
                     // Previous iterations consumed the root element of the heap.
@@ -254,10 +305,10 @@
                     heapSize -= 1;
                     assert(heapSize > 0);
 
-                    // Sift down to find where the element at index heapSize needs to be moved.
-                    uint32_t rootIndex = 0;
-                    for (;;) {
-                        uint32_t childIndex = rootIndex * 2 + 1;
+                    // Sift down.
+                    heap[0] = heap[heapSize];
+                    for (uint32_t parentIndex = 0; ;) {
+                        uint32_t childIndex = parentIndex * 2 + 1;
                         if (childIndex >= heapSize) {
                             break;
                         }
@@ -267,14 +318,22 @@
                             childIndex += 1;
                         }
 
-                        if (heap[heapSize].distance < heap[childIndex].distance) {
+                        if (heap[parentIndex].distance <= heap[childIndex].distance) {
                             break;
                         }
 
-                        heap[rootIndex] = heap[childIndex];
-                        rootIndex = childIndex;
+                        swap(heap[parentIndex], heap[childIndex]);
+                        parentIndex = childIndex;
                     }
-                    heap[rootIndex] = heap[heapSize];
+
+#if DEBUG_POINTER_ASSIGNMENT
+                    LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+                    for (size_t i = 0; i < heapSize; i++) {
+                        LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                                heap[i].distance);
+                    }
+#endif
                 }
 
                 uint32_t currentPointerIndex = heap[0].currentPointerIndex;
@@ -290,6 +349,11 @@
                 currentTouch.pointers[currentPointerIndex].id = id;
                 currentTouch.idToIndex[id] = currentPointerIndex;
                 usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+                        lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
                 break;
             }
         }
@@ -304,6 +368,11 @@
                 currentTouch.idToIndex[id] = currentPointerIndex;
                 usedIdBits.markBit(id);
 
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+                        currentPointerIndex, id);
+#endif
+
                 if (--i == 0) break; // done
                 matchedCurrentBits.markBit(currentPointerIndex);
             }
@@ -677,12 +746,13 @@
 // --- InputReader ---
 
 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
-        const sp<InputDispatchPolicyInterface>& policy,
+        const sp<InputReaderPolicyInterface>& policy,
         const sp<InputDispatcherInterface>& dispatcher) :
         mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) {
+    configureExcludedDevices();
     resetGlobalMetaState();
     resetDisplayProperties();
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
 }
 
 InputReader::~InputReader() {
@@ -925,7 +995,7 @@
     InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
     if (! device) return;
 
-    onSwitch(rawEvent->when, device, rawEvent->value != 0, rawEvent->scanCode);
+    onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value);
 }
 
 void InputReader::onKey(nsecs_t when, InputDevice* device,
@@ -974,7 +1044,7 @@
     }
 
     int32_t keyEventFlags = KEY_EVENT_FLAG_FROM_SYSTEM;
-    if (policyActions & InputDispatchPolicyInterface::ACTION_WOKE_HERE) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
         keyEventFlags = keyEventFlags | KEY_EVENT_FLAG_WOKE_HERE;
     }
 
@@ -984,12 +1054,12 @@
             device->keyboard.current.downTime);
 }
 
-void InputReader::onSwitch(nsecs_t when, InputDevice* device, bool down,
-        int32_t code) {
-    switch (code) {
-    case SW_LID:
-        mDispatcher->notifyLidSwitchChanged(when, ! down);
-    }
+void InputReader::onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode,
+        int32_t switchValue) {
+    int32_t policyActions = mPolicy->interceptSwitch(when, switchCode, switchValue);
+
+    uint32_t policyFlags = 0;
+    applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags);
 }
 
 void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
@@ -1191,8 +1261,10 @@
 
         int32_t x = device->touchScreen.currentTouch.pointers[0].x;
         int32_t y = device->touchScreen.currentTouch.pointers[0].y;
-        if (device->touchScreen.isPointInsideDisplay(x, y)) {
-            // Pointer moved inside the display area.  Send key cancellation.
+        if (device->touchScreen.isPointInsideDisplay(x, y)
+                || device->touchScreen.currentTouch.pointerCount != 1) {
+            // Pointer moved inside the display area or another pointer also went down.
+            // Send key cancellation.
             device->touchScreen.currentVirtualKey.down = false;
 
 #if DEBUG_VIRTUAL_KEYS
@@ -1210,7 +1282,7 @@
             device->touchScreen.lastTouch.clear();
             return false; // not consumed
         }
-    } else if (device->touchScreen.currentTouch.pointerCount > 0
+    } else if (device->touchScreen.currentTouch.pointerCount == 1
             && device->touchScreen.lastTouch.pointerCount == 0) {
         int32_t x = device->touchScreen.currentTouch.pointers[0].x;
         int32_t y = device->touchScreen.currentTouch.pointers[0].y;
@@ -1256,7 +1328,7 @@
     nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
     int32_t metaState = globalMetaState();
 
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
 
     mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
             keyCode, scanCode, metaState, downTime);
@@ -1333,8 +1405,8 @@
         int32_t motionEventAction) {
     int32_t orientedWidth, orientedHeight;
     switch (mDisplayOrientation) {
-    case InputDispatchPolicyInterface::ROTATION_90:
-    case InputDispatchPolicyInterface::ROTATION_270:
+    case InputReaderPolicyInterface::ROTATION_90:
+    case InputReaderPolicyInterface::ROTATION_270:
         orientedWidth = mDisplayHeight;
         orientedHeight = mDisplayWidth;
         break;
@@ -1369,20 +1441,20 @@
                 * device->touchScreen.precalculated.sizeScale;
 
         switch (mDisplayOrientation) {
-        case InputDispatchPolicyInterface::ROTATION_90: {
+        case InputReaderPolicyInterface::ROTATION_90: {
             float xTemp = x;
             x = y;
-            y = mDisplayHeight - xTemp;
+            y = mDisplayWidth - xTemp;
             break;
         }
-        case InputDispatchPolicyInterface::ROTATION_180: {
+        case InputReaderPolicyInterface::ROTATION_180: {
             x = mDisplayWidth - x;
             y = mDisplayHeight - y;
             break;
         }
-        case InputDispatchPolicyInterface::ROTATION_270: {
+        case InputReaderPolicyInterface::ROTATION_270: {
             float xTemp = x;
-            x = mDisplayWidth - y;
+            x = mDisplayHeight - y;
             y = xTemp;
             break;
         }
@@ -1438,7 +1510,7 @@
 
     uint32_t fields = device->trackball.accumulator.fields;
     bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
-    bool deltaChanged = (fields & DELTA_FIELDS) == DELTA_FIELDS;
+    bool deltaChanged = fields & DELTA_FIELDS;
 
     bool down;
     if (downChanged) {
@@ -1474,27 +1546,27 @@
 
     int32_t pointerId = 0;
     PointerCoords pointerCoords;
-    pointerCoords.x = device->trackball.accumulator.relX
-            * device->trackball.precalculated.xScale;
-    pointerCoords.y = device->trackball.accumulator.relY
-            * device->trackball.precalculated.yScale;
+    pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X
+            ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0;
+    pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y
+            ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0;
     pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
     pointerCoords.size = 0;
 
     float temp;
     switch (mDisplayOrientation) {
-    case InputDispatchPolicyInterface::ROTATION_90:
+    case InputReaderPolicyInterface::ROTATION_90:
         temp = pointerCoords.x;
         pointerCoords.x = pointerCoords.y;
         pointerCoords.y = - temp;
         break;
 
-    case InputDispatchPolicyInterface::ROTATION_180:
+    case InputReaderPolicyInterface::ROTATION_180:
         pointerCoords.x = - pointerCoords.x;
         pointerCoords.y = - pointerCoords.y;
         break;
 
-    case InputDispatchPolicyInterface::ROTATION_270:
+    case InputReaderPolicyInterface::ROTATION_270:
         temp = pointerCoords.x;
         pointerCoords.x = - pointerCoords.y;
         pointerCoords.y = temp;
@@ -1514,51 +1586,30 @@
     resetGlobalMetaState();
 
     // Reset virtual keys, just in case.
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
+
+    // Update input configuration.
+    updateExportedInputConfiguration();
 
     // Enqueue configuration changed.
-    // XXX This stuff probably needs to be tracked elsewhere in an input device registry
-    //     of some kind that can be asynchronously updated and queried.  (Same as above?)
-    int32_t touchScreenConfig = InputDispatchPolicyInterface::TOUCHSCREEN_NOTOUCH;
-    int32_t keyboardConfig = InputDispatchPolicyInterface::KEYBOARD_NOKEYS;
-    int32_t navigationConfig = InputDispatchPolicyInterface::NAVIGATION_NONAV;
-
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        int32_t deviceClasses = device->classes;
-
-        if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
-            touchScreenConfig = InputDispatchPolicyInterface::TOUCHSCREEN_FINGER;
-        }
-        if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) {
-            keyboardConfig = InputDispatchPolicyInterface::KEYBOARD_QWERTY;
-        }
-        if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) {
-            navigationConfig = InputDispatchPolicyInterface::NAVIGATION_TRACKBALL;
-        } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) {
-            navigationConfig = InputDispatchPolicyInterface::NAVIGATION_DPAD;
-        }
-    }
-
-    mDispatcher->notifyConfigurationChanged(when, touchScreenConfig,
-            keyboardConfig, navigationConfig);
+    mDispatcher->notifyConfigurationChanged(when);
 }
 
 bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when,
         int32_t policyActions, uint32_t* policyFlags) {
-    if (policyActions & InputDispatchPolicyInterface::ACTION_APP_SWITCH_COMING) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
         mDispatcher->notifyAppSwitchComing(when);
     }
 
-    if (policyActions & InputDispatchPolicyInterface::ACTION_WOKE_HERE) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
         *policyFlags |= POLICY_FLAG_WOKE_HERE;
     }
 
-    if (policyActions & InputDispatchPolicyInterface::ACTION_BRIGHT_HERE) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) {
         *policyFlags |= POLICY_FLAG_BRIGHT_HERE;
     }
 
-    return policyActions & InputDispatchPolicyInterface::ACTION_DISPATCH;
+    return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
 }
 
 void InputReader::resetDisplayProperties() {
@@ -1706,7 +1757,7 @@
 void InputReader::configureVirtualKeys(InputDevice* device) {
     device->touchScreen.virtualKeys.clear();
 
-    Vector<InputDispatchPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
+    Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
     mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions);
     if (virtualKeyDefinitions.size() == 0) {
         return;
@@ -1720,7 +1771,7 @@
     int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range;
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
-        const InputDispatchPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
+        const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
                 virtualKeyDefinitions[i];
 
         device->touchScreen.virtualKeys.add();
@@ -1779,6 +1830,15 @@
     LOGI("  %s: unknown axis values, setting to zero", name);
 }
 
+void InputReader::configureExcludedDevices() {
+    Vector<String8> excludedDeviceNames;
+    mPolicy->getExcludedDeviceNames(excludedDeviceNames);
+
+    for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
+        mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+    }
+}
+
 void InputReader::resetGlobalMetaState() {
     mGlobalMetaState = -1;
 }
@@ -1796,7 +1856,7 @@
     return mGlobalMetaState;
 }
 
-void InputReader::updateGlobalVirtualKeyState() {
+void InputReader::updateExportedVirtualKeyState() {
     int32_t keyCode = -1, scanCode = -1;
 
     for (size_t i = 0; i < mDevices.size(); i++) {
@@ -1809,20 +1869,96 @@
         }
     }
 
-    {
+    { // acquire exported state lock
         AutoMutex _l(mExportedStateLock);
 
-        mGlobalVirtualKeyCode = keyCode;
-        mGlobalVirtualScanCode = scanCode;
-    }
+        mExportedVirtualKeyCode = keyCode;
+        mExportedVirtualScanCode = scanCode;
+    } // release exported state lock
 }
 
 bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const {
-    AutoMutex _l(mExportedStateLock);
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
 
-    *outKeyCode = mGlobalVirtualKeyCode;
-    *outScanCode = mGlobalVirtualScanCode;
-    return mGlobalVirtualKeyCode != -1;
+        *outKeyCode = mExportedVirtualKeyCode;
+        *outScanCode = mExportedVirtualScanCode;
+        return mExportedVirtualKeyCode != -1;
+    } // release exported state lock
+}
+
+void InputReader::updateExportedInputConfiguration() {
+    int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+    int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+    int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        int32_t deviceClasses = device->classes;
+
+        if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
+            touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+        }
+        if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) {
+            keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
+        }
+        if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) {
+            navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+        } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) {
+            navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+        }
+    }
+
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        mExportedInputConfiguration.touchScreen = touchScreenConfig;
+        mExportedInputConfiguration.keyboard = keyboardConfig;
+        mExportedInputConfiguration.navigation = navigationConfig;
+    } // release exported state lock
+}
+
+void InputReader::getCurrentInputConfiguration(InputConfiguration* outConfiguration) const {
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        *outConfiguration = mExportedInputConfiguration;
+    } // release exported state lock
+}
+
+int32_t InputReader::getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t scanCode) const {
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        if (mExportedVirtualScanCode == scanCode) {
+            return KEY_STATE_VIRTUAL;
+        }
+    } // release exported state lock
+
+    return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode);
+}
+
+int32_t InputReader::getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t keyCode) const {
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        if (mExportedVirtualKeyCode == keyCode) {
+            return KEY_STATE_VIRTUAL;
+        }
+    } // release exported state lock
+
+    return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
+}
+
+int32_t InputReader::getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
+        int32_t sw) const {
+    return mEventHub->getSwitchState(deviceId, deviceClasses, sw);
+}
+
+bool InputReader::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
+    return mEventHub->hasKeys(numCodes, keyCodes, outFlags);
 }
 
 
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index a24180f..86bbd37 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -8,13 +8,13 @@
 //#define LOG_NDEBUG 0
 
 // Log debug messages about channel signalling (send signal, receive signal)
-#define DEBUG_CHANNEL_SIGNALS 1
+#define DEBUG_CHANNEL_SIGNALS 0
 
 // Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 1
+#define DEBUG_CHANNEL_LIFECYCLE 0
 
 // Log debug messages about transport actions (initialize, reset, publish, ...)
-#define DEBUG_TRANSPORT_ACTIONS 1
+#define DEBUG_TRANSPORT_ACTIONS 0
 
 
 #include <cutils/ashmem.h>
@@ -70,7 +70,7 @@
 }
 
 status_t InputChannel::openInputChannelPair(const String8& name,
-        InputChannel** outServerChannel, InputChannel** outClientChannel) {
+        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
     status_t result;
 
     int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
@@ -107,12 +107,12 @@
                     } else {
                         String8 serverChannelName = name;
                         serverChannelName.append(" (server)");
-                        *outServerChannel = new InputChannel(serverChannelName,
+                        outServerChannel = new InputChannel(serverChannelName,
                                 serverAshmemFd, reverse[0], forward[1]);
 
                         String8 clientChannelName = name;
                         clientChannelName.append(" (client)");
-                        *outClientChannel = new InputChannel(clientChannelName,
+                        outClientChannel = new InputChannel(clientChannelName,
                                 clientAshmemFd, forward[0], reverse[1]);
                         return OK;
                     }
@@ -125,8 +125,8 @@
         ::close(serverAshmemFd);
     }
 
-    *outServerChannel = NULL;
-    *outClientChannel = NULL;
+    outServerChannel.clear();
+    outClientChannel.clear();
     return result;
 }
 
@@ -155,6 +155,13 @@
         return OK;
     }
 
+    if (nRead == 0) { // check for EOF
+#if DEBUG_CHANNEL_SIGNALS
+        LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
+#endif
+        return DEAD_OBJECT;
+    }
+
     if (errno == EAGAIN) {
 #if DEBUG_CHANNEL_SIGNALS
         LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
@@ -535,13 +542,13 @@
     return OK;
 }
 
-status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** event) {
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
 #if DEBUG_TRANSPORT_ACTIONS
     LOGD("channel '%s' consumer ~ consume",
             mChannel->getName().string());
 #endif
 
-    *event = NULL;
+    *outEvent = NULL;
 
     int ashmemFd = mChannel->getAshmemFd();
     int result = ashmem_pin_region(ashmemFd, 0, 0);
@@ -583,7 +590,7 @@
 
         populateKeyEvent(keyEvent);
 
-        *event = keyEvent;
+        *outEvent = keyEvent;
         break;
     }
 
@@ -593,7 +600,7 @@
 
         populateMotionEvent(motionEvent);
 
-        *event = motionEvent;
+        *outEvent = motionEvent;
         break;
     }
 
@@ -655,8 +662,8 @@
             mSharedMessage->motion.action,
             mSharedMessage->motion.edgeFlags,
             mSharedMessage->motion.metaState,
-            mSharedMessage->motion.sampleData[0].coords[0].x,
-            mSharedMessage->motion.sampleData[0].coords[0].y,
+            mSharedMessage->motion.xOffset,
+            mSharedMessage->motion.yOffset,
             mSharedMessage->motion.xPrecision,
             mSharedMessage->motion.yPrecision,
             mSharedMessage->motion.downTime,
@@ -676,9 +683,6 @@
             motionEvent->addSample(sampleData->eventTime, sampleData->coords);
         }
     }
-
-    motionEvent->offsetLocation(mSharedMessage->motion.xOffset,
-            mSharedMessage->motion.yOffset);
 }
 
 } // namespace android
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 1ff896b..46d7493 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -3,7 +3,9 @@
 include $(CLEAR_VARS)
 
 test_src_files := \
-    InputDispatcher_test.cpp
+    InputChannel_test.cpp \
+    InputDispatcher_test.cpp \
+    InputPublisherAndConsumer_test.cpp
 
 shared_libraries := \
 	libcutils \
diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp
new file mode 100644
index 0000000..6cec1c0
--- /dev/null
+++ b/libs/ui/tests/InputChannel_test.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputChannelTest : public testing::Test {
+protected:
+    virtual void SetUp() { }
+    virtual void TearDown() { }
+};
+
+
+TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
+    // Our purpose here is to verify that the input channel destructor closes the
+    // file descriptors provided to it.  One easy way is to provide it with one end
+    // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
+    Pipe fakeAshmem, sendPipe, receivePipe;
+
+    sp<InputChannel> inputChannel = new InputChannel(String8("channel name"),
+            fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd);
+
+    EXPECT_STREQ("channel name", inputChannel->getName().string())
+            << "channel should have provided name";
+    EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd())
+            << "channel should have provided ashmem fd";
+    EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd())
+            << "channel should have provided receive pipe fd";
+    EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd())
+            << "channel should have provided send pipe fd";
+
+    inputChannel.clear(); // destroys input channel
+
+    EXPECT_EQ(-EPIPE, fakeAshmem.readSignal())
+            << "channel should have closed ashmem fd when destroyed";
+    EXPECT_EQ(-EPIPE, receivePipe.writeSignal())
+            << "channel should have closed receive pipe fd when destroyed";
+    EXPECT_EQ(-EPIPE, sendPipe.readSignal())
+            << "channel should have closed send pipe fd when destroyed";
+
+    // clean up fds of Pipe endpoints that were closed so we don't try to close them again
+    fakeAshmem.sendFd = -1;
+    receivePipe.receiveFd = -1;
+    sendPipe.sendFd = -1;
+}
+
+TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    // Name
+    EXPECT_STREQ("channel name (server)", serverChannel->getName().string())
+            << "server channel should have suffixed name";
+    EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
+            << "client channel should have suffixed name";
+
+    // Ashmem uniqueness
+    EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd())
+            << "server and client channel should have different ashmem fds because it was dup'd";
+
+    // Ashmem usability
+    ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd());
+    ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd());
+    uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize,
+            PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0));
+    uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize,
+            PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0));
+    ASSERT_TRUE(serverAshmem != NULL)
+            << "server channel ashmem should be mappable";
+    ASSERT_TRUE(clientAshmem != NULL)
+            << "client channel ashmem should be mappable";
+    *serverAshmem = 0xf00dd00d;
+    EXPECT_EQ(0xf00dd00d, *clientAshmem)
+            << "ashmem buffer should be shared by client and server";
+    munmap(serverAshmem, serverAshmemSize);
+    munmap(clientAshmem, clientAshmemSize);
+
+    // Server->Client communication
+    EXPECT_EQ(OK, serverChannel->sendSignal('S'))
+            << "server channel should be able to send signal to client channel";
+    char signal;
+    EXPECT_EQ(OK, clientChannel->receiveSignal(& signal))
+            << "client channel should be able to receive signal from server channel";
+    EXPECT_EQ('S', signal)
+            << "client channel should receive the correct signal from server channel";
+
+    // Client->Server communication
+    EXPECT_EQ(OK, clientChannel->sendSignal('c'))
+            << "client channel should be able to send signal to server channel";
+    EXPECT_EQ(OK, serverChannel->receiveSignal(& signal))
+            << "server channel should be able to receive signal from client channel";
+    EXPECT_EQ('c', signal)
+            << "server channel should receive the correct signal from client channel";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    char signal;
+    EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal))
+            << "receiveSignal should have returned WOULD_BLOCK";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    serverChannel.clear(); // close server channel
+
+    char signal;
+    EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal))
+            << "receiveSignal should have returned DEAD_OBJECT";
+}
+
+TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
+    sp<InputChannel> serverChannel, clientChannel;
+
+    status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+            serverChannel, clientChannel);
+
+    ASSERT_EQ(OK, result)
+            << "should have successfully opened a channel pair";
+
+    serverChannel.clear(); // close server channel
+
+    EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S'))
+            << "sendSignal should have returned DEAD_OBJECT";
+}
+
+
+} // namespace android
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
index 3d92043..1dc6e46 100644
--- a/libs/ui/tests/InputDispatcher_test.cpp
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -12,8 +12,7 @@
 };
 
 TEST_F(InputDispatcherTest, Dummy) {
-    SCOPED_TRACE("Trace");
-    ASSERT_FALSE(true);
+    // TODO
 }
 
 } // namespace android
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
new file mode 100644
index 0000000..2d6b531
--- /dev/null
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -0,0 +1,449 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputPublisherAndConsumerTest : public testing::Test {
+protected:
+    sp<InputChannel> serverChannel, clientChannel;
+    InputPublisher* mPublisher;
+    InputConsumer* mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
+
+    virtual void SetUp() {
+        status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+                serverChannel, clientChannel);
+
+        mPublisher = new InputPublisher(serverChannel);
+        mConsumer = new InputConsumer(clientChannel);
+    }
+
+    virtual void TearDown() {
+        if (mPublisher) {
+            delete mPublisher;
+            mPublisher = NULL;
+        }
+
+        if (mConsumer) {
+            delete mConsumer;
+            mConsumer = NULL;
+        }
+
+        serverChannel.clear();
+        clientChannel.clear();
+    }
+
+    void Initialize();
+    void PublishAndConsumeKeyEvent();
+    void PublishAndConsumeMotionEvent(
+            size_t samplesToAppendBeforeDispatch = 0,
+            size_t samplesToAppendAfterDispatch = 0);
+};
+
+TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
+    EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
+    EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+}
+
+void InputPublisherAndConsumerTest::Initialize() {
+    status_t status;
+
+    status = mPublisher->initialize();
+    ASSERT_EQ(OK, status)
+            << "publisher initialize should return OK";
+
+    status = mConsumer->initialize();
+    ASSERT_EQ(OK, status)
+            << "consumer initialize should return OK";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
+    status_t status;
+
+    const int32_t deviceId = 1;
+    const int32_t nature = INPUT_EVENT_NATURE_KEY;
+    const int32_t action = KEY_EVENT_ACTION_DOWN;
+    const int32_t flags = KEY_EVENT_FLAG_FROM_SYSTEM;
+    const int32_t keyCode = KEYCODE_ENTER;
+    const int32_t scanCode = 13;
+    const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON;
+    const int32_t repeatCount = 1;
+    const nsecs_t downTime = 3;
+    const nsecs_t eventTime = 4;
+
+    status = mPublisher->publishKeyEvent(deviceId, nature, action, flags,
+            keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+    ASSERT_EQ(OK, status)
+            << "publisher publishKeyEvent should return OK";
+
+    status = mPublisher->sendDispatchSignal();
+    ASSERT_EQ(OK, status)
+            << "publisher sendDispatchSignal should return OK";
+
+    status = mConsumer->receiveDispatchSignal();
+    ASSERT_EQ(OK, status)
+            << "consumer receiveDispatchSignal should return OK";
+
+    InputEvent* event;
+    status = mConsumer->consume(& mEventFactory, & event);
+    ASSERT_EQ(OK, status)
+            << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != NULL)
+            << "consumer should have returned non-NULL event";
+    ASSERT_EQ(INPUT_EVENT_TYPE_KEY, event->getType())
+            << "consumer should have returned a key event";
+
+    KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
+    EXPECT_EQ(deviceId, keyEvent->getDeviceId());
+    EXPECT_EQ(nature, keyEvent->getNature());
+    EXPECT_EQ(action, keyEvent->getAction());
+    EXPECT_EQ(flags, keyEvent->getFlags());
+    EXPECT_EQ(keyCode, keyEvent->getKeyCode());
+    EXPECT_EQ(scanCode, keyEvent->getScanCode());
+    EXPECT_EQ(metaState, keyEvent->getMetaState());
+    EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
+    EXPECT_EQ(downTime, keyEvent->getDownTime());
+    EXPECT_EQ(eventTime, keyEvent->getEventTime());
+
+    status = mConsumer->sendFinishedSignal();
+    ASSERT_EQ(OK, status)
+            << "consumer sendFinishedSignal should return OK";
+
+    status = mPublisher->receiveFinishedSignal();
+    ASSERT_EQ(OK, status)
+            << "publisher receiveFinishedSignal should return OK";
+
+    status = mPublisher->reset();
+    ASSERT_EQ(OK, status)
+            << "publisher reset should return OK";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
+        size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) {
+    status_t status;
+
+    const int32_t deviceId = 1;
+    const int32_t nature = INPUT_EVENT_NATURE_TOUCH;
+    const int32_t action = MOTION_EVENT_ACTION_MOVE;
+    const int32_t edgeFlags = MOTION_EVENT_EDGE_FLAG_TOP;
+    const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON;
+    const float xOffset = -10;
+    const float yOffset = -20;
+    const float xPrecision = 0.25;
+    const float yPrecision = 0.5;
+    const nsecs_t downTime = 3;
+    const size_t pointerCount = 3;
+    const int32_t pointerIds[pointerCount] = { 2, 0, 1 };
+
+    Vector<nsecs_t> sampleEventTimes;
+    Vector<PointerCoords> samplePointerCoords;
+
+    for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) {
+        sampleEventTimes.push(i + 10);
+        for (size_t j = 0; j < pointerCount; j++) {
+            samplePointerCoords.push();
+            samplePointerCoords.editTop().x = 100 * i + j;
+            samplePointerCoords.editTop().y = 200 * i + j;
+            samplePointerCoords.editTop().pressure = 0.5 * i + j;
+            samplePointerCoords.editTop().size = 0.7 * i + j;
+        }
+    }
+
+    status = mPublisher->publishMotionEvent(deviceId, nature, action, edgeFlags,
+            metaState, xOffset, yOffset, xPrecision, yPrecision,
+            downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array());
+    ASSERT_EQ(OK, status)
+            << "publisher publishMotionEvent should return OK";
+
+    for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) {
+        size_t sampleIndex = i + 1;
+        status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
+                samplePointerCoords.array() + sampleIndex * pointerCount);
+        ASSERT_EQ(OK, status)
+                << "publisher appendMotionEvent should return OK";
+    }
+
+    status = mPublisher->sendDispatchSignal();
+    ASSERT_EQ(OK, status)
+            << "publisher sendDispatchSignal should return OK";
+
+    for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) {
+        size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch;
+        status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
+                samplePointerCoords.array() + sampleIndex * pointerCount);
+        ASSERT_EQ(OK, status)
+                << "publisher appendMotionEvent should return OK";
+    }
+
+    status = mConsumer->receiveDispatchSignal();
+    ASSERT_EQ(OK, status)
+            << "consumer receiveDispatchSignal should return OK";
+
+    InputEvent* event;
+    status = mConsumer->consume(& mEventFactory, & event);
+    ASSERT_EQ(OK, status)
+            << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != NULL)
+            << "consumer should have returned non-NULL event";
+    ASSERT_EQ(INPUT_EVENT_TYPE_MOTION, event->getType())
+            << "consumer should have returned a motion event";
+
+    size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch;
+
+    MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
+    EXPECT_EQ(deviceId, motionEvent->getDeviceId());
+    EXPECT_EQ(nature, motionEvent->getNature());
+    EXPECT_EQ(action, motionEvent->getAction());
+    EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
+    EXPECT_EQ(metaState, motionEvent->getMetaState());
+    EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
+    EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
+    EXPECT_EQ(downTime, motionEvent->getDownTime());
+    EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
+    EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
+    EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize());
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        SCOPED_TRACE(i);
+        EXPECT_EQ(pointerIds[i], motionEvent->getPointerId(i));
+    }
+
+    for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) {
+        SCOPED_TRACE(sampleIndex);
+        EXPECT_EQ(sampleEventTimes[sampleIndex],
+                motionEvent->getHistoricalEventTime(sampleIndex));
+        for (size_t i = 0; i < pointerCount; i++) {
+            SCOPED_TRACE(i);
+            size_t offset = sampleIndex * pointerCount + i;
+            EXPECT_EQ(samplePointerCoords[offset].x,
+                    motionEvent->getHistoricalRawX(i, sampleIndex));
+            EXPECT_EQ(samplePointerCoords[offset].y,
+                    motionEvent->getHistoricalRawY(i, sampleIndex));
+            EXPECT_EQ(samplePointerCoords[offset].x + xOffset,
+                    motionEvent->getHistoricalX(i, sampleIndex));
+            EXPECT_EQ(samplePointerCoords[offset].y + yOffset,
+                    motionEvent->getHistoricalY(i, sampleIndex));
+            EXPECT_EQ(samplePointerCoords[offset].pressure,
+                    motionEvent->getHistoricalPressure(i, sampleIndex));
+            EXPECT_EQ(samplePointerCoords[offset].size,
+                    motionEvent->getHistoricalSize(i, sampleIndex));
+        }
+    }
+
+    SCOPED_TRACE(lastSampleIndex);
+    EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
+    for (size_t i = 0; i < pointerCount; i++) {
+        SCOPED_TRACE(i);
+        size_t offset = lastSampleIndex * pointerCount + i;
+        EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i));
+        EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i));
+        EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i));
+        EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i));
+        EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i));
+        EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i));
+    }
+
+    status = mConsumer->sendFinishedSignal();
+    ASSERT_EQ(OK, status)
+            << "consumer sendFinishedSignal should return OK";
+
+    status = mPublisher->receiveFinishedSignal();
+    ASSERT_EQ(OK, status)
+            << "publisher receiveFinishedSignal should return OK";
+
+    status = mPublisher->reset();
+    ASSERT_EQ(OK, status)
+            << "publisher reset should return OK";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    ASSERT_EQ(OK, status)
+            << "publisher publishKeyEvent should return OK first time";
+
+    status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    ASSERT_EQ(INVALID_OPERATION, status)
+            << "publisher publishKeyEvent should return INVALID_OPERATION because "
+                    "the publisher was not reset";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    const size_t pointerCount = 1;
+    int32_t pointerIds[pointerCount] = { 0 };
+    PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0 } };
+
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(OK, status)
+            << "publisher publishMotionEvent should return OK";
+
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(INVALID_OPERATION, status)
+            << "publisher publishMotionEvent should return INVALID_OPERATION because ";
+                    "the publisher was not reset";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    const size_t pointerCount = 0;
+    int32_t pointerIds[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(BAD_VALUE, status)
+            << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    const size_t pointerCount = MAX_POINTERS + 1;
+    int32_t pointerIds[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(BAD_VALUE, status)
+            << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0));
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4));
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    PointerCoords pointerCoords[1];
+    status = mPublisher->appendMotionSample(0, pointerCoords);
+    ASSERT_EQ(INVALID_OPERATION, status)
+            << "publisher appendMotionSample should return INVALID_OPERATION";
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    const size_t pointerCount = MAX_POINTERS;
+    int32_t pointerIds[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    status = mPublisher->publishMotionEvent(0, 0, MOTION_EVENT_ACTION_DOWN,
+            0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(OK, status);
+
+    status = mPublisher->appendMotionSample(0, pointerCoords);
+    ASSERT_EQ(INVALID_OPERATION, status)
+            << "publisher appendMotionSample should return INVALID_OPERATION";
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    const size_t pointerCount = MAX_POINTERS;
+    int32_t pointerIds[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    status = mPublisher->publishMotionEvent(0, 0, MOTION_EVENT_ACTION_MOVE,
+            0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(OK, status);
+
+    status = mPublisher->sendDispatchSignal();
+    ASSERT_EQ(OK, status);
+
+    status = mConsumer->receiveDispatchSignal();
+    ASSERT_EQ(OK, status);
+
+    InputEvent* event;
+    status = mConsumer->consume(& mEventFactory, & event);
+    ASSERT_EQ(OK, status);
+
+    status = mPublisher->appendMotionSample(0, pointerCoords);
+    ASSERT_EQ(status_t(FAILED_TRANSACTION), status)
+            << "publisher appendMotionSample should return FAILED_TRANSACTION";
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) {
+    status_t status;
+    ASSERT_NO_FATAL_FAILURE(Initialize());
+
+    const size_t pointerCount = MAX_POINTERS;
+    int32_t pointerIds[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    status = mPublisher->publishMotionEvent(0, 0, MOTION_EVENT_ACTION_MOVE,
+            0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+    ASSERT_EQ(OK, status);
+
+    for (int count = 1;; count++) {
+        ASSERT_LT(count, 100000) << "should eventually reach OOM";
+
+        status = mPublisher->appendMotionSample(0, pointerCoords);
+        if (status != OK) {
+            ASSERT_GT(count, 12) << "should be able to add at least a dozen samples";
+            ASSERT_EQ(NO_MEMORY, status)
+                    << "publisher appendMotionSample should return NO_MEMORY when buffer is full";
+            break;
+        }
+    }
+
+    status = mPublisher->appendMotionSample(0, pointerCoords);
+    ASSERT_EQ(NO_MEMORY, status)
+            << "publisher appendMotionSample should return NO_MEMORY persistently until reset";
+}
+
+} // namespace android
diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp
index 90a3e8b..20a4d13 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -11,7 +11,7 @@
 #define DEBUG_POLL_AND_WAKE 0
 
 // Debugs callback registration and invocation.
-#define DEBUG_CALLBACKS 1
+#define DEBUG_CALLBACKS 0
 
 #include <cutils/log.h>
 #include <utils/PollLoop.h>
@@ -22,7 +22,7 @@
 namespace android {
 
 PollLoop::PollLoop() :
-        mPolling(false) {
+        mPolling(false), mWaiters(0) {
     openWakePipe();
 }
 
@@ -68,6 +68,9 @@
 
 bool PollLoop::pollOnce(int timeoutMillis) {
     mLock.lock();
+    while (mWaiters != 0) {
+        mResume.wait(mLock);
+    }
     mPolling = true;
     mLock.unlock();
 
@@ -156,7 +159,9 @@
 Done:
     mLock.lock();
     mPolling = false;
-    mAwake.broadcast();
+    if (mWaiters != 0) {
+        mAwake.broadcast();
+    }
     mLock.unlock();
 
     if (result) {
@@ -258,10 +263,15 @@
 
 void PollLoop::wakeAndLock() {
     mLock.lock();
+    mWaiters += 1;
     while (mPolling) {
         wake();
         mAwake.wait(mLock);
     }
+    mWaiters -= 1;
+    if (mWaiters == 0) {
+        mResume.signal();
+    }
 }
 
 } // namespace android
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index b09c6ca..289c826 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -108,7 +108,7 @@
 
 ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
 {
-    return insertAt(vector.arrayImpl(), index, vector.size());
+    return insertArrayAt(vector.arrayImpl(), index, vector.size());
 }
 
 ssize_t VectorImpl::appendVector(const VectorImpl& vector)
@@ -116,6 +116,22 @@
     return insertVectorAt(vector, size());
 }
 
+ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length)
+{
+    if (index > size())
+        return BAD_INDEX;
+    void* where = _grow(index, length);
+    if (where) {
+        _do_copy(where, array, length);
+    }
+    return where ? index : (ssize_t)NO_MEMORY;
+}
+
+ssize_t VectorImpl::appendArray(const void* array, size_t length)
+{
+    return insertArrayAt(array, size(), length);
+}
+
 ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
 {
     return insertAt(0, index, numItems);
@@ -220,9 +236,9 @@
     return add(0);
 }
 
-ssize_t VectorImpl::add(const void* item, size_t numItems)
+ssize_t VectorImpl::add(const void* item)
 {
-    return insertAt(item, size(), numItems);
+    return insertAt(item, size());
 }
 
 ssize_t VectorImpl::replaceAt(size_t index)
diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp
index 6c719c8..4848c0f 100644
--- a/libs/utils/tests/PollLoop_test.cpp
+++ b/libs/utils/tests/PollLoop_test.cpp
@@ -16,34 +16,6 @@
 
 namespace android {
 
-class Pipe {
-public:
-    int sendFd;
-    int receiveFd;
-
-    Pipe() {
-        int fds[2];
-        ::pipe(fds);
-
-        receiveFd = fds[0];
-        sendFd = fds[1];
-    }
-
-    ~Pipe() {
-        ::close(sendFd);
-        ::close(receiveFd);
-    }
-
-    bool writeSignal() {
-        return ::write(sendFd, "*", 1) == 1;
-    }
-
-    bool readSignal() {
-        char buf[1];
-        return ::read(receiveFd, buf, 1) == 1;
-    }
-};
-
 class DelayedWake : public DelayedTask {
     sp<PollLoop> mPollLoop;
 
@@ -195,7 +167,7 @@
     Pipe pipe;
     StubCallbackHandler handler(true);
 
-    ASSERT_TRUE(pipe.writeSignal());
+    ASSERT_EQ(OK, pipe.writeSignal());
     handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
 
     StopWatch stopWatch("pollOnce");
@@ -243,7 +215,7 @@
     bool result = mPollLoop->pollOnce(100);
     int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
-    ASSERT_TRUE(pipe.readSignal())
+    ASSERT_EQ(OK, pipe.readSignal())
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should be approx. zero";
@@ -269,7 +241,7 @@
     bool result = mPollLoop->pollOnce(1000);
     int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
-    ASSERT_TRUE(pipe.readSignal())
+    ASSERT_EQ(OK, pipe.readSignal())
             << "signal should actually have been written";
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal signal delay";
@@ -295,7 +267,7 @@
     bool result = mPollLoop->pollOnce(100);
     int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
-    ASSERT_TRUE(pipe.readSignal())
+    ASSERT_EQ(OK, pipe.readSignal())
             << "signal should actually have been written";
     EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal timeout because FD was no longer registered";
@@ -318,7 +290,7 @@
     bool result = mPollLoop->pollOnce(0);
     int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
-    ASSERT_TRUE(pipe.readSignal())
+    ASSERT_EQ(OK, pipe.readSignal())
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal zero because FD was already signalled";
@@ -334,7 +306,7 @@
     result = mPollLoop->pollOnce(0);
     elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
-    ASSERT_TRUE(pipe.readSignal())
+    ASSERT_EQ(OK, pipe.readSignal())
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. equal zero because timeout was zero";
@@ -382,7 +354,7 @@
     bool result = mPollLoop->pollOnce(100);
     int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
 
-    ASSERT_TRUE(pipe.readSignal())
+    ASSERT_EQ(OK, pipe.readSignal())
             << "signal should actually have been written";
     EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
             << "elapsed time should approx. zero because FD was already signalled";
diff --git a/libs/utils/tests/TestHelpers.h b/libs/utils/tests/TestHelpers.h
index e55af3c..d8e985e 100644
--- a/libs/utils/tests/TestHelpers.h
+++ b/libs/utils/tests/TestHelpers.h
@@ -21,6 +21,41 @@
 
 namespace android {
 
+class Pipe {
+public:
+    int sendFd;
+    int receiveFd;
+
+    Pipe() {
+        int fds[2];
+        ::pipe(fds);
+
+        receiveFd = fds[0];
+        sendFd = fds[1];
+    }
+
+    ~Pipe() {
+        if (sendFd != -1) {
+            ::close(sendFd);
+        }
+
+        if (receiveFd != -1) {
+            ::close(receiveFd);
+        }
+    }
+
+    status_t writeSignal() {
+        ssize_t nWritten = ::write(sendFd, "*", 1);
+        return nWritten == 1 ? 0 : -errno;
+    }
+
+    status_t readSignal() {
+        char buf[1];
+        ssize_t nRead = ::read(receiveFd, buf, 1);
+        return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+    }
+};
+
 class DelayedTask : public Thread {
     int mDelayMillis;