am bd240c27: media: add AudioTrack::getMinFrameCount().

Merge commit 'bd240c2737913d6ed1982788699f93bbc52330c0' into gingerbread

* commit 'bd240c2737913d6ed1982788699f93bbc52330c0':
  media: add AudioTrack::getMinFrameCount().
diff --git a/api/current.xml b/api/current.xml
index 41b0423..38ba53a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -13112,72 +13112,6 @@
  visibility="public"
 >
 </field>
-<field name="kraken_resource_pad80"
- type="int"
- transient="false"
- volatile="false"
- value="17301681"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad81"
- type="int"
- transient="false"
- volatile="false"
- value="17301680"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad82"
- type="int"
- transient="false"
- volatile="false"
- value="17301679"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad83"
- type="int"
- transient="false"
- volatile="false"
- value="17301678"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad84"
- type="int"
- transient="false"
- volatile="false"
- value="17301677"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="kraken_resource_pad85"
- type="int"
- transient="false"
- volatile="false"
- value="17301676"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="kraken_resource_pad9"
  type="int"
  transient="false"
@@ -13244,6 +13178,39 @@
  visibility="public"
 >
 </field>
+<field name="presence_audio_away"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301679"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="presence_audio_busy"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301680"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="presence_audio_online"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301681"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="presence_away"
  type="int"
  transient="false"
@@ -13299,6 +13266,39 @@
  visibility="public"
 >
 </field>
+<field name="presence_video_away"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301676"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="presence_video_busy"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301677"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="presence_video_online"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301678"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progress_horizontal"
  type="int"
  transient="false"
@@ -26547,6 +26547,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_HIGH_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_INSISTENT"
  type="int"
  transient="false"
@@ -26673,6 +26684,16 @@
  visibility="public"
 >
 </field>
+<field name="fullScreenIntent"
+ type="android.app.PendingIntent"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="icon"
  type="int"
  transient="false"
@@ -117454,17 +117475,6 @@
  visibility="public"
 >
 </field>
-<field name="KRAKEN"
- type="int"
- transient="false"
- volatile="false"
- value="10000"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="Bundle"
  extends="java.lang.Object"
@@ -176794,7 +176804,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="downTime" type="long">
@@ -176894,7 +176904,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="false"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -188136,6 +188146,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_IMMERSIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8388608"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_KEEP_SCREEN_ON"
  type="int"
  transient="false"
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/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/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b4c7edc..2c1f2daf 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1065,8 +1065,9 @@
         case HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder app = data.readStrongBinder();
+            int violationMask = data.readInt();
             ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data);
-            handleApplicationStrictModeViolation(app, ci);
+            handleApplicationStrictModeViolation(app, violationMask, ci);
             reply.writeNoException();
             return true;
         }
@@ -2551,12 +2552,14 @@
     }
 
     public void handleApplicationStrictModeViolation(IBinder app,
+            int violationMask,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(app);
+        data.writeInt(violationMask);
         crashInfo.writeToParcel(data, 0);
         mRemote.transact(HANDLE_APPLICATION_STRICT_MODE_VIOLATION_TRANSACTION, data, reply, 0);
         reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 49f1a8f..0ce790e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4139,7 +4139,6 @@
          */
         if ((data.appInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 &&
             !"user".equals(Build.TYPE)) {
-            StrictMode.setDropBoxManager(ContextImpl.createDropBoxManager());
             StrictMode.setThreadBlockingPolicy(
                 StrictMode.DISALLOW_DISK_WRITE |
                 StrictMode.DISALLOW_DISK_READ |
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index f0cef98..9834c4c 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -52,7 +52,6 @@
     // System property defining default error report receiver
     static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
 
-    
     /**
      * Uninitialized error report.
      */
@@ -74,6 +73,11 @@
     public static final int TYPE_BATTERY = 3;
 
     /**
+     * An error report about a StrictMode violation.
+     */
+    public static final int TYPE_STRICT_MODE_VIOLATION = 4;
+
+    /**
      * Type of this report. Can be one of {@link #TYPE_NONE},
      * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}.
      */
@@ -250,6 +254,10 @@
 
     /**
      * Describes an application crash.
+     *
+     * <p>This is also used to marshal around stack traces of ANRs and
+     * StrictMode violations which aren't necessarily crashes, but have
+     * a lot in common.
      */
     public static class CrashInfo {
         /**
@@ -288,6 +296,12 @@
         public String stackTrace;
 
         /**
+         * For StrictMode violations, the wall time duration of the
+         * violation, when known.
+         */
+        public long durationMillis = -1;
+
+        /**
          * Create an uninitialized instance of CrashInfo.
          */
         public CrashInfo() {
@@ -330,6 +344,7 @@
             throwMethodName = in.readString();
             throwLineNumber = in.readInt();
             stackTrace = in.readString();
+            durationMillis = in.readLong();
         }
 
         /**
@@ -343,6 +358,7 @@
             dest.writeString(throwMethodName);
             dest.writeInt(throwLineNumber);
             dest.writeString(stackTrace);
+            dest.writeLong(durationMillis);
         }
 
         /**
@@ -356,6 +372,9 @@
             pw.println(prefix + "throwMethodName: " + throwMethodName);
             pw.println(prefix + "throwLineNumber: " + throwLineNumber);
             pw.println(prefix + "stackTrace: " + stackTrace);
+            if (durationMillis != -1) {
+                pw.println(prefix + "durationMillis: " + durationMillis);
+            }
         }
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ca09290..542bc41 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -256,7 +256,12 @@
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
     public boolean handleApplicationWtf(IBinder app, String tag,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
-    public void handleApplicationStrictModeViolation(IBinder app,
+
+    // A StrictMode violation to be handled.  The violationMask is a
+    // subset of the original StrictMode policy bitmask, with only the
+    // bit violated and penalty bits to be executed by the
+    // ActivityManagerService remaining set.
+    public void handleApplicationStrictModeViolation(IBinder app, int violationMask,
             ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException;
 
     /*
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 739aca3..83a2024 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -83,10 +83,10 @@
     public int icon;
 
     /**
-     * The number of events that this notification represents.  For example, if this is the
-     * new mail notification, this would be the number of unread messages.  This number is
-     * be superimposed over the icon in the status bar.  If the number is 0 or negative, it
-     * is not shown in the status bar.
+     * The number of events that this notification represents.  For example, in a new mail
+     * notification, this could be the number of unread messages.  This number is superimposed over
+     * the icon in the status bar.  If the number is 0 or negative, it is not shown in the status
+     * bar.
      */
     public int number;
 
@@ -109,13 +109,22 @@
     public PendingIntent deleteIntent;
 
     /**
+     * An intent to launch instead of posting the notification to the status bar. Only for use with
+     * extremely high-priority notifications demanding the user's attention, such as an incoming
+     * call (handled in the core Android Phone app with a full-screen Activity).
+     * Use with {@link #FLAG_HIGH_PRIORITY} to ensure that this notification will reach the user
+     * even when other notifications are suppressed.
+     */
+    public PendingIntent fullScreenIntent;
+
+    /**
      * Text to scroll across the screen when this item is added to
      * the status bar.
      */
     public CharSequence tickerText;
 
     /**
-     * The view that shows when this notification is shown in the expanded status bar.
+     * The view that will represent this notification in the expanded status bar.
      */
     public RemoteViews contentView;
 
@@ -264,6 +273,14 @@
      */
     public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
 
+    /**
+     * Bit to be bitwise-ored into the {@link #flags} field that should be set if this notification
+     * represents a high-priority event that may be shown to the user even if notifications are
+     * otherwise unavailable (that is, when the status bar is hidden). This flag is ideally used
+     * in conjunction with {@link #fullScreenIntent}.
+     */
+    public static final int FLAG_HIGH_PRIORITY = 0x00000080;
+
     public int flags;
 
     /**
@@ -339,6 +356,10 @@
         ledOnMS = parcel.readInt();
         ledOffMS = parcel.readInt();
         iconLevel = parcel.readInt();
+
+        if (parcel.readInt() != 0) {
+            fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+        }
     }
 
     public Notification clone() {
@@ -351,6 +372,7 @@
         // PendingIntents are global, so there's no reason (or way) to clone them.
         that.contentIntent = this.contentIntent;
         that.deleteIntent = this.deleteIntent;
+        that.fullScreenIntent = this.fullScreenIntent;
 
         if (this.tickerText != null) {
             that.tickerText = this.tickerText.toString();
@@ -433,6 +455,13 @@
         parcel.writeInt(ledOnMS);
         parcel.writeInt(ledOffMS);
         parcel.writeInt(iconLevel);
+
+        if (fullScreenIntent != null) {
+            parcel.writeInt(1);
+            fullScreenIntent.writeToParcel(parcel, 0);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     /**
@@ -518,6 +547,11 @@
         }
         sb.append(",defaults=0x");
         sb.append(Integer.toHexString(this.defaults));
+        sb.append(",flags=0x");
+        sb.append(Integer.toHexString(this.flags));
+        if ((this.flags & FLAG_HIGH_PRIORITY) != 0) {
+            sb.append("!!!1!one!");
+        }
         sb.append(")");
         return sb.toString();
     }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4adeaeb..e3f3b87 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -87,7 +87,7 @@
     /**
      * Include all of the data in the stats, including previously saved data.
      */
-    public static final int STATS_TOTAL = 0;
+    public static final int STATS_SINCE_CHARGED = 0;
 
     /**
      * Include only the last run in the stats.
@@ -102,7 +102,7 @@
     /**
      * Include only the run since the last time the device was unplugged in the stats.
      */
-    public static final int STATS_UNPLUGGED = 3;
+    public static final int STATS_SINCE_UNPLUGGED = 3;
 
     // NOTE: Update this list if you add/change any stats above.
     // These characters are supposed to represent "total", "last", "current", 
@@ -377,14 +377,26 @@
         }
     }
 
-    public final class BatteryHistoryRecord implements Parcelable {
-        public BatteryHistoryRecord next;
+    public final class HistoryItem implements Parcelable {
+        public HistoryItem next;
         
         public long time;
-        public byte batteryLevel;
         
-        public static final int STATE_SCREEN_MASK = 0x000000f;
-        public static final int STATE_SCREEN_SHIFT = 0;
+        public static final byte CMD_UPDATE = 0;
+        public static final byte CMD_START = 1;
+        
+        public byte cmd;
+        
+        public byte batteryLevel;
+        public byte batteryStatus;
+        public byte batteryHealth;
+        public byte batteryPlugType;
+        
+        public char batteryTemperature;
+        public char batteryVoltage;
+        
+        public static final int STATE_BRIGHTNESS_MASK = 0x000000f;
+        public static final int STATE_BRIGHTNESS_SHIFT = 0;
         public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
         public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
         public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
@@ -396,23 +408,32 @@
         public static final int STATE_SCREEN_ON_FLAG = 1<<29;
         public static final int STATE_GPS_ON_FLAG = 1<<28;
         public static final int STATE_PHONE_ON_FLAG = 1<<27;
-        public static final int STATE_WIFI_ON_FLAG = 1<<26;
-        public static final int STATE_WIFI_RUNNING_FLAG = 1<<25;
-        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<24;
-        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<23;
-        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<22;
-        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<21;
-        public static final int STATE_AUDIO_ON_FLAG = 1<<20;
-        public static final int STATE_VIDEO_ON_FLAG = 1<<19;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<26;
+        public static final int STATE_WIFI_ON_FLAG = 1<<25;
+        public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
+        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<23;
+        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<22;
+        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<21;
+        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
+        public static final int STATE_AUDIO_ON_FLAG = 1<<19;
+        public static final int STATE_VIDEO_ON_FLAG = 1<<18;
         
         public int states;
 
-        public BatteryHistoryRecord() {
+        public HistoryItem() {
         }
         
-        public BatteryHistoryRecord(long time, Parcel src) {
+        public HistoryItem(long time, Parcel src) {
             this.time = time;
-            batteryLevel = (byte)src.readInt();
+            int bat = src.readInt();
+            cmd = (byte)(bat&0xff);
+            batteryLevel = (byte)((bat>>8)&0xff);
+            batteryStatus = (byte)((bat>>16)&0xf);
+            batteryHealth = (byte)((bat>>20)&0xf);
+            batteryPlugType = (byte)((bat>>24)&0xf);
+            bat = src.readInt();
+            batteryTemperature = (char)(bat&0xffff);
+            batteryVoltage = (char)((bat>>16)&0xffff);
             states = src.readInt();
         }
         
@@ -422,15 +443,56 @@
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeLong(time);
-            dest.writeInt(batteryLevel);
+            int bat = (((int)cmd)&0xff)
+                    | ((((int)batteryLevel)<<8)&0xff00)
+                    | ((((int)batteryStatus)<<16)&0xf0000)
+                    | ((((int)batteryHealth)<<20)&0xf00000)
+                    | ((((int)batteryPlugType)<<24)&0xf000000);
+            dest.writeInt(bat);
+            bat = (((int)batteryTemperature)&0xffff)
+                    | ((((int)batteryVoltage)<<16)&0xffff0000);
+            dest.writeInt(bat);
             dest.writeInt(states);
         }
+        
+        public void setTo(long time, byte cmd, HistoryItem o) {
+            this.time = time;
+            this.cmd = cmd;
+            batteryLevel = o.batteryLevel;
+            batteryStatus = o.batteryStatus;
+            batteryHealth = o.batteryHealth;
+            batteryPlugType = o.batteryPlugType;
+            batteryTemperature = o.batteryTemperature;
+            batteryVoltage = o.batteryVoltage;
+            states = o.states;
+        }
+    }
+    
+    public static final class BitDescription {
+        public final int mask;
+        public final int shift;
+        public final String name;
+        public final String[] values;
+        
+        public BitDescription(int mask, String name) {
+            this.mask = mask;
+            this.shift = -1;
+            this.name = name;
+            this.values = null;
+        }
+        
+        public BitDescription(int mask, int shift, String name, String[] values) {
+            this.mask = mask;
+            this.shift = shift;
+            this.name = name;
+            this.values = values;
+        }
     }
     
     /**
      * Return the current history of battery state changes.
      */
-    public abstract BatteryHistoryRecord getHistory();
+    public abstract HistoryItem getHistory();
     
     /**
      * Returns the number of times the device has been started.
@@ -517,13 +579,23 @@
     public static final int DATA_CONNECTION_GPRS = 1;
     public static final int DATA_CONNECTION_EDGE = 2;
     public static final int DATA_CONNECTION_UMTS = 3;
-    public static final int DATA_CONNECTION_OTHER = 4;
+    public static final int DATA_CONNECTION_CDMA = 4;
+    public static final int DATA_CONNECTION_EVDO_0 = 5;
+    public static final int DATA_CONNECTION_EVDO_A = 6;
+    public static final int DATA_CONNECTION_1xRTT = 7;
+    public static final int DATA_CONNECTION_HSDPA = 8;
+    public static final int DATA_CONNECTION_HSUPA = 9;
+    public static final int DATA_CONNECTION_HSPA = 10;
+    public static final int DATA_CONNECTION_IDEN = 11;
+    public static final int DATA_CONNECTION_EVDO_B = 12;
+    public static final int DATA_CONNECTION_OTHER = 13;
     
     static final String[] DATA_CONNECTION_NAMES = {
-        "none", "gprs", "edge", "umts", "other"
+        "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
+        "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "other"
     };
     
-    public static final int NUM_DATA_CONNECTION_TYPES = 5;
+    public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
     
     /**
      * Returns the time in microseconds that the phone has been running with
@@ -541,6 +613,35 @@
      * {@hide}
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
+    
+    public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
+            = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged"),
+        new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen"),
+        new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps"),
+        new BitDescription(HistoryItem.STATE_PHONE_ON_FLAG, "phone"),
+        new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning"),
+        new BitDescription(HistoryItem.STATE_WIFI_ON_FLAG, "wifi"),
+        new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running"),
+        new BitDescription(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG, "wifi_full_lock"),
+        new BitDescription(HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG, "wifi_scan_lock"),
+        new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast"),
+        new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"),
+        new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"),
+        new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"),
+        new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
+                HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness",
+                SCREEN_BRIGHTNESS_NAMES),
+        new BitDescription(HistoryItem.STATE_SIGNAL_STRENGTH_MASK,
+                HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT, "signal_strength",
+                SIGNAL_STRENGTH_NAMES),
+        new BitDescription(HistoryItem.STATE_PHONE_STATE_MASK,
+                HistoryItem.STATE_PHONE_STATE_SHIFT, "phone_state",
+                new String[] {"in", "out", "emergency", "off"}),
+        new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
+                HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn",
+                DATA_CONNECTION_NAMES),
+    };
 
     /**
      * Returns the time in microseconds that wifi has been on while the device was
@@ -832,7 +933,7 @@
         
         // Dump "battery" stat
         dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, 
-                which == STATS_TOTAL ? getStartCount() : "N/A",
+                which == STATS_SINCE_CHARGED ? getStartCount() : "N/A",
                 whichBatteryRealtime / 1000, whichBatteryUptime / 1000,
                 totalRealtime / 1000, totalUptime / 1000); 
         
@@ -905,7 +1006,7 @@
         }
         dumpLine(pw, 0 /* uid */, category, DATA_CONNECTION_COUNT_DATA, args);
         
-        if (which == STATS_UNPLUGGED) {
+        if (which == STATS_SINCE_UNPLUGGED) {
             dumpLine(pw, 0 /* uid */, category, BATTERY_LEVEL_DATA, getDischargeStartLevel(), 
                     getDischargeCurrentLevel());
         }
@@ -1253,7 +1354,7 @@
         
         pw.println(" ");
 
-        if (which == STATS_UNPLUGGED) {
+        if (which == STATS_SINCE_UNPLUGGED) {
             if (getIsOnBattery()) {
                 pw.print(prefix); pw.println("  Device is currently unplugged");
                 pw.print(prefix); pw.print("    Discharge cycle start level: "); 
@@ -1477,6 +1578,30 @@
         }
     }
 
+    void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
+        int diff = oldval ^ newval;
+        if (diff == 0) return;
+        for (int i=0; i<descriptions.length; i++) {
+            BitDescription bd = descriptions[i];
+            if ((diff&bd.mask) != 0) {
+                if (bd.shift < 0) {
+                    pw.print((newval&bd.mask) != 0 ? " +" : " -");
+                    pw.print(bd.name);
+                } else {
+                    pw.print(" ");
+                    pw.print(bd.name);
+                    pw.print("=");
+                    int val = (newval&bd.mask)>>bd.shift;
+                    if (bd.values != null && val >= 0 && val < bd.values.length) {
+                        pw.print(bd.values[val]);
+                    } else {
+                        pw.print(val);
+                    }
+                }
+            }
+        }
+    }
+    
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -1484,33 +1609,129 @@
      */
     @SuppressWarnings("unused")
     public void dumpLocked(PrintWriter pw) {
-        BatteryHistoryRecord rec = getHistory();
+        HistoryItem rec = getHistory();
         if (rec != null) {
             pw.println("Battery History:");
+            int oldState = 0;
+            int oldStatus = -1;
+            int oldHealth = -1;
+            int oldPlug = -1;
+            int oldTemp = -1;
+            int oldVolt = -1;
             while (rec != null) {
                 pw.print("  ");
                 pw.print(rec.time);
                 pw.print(" ");
-                pw.print(rec.batteryLevel);
-                pw.print(" ");
-                pw.println(Integer.toHexString(rec.states));
+                if (rec.cmd == HistoryItem.CMD_START) {
+                    pw.println(" START");
+                } else {
+                    if (rec.batteryLevel < 10) pw.print("00");
+                    else if (rec.batteryLevel < 100) pw.print("0");
+                    pw.print(rec.batteryLevel);
+                    pw.print(" ");
+                    if (rec.states < 0x10) pw.print("0000000");
+                    else if (rec.states < 0x100) pw.print("000000");
+                    else if (rec.states < 0x1000) pw.print("00000");
+                    else if (rec.states < 0x10000) pw.print("0000");
+                    else if (rec.states < 0x100000) pw.print("000");
+                    else if (rec.states < 0x1000000) pw.print("00");
+                    else if (rec.states < 0x10000000) pw.print("0");
+                    pw.print(Integer.toHexString(rec.states));
+                    if (oldStatus != rec.batteryStatus) {
+                        oldStatus = rec.batteryStatus;
+                        pw.print(" status=");
+                        switch (oldStatus) {
+                            case BatteryManager.BATTERY_STATUS_UNKNOWN:
+                                pw.print("unknown");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_CHARGING:
+                                pw.print("charging");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_DISCHARGING:
+                                pw.print("discharging");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+                                pw.print("not-charging");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_FULL:
+                                pw.print("full");
+                                break;
+                            default:
+                                pw.print(oldStatus);
+                                break;
+                        }
+                    }
+                    if (oldHealth != rec.batteryHealth) {
+                        oldHealth = rec.batteryHealth;
+                        pw.print(" health=");
+                        switch (oldHealth) {
+                            case BatteryManager.BATTERY_HEALTH_UNKNOWN:
+                                pw.print("unknown");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_GOOD:
+                                pw.print("good");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_OVERHEAT:
+                                pw.print("overheat");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_DEAD:
+                                pw.print("dead");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
+                                pw.print("over-voltage");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
+                                pw.print("failure");
+                                break;
+                            default:
+                                pw.print(oldHealth);
+                                break;
+                        }
+                    }
+                    if (oldPlug != rec.batteryPlugType) {
+                        oldPlug = rec.batteryPlugType;
+                        pw.print(" plug=");
+                        switch (oldPlug) {
+                            case 0:
+                                pw.print("none");
+                                break;
+                            case BatteryManager.BATTERY_PLUGGED_AC:
+                                pw.print("ac");
+                                break;
+                            case BatteryManager.BATTERY_PLUGGED_USB:
+                                pw.print("usb");
+                                break;
+                            default:
+                                pw.print(oldPlug);
+                                break;
+                        }
+                    }
+                    if (oldTemp != rec.batteryTemperature) {
+                        oldTemp = rec.batteryTemperature;
+                        pw.print(" temp=");
+                        pw.print(oldTemp);
+                    }
+                    if (oldVolt != rec.batteryVoltage) {
+                        oldVolt = rec.batteryVoltage;
+                        pw.print(" volt=");
+                        pw.print(oldVolt);
+                    }
+                    printBitDescriptions(pw, oldState, rec.states,
+                            HISTORY_STATE_DESCRIPTIONS);
+                    pw.println();
+                }
+                oldState = rec.states;
                 rec = rec.next;
             }
         }
         
-        pw.println("Total Statistics (Current and Historic):");
+        pw.println("Statistics since last charge:");
         pw.println("  System starts: " + getStartCount()
                 + ", currently on battery: " + getIsOnBattery());
-        dumpLocked(pw, "", STATS_TOTAL, -1);
+        dumpLocked(pw, "", STATS_SINCE_CHARGED, -1);
         pw.println("");
-        pw.println("Last Run Statistics (Previous run of system):");
-        dumpLocked(pw, "", STATS_LAST, -1);
-        pw.println("");
-        pw.println("Current Battery Statistics (Currently running system):");
-        dumpLocked(pw, "", STATS_CURRENT, -1);
-        pw.println("");
-        pw.println("Unplugged Statistics (Since last unplugged from power):");
-        dumpLocked(pw, "", STATS_UNPLUGGED, -1);
+        pw.println("Statistics since last unplugged:");
+        dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, -1);
     }
     
     @SuppressWarnings("unused")
@@ -1525,14 +1746,11 @@
         }
         
         if (isUnpluggedOnly) {
-            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
+            dumpCheckinLocked(pw, STATS_SINCE_UNPLUGGED, -1);
         }
         else {
-            dumpCheckinLocked(pw, STATS_TOTAL, -1);
-            dumpCheckinLocked(pw, STATS_LAST, -1);
-            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
-            dumpCheckinLocked(pw, STATS_CURRENT, -1);
+            dumpCheckinLocked(pw, STATS_SINCE_CHARGED, -1);
+            dumpCheckinLocked(pw, STATS_SINCE_UNPLUGGED, -1);
         }
     }
-    
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9d1a634..fdd3573 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -179,10 +179,22 @@
          */
         public static final int ECLAIR_MR1 = 7;
         
+        /**
+         * June 2010: Android 2.2
+         */
         public static final int FROYO = 8;
         
-        public static final int KRAKEN = CUR_DEVELOPMENT;
-        
+        /**
+         * Next version of Android.
+         * 
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> The status bar is now dark.  Targeting this version allows
+         * the platform to perform performing compatibility on status bar
+         * graphics to ensure they look okay on a dark background.
+         * </ul>
+         */
         public static final int GINGERBREAD = CUR_DEVELOPMENT;
     }
     
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 7889a92..4df33e0 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -242,7 +242,7 @@
         if (file == null) throw new NullPointerException();
         Entry entry = new Entry(tag, 0, file, flags);
         try {
-            mService.add(new Entry(tag, 0, file, flags));
+            mService.add(entry);
         } catch (RemoteException e) {
             // ignore
         } finally {
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 876ec39..9b18719 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -23,6 +23,8 @@
 
 import dalvik.system.BlockGuard;
 
+import java.util.HashMap;
+
 /**
  * <p>StrictMode lets you impose stricter rules under which your
  * application runs.</p>
@@ -30,6 +32,12 @@
 public final class StrictMode {
     private static final String TAG = "StrictMode";
 
+    // Only log a duplicate stack trace to the logs every second.
+    private static final long MIN_LOG_INTERVAL_MS = 1000;
+
+    // Only show an annoying dialog at most every 30 seconds
+    private static final long MIN_DIALOG_INTERVAL_MS = 30000;
+
     private StrictMode() {}
 
     public static final int DISALLOW_DISK_WRITE = 0x01;
@@ -73,6 +81,10 @@
      * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
      */
     public static void setThreadBlockingPolicy(final int policyMask) {
+        if (policyMask == 0) {
+            BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
+            return;
+        }
         BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
         if (!(policy instanceof AndroidBlockGuardPolicy)) {
             BlockGuard.setThreadPolicy(new AndroidBlockGuardPolicy(policyMask));
@@ -91,19 +103,13 @@
         return BlockGuard.getThreadPolicy().getPolicyMask();
     }
 
-    /** @hide */
-    public static void setDropBoxManager(DropBoxManager dropBoxManager) {
-        BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
-        if (!(policy instanceof AndroidBlockGuardPolicy)) {
-            policy = new AndroidBlockGuardPolicy(0);
-            BlockGuard.setThreadPolicy(policy);
-        }
-        ((AndroidBlockGuardPolicy) policy).setDropBoxManager(dropBoxManager);
-    }
-
     private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
         private int mPolicyMask;
-        private DropBoxManager mDropBoxManager = null;
+
+        // Map from violation stacktrace hashcode -> uptimeMillis of
+        // last violation.  No locking needed, as this is only
+        // accessed by the same thread.
+        private final HashMap<Integer, Long> mLastViolationTime = new HashMap<Integer, Long>();
 
         public AndroidBlockGuardPolicy(final int policyMask) {
             mPolicyMask = policyMask;
@@ -142,10 +148,6 @@
             mPolicyMask = policyMask;
         }
 
-        public void setDropBoxManager(DropBoxManager dropBoxManager) {
-            mDropBoxManager = dropBoxManager;
-        }
-
         private void handleViolation(int violationBit) {
             final BlockGuard.BlockGuardPolicyException violation =
                     new BlockGuard.BlockGuardPolicyException(mPolicyMask, violationBit);
@@ -182,7 +184,24 @@
             // the old policy here.
             int policy = violation.getPolicy();
 
-            if ((policy & PENALTY_LOG) != 0) {
+            // Not _really_ a Crash, but we use the same data structure...
+            ApplicationErrorReport.CrashInfo crashInfo =
+                    new ApplicationErrorReport.CrashInfo(violation);
+            crashInfo.durationMillis = durationMillis;
+
+            // Not perfect, but fast and good enough for dup suppression.
+            Integer crashFingerprint = crashInfo.stackTrace.hashCode();
+            long lastViolationTime = 0;
+            if (mLastViolationTime.containsKey(crashFingerprint)) {
+                lastViolationTime = mLastViolationTime.get(crashFingerprint);
+            }
+            long now = SystemClock.uptimeMillis();
+            mLastViolationTime.put(crashFingerprint, now);
+            long timeSinceLastViolationMillis = lastViolationTime == 0 ?
+                    Long.MAX_VALUE : (now - lastViolationTime);
+
+            if ((policy & PENALTY_LOG) != 0 &&
+                timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
                 if (durationMillis != -1) {
                     Log.d(TAG, "StrictMode policy violation; ~duration=" + durationMillis + " ms",
                           violation);
@@ -191,24 +210,33 @@
                 }
             }
 
-            if ((policy & PENALTY_DIALOG) != 0) {
-                // Currently this is just used for the dialog.
+            // The violationMask, passed to ActivityManager, is a
+            // subset of the original StrictMode policy bitmask, with
+            // only the bit violated and penalty bits to be executed
+            // by the ActivityManagerService remaining set.
+            int violationMask = 0;
+
+            if ((policy & PENALTY_DIALOG) != 0 &&
+                timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) {
+                violationMask |= PENALTY_DIALOG;
+            }
+
+            if ((policy & PENALTY_DROPBOX) != 0 && lastViolationTime == 0) {
+                violationMask |= PENALTY_DROPBOX;
+            }
+
+            if (violationMask != 0) {
+                violationMask |= violation.getPolicyViolation();
                 try {
                     ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
                         RuntimeInit.getApplicationObject(),
+                        violationMask,
                         new ApplicationErrorReport.CrashInfo(violation));
                 } catch (RemoteException e) {
-                    Log.e(TAG, "RemoteException trying to open strict mode dialog", e);
+                    Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
                 }
             }
 
-            if ((policy & PENALTY_DROPBOX) != 0) {
-                // TODO: call into ActivityManagerNative to do the dropboxing.
-                // But do the first-layer signature dup-checking first client-side.
-                // This conditional should be combined with the above, too, along
-                // with PENALTY_DEATH below.
-            }
-
             if ((policy & PENALTY_DEATH) != 0) {
                 System.err.println("StrictMode policy violation with POLICY_DEATH; shutting down.");
                 Process.killProcess(Process.myPid());
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 1f06191..ae8c21d 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -27,6 +27,7 @@
  * it is being used for.
  */
 public final class MotionEvent implements Parcelable {
+    private static final long MS_PER_NS = 1000000;
     static final boolean DEBUG_POINTERS = false;
     
     /**
@@ -218,31 +219,32 @@
     static private int gRecyclerUsed = 0;
     static private MotionEvent gRecyclerTop = null;
 
-    private long mDownTime;
-    private long mEventTimeNano;
+    private long mDownTimeNano;
     private int mAction;
-    private float mRawX;
-    private float mRawY;
+    private float mXOffset;
+    private float mYOffset;
     private float mXPrecision;
     private float mYPrecision;
     private int mDeviceId;
     private int mEdgeFlags;
     private int mMetaState;
     
-    // Here is the actual event data.  Note that the order of the array
-    // is a little odd: the first entry is the most recent, and the ones
-    // following it are the historical data from oldest to newest.  This
-    // allows us to easily retrieve the most recent data, without having
-    // to copy the arrays every time a new sample is added.
-    
     private int mNumPointers;
     private int mNumSamples;
+    
+    private int mLastDataSampleIndex;
+    private int mLastEventTimeNanoSampleIndex;
+    
     // Array of mNumPointers size of identifiers for each pointer of data.
     private int[] mPointerIdentifiers;
+    
     // Array of (mNumSamples * mNumPointers * NUM_SAMPLE_DATA) size of event data.
+    // Samples are ordered from oldest to newest.
     private float[] mDataSamples;
-    // Array of mNumSamples size of time stamps.
-    private long[] mTimeSamples;
+    
+    // Array of mNumSamples size of event time stamps in nanoseconds.
+    // Samples are ordered from oldest to newest.
+    private long[] mEventTimeNanoSamples;
 
     private MotionEvent mNext;
     private RuntimeException mRecycledLocation;
@@ -251,26 +253,9 @@
     private MotionEvent(int pointerCount, int sampleCount) {
         mPointerIdentifiers = new int[pointerCount];
         mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
-        mTimeSamples = new long[sampleCount];
+        mEventTimeNanoSamples = new long[sampleCount];
     }
 
-    static private MotionEvent obtain() {
-        final MotionEvent ev;
-        synchronized (gRecyclerLock) {
-            if (gRecyclerTop == null) {
-                return new MotionEvent(BASE_AVAIL_POINTERS, BASE_AVAIL_SAMPLES);
-            }
-            ev = gRecyclerTop;
-            gRecyclerTop = ev.mNext;
-            gRecyclerUsed--;
-        }
-        ev.mRecycledLocation = null;
-        ev.mRecycled = false;
-        ev.mNext = null;
-        return ev;
-    }
-    
-    @SuppressWarnings("unused") // used by native code
     static private MotionEvent obtain(int pointerCount, int sampleCount) {
         final MotionEvent ev;
         synchronized (gRecyclerLock) {
@@ -285,7 +270,7 @@
             }
             ev = gRecyclerTop;
             gRecyclerTop = ev.mNext;
-            gRecyclerUsed--;
+            gRecyclerUsed -= 1;
         }
         ev.mRecycledLocation = null;
         ev.mRecycled = false;
@@ -295,20 +280,18 @@
             ev.mPointerIdentifiers = new int[pointerCount];
         }
         
-        final int timeSamplesLength = ev.mTimeSamples.length;
-        if (timeSamplesLength < sampleCount) {
-            ev.mTimeSamples = new long[sampleCount];
+        if (ev.mEventTimeNanoSamples.length < sampleCount) {
+            ev.mEventTimeNanoSamples = new long[sampleCount];
         }
         
-        final int dataSamplesLength = ev.mDataSamples.length;
         final int neededDataSamplesLength = pointerCount * sampleCount * NUM_SAMPLE_DATA;
-        if (dataSamplesLength < neededDataSamplesLength) {
+        if (ev.mDataSamples.length < neededDataSamplesLength) {
             ev.mDataSamples = new float[neededDataSamplesLength];
         }
         
         return ev;
     }
-
+    
     /**
      * Create a new MotionEvent, filling in all of the basic values that
      * define the motion.
@@ -342,45 +325,39 @@
     static public MotionEvent obtainNano(long downTime, long eventTime, long eventTimeNano,
             int action, int pointers, int[] inPointerIds, float[] inData, int metaState,
             float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
-        MotionEvent ev = obtain();
+        MotionEvent ev = obtain(pointers, 1);
         ev.mDeviceId = deviceId;
         ev.mEdgeFlags = edgeFlags;
-        ev.mDownTime = downTime;
-        ev.mEventTimeNano = eventTimeNano;
+        ev.mDownTimeNano = downTime * MS_PER_NS;
         ev.mAction = action;
         ev.mMetaState = metaState;
-        ev.mRawX = inData[SAMPLE_X];
-        ev.mRawY = inData[SAMPLE_Y];
+        ev.mXOffset = 0;
+        ev.mYOffset = 0;
         ev.mXPrecision = xPrecision;
         ev.mYPrecision = yPrecision;
+        
         ev.mNumPointers = pointers;
         ev.mNumSamples = 1;
         
-        int[] pointerIdentifiers = ev.mPointerIdentifiers;
-        if (pointerIdentifiers.length < pointers) {
-            ev.mPointerIdentifiers = pointerIdentifiers = new int[pointers];
-        }
-        System.arraycopy(inPointerIds, 0, pointerIdentifiers, 0, pointers);
+        ev.mLastDataSampleIndex = 0;
+        ev.mLastEventTimeNanoSampleIndex = 0;
         
-        final int ND = pointers * NUM_SAMPLE_DATA;
-        float[] dataSamples = ev.mDataSamples;
-        if (dataSamples.length < ND) {
-            ev.mDataSamples = dataSamples = new float[ND];
-        }
-        System.arraycopy(inData, 0, dataSamples, 0, ND);
+        System.arraycopy(inPointerIds, 0, ev.mPointerIdentifiers, 0, pointers);
         
-        ev.mTimeSamples[0] = eventTime;
+        ev.mEventTimeNanoSamples[0] = eventTimeNano;
+        
+        System.arraycopy(inData, 0, ev.mDataSamples, 0, pointers * NUM_SAMPLE_DATA);
 
         if (DEBUG_POINTERS) {
             StringBuilder sb = new StringBuilder(128);
             sb.append("New:");
-            for (int i=0; i<pointers; i++) {
+            for (int i = 0; i < pointers; i++) {
                 sb.append(" #");
-                sb.append(ev.mPointerIdentifiers[i]);
+                sb.append(ev.getPointerId(i));
                 sb.append("(");
-                sb.append(ev.mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_X]);
+                sb.append(ev.getX(i));
                 sb.append(",");
-                sb.append(ev.mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_Y]);
+                sb.append(ev.getY(i));
                 sb.append(")");
             }
             Log.v("MotionEvent", sb.toString());
@@ -423,27 +400,32 @@
     static public MotionEvent obtain(long downTime, long eventTime, int action,
             float x, float y, float pressure, float size, int metaState,
             float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
-        MotionEvent ev = obtain();
+        MotionEvent ev = obtain(1, 1);
         ev.mDeviceId = deviceId;
         ev.mEdgeFlags = edgeFlags;
-        ev.mDownTime = downTime;
-        ev.mEventTimeNano = eventTime * 1000000;
+        ev.mDownTimeNano = downTime * MS_PER_NS;
         ev.mAction = action;
         ev.mMetaState = metaState;
+        ev.mXOffset = 0;
+        ev.mYOffset = 0;
         ev.mXPrecision = xPrecision;
         ev.mYPrecision = yPrecision;
-
+        
         ev.mNumPointers = 1;
         ev.mNumSamples = 1;
-        int[] pointerIds = ev.mPointerIdentifiers;
-        pointerIds[0] = 0;
-        float[] data = ev.mDataSamples;
-        data[SAMPLE_X] = ev.mRawX = x;
-        data[SAMPLE_Y] = ev.mRawY = y;
-        data[SAMPLE_PRESSURE] = pressure;
-        data[SAMPLE_SIZE] = size;
-        ev.mTimeSamples[0] = eventTime;
-
+        
+        ev.mLastDataSampleIndex = 0;
+        ev.mLastEventTimeNanoSampleIndex = 0;
+        
+        ev.mPointerIdentifiers[0] = 0;
+        
+        ev.mEventTimeNanoSamples[0] = eventTime * MS_PER_NS;
+        
+        float[] dataSamples = ev.mDataSamples;
+        dataSamples[SAMPLE_X] = x;
+        dataSamples[SAMPLE_Y] = y;
+        dataSamples[SAMPLE_PRESSURE] = pressure;
+        dataSamples[SAMPLE_SIZE] = size;
         return ev;
     }
 
@@ -478,33 +460,16 @@
      * numbers are arbitrary and you shouldn't depend on the values.
      * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
      * MotionEvent.
+     * 
+     * @deprecated Use {@link #obtain(long, long, int, float, float, float, float, int, float, float, int, int)}
+     * instead.
      */
+    @Deprecated
     static public MotionEvent obtain(long downTime, long eventTime, int action,
             int pointers, float x, float y, float pressure, float size, int metaState,
             float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
-        MotionEvent ev = obtain();
-        ev.mDeviceId = deviceId;
-        ev.mEdgeFlags = edgeFlags;
-        ev.mDownTime = downTime;
-        ev.mEventTimeNano = eventTime * 1000000;
-        ev.mAction = action;
-        ev.mNumPointers = pointers;
-        ev.mMetaState = metaState;
-        ev.mXPrecision = xPrecision;
-        ev.mYPrecision = yPrecision;
-
-        ev.mNumPointers = 1;
-        ev.mNumSamples = 1;
-        int[] pointerIds = ev.mPointerIdentifiers;
-        pointerIds[0] = 0;
-        float[] data = ev.mDataSamples;
-        data[SAMPLE_X] = ev.mRawX = x;
-        data[SAMPLE_Y] = ev.mRawY = y;
-        data[SAMPLE_PRESSURE] = pressure;
-        data[SAMPLE_SIZE] = size;
-        ev.mTimeSamples[0] = eventTime;
-
-        return ev;
+        return obtain(downTime, eventTime, action, x, y, pressure, size,
+                metaState, xPrecision, yPrecision, deviceId, edgeFlags);
     }
 
     /**
@@ -526,89 +491,36 @@
      */
     static public MotionEvent obtain(long downTime, long eventTime, int action,
             float x, float y, int metaState) {
-        MotionEvent ev = obtain();
-        ev.mDeviceId = 0;
-        ev.mEdgeFlags = 0;
-        ev.mDownTime = downTime;
-        ev.mEventTimeNano = eventTime * 1000000;
-        ev.mAction = action;
-        ev.mNumPointers = 1;
-        ev.mMetaState = metaState;
-        ev.mXPrecision = 1.0f;
-        ev.mYPrecision = 1.0f;
-
-        ev.mNumPointers = 1;
-        ev.mNumSamples = 1;
-        int[] pointerIds = ev.mPointerIdentifiers;
-        pointerIds[0] = 0;
-        float[] data = ev.mDataSamples;
-        data[SAMPLE_X] = ev.mRawX = x;
-        data[SAMPLE_Y] = ev.mRawY = y;
-        data[SAMPLE_PRESSURE] = 1.0f;
-        data[SAMPLE_SIZE] = 1.0f;
-        ev.mTimeSamples[0] = eventTime;
-
-        return ev;
-    }
-
-    /**
-     * Scales down the coordination of this event by the given scale.
-     *
-     * @hide
-     */
-    public void scale(float scale) {
-        mRawX *= scale;
-        mRawY *= scale;
-        mXPrecision *= scale;
-        mYPrecision *= scale;
-        float[] history = mDataSamples;
-        final int length = mNumPointers * mNumSamples * NUM_SAMPLE_DATA;
-        for (int i = 0; i < length; i += NUM_SAMPLE_DATA) {
-            history[i + SAMPLE_X] *= scale;
-            history[i + SAMPLE_Y] *= scale;
-            // no need to scale pressure
-            history[i + SAMPLE_SIZE] *= scale;    // TODO: square this?
-        }
+        return obtain(downTime, eventTime, action, x, y, 1.0f, 1.0f,
+                metaState, 1.0f, 1.0f, 0, 0);
     }
 
     /**
      * Create a new MotionEvent, copying from an existing one.
      */
     static public MotionEvent obtain(MotionEvent o) {
-        MotionEvent ev = obtain();
+        MotionEvent ev = obtain(o.mNumPointers, o.mNumSamples);
         ev.mDeviceId = o.mDeviceId;
         ev.mEdgeFlags = o.mEdgeFlags;
-        ev.mDownTime = o.mDownTime;
-        ev.mEventTimeNano = o.mEventTimeNano;
+        ev.mDownTimeNano = o.mDownTimeNano;
         ev.mAction = o.mAction;
-        ev.mNumPointers = o.mNumPointers;
-        ev.mRawX = o.mRawX;
-        ev.mRawY = o.mRawY;
         ev.mMetaState = o.mMetaState;
+        ev.mXOffset = o.mXOffset;
+        ev.mYOffset = o.mYOffset;
         ev.mXPrecision = o.mXPrecision;
         ev.mYPrecision = o.mYPrecision;
+        int numPointers = ev.mNumPointers = o.mNumPointers;
+        int numSamples = ev.mNumSamples = o.mNumSamples;
         
-        final int NS = ev.mNumSamples = o.mNumSamples;
-        if (ev.mTimeSamples.length >= NS) {
-            System.arraycopy(o.mTimeSamples, 0, ev.mTimeSamples, 0, NS);
-        } else {
-            ev.mTimeSamples = (long[])o.mTimeSamples.clone();
-        }
+        ev.mLastDataSampleIndex = o.mLastDataSampleIndex;
+        ev.mLastEventTimeNanoSampleIndex = o.mLastEventTimeNanoSampleIndex;
         
-        final int NP = (ev.mNumPointers=o.mNumPointers);
-        if (ev.mPointerIdentifiers.length >= NP) {
-            System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, NP);
-        } else {
-            ev.mPointerIdentifiers = (int[])o.mPointerIdentifiers.clone();
-        }
+        System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, numPointers);
         
-        final int ND = NP * NS * NUM_SAMPLE_DATA;
-        if (ev.mDataSamples.length >= ND) {
-            System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, ND);
-        } else {
-            ev.mDataSamples = (float[])o.mDataSamples.clone();
-        }
+        System.arraycopy(o.mEventTimeNanoSamples, 0, ev.mEventTimeNanoSamples, 0, numSamples);
         
+        System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0,
+                numPointers * numSamples * NUM_SAMPLE_DATA);
         return ev;
     }
 
@@ -617,36 +529,29 @@
      * any historical point information.
      */
     static public MotionEvent obtainNoHistory(MotionEvent o) {
-        MotionEvent ev = obtain();
+        MotionEvent ev = obtain(o.mNumPointers, 1);
         ev.mDeviceId = o.mDeviceId;
         ev.mEdgeFlags = o.mEdgeFlags;
-        ev.mDownTime = o.mDownTime;
-        ev.mEventTimeNano = o.mEventTimeNano;
+        ev.mDownTimeNano = o.mDownTimeNano;
         ev.mAction = o.mAction;
-        ev.mNumPointers = o.mNumPointers;
-        ev.mRawX = o.mRawX;
-        ev.mRawY = o.mRawY;
         ev.mMetaState = o.mMetaState;
+        ev.mXOffset = o.mXOffset;
+        ev.mYOffset = o.mYOffset;
         ev.mXPrecision = o.mXPrecision;
         ev.mYPrecision = o.mYPrecision;
         
+        int numPointers = ev.mNumPointers = o.mNumPointers;
         ev.mNumSamples = 1;
-        ev.mTimeSamples[0] = o.mTimeSamples[0];
         
-        final int NP = (ev.mNumPointers=o.mNumPointers);
-        if (ev.mPointerIdentifiers.length >= NP) {
-            System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, NP);
-        } else {
-            ev.mPointerIdentifiers = (int[])o.mPointerIdentifiers.clone();
-        }
+        ev.mLastDataSampleIndex = 0;
+        ev.mLastEventTimeNanoSampleIndex = 0;
         
-        final int ND = NP * NUM_SAMPLE_DATA;
-        if (ev.mDataSamples.length >= ND) {
-            System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, ND);
-        } else {
-            ev.mDataSamples = (float[])o.mDataSamples.clone();
-        }
+        System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, numPointers);
         
+        ev.mEventTimeNanoSamples[0] = o.mEventTimeNanoSamples[o.mLastEventTimeNanoSampleIndex];
+        
+        System.arraycopy(o.mDataSamples, o.mLastDataSampleIndex, ev.mDataSamples, 0,
+                numPointers * NUM_SAMPLE_DATA);
         return ev;
     }
 
@@ -654,7 +559,7 @@
      * Recycle the MotionEvent, to be re-used by a later caller.  After calling
      * this function you must not ever touch the event again.
      */
-    public void recycle() {
+    public final void recycle() {
         // Ensure recycle is only called once!
         if (TRACK_RECYCLED_LOCATION) {
             if (mRecycledLocation != null) {
@@ -678,6 +583,27 @@
             }
         }
     }
+    
+    /**
+     * Scales down the coordination of this event by the given scale.
+     *
+     * @hide
+     */
+    public final void scale(float scale) {
+        mXOffset *= scale;
+        mYOffset *= scale;
+        mXPrecision *= scale;
+        mYPrecision *= scale;
+        
+        float[] history = mDataSamples;
+        final int length = mNumPointers * mNumSamples * NUM_SAMPLE_DATA;
+        for (int i = 0; i < length; i += NUM_SAMPLE_DATA) {
+            history[i + SAMPLE_X] *= scale;
+            history[i + SAMPLE_Y] *= scale;
+            // no need to scale pressure
+            history[i + SAMPLE_SIZE] *= scale;    // TODO: square this?
+        }
+    }
 
     /**
      * Return the kind of action being performed -- one of either
@@ -719,14 +645,14 @@
      * a stream of position events.
      */
     public final long getDownTime() {
-        return mDownTime;
+        return mDownTimeNano / MS_PER_NS;
     }
 
     /**
      * Returns the time (in ms) when this specific event was generated.
      */
     public final long getEventTime() {
-        return mTimeSamples[0];
+        return mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] / MS_PER_NS;
     }
 
     /**
@@ -736,7 +662,7 @@
      * @hide
      */
     public final long getEventTimeNano() {
-        return mEventTimeNano;
+        return mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex];
     }
 
     /**
@@ -744,7 +670,7 @@
      * arbitrary pointer identifier).
      */
     public final float getX() {
-        return mDataSamples[SAMPLE_X];
+        return mDataSamples[mLastDataSampleIndex + SAMPLE_X] + mXOffset;
     }
 
     /**
@@ -752,7 +678,7 @@
      * arbitrary pointer identifier).
      */
     public final float getY() {
-        return mDataSamples[SAMPLE_Y];
+        return mDataSamples[mLastDataSampleIndex + SAMPLE_Y] + mYOffset;
     }
 
     /**
@@ -760,7 +686,7 @@
      * arbitrary pointer identifier).
      */
     public final float getPressure() {
-        return mDataSamples[SAMPLE_PRESSURE];
+        return mDataSamples[mLastDataSampleIndex + SAMPLE_PRESSURE];
     }
 
     /**
@@ -768,7 +694,7 @@
      * arbitrary pointer identifier).
      */
     public final float getSize() {
-        return mDataSamples[SAMPLE_SIZE];
+        return mDataSamples[mLastDataSampleIndex + SAMPLE_SIZE];
     }
 
     /**
@@ -820,7 +746,8 @@
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      */
     public final float getX(int pointerIndex) {
-        return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X];
+        return mDataSamples[mLastDataSampleIndex
+                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
     }
 
     /**
@@ -833,7 +760,8 @@
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      */
     public final float getY(int pointerIndex) {
-        return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_Y];
+        return mDataSamples[mLastDataSampleIndex
+                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset;
     }
 
     /**
@@ -848,7 +776,8 @@
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      */
     public final float getPressure(int pointerIndex) {
-        return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_PRESSURE];
+        return mDataSamples[mLastDataSampleIndex
+                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_PRESSURE];
     }
 
     /**
@@ -864,7 +793,8 @@
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      */
     public final float getSize(int pointerIndex) {
-        return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_SIZE];
+        return mDataSamples[mLastDataSampleIndex
+                            + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_SIZE];
     }
 
     /**
@@ -888,7 +818,7 @@
      * and views.
      */
     public final float getRawX() {
-        return mRawX;
+        return mDataSamples[mLastDataSampleIndex + SAMPLE_X];
     }
 
     /**
@@ -898,7 +828,7 @@
      * and views.
      */
     public final float getRawY() {
-        return mRawY;
+        return mDataSamples[mLastDataSampleIndex + SAMPLE_Y];
     }
 
     /**
@@ -930,7 +860,7 @@
      * @return Returns the number of historical points in the event.
      */
     public final int getHistorySize() {
-        return mNumSamples - 1;
+        return mLastEventTimeNanoSampleIndex;
     }
 
     /**
@@ -944,7 +874,7 @@
      * @see #getEventTime
      */
     public final long getHistoricalEventTime(int pos) {
-        return mTimeSamples[pos + 1];
+        return mEventTimeNanoSamples[pos] / MS_PER_NS;
     }
 
     /**
@@ -952,7 +882,7 @@
      * arbitrary pointer identifier).
      */
     public final float getHistoricalX(int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_X];
+        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
     }
 
     /**
@@ -960,7 +890,7 @@
      * arbitrary pointer identifier).
      */
     public final float getHistoricalY(int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_Y];
+        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset;
     }
 
     /**
@@ -968,7 +898,7 @@
      * arbitrary pointer identifier).
      */
     public final float getHistoricalPressure(int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_PRESSURE];
+        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_PRESSURE];
     }
 
     /**
@@ -976,7 +906,7 @@
      * arbitrary pointer identifier).
      */
     public final float getHistoricalSize(int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_SIZE];
+        return mDataSamples[pos * mNumPointers * NUM_SAMPLE_DATA + SAMPLE_SIZE];
     }
 
     /**
@@ -993,8 +923,8 @@
      * @see #getX
      */
     public final float getHistoricalX(int pointerIndex, int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
-                            + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_X];
+        return mDataSamples[(pos * mNumPointers + pointerIndex)
+                            * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
     }
 
     /**
@@ -1011,8 +941,8 @@
      * @see #getY
      */
     public final float getHistoricalY(int pointerIndex, int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
-                            + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_Y];
+        return mDataSamples[(pos * mNumPointers + pointerIndex)
+                            * NUM_SAMPLE_DATA + SAMPLE_Y] + mYOffset;
     }
 
     /**
@@ -1029,8 +959,8 @@
      * @see #getPressure
      */
     public final float getHistoricalPressure(int pointerIndex, int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
-                            + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_PRESSURE];
+        return mDataSamples[(pos * mNumPointers + pointerIndex)
+                            * NUM_SAMPLE_DATA + SAMPLE_PRESSURE];
     }
 
     /**
@@ -1047,8 +977,8 @@
      * @see #getSize
      */
     public final float getHistoricalSize(int pointerIndex, int pos) {
-        return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
-                            + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_SIZE];
+        return mDataSamples[(pos * mNumPointers + pointerIndex)
+                            * NUM_SAMPLE_DATA + SAMPLE_SIZE];
     }
 
     /**
@@ -1098,12 +1028,8 @@
      * @param deltaY Amount to add to the current Y coordinate of the event.
      */
     public final void offsetLocation(float deltaX, float deltaY) {
-        final int N = mNumPointers*mNumSamples*4;
-        final float[] pos = mDataSamples;
-        for (int i=0; i<N; i+=NUM_SAMPLE_DATA) {
-            pos[i+SAMPLE_X] += deltaX;
-            pos[i+SAMPLE_Y] += deltaY;
-        }
+        mXOffset += deltaX;
+        mYOffset += deltaY;
     }
 
     /**
@@ -1114,11 +1040,28 @@
      * @param y New absolute Y location.
      */
     public final void setLocation(float x, float y) {
-        float deltaX = x-mDataSamples[SAMPLE_X];
-        float deltaY = y-mDataSamples[SAMPLE_Y];
-        if (deltaX != 0 || deltaY != 0) {
-            offsetLocation(deltaX, deltaY);
+        mXOffset = x - mDataSamples[mLastDataSampleIndex + SAMPLE_X];
+        mYOffset = y - mDataSamples[mLastDataSampleIndex + SAMPLE_Y];
+    }
+    
+    private final void incrementNumSamplesAndReserveStorage(int dataSampleStride) {
+        if (mNumSamples == mEventTimeNanoSamples.length) {
+            long[] newEventTimeNanoSamples = new long[mNumSamples + BASE_AVAIL_SAMPLES];
+            System.arraycopy(mEventTimeNanoSamples, 0, newEventTimeNanoSamples, 0, mNumSamples);
+            mEventTimeNanoSamples = newEventTimeNanoSamples;
         }
+        
+        int nextDataSampleIndex = mLastDataSampleIndex + dataSampleStride;
+        if (nextDataSampleIndex + dataSampleStride > mDataSamples.length) {
+            float[] newDataSamples = new float[nextDataSampleIndex
+                                               + BASE_AVAIL_SAMPLES * dataSampleStride];
+            System.arraycopy(mDataSamples, 0, newDataSamples, 0, nextDataSampleIndex);
+            mDataSamples = newDataSamples;
+        }
+        
+        mLastEventTimeNanoSampleIndex = mNumSamples;
+        mLastDataSampleIndex = nextDataSampleIndex;
+        mNumSamples += 1;
     }
 
     /**
@@ -1136,42 +1079,16 @@
      */
     public final void addBatch(long eventTime, float x, float y,
             float pressure, float size, int metaState) {
-        float[] data = mDataSamples;
-        long[] times = mTimeSamples;
+        incrementNumSamplesAndReserveStorage(NUM_SAMPLE_DATA);
         
-        final int NP = mNumPointers;
-        final int NS = mNumSamples;
-        final int NI = NP*NS;
-        final int ND = NI * NUM_SAMPLE_DATA;
-        if (data.length <= ND) {
-            final int NEW_ND = ND + (NP * (BASE_AVAIL_SAMPLES * NUM_SAMPLE_DATA));
-            float[] newData = new float[NEW_ND];
-            System.arraycopy(data, 0, newData, 0, ND);
-            mDataSamples = data = newData;
-        }
-        if (times.length <= NS) {
-            final int NEW_NS = NS + BASE_AVAIL_SAMPLES;
-            long[] newHistoryTimes = new long[NEW_NS];
-            System.arraycopy(times, 0, newHistoryTimes, 0, NS);
-            mTimeSamples = times = newHistoryTimes;
-        }
+        mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] = eventTime * MS_PER_NS;
         
-        times[NS] = times[0];
-        times[0] = eventTime;
+        float[] dataSamples = mDataSamples;
+        dataSamples[mLastDataSampleIndex + SAMPLE_X] = x - mXOffset;
+        dataSamples[mLastDataSampleIndex + SAMPLE_Y] = y - mYOffset;
+        dataSamples[mLastDataSampleIndex + SAMPLE_PRESSURE] = pressure;
+        dataSamples[mLastDataSampleIndex + SAMPLE_SIZE] = size;
         
-        final int pos = NS*NUM_SAMPLE_DATA;
-        data[pos+SAMPLE_X] = data[SAMPLE_X];
-        data[pos+SAMPLE_Y] = data[SAMPLE_Y];
-        data[pos+SAMPLE_PRESSURE] = data[SAMPLE_PRESSURE];
-        data[pos+SAMPLE_SIZE] = data[SAMPLE_SIZE];
-        data[SAMPLE_X] = x;
-        data[SAMPLE_Y] = y;
-        data[SAMPLE_PRESSURE] = pressure;
-        data[SAMPLE_SIZE] = size;
-        mNumSamples = NS+1;
-
-        mRawX = x;
-        mRawY = y;
         mMetaState |= metaState;
     }
 
@@ -1187,48 +1104,36 @@
      * @hide
      */
     public final void addBatch(long eventTime, float[] inData, int metaState) {
-        float[] data = mDataSamples;
-        long[] times = mTimeSamples;
+        final int numPointers = mNumPointers;
+        final int dataSampleStride = numPointers * NUM_SAMPLE_DATA;
+        incrementNumSamplesAndReserveStorage(dataSampleStride);
         
-        final int NP = mNumPointers;
-        final int NS = mNumSamples;
-        final int NI = NP*NS;
-        final int ND = NI * NUM_SAMPLE_DATA;
-        if (data.length < (ND+(NP*NUM_SAMPLE_DATA))) {
-            final int NEW_ND = ND + (NP * (BASE_AVAIL_SAMPLES * NUM_SAMPLE_DATA));
-            float[] newData = new float[NEW_ND];
-            System.arraycopy(data, 0, newData, 0, ND);
-            mDataSamples = data = newData;
-        }
-        if (times.length < (NS+1)) {
-            final int NEW_NS = NS + BASE_AVAIL_SAMPLES;
-            long[] newHistoryTimes = new long[NEW_NS];
-            System.arraycopy(times, 0, newHistoryTimes, 0, NS);
-            mTimeSamples = times = newHistoryTimes;
-        }
+        mEventTimeNanoSamples[mLastEventTimeNanoSampleIndex] = eventTime * MS_PER_NS;
         
-        times[NS] = times[0];
-        times[0] = eventTime;
-        
-        System.arraycopy(data, 0, data, ND, mNumPointers*NUM_SAMPLE_DATA);
-        System.arraycopy(inData, 0, data, 0, mNumPointers*NUM_SAMPLE_DATA);
-        
-        mNumSamples = NS+1;
+        float[] dataSamples = mDataSamples;
+        System.arraycopy(inData, 0, dataSamples, mLastDataSampleIndex, dataSampleStride);
 
-        mRawX = inData[SAMPLE_X];
-        mRawY = inData[SAMPLE_Y];
+        if (mXOffset != 0 || mYOffset != 0) {
+            int index = mLastEventTimeNanoSampleIndex;
+            for (int i = 0; i < numPointers; i++) {
+                dataSamples[index + SAMPLE_X] -= mXOffset;
+                dataSamples[index + SAMPLE_Y] -= mYOffset;
+                index += NUM_SAMPLE_DATA;
+            }
+        }
+        
         mMetaState |= metaState;
         
         if (DEBUG_POINTERS) {
             StringBuilder sb = new StringBuilder(128);
             sb.append("Add:");
-            for (int i=0; i<mNumPointers; i++) {
+            for (int i = 0; i < mNumPointers; i++) {
                 sb.append(" #");
-                sb.append(mPointerIdentifiers[i]);
+                sb.append(getPointerId(i));
                 sb.append("(");
-                sb.append(mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_X]);
+                sb.append(getX(i));
                 sb.append(",");
-                sb.append(mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_Y]);
+                sb.append(getY(i));
                 sb.append(")");
             }
             Log.v("MotionEvent", sb.toString());
@@ -1245,8 +1150,41 @@
     public static final Parcelable.Creator<MotionEvent> CREATOR
             = new Parcelable.Creator<MotionEvent>() {
         public MotionEvent createFromParcel(Parcel in) {
-            MotionEvent ev = obtain();
-            ev.readFromParcel(in);
+            final int NP = in.readInt();
+            final int NS = in.readInt();
+            final int NI = NP * NS * NUM_SAMPLE_DATA;
+            
+            MotionEvent ev = obtain(NP, NS);
+            ev.mNumPointers = NP;
+            ev.mNumSamples = NS;
+            
+            ev.mDownTimeNano = in.readLong();
+            ev.mAction = in.readInt();
+            ev.mXOffset = in.readFloat();
+            ev.mYOffset = in.readFloat();
+            ev.mXPrecision = in.readFloat();
+            ev.mYPrecision = in.readFloat();
+            ev.mDeviceId = in.readInt();
+            ev.mEdgeFlags = in.readInt();
+            ev.mMetaState = in.readInt();
+            
+            final int[] pointerIdentifiers = ev.mPointerIdentifiers;
+            for (int i = 0; i < NP; i++) {
+                pointerIdentifiers[i] = in.readInt();
+            }
+            
+            final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples;
+            for (int i = 0; i < NS; i++) {
+                eventTimeNanoSamples[i] = in.readLong();
+            }
+
+            final float[] dataSamples = ev.mDataSamples;
+            for (int i = 0; i < NI; i++) {
+                dataSamples[i] = in.readFloat();
+            }
+            
+            ev.mLastEventTimeNanoSampleIndex = NS - 1;
+            ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA;
             return ev;
         }
 
@@ -1260,79 +1198,36 @@
     }
 
     public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mDownTime);
-        out.writeLong(mEventTimeNano);
-        out.writeInt(mAction);
-        out.writeInt(mMetaState);
-        out.writeFloat(mRawX);
-        out.writeFloat(mRawY);
         final int NP = mNumPointers;
-        out.writeInt(NP);
         final int NS = mNumSamples;
+        final int NI = NP * NS * NUM_SAMPLE_DATA;
+        
+        out.writeInt(NP);
         out.writeInt(NS);
-        final int NI = NP*NS;
-        if (NI > 0) {
-            int i;
-            int[] state = mPointerIdentifiers;
-            for (i=0; i<NP; i++) {
-                out.writeInt(state[i]);
-            }
-            final int ND = NI*NUM_SAMPLE_DATA;
-            float[] history = mDataSamples;
-            for (i=0; i<ND; i++) {
-                out.writeFloat(history[i]);
-            }
-            long[] times = mTimeSamples;
-            for (i=0; i<NS; i++) {
-                out.writeLong(times[i]);
-            }
-        }
+        
+        out.writeLong(mDownTimeNano);
+        out.writeInt(mAction);
+        out.writeFloat(mXOffset);
+        out.writeFloat(mYOffset);
         out.writeFloat(mXPrecision);
         out.writeFloat(mYPrecision);
         out.writeInt(mDeviceId);
         out.writeInt(mEdgeFlags);
-    }
-
-    private void readFromParcel(Parcel in) {
-        mDownTime = in.readLong();
-        mEventTimeNano = in.readLong();
-        mAction = in.readInt();
-        mMetaState = in.readInt();
-        mRawX = in.readFloat();
-        mRawY = in.readFloat();
-        final int NP = in.readInt();
-        mNumPointers = NP;
-        final int NS = in.readInt();
-        mNumSamples = NS;
-        final int NI = NP*NS;
-        if (NI > 0) {
-            int[] ids = mPointerIdentifiers;
-            if (ids.length < NP) {
-                mPointerIdentifiers = ids = new int[NP];
-            }
-            for (int i=0; i<NP; i++) {
-                ids[i] = in.readInt();
-            }
-            float[] history = mDataSamples;
-            final int ND = NI*NUM_SAMPLE_DATA;
-            if (history.length < ND) {
-                mDataSamples = history = new float[ND];
-            }
-            for (int i=0; i<ND; i++) {
-                history[i] = in.readFloat();
-            }
-            long[] times = mTimeSamples;
-            if (times == null || times.length < NS) {
-                mTimeSamples = times = new long[NS];
-            }
-            for (int i=0; i<NS; i++) {
-                times[i] = in.readLong();
-            }
+        out.writeInt(mMetaState);
+        
+        final int[] pointerIdentifiers = mPointerIdentifiers;
+        for (int i = 0; i < NP; i++) {
+            out.writeInt(pointerIdentifiers[i]);
         }
-        mXPrecision = in.readFloat();
-        mYPrecision = in.readFloat();
-        mDeviceId = in.readInt();
-        mEdgeFlags = in.readInt();
-    }
+        
+        final long[] eventTimeNanoSamples = mEventTimeNanoSamples;
+        for (int i = 0; i < NS; i++) {
+            out.writeLong(eventTimeNanoSamples[i]);
+        }
 
+        final float[] dataSamples = mDataSamples;
+        for (int i = 0; i < NI; i++) {
+            out.writeFloat(dataSamples[i]);
+        }
+    }
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 8beceec..d1a0f75 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -724,6 +724,12 @@
         }
 
         public void setFormat(int format) {
+
+            // for backward compatibility reason, OPAQUE always
+            // means 565 for SurfaceView
+            if (format == PixelFormat.OPAQUE)
+                format = PixelFormat.RGB_565;
+
             mRequestedFormat = format;
             if (mWindow != null) {
                 updateWindow(false);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0aa1fde..e3ed91e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -580,9 +580,23 @@
          * If the keyguard is currently active and is secure (requires an
          * unlock pattern) than the user will still need to confirm it before
          * seeing this window, unless {@link #FLAG_SHOW_WHEN_LOCKED} has
-         * also been set. */
+         * also been set.
+         */
         public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
-        
+
+        /** Window flag: This window corresponds to an immersive activity
+         * that wishes not to be interrupted with notifications.  In general,
+         * applications may simply hide the status bar with
+         * {@link #FLAG_FULLSCREEN} to suppress most notifications, but will
+         * still be interrupted by those with
+         * {@link android.app.Notification#fullScreenIntent fullScreenIntent}
+         * set (example: an incoming call). Setting {@link #FLAG_IMMERSIVE}
+         * will suppress the full-screen intent and show the status bar
+         * briefly for those important notifications instead.
+         * {@see android.app.Notification#FLAG_HIGH_PRIORITY}
+         */
+        public static final int FLAG_IMMERSIVE = 0x00800000;
+
         /** Window flag: *sigh* The lock screen wants to continue running its
          * animation while it is fading.  A kind-of hack to allow this.  Maybe
          * in the future we just make this the default behavior.
@@ -685,7 +699,7 @@
         /** Adjustment option for {@link #softInputMode}: set to allow the
          * window to be resized when an input
          * method is shown, so that its contents are not covered by the input
-         * method.  This can <em>not<em> be combined with
+         * method.  This can <em>not</em> be combined with
          * {@link #SOFT_INPUT_ADJUST_PAN}; if
          * neither of these are set, then the system will try to pick one or
          * the other depending on the contents of the window.
@@ -696,7 +710,7 @@
          * pan when an input method is
          * shown, so it doesn't need to deal with resizing but just panned
          * by the framework to ensure the current input focus is visible.  This
-         * can <em>not<em> be combined with {@link #SOFT_INPUT_ADJUST_RESIZE}; if
+         * can <em>not</em> be combined with {@link #SOFT_INPUT_ADJUST_RESIZE}; if
          * neither of these are set, then the system will try to pick one or
          * the other depending on the contents of the window.
          */
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d5ccdeb..88ef395 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -50,8 +50,7 @@
     void noteScanWifiLockReleased(int uid);
     void noteWifiMulticastEnabled(int uid);
     void noteWifiMulticastDisabled(int uid);
-    void setOnBattery(boolean onBattery, int level);
-    void recordCurrentLevel(int level);
+    void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
     long getAwakeTimeBattery();
     long getAwakeTimePlugged();
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3833725..f8ab04c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -20,6 +20,7 @@
 
 import android.bluetooth.BluetoothHeadset;
 import android.net.TrafficStats;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Parcel;
 import android.os.ParcelFormatException;
@@ -62,8 +63,11 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 44;
+    private static final int VERSION = 48;
 
+    // Maximum number of items we will record in the history.
+    private static final int MAX_HISTORY_ITEMS = 1000;
+    
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
     // in to one common name.
@@ -95,10 +99,16 @@
     // is unplugged from power.
     final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
     
-    BatteryHistoryRecord mHistory;
-    BatteryHistoryRecord mHistoryEnd;
-    BatteryHistoryRecord mHistoryCache;
-    final BatteryHistoryRecord mHistoryCur = new BatteryHistoryRecord();
+    boolean mShuttingDown;
+    
+    long mHistoryBaseTime;
+    boolean mHaveBatteryLevel = false;
+    boolean mRecordingHistory = true;
+    int mNumHistoryItems;
+    HistoryItem mHistory;
+    HistoryItem mHistoryEnd;
+    HistoryItem mHistoryCache;
+    final HistoryItem mHistoryCur = new HistoryItem();
     
     int mStartCount;
 
@@ -172,6 +182,7 @@
      * These keep track of battery levels (1-100) at the last plug event and the last unplug event.
      */
     int mDischargeStartLevel;
+    int mDischargeUnplugLevel;
     int mDischargeCurrentLevel;
 
     long mLastWriteTime = 0; // Milliseconds
@@ -238,12 +249,14 @@
      */
     public static class Counter extends BatteryStats.Counter implements Unpluggable {
         final AtomicInteger mCount = new AtomicInteger();
+        final ArrayList<Unpluggable> mUnpluggables;
         int mLoadedCount;
         int mLastCount;
         int mUnpluggedCount;
         int mPluggedCount;
         
         Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
+            mUnpluggables = unpluggables;
             mPluggedCount = in.readInt();
             mCount.set(mPluggedCount);
             mLoadedCount = in.readInt();
@@ -253,6 +266,7 @@
         }
 
         Counter(ArrayList<Unpluggable> unpluggables) {
+            mUnpluggables = unpluggables;
             unpluggables.add(this);
         }
         
@@ -295,9 +309,9 @@
                 val = mLastCount;
             } else {
                 val = mCount.get();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     val -= mUnpluggedCount;
-                } else if (which != STATS_TOTAL) {
+                } else if (which != STATS_SINCE_CHARGED) {
                     val -= mLoadedCount;
                 }
             }
@@ -316,6 +330,21 @@
             mCount.incrementAndGet();
         }
 
+        /**
+         * Clear state of this counter.
+         */
+        void reset(boolean detachIfReset) {
+            mCount.set(0);
+            mLoadedCount = mLastCount = mPluggedCount = mUnpluggedCount = 0;
+            if (detachIfReset) {
+                detach();
+            }
+        }
+        
+        void detach() {
+            mUnpluggables.remove(this);
+        }
+        
         void writeSummaryFromParcelLocked(Parcel out) {
             int count = mCount.get();
             out.writeInt(count);
@@ -350,7 +379,7 @@
      */
     public static abstract class Timer extends BatteryStats.Timer implements Unpluggable {
         final int mType;
-
+        final ArrayList<Unpluggable> mUnpluggables;
         
         int mCount;
         int mLoadedCount;
@@ -395,6 +424,7 @@
          */
         Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) {
             mType = type;
+            mUnpluggables = unpluggables;
             
             mCount = in.readInt();
             mLoadedCount = in.readInt();
@@ -409,6 +439,7 @@
 
         Timer(int type, ArrayList<Unpluggable> unpluggables) {
             mType = type;
+            mUnpluggables = unpluggables;
             unpluggables.add(this);
         }
 
@@ -416,6 +447,22 @@
         
         protected abstract int computeCurrentCountLocked();
         
+        /**
+         * Clear state of this timer.  Returns true if the timer is inactive
+         * so can be completely dropped.
+         */
+        boolean reset(boolean detachIfReset) {
+            mTotalTime = mLoadedTime = mLastTime = 0;
+            mCount = mLoadedCount = mLastCount = 0;
+            if (detachIfReset) {
+                detach();
+            }
+            return true;
+        }
+        
+        void detach() {
+            mUnpluggables.remove(this);
+        }
         
         public void writeToParcel(Parcel out, long batteryRealtime) {
             out.writeInt(mCount);
@@ -480,9 +527,9 @@
                 val = mLastTime;
             } else {
                 val = computeRunTimeLocked(batteryRealtime);
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     val -= mUnpluggedTime;
-                } else if (which != STATS_TOTAL) {
+                } else if (which != STATS_SINCE_CHARGED) {
                     val -= mLoadedTime;
                 }
             }
@@ -497,9 +544,9 @@
                 val = mLastCount;
             } else {
                 val = computeCurrentCountLocked();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     val -= mUnpluggedCount;
-                } else if (which != STATS_TOTAL) {
+                } else if (which != STATS_SINCE_CHARGED) {
                     val -= mLoadedCount;
                 }
             }
@@ -670,6 +717,12 @@
             out.writeInt(mTrackingReportedValues ? 1 : 0);
         }
         
+        boolean reset(boolean detachIfReset) {
+            super.reset(detachIfReset);
+            setStale();
+            return true;
+        }
+        
         void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
             super.writeSummaryFromParcelLocked(out, batteryRealtime);
             out.writeLong(mCurrentReportedTotalTime);
@@ -692,7 +745,6 @@
         final ArrayList<StopwatchTimer> mTimerPool;
         int mNesting;
 
-
         /**
          * The last time at which we updated the timer.  If mNesting is > 0,
          * subtract this from the current battery time to find the amount of
@@ -842,6 +894,19 @@
             return mCount;
         }
 
+        boolean reset(boolean detachIfReset) {
+            boolean canDetach = mNesting <= 0;
+            super.reset(canDetach && detachIfReset);
+            return canDetach;
+        }
+        
+        void detach() {
+            super.detach();
+            if (mTimerPool != null) {
+                mTimerPool.remove(this);
+            }
+        }
+        
         void readSummaryFromParcelLocked(Parcel in) {
             super.readSummaryFromParcelLocked(in);
             mNesting = 0;
@@ -979,12 +1044,12 @@
     }
 
     private void doDataPlug(long[] dataTransfer, long currentBytes) {
-        dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED];
-        dataTransfer[STATS_UNPLUGGED] = -1;
+        dataTransfer[STATS_LAST] = dataTransfer[STATS_SINCE_UNPLUGGED];
+        dataTransfer[STATS_SINCE_UNPLUGGED] = -1;
     }
 
     private void doDataUnplug(long[] dataTransfer, long currentBytes) {
-        dataTransfer[STATS_UNPLUGGED] = currentBytes;
+        dataTransfer[STATS_SINCE_UNPLUGGED] = currentBytes;
     }
 
     /**
@@ -1048,20 +1113,35 @@
         mBtHeadset = headset;
     }
 
-    void addHistoryRecord(long curTime) {
-        BatteryHistoryRecord rec = mHistoryCache;
+    void addHistoryRecordLocked(long curTime) {
+        if (!mHaveBatteryLevel || !mRecordingHistory) {
+            return;
+        }
+        if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
+            // Once we've reached the maximum number of items, we only
+            // record changes to the battery level.
+            if (mHistoryEnd != null && mHistoryEnd.batteryLevel
+                    == mHistoryCur.batteryLevel) {
+                return;
+            }
+        }
+        addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
+    }
+    
+    void addHistoryRecordLocked(long curTime, byte cmd) {
+        HistoryItem rec = mHistoryCache;
         if (rec != null) {
             mHistoryCache = rec.next;
         } else {
-            rec = new BatteryHistoryRecord();
+            rec = new HistoryItem();
         }
-        rec.time = curTime;
-        rec.batteryLevel = mHistoryCur.batteryLevel;
-        rec.states = mHistoryCur.states;
-        addHistoryRecord(rec);
+        rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        
+        addHistoryRecordLocked(rec);
     }
     
-    void addHistoryRecord(BatteryHistoryRecord rec) {
+    void addHistoryRecordLocked(HistoryItem rec) {
+        mNumHistoryItems++;
         rec.next = null;
         if (mHistoryEnd != null) {
             mHistoryEnd.next = rec;
@@ -1071,15 +1151,17 @@
         }
     }
     
-    void clearHistory() {
+    void clearHistoryLocked() {
         if (mHistory != null) {
             mHistoryEnd.next = mHistoryCache;
             mHistoryCache = mHistory;
             mHistory = mHistoryEnd = null;
         }
+        mNumHistoryItems = 0;
+        mHistoryBaseTime = 0;
     }
     
-    public void doUnplug(long batteryUptime, long batteryRealtime) {
+    public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
             u.mStartedTcpBytesReceived = TrafficStats.getUidRxBytes(u.mUid);
@@ -1103,7 +1185,7 @@
         mBluetoothPingCount = 0;
     }
 
-    public void doPlug(long batteryUptime, long batteryRealtime) {
+    public void doPlugLocked(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
             if (u.mStartedTcpBytesReceived >= 0) {
@@ -1133,34 +1215,34 @@
 
     int mGpsNesting;
     
-    public void noteStartGps(int uid) {
+    public void noteStartGpsLocked(int uid) {
         if (mGpsNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mGpsNesting++;
         getUidStatsLocked(uid).noteStartGps();
     }
     
-    public void noteStopGps(int uid) {
+    public void noteStopGpsLocked(int uid) {
         mGpsNesting--;
         if (mGpsNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteStopGps();
     }
 
     public void noteScreenOnLocked() {
         if (!mScreenOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mGpsNesting++;
             mScreenOn = true;
             mScreenOnTimer.startRunningLocked(this);
@@ -1172,10 +1254,10 @@
     
     public void noteScreenOffLocked() {
         if (mScreenOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mScreenOn = false;
             mScreenOnTimer.stopRunningLocked(this);
             if (mScreenBrightnessBin >= 0) {
@@ -1190,11 +1272,11 @@
         if (bin < 0) bin = 0;
         else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
         if (mScreenBrightnessBin != bin) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SCREEN_MASK)
-                    | (bin << BatteryHistoryRecord.STATE_SCREEN_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
+                    | (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             if (mScreenOn) {
                 if (mScreenBrightnessBin >= 0) {
                     mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
@@ -1215,10 +1297,10 @@
     
     public void notePhoneOnLocked() {
         if (!mPhoneOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_PHONE_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneOn = true;
             mPhoneOnTimer.startRunningLocked(this);
         }
@@ -1226,10 +1308,10 @@
     
     public void notePhoneOffLocked() {
         if (mPhoneOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_PHONE_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneOn = false;
             mPhoneOnTimer.stopRunningLocked(this);
         }
@@ -1251,7 +1333,11 @@
             }
         }
         // Stop Signal Scanning timer, in case we're going into service
-        while (mPhoneSignalScanningTimer.isRunningLocked()) {
+        if (mPhoneSignalScanningTimer.isRunningLocked()) {
+            mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneSignalScanningTimer.stopRunningLocked(this);
         }
 
@@ -1267,16 +1353,20 @@
                 mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].startRunningLocked(this);
             }
             if (!mPhoneSignalScanningTimer.isRunningLocked()) {
+                mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(SystemClock.elapsedRealtime());
                 mPhoneSignalScanningTimer.startRunningLocked(this);
             }
         }
         
         if (mPhoneServiceState != state) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_PHONE_STATE_MASK)
-                    | (state << BatteryHistoryRecord.STATE_PHONE_STATE_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_STATE_MASK)
+                    | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneServiceState = state;
         }
     }
@@ -1305,11 +1395,11 @@
             else bin = SIGNAL_STRENGTH_POOR;
         }
         if (mPhoneSignalStrengthBin != bin) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_MASK)
-                    | (bin << BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
+                    | (bin << HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             if (mPhoneSignalStrengthBin >= 0) {
                 mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this);
             }
@@ -1331,6 +1421,33 @@
                 case TelephonyManager.NETWORK_TYPE_UMTS:
                     bin = DATA_CONNECTION_UMTS;
                     break;
+                case TelephonyManager.NETWORK_TYPE_CDMA:
+                    bin = DATA_CONNECTION_CDMA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                    bin = DATA_CONNECTION_EVDO_0;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                    bin = DATA_CONNECTION_EVDO_A;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_1xRTT:
+                    bin = DATA_CONNECTION_1xRTT;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSDPA:
+                    bin = DATA_CONNECTION_HSDPA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSUPA:
+                    bin = DATA_CONNECTION_HSUPA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSPA:
+                    bin = DATA_CONNECTION_HSPA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_IDEN:
+                    bin = DATA_CONNECTION_IDEN;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                    bin = DATA_CONNECTION_EVDO_B;
+                    break;
                 default:
                     bin = DATA_CONNECTION_OTHER;
                     break;
@@ -1338,11 +1455,11 @@
         }
         if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_DATA_CONNECTION_MASK)
-                    | (bin << BatteryHistoryRecord.STATE_DATA_CONNECTION_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
+                    | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
             }
@@ -1353,10 +1470,10 @@
     
     public void noteWifiOnLocked(int uid) {
         if (!mWifiOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiOn = true;
             mWifiOnTimer.startRunningLocked(this);
         }
@@ -1371,10 +1488,10 @@
     
     public void noteWifiOffLocked(int uid) {
         if (mWifiOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiOn = false;
             mWifiOnTimer.stopRunningLocked(this);
         }
@@ -1386,10 +1503,10 @@
 
     public void noteAudioOnLocked(int uid) {
         if (!mAudioOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mAudioOn = true;
             mAudioOnTimer.startRunningLocked(this);
         }
@@ -1398,10 +1515,10 @@
     
     public void noteAudioOffLocked(int uid) {
         if (mAudioOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mAudioOn = false;
             mAudioOnTimer.stopRunningLocked(this);
         }
@@ -1410,10 +1527,10 @@
 
     public void noteVideoOnLocked(int uid) {
         if (!mVideoOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mVideoOn = true;
             mVideoOnTimer.startRunningLocked(this);
         }
@@ -1422,10 +1539,10 @@
     
     public void noteVideoOffLocked(int uid) {
         if (mVideoOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mVideoOn = false;
             mVideoOnTimer.stopRunningLocked(this);
         }
@@ -1434,10 +1551,10 @@
 
     public void noteWifiRunningLocked() {
         if (!mWifiRunning) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiRunning = true;
             mWifiRunningTimer.startRunningLocked(this);
         }
@@ -1445,10 +1562,10 @@
 
     public void noteWifiStoppedLocked() {
         if (mWifiRunning) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiRunning = false;
             mWifiRunningTimer.stopRunningLocked(this);
         }
@@ -1456,10 +1573,10 @@
 
     public void noteBluetoothOnLocked() {
         if (!mBluetoothOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mBluetoothOn = true;
             mBluetoothOnTimer.startRunningLocked(this);
         }
@@ -1467,10 +1584,10 @@
     
     public void noteBluetoothOffLocked() {
         if (mBluetoothOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mBluetoothOn = false;
             mBluetoothOnTimer.stopRunningLocked(this);
         }
@@ -1480,10 +1597,10 @@
     
     public void noteFullWifiLockAcquiredLocked(int uid) {
         if (mWifiFullLockNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked();
@@ -1492,10 +1609,10 @@
     public void noteFullWifiLockReleasedLocked(int uid) {
         mWifiFullLockNesting--;
         if (mWifiFullLockNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked();
     }
@@ -1504,10 +1621,10 @@
     
     public void noteScanWifiLockAcquiredLocked(int uid) {
         if (mWifiScanLockNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mWifiScanLockNesting++;
         getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked();
@@ -1516,10 +1633,10 @@
     public void noteScanWifiLockReleasedLocked(int uid) {
         mWifiScanLockNesting--;
         if (mWifiScanLockNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteScanWifiLockReleasedLocked();
     }
@@ -1528,10 +1645,10 @@
     
     public void noteWifiMulticastEnabledLocked(int uid) {
         if (mWifiMulticastNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked();
@@ -1540,10 +1657,10 @@
     public void noteWifiMulticastDisabledLocked(int uid) {
         mWifiMulticastNesting--;
         if (mWifiMulticastNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
     }
@@ -1712,9 +1829,9 @@
                 return mLoadedTcpBytesReceived;
             } else {
                 long current = computeCurrentTcpBytesReceived();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     current -= mTcpBytesReceivedAtLastUnplug;
-                } else if (which == STATS_TOTAL) {
+                } else if (which == STATS_SINCE_CHARGED) {
                     current += mLoadedTcpBytesReceived;
                 }
                 return current;
@@ -1732,9 +1849,9 @@
                 return mLoadedTcpBytesSent;
             } else {
                 long current = computeCurrentTcpBytesSent();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     current -= mTcpBytesSentAtLastUnplug;
-                } else if (which == STATS_TOTAL) {
+                } else if (which == STATS_SINCE_CHARGED) {
                     current += mLoadedTcpBytesSent;
                 }
                 return current;
@@ -1745,6 +1862,10 @@
         public void noteWifiTurnedOnLocked() {
             if (!mWifiTurnedOn) {
                 mWifiTurnedOn = true;
+                if (mWifiTurnedOnTimer == null) {
+                    mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                            null, mUnpluggables);
+                }
                 mWifiTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
@@ -1761,43 +1882,15 @@
         public void noteFullWifiLockAcquiredLocked() {
             if (!mFullWifiLockOut) {
                 mFullWifiLockOut = true;
+                if (mFullWifiLockTimer == null) {
+                    mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                            null, mUnpluggables);
+                }
                 mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
         
         @Override
-        public void noteVideoTurnedOnLocked() {
-            if (!mVideoTurnedOn) {
-                mVideoTurnedOn = true;
-                mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
-        public void noteVideoTurnedOffLocked() {
-            if (mVideoTurnedOn) {
-                mVideoTurnedOn = false;
-                mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
-        public void noteAudioTurnedOnLocked() {
-            if (!mAudioTurnedOn) {
-                mAudioTurnedOn = true;
-                mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
-        public void noteAudioTurnedOffLocked() {
-            if (mAudioTurnedOn) {
-                mAudioTurnedOn = false;
-                mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
         public void noteFullWifiLockReleasedLocked() {
             if (mFullWifiLockOut) {
                 mFullWifiLockOut = false;
@@ -1809,6 +1902,10 @@
         public void noteScanWifiLockAcquiredLocked() {
             if (!mScanWifiLockOut) {
                 mScanWifiLockOut = true;
+                if (mScanWifiLockTimer == null) {
+                    mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                            null, mUnpluggables);
+                }
                 mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
@@ -1825,6 +1922,10 @@
         public void noteWifiMulticastEnabledLocked() {
             if (!mWifiMulticastEnabled) {
                 mWifiMulticastEnabled = true;
+                if (mWifiMulticastTimer == null) {
+                    mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                            null, mUnpluggables);
+                }
                 mWifiMulticastTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
@@ -1837,37 +1938,95 @@
             }
         }
 
+        @Override
+        public void noteAudioTurnedOnLocked() {
+            if (!mAudioTurnedOn) {
+                mAudioTurnedOn = true;
+                if (mAudioTurnedOnTimer == null) {
+                    mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                            null, mUnpluggables);
+                }
+                mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
+        @Override
+        public void noteAudioTurnedOffLocked() {
+            if (mAudioTurnedOn) {
+                mAudioTurnedOn = false;
+                mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
+        @Override
+        public void noteVideoTurnedOnLocked() {
+            if (!mVideoTurnedOn) {
+                mVideoTurnedOn = true;
+                if (mVideoTurnedOnTimer == null) {
+                    mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                            null, mUnpluggables);
+                }
+                mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
+        @Override
+        public void noteVideoTurnedOffLocked() {
+            if (mVideoTurnedOn) {
+                mVideoTurnedOn = false;
+                mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
         @Override 
         public long getWifiTurnedOnTime(long batteryRealtime, int which) {
+            if (mWifiTurnedOnTimer == null) {
+                return 0;
+            }
             return mWifiTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
         }
 
         @Override 
-        public long getAudioTurnedOnTime(long batteryRealtime, int which) {
-            return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
-        }
-
-        @Override 
-        public long getVideoTurnedOnTime(long batteryRealtime, int which) {
-            return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
-        }
-
-        @Override 
         public long getFullWifiLockTime(long batteryRealtime, int which) {
+            if (mFullWifiLockTimer == null) {
+                return 0;
+            }
             return mFullWifiLockTimer.getTotalTimeLocked(batteryRealtime, which);
         }
         
         @Override 
         public long getScanWifiLockTime(long batteryRealtime, int which) {
+            if (mScanWifiLockTimer == null) {
+                return 0;
+            }
             return mScanWifiLockTimer.getTotalTimeLocked(batteryRealtime, which);
         }
 
         @Override
         public long getWifiMulticastTime(long batteryRealtime, int which) {
+            if (mWifiMulticastTimer == null) {
+                return 0;
+            }
             return mWifiMulticastTimer.getTotalTimeLocked(batteryRealtime,
                                                           which);
         }
 
+        @Override 
+        public long getAudioTurnedOnTime(long batteryRealtime, int which) {
+            if (mAudioTurnedOnTimer == null) {
+                return 0;
+            }
+            return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
+        }
+
+        @Override 
+        public long getVideoTurnedOnTime(long batteryRealtime, int which) {
+            if (mVideoTurnedOnTimer == null) {
+                return 0;
+            }
+            return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
+        }
+
         @Override
         public void noteUserActivityLocked(int type) {
             if (mUserActivityCounters == null) {
@@ -1903,6 +2062,126 @@
                     ? (TrafficStats.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0);
         }
 
+        /**
+         * Clear all stats for this uid.  Returns true if the uid is completely
+         * inactive so can be dropped.
+         */
+        boolean reset() {
+            boolean active = false;
+            
+            if (mWifiTurnedOnTimer != null) {
+                active |= !mWifiTurnedOnTimer.reset(false);
+                active |= mWifiTurnedOn;
+            }
+            if (mFullWifiLockTimer != null) {
+                active |= !mFullWifiLockTimer.reset(false);
+                active |= mFullWifiLockOut;
+            }
+            if (mScanWifiLockTimer != null) {
+                active |= !mScanWifiLockTimer.reset(false);
+                active |= mScanWifiLockOut;
+            }
+            if (mWifiMulticastTimer != null) {
+                active |= !mWifiMulticastTimer.reset(false);
+                active |= mWifiMulticastEnabled;
+            }
+            if (mAudioTurnedOnTimer != null) {
+                active |= !mAudioTurnedOnTimer.reset(false);
+                active |= mAudioTurnedOn;
+            }
+            if (mVideoTurnedOnTimer != null) {
+                active |= !mVideoTurnedOnTimer.reset(false);
+                active |= mVideoTurnedOn;
+            }
+            
+            mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
+            mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
+            
+            if (mUserActivityCounters != null) {
+                for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
+                    mUserActivityCounters[i].reset(false);
+                }
+            }
+
+            if (mWakelockStats.size() > 0) {
+                Iterator<Map.Entry<String, Wakelock>> it = mWakelockStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Wakelock> wakelockEntry = it.next();
+                    Wakelock wl = wakelockEntry.getValue();
+                    if (wl.reset()) {
+                        it.remove();
+                    } else {
+                        active = true;
+                    }
+                }
+            }
+            if (mSensorStats.size() > 0) {
+                Iterator<Map.Entry<Integer, Sensor>> it = mSensorStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<Integer, Sensor> sensorEntry = it.next();
+                    Sensor s = sensorEntry.getValue();
+                    if (s.reset()) {
+                        it.remove();
+                    } else {
+                        active = true;
+                    }
+                }
+            }
+            if (mProcessStats.size() > 0) {
+                Iterator<Map.Entry<String, Proc>> it = mProcessStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Proc> procEntry = it.next();
+                    procEntry.getValue().detach();
+                }
+                mProcessStats.clear();
+            }
+            if (mPackageStats.size() > 0) {
+                Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Pkg> pkgEntry = it.next();
+                    Pkg p = pkgEntry.getValue();
+                    p.detach();
+                    if (p.mServiceStats.size() > 0) {
+                        Iterator<Map.Entry<String, Pkg.Serv>> it2
+                                = p.mServiceStats.entrySet().iterator();
+                        while (it2.hasNext()) {
+                            Map.Entry<String, Pkg.Serv> servEntry = it2.next();
+                            servEntry.getValue().detach();
+                        }
+                    }
+                }
+                mPackageStats.clear();
+            }
+            
+            if (!active) {
+                if (mWifiTurnedOnTimer != null) {
+                    mWifiTurnedOnTimer.detach();
+                }
+                if (mFullWifiLockTimer != null) {
+                    mFullWifiLockTimer.detach();
+                }
+                if (mScanWifiLockTimer != null) {
+                    mScanWifiLockTimer.detach();
+                }
+                if (mWifiMulticastTimer != null) {
+                    mWifiMulticastTimer.detach();
+                }
+                if (mAudioTurnedOnTimer != null) {
+                    mAudioTurnedOnTimer.detach();
+                }
+                if (mVideoTurnedOnTimer != null) {
+                    mVideoTurnedOnTimer.detach();
+                }
+                if (mUserActivityCounters != null) {
+                    for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
+                        mUserActivityCounters[i].detach();
+                    }
+                }
+            }
+            
+            return !active;
+        }
+        
         void writeToParcelLocked(Parcel out, long batteryRealtime) {
             out.writeInt(mWakelockStats.size());
             for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) {
@@ -1938,19 +2217,49 @@
             out.writeLong(computeCurrentTcpBytesSent());
             out.writeLong(mTcpBytesReceivedAtLastUnplug);
             out.writeLong(mTcpBytesSentAtLastUnplug);
-            mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime);
-            mFullWifiLockTimer.writeToParcel(out, batteryRealtime);
-            mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime);
-            mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime);
-            mScanWifiLockTimer.writeToParcel(out, batteryRealtime);
-            mWifiMulticastTimer.writeToParcel(out, batteryRealtime);
-            if (mUserActivityCounters == null) {
-                out.writeInt(0);
+            if (mWifiTurnedOnTimer != null) {
+                out.writeInt(1);
+                mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime);
             } else {
+                out.writeInt(0);
+            }
+            if (mFullWifiLockTimer != null) {
+                out.writeInt(1);
+                mFullWifiLockTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mScanWifiLockTimer != null) {
+                out.writeInt(1);
+                mScanWifiLockTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mWifiMulticastTimer != null) {
+                out.writeInt(1);
+                mWifiMulticastTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mAudioTurnedOnTimer != null) {
+                out.writeInt(1);
+                mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mVideoTurnedOnTimer != null) {
+                out.writeInt(1);
+                mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mUserActivityCounters != null) {
                 out.writeInt(1);
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i].writeToParcel(out);
                 }
+            } else {
+                out.writeInt(0);
             }
         }
 
@@ -2003,25 +2312,54 @@
             mTcpBytesReceivedAtLastUnplug = in.readLong();
             mTcpBytesSentAtLastUnplug = in.readLong();
             mWifiTurnedOn = false;
-            mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables, in);
-            mFullWifiLockOut = false;
-            mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK, null, mUnpluggables, in);
-            mAudioTurnedOn = false;
-            mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables, in);
-            mVideoTurnedOn = false;
-            mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables, in);
-            mScanWifiLockOut = false;
-            mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables, in);
-            mWifiMulticastEnabled = false;
-            mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
-                    null, mUnpluggables, in);
-            if (in.readInt() == 0) {
-                mUserActivityCounters = null;
+            if (in.readInt() != 0) {
+                mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                        null, mUnpluggables, in);
             } else {
+                mWifiTurnedOnTimer = null;
+            }
+            mFullWifiLockOut = false;
+            if (in.readInt() != 0) {
+                mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                        null, mUnpluggables, in);
+            } else {
+                mFullWifiLockTimer = null;
+            }
+            mScanWifiLockOut = false;
+            if (in.readInt() != 0) {
+                mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                        null, mUnpluggables, in);
+            } else {
+                mScanWifiLockTimer = null;
+            }
+            mWifiMulticastEnabled = false;
+            if (in.readInt() != 0) {
+                mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                        null, mUnpluggables, in);
+            } else {
+                mWifiMulticastTimer = null;
+            }
+            mAudioTurnedOn = false;
+            if (in.readInt() != 0) {
+                mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                        null, mUnpluggables, in);
+            } else {
+                mAudioTurnedOnTimer = null;
+            }
+            mVideoTurnedOn = false;
+            if (in.readInt() != 0) {
+                mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                        null, mUnpluggables, in);
+            } else {
+                mVideoTurnedOnTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i] = new Counter(mUnpluggables, in);
                 }
+            } else {
+                mUserActivityCounters = null;
             }
         }
 
@@ -2060,6 +2398,34 @@
                 return new StopwatchTimer(type, pool, unpluggables, in);
             }
 
+            boolean reset() {
+                boolean wlactive = false;
+                if (mTimerFull != null) {
+                    wlactive |= !mTimerFull.reset(false);
+                }
+                if (mTimerPartial != null) {
+                    wlactive |= !mTimerPartial.reset(false);
+                }
+                if (mTimerWindow != null) {
+                    wlactive |= !mTimerWindow.reset(false);
+                }
+                if (!wlactive) {
+                    if (mTimerFull != null) {
+                        mTimerFull.detach();
+                        mTimerFull = null;
+                    }
+                    if (mTimerPartial != null) {
+                        mTimerPartial.detach();
+                        mTimerPartial = null;
+                    }
+                    if (mTimerWindow != null) {
+                        mTimerWindow.detach();
+                        mTimerWindow = null;
+                    }
+                }
+                return !wlactive;
+            }
+            
             void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
                 mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
                         mPartialTimers, unpluggables, in);
@@ -2108,6 +2474,14 @@
                 return new StopwatchTimer(0, pool, unpluggables, in);
             }
 
+            boolean reset() {
+                if (mTimer.reset(true)) {
+                    mTimer = null;
+                    return true;
+                }
+                return false;
+            }
+            
             void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
                 mTimer = readTimerFromParcel(unpluggables, in);
             }
@@ -2216,9 +2590,6 @@
             Proc() {
                 mUnpluggables.add(this);
                 mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
-                for (int i = 0; i < mSpeedBins.length; i++) {
-                    mSpeedBins[i] = new SamplingCounter(mUnpluggables);
-                }
             }
 
             public void unplug(long batteryUptime, long batteryRealtime) {
@@ -2231,10 +2602,18 @@
             public void plug(long batteryUptime, long batteryRealtime) {
             }
             
+            void detach() {
+                mUnpluggables.remove(this);
+                for (int i = 0; i < mSpeedBins.length; i++) {
+                    SamplingCounter c = mSpeedBins[i];
+                    if (c != null) {
+                        mUnpluggables.remove(c);
+                        mSpeedBins[i] = null;
+                    }
+                }
+            }
+            
             void writeToParcelLocked(Parcel out) {
-                final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
-                final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
-                
                 out.writeLong(mUserTime);
                 out.writeLong(mSystemTime);
                 out.writeLong(mForegroundTime);
@@ -2254,7 +2633,13 @@
 
                 out.writeInt(mSpeedBins.length);
                 for (int i = 0; i < mSpeedBins.length; i++) {
-                    mSpeedBins[i].writeToParcel(out);
+                    SamplingCounter c = mSpeedBins[i];
+                    if (c != null) {
+                        out.writeInt(1);
+                        c.writeToParcel(out);
+                    } else {
+                        out.writeInt(0);
+                    }
                 }
             }
 
@@ -2277,9 +2662,12 @@
                 mUnpluggedStarts = in.readInt();
 
                 int bins = in.readInt();
-                mSpeedBins = new SamplingCounter[bins];
+                int steps = getCpuSpeedSteps();
+                mSpeedBins = new SamplingCounter[bins >= steps ? bins : steps];
                 for (int i = 0; i < bins; i++) {
-                    mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
+                    if (in.readInt() != 0) {
+                        mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
+                    }
                 }
             }
 
@@ -2309,7 +2697,7 @@
                     val = mUserTime;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedUserTime;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedUserTime;
                     }
                 }
@@ -2325,7 +2713,7 @@
                     val = mSystemTime;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedSystemTime;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedSystemTime;
                     }
                 }
@@ -2341,7 +2729,7 @@
                     val = mForegroundTime;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedForegroundTime;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedForegroundTime;
                     }
                 }
@@ -2357,7 +2745,7 @@
                     val = mStarts;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedStarts;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedStarts;
                     }
                 }
@@ -2367,14 +2755,22 @@
             /* Called by ActivityManagerService when CPU times are updated. */
             public void addSpeedStepTimes(long[] values) {
                 for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
-                    mSpeedBins[i].addCountAtomic(values[i]);
+                    long amt = values[i];
+                    if (amt != 0) {
+                        SamplingCounter c = mSpeedBins[i];
+                        if (c == null) {
+                            mSpeedBins[i] = c = new SamplingCounter(mUnpluggables);
+                        }
+                        c.addCountAtomic(values[i]);
+                    }
                 }
             }
 
             @Override
             public long getTimeAtCpuSpeedStep(int speedStep, int which) {
                 if (speedStep < mSpeedBins.length) {
-                    return mSpeedBins[speedStep].getCountLocked(which);
+                    SamplingCounter c = mSpeedBins[speedStep];
+                    return c != null ? c.getCountLocked(which) : 0;
                 } else {
                     return 0;
                 }
@@ -2425,6 +2821,10 @@
             public void plug(long batteryUptime, long batteryRealtime) {
             }
             
+            void detach() {
+                mUnpluggables.remove(this);
+            }
+            
             void readFromParcelLocked(Parcel in) {
                 mWakeups = in.readInt();
                 mLoadedWakeups = in.readInt();
@@ -2471,7 +2871,7 @@
                     val = mWakeups;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedWakeups;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedWakeups;
                     }
                 }
@@ -2586,6 +2986,10 @@
                 public void plug(long batteryUptime, long batteryRealtime) {
                 }
                 
+                void detach() {
+                    mUnpluggables.remove(this);
+                }
+                
                 void readFromParcelLocked(Parcel in) {
                     mStartTime = in.readLong();
                     mRunningSince = in.readLong();
@@ -2690,7 +3094,7 @@
                         val = mLaunches;
                         if (which == STATS_CURRENT) {
                             val -= mLoadedLaunches;
-                        } else if (which == STATS_UNPLUGGED) {
+                        } else if (which == STATS_SINCE_UNPLUGGED) {
                             val -= mUnpluggedLaunches;
                         }
                     }
@@ -2707,7 +3111,7 @@
                         val = getStartTimeToNowLocked(now);
                         if (which == STATS_CURRENT) {
                             val -= mLoadedStartTime;
-                        } else if (which == STATS_UNPLUGGED) {
+                        } else if (which == STATS_SINCE_UNPLUGGED) {
                             val -= mUnpluggedStartTime;
                         }
                     }
@@ -2724,7 +3128,7 @@
                         val = mStarts;
                         if (which == STATS_CURRENT) {
                             val -= mLoadedStarts;
-                        } else if (which == STATS_UNPLUGGED) {
+                        } else if (which == STATS_SINCE_UNPLUGGED) {
                             val -= mUnpluggedStarts;
                         }
                     }
@@ -2920,7 +3324,9 @@
         mWifiRunningTimer = new StopwatchTimer(-4, null, mUnpluggables);
         mBluetoothOnTimer = new StopwatchTimer(-5, null, mUnpluggables);
         mAudioOnTimer = new StopwatchTimer(-6, null, mUnpluggables);
+        mVideoOnTimer = new StopwatchTimer(-7, null, mUnpluggables);
         mOnBattery = mOnBatteryInternal = false;
+        initTimes();
         mTrackBatteryPastUptime = 0;
         mTrackBatteryPastRealtime = 0;
         mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
@@ -2928,6 +3334,7 @@
         mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
         mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
         mDischargeStartLevel = 0;
+        mDischargeUnplugLevel = 0;
         mDischargeCurrentLevel = 0;
     }
 
@@ -2947,7 +3354,7 @@
     }
 
     @Override
-    public BatteryHistoryRecord getHistory() {
+    public HistoryItem getHistory() {
         return mHistory;
     }
     
@@ -2960,53 +3367,174 @@
         return mOnBattery;
     }
 
-    public void setOnBattery(boolean onBattery, int level) {
+    void initTimes() {
+        mBatteryRealtime = mTrackBatteryPastUptime = 0;
+        mBatteryUptime = mTrackBatteryPastRealtime = 0;
+        mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
+        mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000;
+        mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
+        mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
+    }
+    
+    public void resetAllStatsLocked() {
+        mStartCount = 0;
+        initTimes();
+        mScreenOnTimer.reset(false);
+        for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+            mScreenBrightnessTimer[i].reset(false);
+        }
+        mInputEventCounter.reset(false);
+        mPhoneOnTimer.reset(false);
+        mAudioOnTimer.reset(false);
+        mVideoOnTimer.reset(false);
+        for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+            mPhoneSignalStrengthsTimer[i].reset(false);
+        }
+        mPhoneSignalScanningTimer.reset(false);
+        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+            mPhoneDataConnectionsTimer[i].reset(false);
+        }
+        mWifiOnTimer.reset(false);
+        mWifiRunningTimer.reset(false);
+        mBluetoothOnTimer.reset(false);
+        
+        for (int i=0; i<mUidStats.size(); i++) {
+            if (mUidStats.valueAt(i).reset()) {
+                mUidStats.remove(mUidStats.keyAt(i));
+                i--;
+            }
+        }
+        
+        if (mKernelWakelockStats.size() > 0) {
+            for (SamplingTimer timer : mKernelWakelockStats.values()) {
+                mUnpluggables.remove(timer);
+            }
+            mKernelWakelockStats.clear();
+        }
+        
+        clearHistoryLocked();
+    }
+    
+    void setOnBattery(boolean onBattery, int oldStatus, int level) {
         synchronized(this) {
-            updateKernelWakelocksLocked();
-            if (mOnBattery != onBattery) {
-                mOnBattery = mOnBatteryInternal = onBattery;
-                
-                long uptime = SystemClock.uptimeMillis() * 1000;
-                long mSecRealtime = SystemClock.elapsedRealtime();
-                long realtime = mSecRealtime * 1000;
-                if (onBattery) {
-                    clearHistory();
-                    mHistoryCur.batteryLevel = (byte)level;
-                    mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
-                    if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
-                            + Integer.toHexString(mHistoryCur.states));
-                    addHistoryRecord(mSecRealtime);
-                    mTrackBatteryUptimeStart = uptime;
-                    mTrackBatteryRealtimeStart = realtime;
-                    mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
-                    mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime);
-                    mDischargeCurrentLevel = mDischargeStartLevel = level;
-                    doUnplug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
-                } else {
-                    mHistoryCur.batteryLevel = (byte)level;
-                    mHistoryCur.states |= BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
-                    if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
-                            + Integer.toHexString(mHistoryCur.states));
-                    addHistoryRecord(mSecRealtime);
-                    mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
-                    mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
-                    mDischargeCurrentLevel = level;
-                    doPlug(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+            boolean doWrite = false;
+            mOnBattery = mOnBatteryInternal = onBattery;
+            
+            long uptime = SystemClock.uptimeMillis() * 1000;
+            long mSecRealtime = SystemClock.elapsedRealtime();
+            long realtime = mSecRealtime * 1000;
+            if (onBattery) {
+                // We will reset our status if we are unplugging after the
+                // battery was last full, or the level is at 100, or
+                // we have gone through a significant charge (from a very low
+                // level to a now very high level).
+                if (oldStatus == BatteryManager.BATTERY_STATUS_FULL
+                        || level >= 100
+                        || (mDischargeCurrentLevel < 20 && level > 90)) {
+                    doWrite = true;
+                    resetAllStatsLocked();
+                    mDischargeStartLevel = level;
                 }
-                if ((mLastWriteTime + (60 * 1000)) < mSecRealtime) {
-                    if (mFile != null) {
-                        writeLocked();
-                    }
+                updateKernelWakelocksLocked();
+                mHistoryCur.batteryLevel = (byte)level;
+                mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(mSecRealtime);
+                mTrackBatteryUptimeStart = uptime;
+                mTrackBatteryRealtimeStart = realtime;
+                mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
+                mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime);
+                mDischargeCurrentLevel = mDischargeUnplugLevel = level;
+                doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
+            } else {
+                updateKernelWakelocksLocked();
+                mHistoryCur.batteryLevel = (byte)level;
+                mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(mSecRealtime);
+                mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
+                mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
+                mDischargeCurrentLevel = level;
+                doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+            }
+            if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
+                if (mFile != null) {
+                    writeLocked();
                 }
             }
         }
     }
     
-    public void recordCurrentLevel(int level) {
-        if (mDischargeCurrentLevel != level) {
+    // This should probably be exposed in the API, though it's not critical
+    private static final int BATTERY_PLUGGED_NONE = 0;
+    
+    public void setBatteryState(int status, int health, int plugType, int level,
+            int temp, int volt) {
+        boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+        int oldStatus = mHistoryCur.batteryStatus;
+        if (!mHaveBatteryLevel) {
+            mHaveBatteryLevel = true;
+            // We start out assuming that the device is plugged in (not
+            // on battery).  If our first report is now that we are indeed
+            // plugged in, then twiddle our state to correctly reflect that
+            // since we won't be going through the full setOnBattery().
+            if (onBattery == mOnBattery) {
+                if (onBattery) {
+                    mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                } else {
+                    mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                }
+            }
+            oldStatus = status;
+        }
+        if (onBattery) {
             mDischargeCurrentLevel = level;
+            mRecordingHistory = true;
+        }
+        if (onBattery != mOnBattery) {
             mHistoryCur.batteryLevel = (byte)level;
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            mHistoryCur.batteryStatus = (byte)status;
+            mHistoryCur.batteryHealth = (byte)health;
+            mHistoryCur.batteryPlugType = (byte)plugType;
+            mHistoryCur.batteryTemperature = (char)temp;
+            mHistoryCur.batteryVoltage = (char)volt;
+            setOnBattery(onBattery, oldStatus, level);
+        } else {
+            boolean changed = false;
+            if (mHistoryCur.batteryLevel != level) {
+                mHistoryCur.batteryLevel = (byte)level;
+                changed = true;
+            }
+            if (mHistoryCur.batteryStatus != status) {
+                mHistoryCur.batteryStatus = (byte)status;
+                changed = true;
+            }
+            if (mHistoryCur.batteryHealth != health) {
+                mHistoryCur.batteryHealth = (byte)health;
+                changed = true;
+            }
+            if (mHistoryCur.batteryPlugType != plugType) {
+                mHistoryCur.batteryPlugType = (byte)plugType;
+                changed = true;
+            }
+            if (mHistoryCur.batteryTemperature != temp) {
+                mHistoryCur.batteryTemperature = (char)temp;
+                changed = true;
+            }
+            if (mHistoryCur.batteryVoltage != volt) {
+                mHistoryCur.batteryVoltage = (char)volt;
+                changed = true;
+            }
+            if (changed) {
+                addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            }
+        }
+        if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
+            // We don't record history while we are plugged in and fully charged.
+            // The next time we are unplugged, history will be cleared.
+            mRecordingHistory = false;
         }
     }
     
@@ -3056,10 +3584,10 @@
     @Override
     public long computeUptime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL: return mUptime + (curTime-mUptimeStart);
+            case STATS_SINCE_CHARGED: return mUptime + (curTime-mUptimeStart);
             case STATS_LAST: return mLastUptime;
             case STATS_CURRENT: return (curTime-mUptimeStart);
-            case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart);
+            case STATS_SINCE_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart);
         }
         return 0;
     }
@@ -3067,10 +3595,10 @@
     @Override
     public long computeRealtime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart);
+            case STATS_SINCE_CHARGED: return mRealtime + (curTime-mRealtimeStart);
             case STATS_LAST: return mLastRealtime;
             case STATS_CURRENT: return (curTime-mRealtimeStart);
-            case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart);
+            case STATS_SINCE_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart);
         }
         return 0;
     }
@@ -3078,13 +3606,13 @@
     @Override
     public long computeBatteryUptime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL:
+            case STATS_SINCE_CHARGED:
                 return mBatteryUptime + getBatteryUptime(curTime);
             case STATS_LAST:
                 return mBatteryLastUptime;
             case STATS_CURRENT:
                 return getBatteryUptime(curTime);
-            case STATS_UNPLUGGED:
+            case STATS_SINCE_UNPLUGGED:
                 return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime;
         }
         return 0;
@@ -3093,13 +3621,13 @@
     @Override
     public long computeBatteryRealtime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL:
+            case STATS_SINCE_CHARGED:
                 return mBatteryRealtime + getBatteryRealtimeLocked(curTime);
             case STATS_LAST:
                 return mBatteryLastRealtime;
             case STATS_CURRENT:
                 return getBatteryRealtimeLocked(curTime);
-            case STATS_UNPLUGGED:
+            case STATS_SINCE_UNPLUGGED:
                 return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime;
         }
         return 0;
@@ -3139,14 +3667,14 @@
         if (which == STATS_LAST) {
             return dataBytes[STATS_LAST];
         } else {
-            if (which == STATS_UNPLUGGED) {
-                if (dataBytes[STATS_UNPLUGGED] < 0) {
+            if (which == STATS_SINCE_UNPLUGGED) {
+                if (dataBytes[STATS_SINCE_UNPLUGGED] < 0) {
                     return dataBytes[STATS_LAST];
                 } else {
-                    return current - dataBytes[STATS_UNPLUGGED];
+                    return current - dataBytes[STATS_SINCE_UNPLUGGED];
                 }
-            } else if (which == STATS_TOTAL) {
-                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL];
+            } else if (which == STATS_SINCE_CHARGED) {
+                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_SINCE_CHARGED];
             }
             return current - dataBytes[STATS_CURRENT];
         }
@@ -3180,7 +3708,7 @@
     }
     
     public int getDischargeStartLevelLocked() {
-            return mDischargeStartLevel;
+            return mDischargeUnplugLevel;
     }
     
     @Override
@@ -3263,17 +3791,21 @@
         return u.getServiceStatsLocked(pkg, name);
     }
 
-    private static JournaledFile makeJournaledFile() {
-        final String base = "/data/system/device_policies.xml";
-        return new JournaledFile(new File(base), new File(base + ".tmp"));
+    public void shutdownLocked() {
+        writeLocked();
+        mShuttingDown = true;
     }
-
+    
     public void writeLocked() {
         if (mFile == null) {
             Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
             return;
         }
 
+        if (mShuttingDown) {
+            return;
+        }
+        
         try {
             FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
             Parcel out = Parcel.obtain();
@@ -3341,6 +3873,8 @@
         } catch(java.io.IOException e) {
             Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
+        
+        addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
     }
 
     public int describeContents() {
@@ -3349,15 +3883,19 @@
 
     void readHistory(Parcel in) {
         mHistory = mHistoryEnd = mHistoryCache = null;
+        mHistoryBaseTime = 0;
         long time;
         while ((time=in.readLong()) >= 0) {
-            BatteryHistoryRecord rec = new BatteryHistoryRecord(time, in);
-            addHistoryRecord(rec);
+            HistoryItem rec = new HistoryItem(time, in);
+            addHistoryRecordLocked(rec);
+            if (rec.time > mHistoryBaseTime) {
+                mHistoryBaseTime = rec.time;
+            }
         }
     }
     
     void writeHistory(Parcel out) {
-        BatteryHistoryRecord rec = mHistory;
+        HistoryItem rec = mHistory;
         while (rec != null) {
             if (rec.time >= 0) rec.writeToParcel(out, 0);
             rec = rec.next;
@@ -3384,7 +3922,7 @@
         mLastUptime = in.readLong();
         mRealtime = in.readLong();
         mLastRealtime = in.readLong();
-        mDischargeStartLevel = in.readInt();
+        mDischargeUnplugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
         
         mStartCount++;
@@ -3436,17 +3974,29 @@
             mUidStats.put(uid, u);
 
             u.mWifiTurnedOn = false;
-            u.mWifiTurnedOnTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mWifiTurnedOnTimer.readSummaryFromParcelLocked(in);
+            }
             u.mFullWifiLockOut = false;
-            u.mFullWifiLockTimer.readSummaryFromParcelLocked(in);
-            u.mAudioTurnedOn = false;
-            u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in);
-            u.mVideoTurnedOn = false;
-            u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mFullWifiLockTimer.readSummaryFromParcelLocked(in);
+            }
             u.mScanWifiLockOut = false;
-            u.mScanWifiLockTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mScanWifiLockTimer.readSummaryFromParcelLocked(in);
+            }
             u.mWifiMulticastEnabled = false;
-            u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
+            }
+            u.mAudioTurnedOn = false;
+            if (in.readInt() != 0) {
+                u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in);
+            }
+            u.mVideoTurnedOn = false;
+            if (in.readInt() != 0) {
+                u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in);
+            }
 
             if (in.readInt() != 0) {
                 if (u.mUserActivityCounters == null) {
@@ -3549,15 +4099,15 @@
         writeHistory(out);
         
         out.writeInt(mStartCount);
-        out.writeLong(computeBatteryUptime(NOW_SYS, STATS_TOTAL));
+        out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_CURRENT));
-        out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_TOTAL));
+        out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_CURRENT));
-        out.writeLong(computeUptime(NOW_SYS, STATS_TOTAL));
+        out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT));
-        out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL));
+        out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT));
-        out.writeInt(mDischargeStartLevel);
+        out.writeInt(mDischargeUnplugLevel);
         out.writeInt(mDischargeCurrentLevel);
         
         
@@ -3597,12 +4147,42 @@
             out.writeInt(mUidStats.keyAt(iu));
             Uid u = mUidStats.valueAt(iu);
             
-            u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            if (u.mWifiTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mFullWifiLockTimer != null) {
+                out.writeInt(1);
+                u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mScanWifiLockTimer != null) {
+                out.writeInt(1);
+                u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mWifiMulticastTimer != null) {
+                out.writeInt(1);
+                u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mAudioTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mVideoTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
 
             if (u.mUserActivityCounters == null) {
                 out.writeInt(0);
@@ -3701,8 +4281,8 @@
                 }
             }
             
-            out.writeLong(u.getTcpBytesReceived(STATS_TOTAL));
-            out.writeLong(u.getTcpBytesSent(STATS_TOTAL));
+            out.writeLong(u.getTcpBytesReceived(STATS_SINCE_CHARGED));
+            out.writeLong(u.getTcpBytesSent(STATS_SINCE_CHARGED));
         }
     }
 
@@ -3758,18 +4338,18 @@
         mTrackBatteryRealtimeStart = in.readLong();
         mUnpluggedBatteryUptime = in.readLong();
         mUnpluggedBatteryRealtime = in.readLong();
-        mDischargeStartLevel = in.readInt();
+        mDischargeUnplugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
         mLastWriteTime = in.readLong();
 
         mMobileDataRx[STATS_LAST] = in.readLong();
-        mMobileDataRx[STATS_UNPLUGGED] = -1;
+        mMobileDataRx[STATS_SINCE_UNPLUGGED] = -1;
         mMobileDataTx[STATS_LAST] = in.readLong();
-        mMobileDataTx[STATS_UNPLUGGED] = -1;
+        mMobileDataTx[STATS_SINCE_UNPLUGGED] = -1;
         mTotalDataRx[STATS_LAST] = in.readLong();
-        mTotalDataRx[STATS_UNPLUGGED] = -1;
+        mTotalDataRx[STATS_SINCE_UNPLUGGED] = -1;
         mTotalDataTx[STATS_LAST] = in.readLong();
-        mTotalDataTx[STATS_UNPLUGGED] = -1;
+        mTotalDataTx[STATS_SINCE_UNPLUGGED] = -1;
 
         mRadioDataUptime = in.readLong();
         mRadioDataStart = -1;
@@ -3853,14 +4433,14 @@
         out.writeLong(mTrackBatteryRealtimeStart);
         out.writeLong(mUnpluggedBatteryUptime);
         out.writeLong(mUnpluggedBatteryRealtime);
-        out.writeInt(mDischargeStartLevel);
+        out.writeInt(mDischargeUnplugLevel);
         out.writeInt(mDischargeCurrentLevel);
         out.writeLong(mLastWriteTime);
 
-        out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED));
-        out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED));
-        out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED));
-        out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED));
+        out.writeLong(getMobileTcpBytesReceived(STATS_SINCE_UNPLUGGED));
+        out.writeLong(getMobileTcpBytesSent(STATS_SINCE_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesReceived(STATS_SINCE_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesSent(STATS_SINCE_UNPLUGGED));
 
         // Write radio uptime for data
         out.writeLong(getRadioDataUptime());
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 47bb073..4a4393a 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -121,9 +121,9 @@
     String8 name(nameChars);
     env->ReleaseStringUTFChars(nameObj, nameChars);
 
-    InputChannel* serverChannel;
-    InputChannel* clientChannel;
-    status_t result = InputChannel::openInputChannelPair(name, & serverChannel, & clientChannel);
+    sp<InputChannel> serverChannel;
+    sp<InputChannel> clientChannel;
+    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 
     if (result) {
         LOGE("Could not open input channel pair.  status=%d", result);
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 9cbde25..63e00d7 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -21,6 +21,9 @@
 // Log debug messages about the dispatch cycle.
 #define DEBUG_DISPATCH_CYCLE 1
 
+// Log debug messages about registrations.
+#define DEBUG_REGISTRATION 1
+
 
 #include "JNIHelp.h"
 
@@ -51,7 +54,7 @@
 class NativeInputQueue {
 public:
     NativeInputQueue();
-    virtual ~NativeInputQueue();
+    ~NativeInputQueue();
 
     status_t registerInputChannel(JNIEnv* env, jobject inputChannelObj,
             jobject inputHandlerObj, jobject messageQueueObj);
@@ -75,7 +78,7 @@
 
         Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
 
-        inline const char* getInputChannelName() { return inputChannel->getName().string(); }
+        inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
 
         Status status;
 
@@ -125,6 +128,10 @@
         return BAD_VALUE;
     }
 
+#if DEBUG_REGISTRATION
+    LOGD("channel '%s' - Registered", inputChannel->getName().string());
+#endif
+
     sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj);
 
     int receiveFd;
@@ -154,7 +161,7 @@
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
             handleInputChannelDisposed, this);
 
-    pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, NULL);
+    pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
     return OK;
 }
 
@@ -166,6 +173,10 @@
         return BAD_VALUE;
     }
 
+#if DEBUG_REGISTRATION
+    LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
+#endif
+
     int32_t receiveFd;
     sp<Connection> connection;
     { // acquire lock
diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp
index e2a1f23..a0a7054 100644
--- a/core/jni/android_view_InputTarget.cpp
+++ b/core/jni/android_view_InputTarget.cpp
@@ -19,7 +19,7 @@
 #include "JNIHelp.h"
 
 #include <utils/Log.h>
-#include <ui/InputDispatchPolicy.h>
+#include <ui/InputDispatcher.h>
 #include <ui/InputTransport.h>
 #include "android_view_InputTarget.h"
 #include "android_view_InputChannel.h"
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 629c8fe..78137e2 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -36,11 +36,10 @@
     jmethodID obtain;
     jmethodID recycle;
 
-    jfieldID mDownTime;
-    jfieldID mEventTimeNano;
+    jfieldID mDownTimeNano;
     jfieldID mAction;
-    jfieldID mRawX;
-    jfieldID mRawY;
+    jfieldID mXOffset;
+    jfieldID mYOffset;
     jfieldID mXPrecision;
     jfieldID mYPrecision;
     jfieldID mDeviceId;
@@ -50,7 +49,9 @@
     jfieldID mNumSamples;
     jfieldID mPointerIdentifiers;
     jfieldID mDataSamples;
-    jfieldID mTimeSamples;
+    jfieldID mEventTimeNanoSamples;
+    jfieldID mLastDataSampleIndex;
+    jfieldID mLastEventTimeNanoSampleIndex;
 } gMotionEventClassInfo;
 
 // ----------------------------------------------------------------------------
@@ -69,22 +70,14 @@
         return NULL;
     }
 
-    // MotionEvent.mEventTimeNano is the time of the oldest sample because
-    // MotionEvent.addBatch does not update it as successive samples are added.
-    jlong eventTimeNano = numHistoricalSamples != 0
-            ? event->getHistoricalEventTime(0)
-            : event->getEventTime();
-
-    env->SetLongField(eventObj, gMotionEventClassInfo.mDownTime,
-            nanoseconds_to_milliseconds(event->getDownTime()));
-    env->SetLongField(eventObj, gMotionEventClassInfo.mEventTimeNano,
-            eventTimeNano);
+    env->SetLongField(eventObj, gMotionEventClassInfo.mDownTimeNano,
+            event->getDownTime());
     env->SetIntField(eventObj, gMotionEventClassInfo.mAction,
             event->getAction());
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mRawX,
-            event->getRawX());
-    env->SetFloatField(eventObj, gMotionEventClassInfo.mRawY,
-            event->getRawY());
+    env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset,
+            event->getXOffset());
+    env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset,
+            event->getYOffset());
     env->SetFloatField(eventObj, gMotionEventClassInfo.mXPrecision,
             event->getXPrecision());
     env->SetFloatField(eventObj, gMotionEventClassInfo.mYPrecision,
@@ -99,65 +92,62 @@
             numPointers);
     env->SetIntField(eventObj, gMotionEventClassInfo.mNumSamples,
             numSamples);
+    env->SetIntField(eventObj, gMotionEventClassInfo.mLastDataSampleIndex,
+            (numSamples - 1) * numPointers * NUM_SAMPLE_DATA);
+    env->SetIntField(eventObj, gMotionEventClassInfo.mLastEventTimeNanoSampleIndex,
+            numSamples - 1);
 
     jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
             gMotionEventClassInfo.mPointerIdentifiers));
     jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
             gMotionEventClassInfo.mDataSamples));
-    jlongArray timeSampleArray = jlongArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mTimeSamples));
+    jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj,
+            gMotionEventClassInfo.mEventTimeNanoSamples));
 
     jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
     jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
-    jlong* timeSamples = (jlong*)env->GetPrimitiveArrayCritical(timeSampleArray, NULL);
+    jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical(
+            eventTimeNanoSampleArray, NULL);
 
+    const int32_t* srcPointerIdentifiers = event->getPointerIds();
+    jint* destPointerIdentifiers = pointerIdentifiers;
     for (jint i = 0; i < numPointers; i++) {
-        pointerIdentifiers[i] = event->getPointerId(i);
+        *(destPointerIdentifiers++) = *(srcPointerIdentifiers++);
     }
 
-    // Most recent data is in first slot of the DVM array, followed by the oldest,
-    // and then all others are in order.
-
-    jfloat* currentDataSample = dataSamples;
-    jlong* currentTimeSample = timeSamples;
-
-    *(currentTimeSample++) = nanoseconds_to_milliseconds(event->getEventTime());
-    for (jint j = 0; j < numPointers; j++) {
-        *(currentDataSample++) = event->getX(j);
-        *(currentDataSample++) = event->getY(j);
-        *(currentDataSample++) = event->getPressure(j);
-        *(currentDataSample++) = event->getSize(j);
+    const nsecs_t* srcSampleEventTimes = event->getSampleEventTimes();
+    jlong* destEventTimeNanoSamples = eventTimeNanoSamples;
+    for (jint i = 0; i < numSamples; i++) {
+        *(destEventTimeNanoSamples++) = *(srcSampleEventTimes++);
     }
 
-    for (jint i = 0; i < numHistoricalSamples; i++) {
-        *(currentTimeSample++) = nanoseconds_to_milliseconds(event->getHistoricalEventTime(i));
-        for (jint j = 0; j < numPointers; j++) {
-            *(currentDataSample++) = event->getHistoricalX(j, i);
-            *(currentDataSample++) = event->getHistoricalY(j, i);
-            *(currentDataSample++) = event->getHistoricalPressure(j, i);
-            *(currentDataSample++) = event->getHistoricalSize(j, i);
-        }
+    const PointerCoords* srcSamplePointerCoords = event->getSamplePointerCoords();
+    jfloat* destDataSamples = dataSamples;
+    jint numItems = numSamples * numPointers;
+    for (jint i = 0; i < numItems; i++) {
+        *(destDataSamples++) = srcSamplePointerCoords->x;
+        *(destDataSamples++) = srcSamplePointerCoords->y;
+        *(destDataSamples++) = srcSamplePointerCoords->pressure;
+        *(destDataSamples++) = srcSamplePointerCoords->size;
+        srcSamplePointerCoords += 1;
     }
 
     env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, 0);
     env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
-    env->ReleasePrimitiveArrayCritical(timeSampleArray, timeSamples, 0);
+    env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, 0);
 
     env->DeleteLocalRef(pointerIdentifierArray);
     env->DeleteLocalRef(dataSampleArray);
-    env->DeleteLocalRef(timeSampleArray);
+    env->DeleteLocalRef(eventTimeNanoSampleArray);
     return eventObj;
 }
 
 void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature,
         MotionEvent* event) {
-    // MotionEvent.mEventTimeNano is the time of the oldest sample because
-    // MotionEvent.addBatch does not update it as successive samples are added.
-    jlong downTime = env->GetLongField(eventObj, gMotionEventClassInfo.mDownTime);
-    jlong eventTimeNano = env->GetLongField(eventObj, gMotionEventClassInfo.mEventTimeNano);
+    jlong downTimeNano = env->GetLongField(eventObj, gMotionEventClassInfo.mDownTimeNano);
     jint action = env->GetIntField(eventObj, gMotionEventClassInfo.mAction);
-    jfloat rawX = env->GetFloatField(eventObj, gMotionEventClassInfo.mRawX);
-    jfloat rawY = env->GetFloatField(eventObj, gMotionEventClassInfo.mRawY);
+    jfloat xOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset);
+    jfloat yOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset);
     jfloat xPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mXPrecision);
     jfloat yPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mYPrecision);
     jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId);
@@ -169,72 +159,51 @@
             gMotionEventClassInfo.mPointerIdentifiers));
     jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
             gMotionEventClassInfo.mDataSamples));
-    jlongArray timeSampleArray = jlongArray(env->GetObjectField(eventObj,
-            gMotionEventClassInfo.mTimeSamples));
+    jlongArray eventTimeNanoSampleArray = jlongArray(env->GetObjectField(eventObj,
+            gMotionEventClassInfo.mEventTimeNanoSamples));
 
     LOG_FATAL_IF(numPointers == 0, "numPointers was zero");
     LOG_FATAL_IF(numSamples == 0, "numSamples was zero");
 
     jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
     jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
-    jlong* timeSamples = (jlong*)env->GetPrimitiveArrayCritical(timeSampleArray, NULL);
+    jlong* eventTimeNanoSamples = (jlong*)env->GetPrimitiveArrayCritical(
+            eventTimeNanoSampleArray, NULL);
 
-    // Most recent data is in first slot of the DVM array, followed by the oldest,
-    // and then all others are in order.  eventTimeNano is the time of the oldest sample
-    // since MotionEvent.addBatch does not update it.
+    jfloat* srcDataSamples = dataSamples;
+    jlong* srcEventTimeNanoSamples = eventTimeNanoSamples;
 
-    jint numHistoricalSamples = numSamples - 1;
-    jint dataSampleStride = numPointers * NUM_SAMPLE_DATA;
-
-    const jfloat* currentDataSample;
-    const jlong* currentTimeSample;
-    if (numHistoricalSamples == 0) {
-        currentDataSample = dataSamples;
-        currentTimeSample = timeSamples;
-    } else {
-        currentDataSample = dataSamples + dataSampleStride;
-        currentTimeSample = timeSamples + 1;
-    }
-
-    PointerCoords pointerCoords[MAX_POINTERS];
+    jlong sampleEventTime = *(srcEventTimeNanoSamples++);
+    PointerCoords samplePointerCoords[MAX_POINTERS];
     for (jint j = 0; j < numPointers; j++) {
-        pointerCoords[j].x = *(currentDataSample++);
-        pointerCoords[j].y = *(currentDataSample++);
-        pointerCoords[j].pressure = *(currentDataSample++);
-        pointerCoords[j].size = *(currentDataSample++);
+        samplePointerCoords[j].x = *(srcDataSamples++);
+        samplePointerCoords[j].y = *(srcDataSamples++);
+        samplePointerCoords[j].pressure = *(srcDataSamples++);
+        samplePointerCoords[j].size = *(srcDataSamples++);
     }
 
     event->initialize(deviceId, nature, action, edgeFlags, metaState,
-            rawX, rawY, xPrecision, yPrecision,
-            milliseconds_to_nanoseconds(downTime), eventTimeNano,
-            numPointers, pointerIdentifiers, pointerCoords);
+            xOffset, yOffset, xPrecision, yPrecision, downTimeNano, sampleEventTime,
+            numPointers, pointerIdentifiers, samplePointerCoords);
 
-    while (numHistoricalSamples > 0) {
-        numHistoricalSamples -= 1;
-        if (numHistoricalSamples == 0) {
-            currentDataSample = dataSamples;
-            currentTimeSample = timeSamples;
-        }
-
-        nsecs_t sampleEventTime = milliseconds_to_nanoseconds(*(currentTimeSample++));
-
+    for (jint i = 1; i < numSamples; i++) {
+        sampleEventTime = *(srcEventTimeNanoSamples++);
         for (jint j = 0; j < numPointers; j++) {
-            pointerCoords[j].x = *(currentDataSample++);
-            pointerCoords[j].y = *(currentDataSample++);
-            pointerCoords[j].pressure = *(currentDataSample++);
-            pointerCoords[j].size = *(currentDataSample++);
+            samplePointerCoords[j].x = *(srcDataSamples++);
+            samplePointerCoords[j].y = *(srcDataSamples++);
+            samplePointerCoords[j].pressure = *(srcDataSamples++);
+            samplePointerCoords[j].size = *(srcDataSamples++);
         }
-
-        event->addSample(sampleEventTime, pointerCoords);
+        event->addSample(sampleEventTime, samplePointerCoords);
     }
 
     env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT);
     env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT);
-    env->ReleasePrimitiveArrayCritical(timeSampleArray, timeSamples, JNI_ABORT);
+    env->ReleasePrimitiveArrayCritical(eventTimeNanoSampleArray, eventTimeNanoSamples, JNI_ABORT);
 
     env->DeleteLocalRef(pointerIdentifierArray);
     env->DeleteLocalRef(dataSampleArray);
-    env->DeleteLocalRef(timeSampleArray);
+    env->DeleteLocalRef(eventTimeNanoSampleArray);
 }
 
 void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
@@ -273,16 +242,14 @@
     GET_METHOD_ID(gMotionEventClassInfo.recycle, gMotionEventClassInfo.clazz,
             "recycle", "()V");
 
-    GET_FIELD_ID(gMotionEventClassInfo.mDownTime, gMotionEventClassInfo.clazz,
-            "mDownTime", "J");
-    GET_FIELD_ID(gMotionEventClassInfo.mEventTimeNano, gMotionEventClassInfo.clazz,
-            "mEventTimeNano", "J");
+    GET_FIELD_ID(gMotionEventClassInfo.mDownTimeNano, gMotionEventClassInfo.clazz,
+            "mDownTimeNano", "J");
     GET_FIELD_ID(gMotionEventClassInfo.mAction, gMotionEventClassInfo.clazz,
             "mAction", "I");
-    GET_FIELD_ID(gMotionEventClassInfo.mRawX, gMotionEventClassInfo.clazz,
-            "mRawX", "F");
-    GET_FIELD_ID(gMotionEventClassInfo.mRawY, gMotionEventClassInfo.clazz,
-            "mRawY", "F");
+    GET_FIELD_ID(gMotionEventClassInfo.mXOffset, gMotionEventClassInfo.clazz,
+            "mXOffset", "F");
+    GET_FIELD_ID(gMotionEventClassInfo.mYOffset, gMotionEventClassInfo.clazz,
+            "mYOffset", "F");
     GET_FIELD_ID(gMotionEventClassInfo.mXPrecision, gMotionEventClassInfo.clazz,
             "mXPrecision", "F");
     GET_FIELD_ID(gMotionEventClassInfo.mYPrecision, gMotionEventClassInfo.clazz,
@@ -301,8 +268,12 @@
             "mPointerIdentifiers", "[I");
     GET_FIELD_ID(gMotionEventClassInfo.mDataSamples, gMotionEventClassInfo.clazz,
             "mDataSamples", "[F");
-    GET_FIELD_ID(gMotionEventClassInfo.mTimeSamples, gMotionEventClassInfo.clazz,
-            "mTimeSamples", "[J");
+    GET_FIELD_ID(gMotionEventClassInfo.mEventTimeNanoSamples, gMotionEventClassInfo.clazz,
+            "mEventTimeNanoSamples", "[J");
+    GET_FIELD_ID(gMotionEventClassInfo.mLastDataSampleIndex, gMotionEventClassInfo.clazz,
+            "mLastDataSampleIndex", "I");
+    GET_FIELD_ID(gMotionEventClassInfo.mLastEventTimeNanoSampleIndex, gMotionEventClassInfo.clazz,
+            "mLastEventTimeNanoSampleIndex", "I");
 
     return 0;
 }
diff --git a/core/res/res/drawable-hdpi/presence_audio_away.png b/core/res/res/drawable-hdpi/presence_audio_away.png
new file mode 100644
index 0000000..f18b6c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_audio_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_audio_busy.png b/core/res/res/drawable-hdpi/presence_audio_busy.png
new file mode 100644
index 0000000..ae7b983
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_audio_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_audio_online.png b/core/res/res/drawable-hdpi/presence_audio_online.png
new file mode 100644
index 0000000..15e4513
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_audio_online.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_away.png b/core/res/res/drawable-hdpi/presence_video_away.png
new file mode 100644
index 0000000..f18b6c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_video_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_busy.png b/core/res/res/drawable-hdpi/presence_video_busy.png
new file mode 100644
index 0000000..ae7b983
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_video_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_online.png b/core/res/res/drawable-hdpi/presence_video_online.png
new file mode 100644
index 0000000..15e4513
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_video_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_away.png b/core/res/res/drawable-mdpi/presence_audio_away.png
new file mode 100644
index 0000000..f67e64e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/presence_audio_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_busy.png b/core/res/res/drawable-mdpi/presence_audio_busy.png
new file mode 100644
index 0000000..0ad991b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/presence_audio_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_online.png b/core/res/res/drawable-mdpi/presence_audio_online.png
new file mode 100644
index 0000000..6cc3d1a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/presence_audio_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_away.png b/core/res/res/drawable-mdpi/presence_video_away.png
new file mode 100644
index 0000000..f67e64e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/presence_video_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_busy.png b/core/res/res/drawable-mdpi/presence_video_busy.png
new file mode 100644
index 0000000..0ad991b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/presence_video_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_online.png b/core/res/res/drawable-mdpi/presence_video_online.png
new file mode 100644
index 0000000..6cc3d1a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/presence_video_online.png
Binary files differ
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1932771..27cb763 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1250,7 +1250,16 @@
   
   <public-padding type="id" name="kraken_resource_pad" end="0x01020040" />
   <public-padding type="anim" name="kraken_resource_pad" end="0x010a0020" />
+
+  <!-- presence drawables for videochat or audiochat capable contacts -->
+  <public type="drawable" name="presence_video_away" id="0x010800ac" />
+  <public type="drawable" name="presence_video_busy" id="0x010800ad" />
+  <public type="drawable" name="presence_video_online" id="0x010800ae" />
+  <public type="drawable" name="presence_audio_away" id="0x010800af" />
+  <public type="drawable" name="presence_audio_busy" id="0x010800b0" />
+  <public type="drawable" name="presence_audio_online" id="0x010800b1" />  
   <public-padding type="drawable" name="kraken_resource_pad" end="0x01080100" />
+  
   <public-padding type="style" name="kraken_resource_pad" end="0x01030090" />
   <public-padding type="string" name="kraken_resource_pad" end="0x01040020" />
   <public-padding type="integer" name="kraken_resource_pad" end="0x010e0010" />
diff --git a/tests/framework-tests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
similarity index 98%
rename from tests/framework-tests/src/android/text/PackedIntVectorTest.java
rename to core/tests/coretests/src/android/text/PackedIntVectorTest.java
index 78cdee9..1dc683e 100644
--- a/tests/framework-tests/src/android/text/PackedIntVectorTest.java
+++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-import android.text.PackedIntVector;
 import junit.framework.TestCase;
 
 /**
diff --git a/tests/framework-tests/src/com/android/internal/http/multipart/MultipartTest.java b/core/tests/coretests/src/com/android/internal/http/multipart/MultipartTest.java
similarity index 100%
rename from tests/framework-tests/src/com/android/internal/http/multipart/MultipartTest.java
rename to core/tests/coretests/src/com/android/internal/http/multipart/MultipartTest.java
diff --git a/tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java b/core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
similarity index 100%
rename from tests/framework-tests/src/com/android/internal/os/LoggingPrintStreamTest.java
rename to core/tests/coretests/src/com/android/internal/os/LoggingPrintStreamTest.java
diff --git a/docs/html/guide/topics/resources/index.jd b/docs/html/guide/topics/resources/index.jd
index 2aa697e..84eac73 100644
--- a/docs/html/guide/topics/resources/index.jd
+++ b/docs/html/guide/topics/resources/index.jd
@@ -24,20 +24,21 @@
 resources also allows you to provide alternative resources that support specific device
 configurations such as different languages or screen sizes, which becomes increasingly
 important as more Android-powered devices become available with different configurations. In order
-to provide this functionality, you must organize resources in your project's {@code res/}
-directory, using various sub-directories that group resources by type and configuration.</p>
+to provide compatibility with different configurations, you must organize resources in your
+project's {@code res/} directory, using various sub-directories that group resources by type and
+configuration.</p>
 
 <div class="figure" style="width:421px">
 <img src="{@docRoot}images/resources/resource_devices_diagram1.png" height="137" alt="" />
 <p class="img-caption">
-<strong>Figure 1.</strong> Two device configurations, both using default
+<strong>Figure 1.</strong> Two different devices, both using default
 resources.</p>
 </div>
 
 <div class="figure" style="width:421px">
 <img src="{@docRoot}images/resources/resource_devices_diagram2.png" height="137" alt="" />
 <p class="img-caption">
-<strong>Figure 2.</strong> Two device configurations, one using alternative
+<strong>Figure 2.</strong> Two different devices, one using alternative
 resources.</p>
 </div>
 
@@ -55,10 +56,10 @@
 <p>For example, while your default UI
 layout is saved in the {@code res/layout/} directory, you might specify a different UI layout to
 be used when the screen is in landscape orientation, by saving it in the {@code res/layout-land/}
-directory. The Android system will automatically apply the appropriate resources by matching the
+directory. Android automatically applies the appropriate resources by matching the
 device's current configuration to your resource directory names.</p>
 
-<p>Figure 1 demonstrates how a collection of default resources from an application will be applied
+<p>Figure 1 demonstrates how a collection of default resources from an application are applied
 to two different devices when there are no alternative resources available. Figure 2 shows
 the same application with a set of alternative resources that qualify for one of the device
 configurations, thus, the two devices uses different resources.</p>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 0f3d389..cac85e8 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -9,6 +9,8 @@
   <ul>
     <li>Different types of resources belong in different subdirectories of {@code res/}</li>
     <li>Alternative resources provide configuration-specific resource files</li>
+    <li>Always include default resources so your app does not depend on specific
+device configurations</li>
   </ul>
   <h2>In this document</h2>
   <ol>
@@ -19,7 +21,14 @@
         <li><a href="#AliasResources">Creating alias resources</a></li>
       </ol>
     </li>
+    <li><a href="#Compatibility">Providing the Best Device Compatibility with Resources</a>
+      <ol>
+        <li><a href="#ScreenCompatibility">Providing screen resource compatibility for Android
+1.5</a></li>
+      </ol>
+    </li>
     <li><a href="#BestMatch">How Android Finds the Best-matching Resource</a></li>
+    <li><a href="#KnownIssues">Known Issues</a></li>
   </ol>
 
   <h2>See also</h2>
@@ -33,15 +42,17 @@
 </div>
 
 <p>You should always externalize application resources such as images and strings from your
-code, so that you can maintain them independently. You can also provide alternative resources for
-specific device configurations, by grouping them in specially-named resource directories. Android
-will then automatically apply the appropriate resource based on the current configuration. For
-instance, you might want to provide a different UI layout depending on the screen size.</p>
+code, so that you can maintain them independently. You should also provide alternative resources for
+specific device configurations, by grouping them in specially-named resource directories. At
+runtime, Android uses uses the appropriate resource based on the current configuration. For
+example, you might want to provide a different UI layout depending on the screen size or different
+strings depending on the language setting.</p>
 
-<p>Once you save your resources external to your application code, you can access them
+<p>Once you externalize your application resources, you can access them
 using resource IDs that are generated in your project's {@code R} class. How to use
 resources in your application is discussed in <a href="accessing-resources.html">Accessing
-Resources</a>.</p>
+Resources</a>. This document shows you how to group your resources in your Android project and
+provide alternative resources for specific device configurations.</p>
 
 
 <h2 id="ResourceTypes">Grouping Resource Types</h2>
@@ -63,9 +74,9 @@
             strings.xml  </span>
 </pre>
 
-<p>The {@code res/} directory contains all the resources (in subdirectories): an image resource, two
-layout resources, and a string resource file. The resource directory names are important and are
-described in table 1.</p>
+<p>As you can see in this example, the {@code res/} directory contains all the resources (in
+subdirectories): an image resource, two layout resources, and a string resource file. The resource
+directory names are important and are described in table 1.</p>
 
 <p class="table-caption" id="table1"><strong>Table 1.</strong> Resource directories
 supported inside project {@code res/} directory.</p>
@@ -96,9 +107,9 @@
         <li>Bitmap files</li>
         <li>Nine-Patches (re-sizable bitmaps)</li>
         <li>State lists</li>
-        <li>Color drawables</li>
         <li>Shapes</li>
         <li>Animation drawables</li>
+        <li>Other drawables</li>
       </ul>
       <p>See <a href="drawable-resource.html">Drawable Resources</a>.</p>
     </td>
@@ -168,16 +179,21 @@
   </tr>
 </table>
 
-<p class="note"><strong>Note:</strong> You should never save resource files directly inside the
-{@code res/} directory.</p>
+<p class="caution"><strong>Caution:</strong> Never save resource files directly inside the
+{@code res/} directory&mdash;it will cause a compiler error.</p>
 
 <p>For more information about certain types of resources, see the <a
 href="available-resources.html">Resource Types</a> documentation.</p>
 
-<p>How to access resources in the {@code res/} subdirectories is discussed in <a
-href="accessing-resources.html">Accessing Resources</a>.
-</p>
-
+<p>The resources that you save in the subdirectories defined in table 1 are your "default"
+resources. That is, these resources define the default design and content for your application.
+However, different types of Android-powered devices might call for different types of resources.
+For example, if a device has a larger than normal screen, then you should provide
+different layout resources that take advantage of the extra screen space. Or, if a device has a
+different language setting, then you should provide different string resources that translate the
+text in your user interface. To provide these different resources for different device
+configurations, you need to provide alternative resources, in addition to your default
+resources.</p>
 
 
 <h2 id="AlternativeResources">Providing Alternative Resources</h2>
@@ -186,14 +202,14 @@
 <div class="figure" style="width:421px">
 <img src="{@docRoot}images/resources/resource_devices_diagram2.png" height="137" alt="" />
 <p class="img-caption">
-<strong>Figure 1.</strong> Two device configurations, one using alternative resources.</p>
+<strong>Figure 1.</strong> Two different devices, one using alternative resources.</p>
 </div>
 
 <p>Almost every application should provide alternative resources to support specific device
 configurations. For instance, you should include alternative drawable resources for different
 screen densities and alternative string resources for different languages. At runtime, Android
-automatically detects the current device configuration and loads the appropriate
-resources.</p>
+detects the current device configuration and loads the appropriate
+resources for your application.</p>
 
 <p>To specify configuration-specific alternatives for a set of resources:</p>
 <ol>
@@ -201,15 +217,15 @@
 <em>&lt;resources_name&gt;</em>-<em>&lt;config_qualifier&gt;</em>}.
     <ul>
       <li><em>{@code &lt;resources_name&gt;}</em> is the directory name of the corresponding default
-resources.</li>
-      <li><em>{@code &lt;config_qualifier&gt;}</em> is a name that specifies a configuration
-for which these resources are to be used.</li>
+resources (defined in table 1).</li>
+      <li><em>{@code &lt;qualifier&gt;}</em> is a name that specifies an individual configuration
+for which these resources are to be used (defined in table 2).</li>
     </ul>
-    <p>You can append more than one <em>{@code &lt;config_qualifier&gt;}</em>. Separate each
+    <p>You can append more than one <em>{@code &lt;qualifier&gt;}</em>. Separate each
 one with a dash.</p>
   </li>
-  <li>Save your alternative resources in this new directory. The resource files must be named
-exactly the same as the default resource files.</li>
+  <li>Save the respective alternative resources in this new directory. The resource files must be
+named exactly the same as the default resource files.</li>
 </ol>
 
 <p>For example, here are some default and alternative resources:</p>
@@ -225,20 +241,27 @@
 </pre>
 
 <p>The {@code hdpi} qualifier indicates that the resources in that directory are for devices with a
-high-density screen. While the images in each drawable directory are sized for a specific screen
-density, the filenames are
+high-density screen. The images in each of these drawable directories are sized for a specific
+screen density, but the filenames are exactly
 the same. This way, the resource ID that you use to reference the {@code icon.png} or {@code
 background.png} image is always the same, but Android selects the
-version of that drawable that best matches the current device configuration.</p>
+version of each resource that best matches the current device, by comparing the device
+configuration information with the qualifiers in the alternative resource directory name.</p>
 
 <p>Android supports several configuration qualifiers and you can
 add multiple qualifiers to one directory name, by separating each qualifier with a dash. Table 2
 lists the valid configuration qualifiers, in order of precedence&mdash;if you use multiple
-qualifiers, they must be added to the directory name in the order they are listed in the
-table.</p>
+qualifiers for one resource directory, they must be added to the directory name in the order they
+are listed in the table.</p>
 
+<p class="note"><strong>Note:</strong> Some configuration qualifiers were added after Android 1.0,
+so not
+all versions of Android support all the qualifiers listed in table 2. New qualifiers
+indicate the version in which they were added. To avoid any issues, always include a set of default
+resources for resources that your application uses. For more information, see the section about <a
+href="#Compatibility">Providing the Best Device Compatibility with Resources</a>.</p>
 
-<p class="table-caption" id="table2"><strong>Table 2.</strong> Alternative resource qualifier
+<p class="table-caption" id="table2"><strong>Table 2.</strong> Configuration qualifier
 names.</p>
 <table>
     <tr>
@@ -246,7 +269,7 @@
         <th>Values</th>
         <th>Description</th>
     </tr>
-    <tr>
+    <tr id="MccQualifier">
       <td>MCC and MNC</td>
       <td>Examples:<br/>
         <code>mcc310</code><br/>
@@ -272,7 +295,7 @@
 and mobile network code, respectively.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="LocaleQualifier">
       <td>Language and region</td>
       <td>Examples:<br/>
         <code>en</code><br/>
@@ -297,12 +320,12 @@
 href="runtime-changes.html">Handling Runtime Changes</a> for information about
 how this can affect your application during runtime.</p>
         <p>See <a href="localization.html">Localization</a> for a complete guide to localizing
-your application for other langauges.</p>
+your application for other languages.</p>
         <p>Also see the {@link android.content.res.Configuration#locale} configuration field, which
 indicates the current locale.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="ScreenSizeQualifier">
       <td>Screen size</td>
       <td>
         <code>small</code><br/>
@@ -326,6 +349,7 @@
         available space in both width and height than an HVGA display.
         Examples are VGA and WVGA medium density screens.</li>
         </ul>
+        <p><em>Added in API Level 4.</em></p>
         <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
 Screens</a> for more information.</p>
         <p>Also see the {@link android.content.res.Configuration#screenLayout} configuration field,
@@ -333,8 +357,8 @@
 or large.</p>
       </td>
     </tr>
-    <tr>
-      <td>Wider/taller screens</td>
+    <tr id="ScreenAspectQualifier">
+      <td>Screen aspect</td>
       <td>
         <code>long</code><br/>
         <code>notlong</code>
@@ -344,13 +368,14 @@
           <li>{@code long}: Long screens, such as WQVGA, WVGA, FWVGA</li>
           <li>{@code notlong}: Not long screens, such as QVGA, HVGA, and VGA</li>
         </ul>
+        <p><em>Added in API Level 4.</em></p>
         <p>This is based purely on the aspect ratio of the screen (a "long" screen is wider). This
 is not related to the screen orientation.</p>
         <p>Also see the {@link android.content.res.Configuration#screenLayout} configuration field,
 which indicates whether the screen is long.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="OrientationQualifier">
       <td>Screen orientation</td>
       <td>
         <code>port</code><br/>
@@ -370,7 +395,7 @@
 which indicates the current device orientation.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="DockQualifier">
       <td>Dock mode</td>
       <td>
         <code>car</code><br/>
@@ -383,12 +408,12 @@
         </ul>
         <p><em>Added in API Level 8.</em></p>
         <p>This can change during the life of your application if the user places the device in a
-dock. You can eneable or disable this mode using {@link
+dock. You can enable or disable this mode using {@link
 android.app.UiModeManager}. See <a href="runtime-changes.html">Handling Runtime Changes</a> for
 information about how this affects your application during runtime.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="NightQualifier">
       <td>Night mode</td>
       <td>
         <code>night</code><br/>
@@ -401,13 +426,13 @@
         </ul>
         <p><em>Added in API Level 8.</em></p>
         <p>This can change during the life of your application if night mode is left in
-auto mode (default), in which case the mode changes based on the time of day.  You can eneable
+auto mode (default), in which case the mode changes based on the time of day.  You can enable
 or disable this mode using {@link android.app.UiModeManager}. See <a
 href="runtime-changes.html">Handling Runtime Changes</a> for information about how this affects your
 application during runtime.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="DensityQualifier">
       <td>Screen pixel density (dpi)</td>
       <td>
         <code>ldpi</code><br/>
@@ -424,6 +449,7 @@
           <li>{@code nodpi}: This can be used for bitmap resources that you do not want to be scaled
 to match the device density.</li>
         </ul>
+        <p><em>Added in API Level 4.</em></p>
         <p>There is thus a 4:3 scaling factor between each density, so a 9x9 bitmap
          in ldpi is 12x12 in mdpi and 16x16 in hdpi.</p>
         <p>When Android selects which resource files to use,
@@ -439,7 +465,7 @@
 your bitmaps.</p>
        </td>
     </tr>
-    <tr>
+    <tr id="TouchscreenQualifier">
       <td>Touchscreen type</td>
       <td>
         <code>notouch</code><br/>
@@ -457,7 +483,7 @@
 which indicates the type of touchscreen on the device.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="KeyboardAvailQualifier">
       <td>Keyboard availability</td>
       <td>
         <code>keysexposed</code><br/>
@@ -487,7 +513,7 @@
 keyboard and and the visibility of any kind of keyboard (including software), respectively.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="ImeQualifier">
       <td>Primary text input method</td>
       <td>
         <code>nokeys</code><br/>
@@ -497,7 +523,8 @@
       <td>
         <ul class="nolist">
           <li>{@code nokeys}: Device has no hardware keys for text input.</li>
-          <li>{@code qwert}: Device has a hardware qwerty keyboard, whether it's visible to the user
+          <li>{@code qwerty}: Device has a hardware qwerty keyboard, whether it's visible to the
+user
 or not.</li>
           <li>{@code 12key}: Device has a hardware 12-key keyboard, whether it's visible to the user
 or not.</li>
@@ -506,7 +533,7 @@
 which indicates the primary text input method available.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="NavAvailQualifier">
       <td>Navigation key availability</td>
       <td>
         <code>navexposed</code><br/>
@@ -525,7 +552,7 @@
 field, which indicates whether navigation keys are hidden.</p>
       </td>
     </tr>
-    <tr>
+    <tr id="TouchQualifier">
       <td>Primary non-touch navigation method</td>
       <td>
         <code>nonav</code><br/>
@@ -560,19 +587,22 @@
       </td>
     </tr>
 -->
-    <tr>
-      <td>API Level</td>
+    <tr id="VersionQualifier">
+      <td>System Version (API Level)</td>
       <td>Examples:<br/>
+        <code>v3</code><br/>
         <code>v4</code><br/>
-        <code>v5</code><br/>
-        <code>v6</code><br/>
         <code>v7</code><br/>
         etc.</td>
       <td>
-        <p>The API Level supported by the device, for example <code>v1</code> for API Level 1
-(Android 1.0) or <code>v5</code> for API Level 5 (Android 2.0). See the <a
+        <p>The API Level supported by the device. For example, <code>v1</code> for API Level
+1 (devices with Android 1.0 or higher) and <code>v4</code> for API Level 4 (devices with Android
+1.6 or higher). See the <a
 href="{@docRoot}guide/appendix/api-levels.html">Android API Levels</a> document for more information
 about these values.</p>
+        <p class="caution"><strong>Caution:</strong> Android 1.5 and 1.6 only match resources
+with this qualifier when it exactly matches the system version. See the section below about <a
+href="#KnownIssues">Known Issues</a> for more information.</p>
       </td>
     </tr>
 </table>
@@ -580,7 +610,7 @@
 
 <h3 id="QualifierRules">Qualifier name rules</h3>
 
-<p>Here are some rules about using resource qualifier names:</p>
+<p>Here are some rules about using configuration qualifier names:</p>
 
 <ul>
     <li>You can specify multiple qualifiers for a single set of resources, separated by dashes. For
@@ -611,15 +641,19 @@
 these qualifiers, Android automatically applies the resources in your application based on the
 current device configuration. Each time a resource is requested, Android checks for alternative
 resource directories that contain the requested resource file, then <a href="#BestMatch">finds the
-best-matching resource</a> (discussed below).</p>
+best-matching resource</a> (discussed below). If there are no alternative resources that match
+a particular device configuration, then Android uses the corresponding default resources (the
+set of resources for a particular resource type that does not include a configuration
+qualifier).</p>
 
 
 
 <h3 id="AliasResources">Creating alias resources</h3>
 
 <p>When you have a resource that you'd like to use for more than one device
-configuration (but not for all configurations), you do not need to put the same resource in
-each alternative resource directory. Instead, you can (in some cases) create an alternative
+configuration (but do not want to provide as a default resource), you do not need to put the same
+resource in more than one alternative resource directory. Instead, you can (in some cases) create an
+alternative
 resource that acts as an alias for a resource saved in your default resource directory.</p>
 
 <p class="note"><strong>Note:</strong> Not all resources offer a mechanism by which you can
@@ -701,6 +735,112 @@
 
 
 
+<h2 id="Compatibility">Providing the Best Device Compatibility with Resources</h2>
+
+<p>In order for your application to support multiple device configurations, it's very important that
+you always provide default resources for each type of resource that your application uses.</p>
+
+<p>For example, if your application supports several languages, always include a {@code
+values/} directory (in which your strings are saved) <em>without</em> a <a
+href="#LocalQualifier">language and region qualifier</a>. If you instead put all your string files
+in directories that have a language and region qualifier, then your application will crash when run
+on a device set to a language that your strings do not support. But, as long as you provide default
+{@code values/} resources, then your application will run properly (even if the user doesn't
+understand that language&mdash;it's better than crashing).</p>
+
+<p>Likewise, if you provide different layout resources based on the screen orientation, you should
+pick one orientation as your default. For example, instead of providing layout resources in {@code
+layout-land/} for landscape and {@code layout-port/} for portrait, leave one as the default, such as
+{@code layout/} for landscape and {@code layout-port/} for portrait.</p>
+
+<p>Providing default resources is important not only because your application might run on a
+configuration you had not anticipated, but also because new versions of Android sometimes add
+configuration qualifiers that older versions do not support. If you use a new resource qualifier,
+but maintain code compatibility with older versions of Android, then when an older version of
+Android runs your application, it will crash if you do not provide default resources, because it
+cannot use the resources named with the new qualifier. For example, if your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+minSdkVersion}</a> is set to 4, and you qualify all of your drawable resources using <a
+href="NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API
+Level 8), then an API Level 4 device cannot access your drawable resources and will crash. In this
+case, you probably want {@code notnight} to be your default resources, so you should exclude that
+qualifier so your drawable resources are in either {@code drawable/} or {@code drawable-night/}.</p>
+
+<p>So, in order to provide the best device compatibility, always provide default
+resources for the resources your application needs to perform properly. Then create alternative
+resources for specific device configurations using the configuration qualifiers.</p>
+
+<p>There is one exception to this rule: If your application's <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> is 4 or
+greater, you <em>do not</em> need default drawable resources when you provide alternative drawable
+resources with the <a href="#DensityQualifier">screen density</a> qualifier. Even without default
+drawable resources, Android can find the best match among the alternative screen densities and scale
+the bitmaps as necessary. However, for the best experience on all types of devices, you should
+provide alternative drawables for all three types of density. If your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> is
+<em>less than</em> 4 (Android 1.5 or lower), be aware that the screen size, density, and aspect
+qualifiers are not supported on Android 1.5 or lower, so you might need to perform additional
+compatibility for these versions.</p>
+
+
+<h3 id="ScreenCompatibility">Providing screen resource compatibility for Android 1.5</h3>
+
+<p>Android 1.5 (and lower) does not support the following configuration qualifers:</p>
+<dl>
+  <dt><a href="#DensityQualifier">Density</a></dt>
+    <dd>{@code ldpi}, {@code mdpi}, {@code ldpi}, and {@code nodpi}</dd>
+  <dt><a href="#ScreenSizeQualifier">Screen size</a></dt>
+    <dd>{@code small}, {@code normal}, and {@code large}</dd>
+  <dt><a href="#ScreenAspectQualifier">Screen aspect</a></dt>
+    <dd>{@code long} and {@code notlong}</dd>
+</dl>
+
+<p>These configuration qualifiers were introduced in Android 1.6, so Android 1.5 (API Level 3) and
+lower does not support them. If you use these configuration qualifiers and do not provide
+corresponding default resources, then an Android 1.5 device might use any one of the resource
+directories named with the above screen configuration qualifiers, because it ignores these
+qualifiers and uses whichever otherwise-matching drawable resource it finds first.</p>
+
+<p>For example, if your application supports Android 1.5 and includes drawable resources for
+each density type ({@code drawable-ldpi/}, {@code drawable-mdpi/}, and {@code drawable-ldpi/}),
+and does <em>not</em> include default drawable resources ({@code drawable/}), then
+an Android 1.5 will use drawables from any one of the alternative resource directories, which
+can result in a user interface that's less than ideal.<p>
+
+<p>So, to provide compatibility with Android 1.5 (and lower) when using the screen configuration
+qualifiers:</p>
+<ol>
+  <li>Provide default resources that are for medium-density, normal, and notlong screens.
+
+    <p>Because all Android 1.5 devices have medium-density, normal, not-long screens, you can
+place these kinds of resources in the corresponding default resource directory. For example, put all
+medium density drawable resources in {@code drawable/} (instead of {@code drawable-mdpi/}),
+put {@code normal} size resources in the corresponding default resource directory, and {@code
+notlong} resources in the corresponding default resource directory.</p>
+  </li>
+
+  <li>Ensure that your <a href="{@docRoot}sdk/tools-notes.html">SDK Tools</a> version
+is r6 or greater.
+
+    <p>You need SDK Tools, Revision 6 (or greater), because it includes a new packaging tool that
+automatically applies an appropriate <a href="#VersionQualifier">version qualifier</a> to any
+resource directory named with a qualifier that does not exist in Android 1.0. For example, because
+the density qualifier was introduced in Android 1.6 (API Level 4), when the packaging tool
+encounters a resource directory using the density qualifier, it adds {@code v4} to the directory
+name to ensure that older versions do not use those resources (only API Level 4 and higher support
+that qualifier). Thus, by putting your medium-density resources in a directory <em>without</em> the
+{@code mdpi} qualifier, they are still accessible by Android 1.5, and any device that supports the
+density qualifer and has a medium-density screen also uses the default resources (which are mdpi)
+because they are the best match for the device (instead of using the {@code ldpi} or {@code hdpi}
+resources).</p>
+</li>
+</ol>
+
+<p class="note"><strong>Note:</strong> Later versions of Android, such as API Level 8,
+introduce other configuration qualifiers that older version do not support. To provide the best
+compatibility, you should always include a set of default resources for each type of resource
+that your application uses, as discussed above to provide the best device compatibility.</p>
+
 
 
 <h2 id="BestMatch">How Android Finds the Best-matching Resource</h2>
@@ -820,3 +960,27 @@
 href="accessing-resources.html">Accessing Resources</a>.</p>
 
 
+
+
+<h2 id="KnownIssues">Known Issues</h2>
+
+<h3>Android 1.5 and 1.6: Version qualifier performs exact match, instead of best match</h3>
+
+<p>The correct behavior is for the system to match resources marked with a <a
+href="#VersionQualifier">version qualifier</a> equal
+to or less than the system version on the device, but on Android 1.5 and 1.6, (API Level 3 and 4),
+there is a bug that causes the system to match resources marked with the version qualifier
+only when it exactly matches the version on the device.</p>
+
+<p><b>The workaround:</b> To provide version-specific resources, abide by this behavior. However,
+because this bug is fixed in versions of Android available after 1.6, if
+you need to differentiate resources between Android 1.5, 1.6, and later versions, then you only need
+to apply the version qualifier to the 1.6 resources and one to match all later versions. Thus, this
+is effectively a non-issue.</p>
+
+<p>For example, if you want drawable resources that are different on each Android 1.5, 1.6,
+and 2.0.1 (and later), create three drawable directories: {@code drawable/} (for 1.5 and lower),
+{@code drawable-v4} (for 1.6), and {@code drawable-v6} (for 2.0.1 and later&mdash;version 2.0, v5,
+is no longer available).</p>
+
+
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 6638bef..5e75105 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -50,7 +50,7 @@
 <div class="dashboard-panel">
 
 <img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.1,27.6,26.8,0.1,0.3,45.1&chl=
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.1,24.6,25.0,0.1,0.3,50.0&chl=
 Android%201.1|Android%201.5|Android%201.6|Android%202.0|Android%202.0.1|Android%202.1&chco=c4df9b,
 6fad0c" />
 
@@ -59,13 +59,13 @@
   <th>Android Platform</th>
   <th>Percent of Devices</th>
 </tr>
-<tr><td>Android 1.1</td><td>0.1%</td></tr>
-<tr><td>Android 1.5</td><td>27.6%</td></tr>
-<tr><td>Android 1.6</td><td>26.8%</td></tr>
-<tr><td>Android 2.0</td><td>0.1%</td></tr>
-<tr><td>Android 2.0.1</td><td>0.3%</td></tr>
-<tr><td>Android 2.1</td><td>45.1%</td></tr>
+<tr><td>Android 1.1</td><td>0.1%</td></tr> 
+<tr><td>Android 1.5</td><td>24.6%</td></tr> 
+<tr><td>Android 1.6</td><td>25.0%</td></tr> 
+<tr><td>Android 2.0</td><td>0.1%</td></tr> 
+<tr><td>Android 2.0.1</td><td>0.3%</td></tr> 
+<tr><td>Android 2.1</td><td>50.0%</td></tr> 
 </table>
-<p><em>Data collected during two weeks ending on June 1, 2010</em></p>
+<p><em>Data collected during two weeks ending on June 16, 2010</em></p>
 </div>
 
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index 5da217b..f8130ea 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -49,7 +49,7 @@
 <div class="dashboard-panel">
 
 <img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.0,61.6,37.4&chl=Small%20/%20ldpi|
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,57.8,41.0&chl=Small%20/%20ldpi|
 Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
 
 <table>
@@ -60,14 +60,14 @@
 <th scope="col">High Density</th>
 </tr>
 <tr><th scope="row">Small</th> 
-<td class='cent '>1.0%</td> 
+<td class='cent hi'>1.1%</td> 
 <td></td> 
 <td></td> 
 </tr> 
 <tr><th scope="row">Normal</th> 
 <td></td> 
-<td class='cent hi'>61.3%</td> 
-<td class='cent hi'>37.7%</td> 
+<td class='cent hi'>57.8%</td> 
+<td class='cent hi'>41.0%</td> 
 </tr> 
 <tr><th scope="row">Large</th> 
 <td></td> 
@@ -76,6 +76,6 @@
 </tr> 
 </table>
 
-<p><em>Data collected during two weeks ending on June 1, 2010</em></p>
+<p><em>Data collected during two weeks ending on June 16, 2010</em></p>
 </div>
 
diff --git a/docs/html/shareables/icon_templates-v2.0.zip b/docs/html/shareables/icon_templates-v2.0.zip
index 1c4b4988..1b94698 100644
--- a/docs/html/shareables/icon_templates-v2.0.zip
+++ b/docs/html/shareables/icon_templates-v2.0.zip
Binary files differ
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 6a5c8a8..92ff872 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -73,7 +73,7 @@
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
 
-    /* These flags are set by the input dispatch policy as it intercepts each event. */
+    /* 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..80a20c9 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,82 @@
 
 namespace android {
 
+/*
+ * 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. */
+    virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 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. */
+    virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+            Vector<InputTarget>& outTargets) = 0;
+
+    /* Gets the input targets for a motion event. */
+    virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            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 +126,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,
@@ -76,19 +147,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,
@@ -119,12 +204,11 @@
         int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
+
+        bool dispatchInProgress; // initially false, set to true while dispatching
     };
 
     struct ConfigurationChangedEntry : EventEntry {
-        int32_t touchScreenConfig;
-        int32_t keyboardConfig;
-        int32_t navigationConfig;
     };
 
     struct KeyEntry : EventEntry {
@@ -165,6 +249,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 +274,36 @@
         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);
+
+    struct CommandEntry : Link<CommandEntry> {
+        CommandEntry();
+        ~CommandEntry();
+
+        Command command;
+
+        // parameters for the command (usage varies by command)
+        sp<InputChannel> inputChannel;
+    };
+
+    // Generic queue implementation.
     template <typename T>
     struct Queue {
         T head;
@@ -242,17 +357,17 @@
         KeyEntry* obtainKeyEntry();
         MotionEntry* obtainMotionEntry();
         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);
 
     private:
         Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
@@ -260,6 +375,7 @@
         Pool<MotionEntry> mMotionEntryPool;
         Pool<MotionSample> mMotionSamplePool;
         Pool<DispatchEntry> mDispatchEntryPool;
+        Pool<CommandEntry> mCommandEntryPool;
     };
 
     /* Manages the dispatch state associated with a single input channel. */
@@ -291,7 +407,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.
@@ -323,22 +441,23 @@
         status_t initialize();
     };
 
-    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.
     Vector<Connection*> mActiveConnections;
 
-    // Pool of key and motion event objects used only to ask the input dispatch policy
+    // 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 +466,7 @@
     // 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
 
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
@@ -357,17 +477,27 @@
 
     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,
@@ -384,12 +514,22 @@
     void activateConnectionLocked(Connection* connection);
     void deactivateConnectionLocked(Connection* connection);
 
+    // Outbound policy interactions.
+    void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry);
+
     // 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, 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 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..3872c26 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,11 @@
     /* Unregisters an input channel. */
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 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 +93,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 +102,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 +118,7 @@
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
+    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 +128,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..14dcada 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -19,6 +19,9 @@
 // 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
 
@@ -42,7 +45,7 @@
 
 // --- InputDispatcher ---
 
-InputDispatcher::InputDispatcher(const sp<InputDispatchPolicyInterface>& policy) :
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
     mPolicy(policy) {
     mPollLoop = new PollLoop();
 
@@ -55,6 +58,8 @@
     mInboundQueue.tail.eventTime = LONG_LONG_MAX;
 
     mKeyRepeatState.lastKeyEntry = NULL;
+
+    mCurrentInputTargetsValid = false;
 }
 
 InputDispatcher::~InputDispatcher() {
@@ -72,8 +77,9 @@
 }
 
 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
@@ -84,7 +90,7 @@
         // 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();
         }
 
@@ -121,8 +127,8 @@
             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 +136,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 +167,67 @@
                     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();
     } // 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'
+
+        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 +251,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 +259,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
@@ -252,7 +295,7 @@
     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 +306,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,15 +340,19 @@
     }
 #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);
@@ -313,19 +361,25 @@
     mPolicy->getKeyEventTargets(& mReusableKeyEvent, entry->policyFlags,
             mCurrentInputTargets);
 
+    mLock.lock();
+    mCurrentInputTargetsValid = true;
+
     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);
 
@@ -333,17 +387,22 @@
     mPolicy->getMotionEventTargets(& mReusableMotionEvent, entry->policyFlags,
             mCurrentInputTargets);
 
+    mLock.lock();
+    mCurrentInputTargetsValid = true;
+
     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);
 
@@ -365,7 +424,7 @@
         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 +436,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;
     }
 
@@ -631,14 +690,15 @@
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, 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;
     }
 
@@ -722,34 +782,37 @@
 bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
         bool broken) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ abortDispatchCycle, broken=%s",
+    LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
             connection->getInputChannelName(), broken ? "true" : "false");
 #endif
 
-    if (connection->status == Connection::STATUS_BROKEN) {
-        return false;
-    }
-
     // 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);
-    }
+    bool deactivated = ! connection->outboundQueue.isEmpty();
+    if (deactivated) {
+        do {
+            DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+            mAllocator.releaseDispatchEntry(dispatchEntry);
+        } while (! connection->outboundQueue.isEmpty());
 
-    // Outbound queue is empty, deactivate the connection.
-    deactivateConnectionLocked(connection);
+        deactivateConnectionLocked(connection);
+    }
 
     // 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*/
+
+    return deactivated;
 }
 
 bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -772,6 +835,7 @@
             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->runCommandsLockedInterruptible();
             return false; // remove the callback
         }
 
@@ -786,20 +850,19 @@
             LOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                     connection->getInputChannelName(), status);
             d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+            d->runCommandsLockedInterruptible();
             return false; // remove the callback
         }
 
         d->finishDispatchCycleLocked(currentTime, connection.get());
+        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;
@@ -808,9 +871,6 @@
 
         ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry();
         newEntry->eventTime = eventTime;
-        newEntry->touchScreenConfig = touchScreenConfig;
-        newEntry->keyboardConfig = keyboardConfig;
-        newEntry->navigationConfig = navigationConfig;
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -821,16 +881,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);
@@ -949,13 +999,25 @@
                 }
 
                 // The last motion event is a move and is compatible for appending.
-                // Do the batching magic and exit.
+                // Do the batching magic.
                 mAllocator.appendMotionSample(motionEntry, eventTime, pointerCount, 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 +1039,38 @@
             // 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) {
+                                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,
+                                    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*/);
+
+                            runCommandsLockedInterruptible();
+                            return; // done!
+                        }
                     }
                 }
             }
@@ -1049,6 +1115,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 +1139,8 @@
         }
 
         mConnectionsByReceiveFd.add(receiveFd, connection);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
@@ -1076,6 +1148,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);
@@ -1095,6 +1171,8 @@
 
         nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
         abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->removeCallback(receiveFd);
@@ -1123,11 +1201,12 @@
     }
 }
 
-void InputDispatcher::onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleStartedLocked(
+        nsecs_t currentTime, Connection* connection) {
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
-        Connection* connection, bool recoveredFromANR) {
+void InputDispatcher::onDispatchCycleFinishedLocked(
+        nsecs_t currentTime, 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,26 +1215,65 @@
                 connection->getDispatchLatencyMillis(currentTime),
                 connection->getANRLatencyMillis(currentTime));
 
-        // TODO tell framework
+        CommandEntry* commandEntry = postCommandLocked(
+                & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
+        commandEntry->inputChannel = connection->inputChannel;
     }
 }
 
-void InputDispatcher::onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleANRLocked(
+        nsecs_t currentTime, 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->inputChannel = connection->inputChannel;
 }
 
-void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleBrokenLocked(
+        nsecs_t currentTime, Connection* connection) {
     LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
             connection->getInputChannelName());
 
-    // TODO tell framework
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+    commandEntry->inputChannel = connection->inputChannel;
 }
 
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyInputChannelBroken(commandEntry->inputChannel);
+    commandEntry->inputChannel.clear();
+
+    mLock.lock();
+}
+
+void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
+    commandEntry->inputChannel.clear();
+
+    mLock.lock();
+}
+
+void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyInputChannelRecoveredFromANR(commandEntry->inputChannel);
+    commandEntry->inputChannel.clear();
+
+    mLock.lock();
+}
+
+
 // --- InputDispatcher::Allocator ---
 
 InputDispatcher::Allocator::Allocator() {
@@ -1166,6 +1284,7 @@
     ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
     entry->refCount = 1;
     entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED;
+    entry->dispatchInProgress = false;
     return entry;
 }
 
@@ -1173,6 +1292,7 @@
     KeyEntry* entry = mKeyEntryPool.alloc();
     entry->refCount = 1;
     entry->type = EventEntry::TYPE_KEY;
+    entry->dispatchInProgress = false;
     return entry;
 }
 
@@ -1181,6 +1301,7 @@
     entry->refCount = 1;
     entry->type = EventEntry::TYPE_MOTION;
     entry->firstSample.next = NULL;
+    entry->dispatchInProgress = false;
     return entry;
 }
 
@@ -1192,6 +1313,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 +1358,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,6 +1374,10 @@
     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) {
     MotionSample* sample = mMotionSamplePool.alloc();
@@ -1256,18 +1391,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 +1407,25 @@
     return inputPublisher.initialize();
 }
 
+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 +1437,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..7538dd0 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,26 @@
     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);
+void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
+    mReader->getCurrentInputConfiguration(outConfiguration);
 }
 
-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;
-        }
-    }
+int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t scanCode) const {
+    return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode);
+}
 
-    return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
+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 796abd0f..5a280ae 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,18 +1441,18 @@
                 * device->touchScreen.precalculated.sizeScale;
 
         switch (mDisplayOrientation) {
-        case InputDispatchPolicyInterface::ROTATION_90: {
+        case InputReaderPolicyInterface::ROTATION_90: {
             float xTemp = x;
             x = y;
             y = mDisplayHeight - 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;
             y = xTemp;
@@ -1483,18 +1555,18 @@
 
     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 018f18d..46d7493 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -3,9 +3,11 @@
 include $(CLEAR_VARS)
 
 test_src_files := \
-    InputDispatcher_test.cpp
+    InputChannel_test.cpp \
+    InputDispatcher_test.cpp \
+    InputPublisherAndConsumer_test.cpp
 
-LOCAL_SHARED_LIBRARIES := \
+shared_libraries := \
 	libcutils \
 	libutils \
 	libEGL \
@@ -16,21 +18,26 @@
 	libui \
 	libstlport
 
-LOCAL_STATIC_LIBRARIES := \
+static_libraries := \
 	libgtest \
 	libgtest_main
 
-LOCAL_C_INCLUDES := \
+c_includes := \
     bionic \
     bionic/libstdc++/include \
     external/gtest/include \
     external/stlport/stlport
 
-LOCAL_MODULE_TAGS := eng tests
+module_tags := eng tests
 
 $(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
     $(eval include $(BUILD_EXECUTABLE)) \
 )
 
diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp
new file mode 100644
index 0000000..6cec1c02
--- /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/Android.mk b/libs/utils/tests/Android.mk
index 45e8061..92ebfd7c 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -5,18 +5,18 @@
 test_src_files := \
 	PollLoop_test.cpp
 
-LOCAL_SHARED_LIBRARIES := \
+shared_libraries := \
 	libz \
 	liblog \
 	libcutils \
 	libutils \
 	libstlport
 
-LOCAL_STATIC_LIBRARIES := \
+static_libraries := \
 	libgtest \
 	libgtest_main
 
-LOCAL_C_INCLUDES := \
+c_includes := \
     external/zlib \
     external/icu4c/common \
     bionic \
@@ -24,10 +24,15 @@
     external/gtest/include \
     external/stlport/stlport
 
-LOCAL_MODULE_TAGS := eng tests
+module_tags := eng tests
 
 $(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
     $(eval LOCAL_SRC_FILES := $(file)) \
     $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
     $(eval include $(BUILD_EXECUTABLE)) \
 )
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;
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index a7ccce4..d49c4e0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -30,6 +30,7 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
+#include <media/MediaProfiles.h>
 #include <camera/ICamera.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
@@ -440,7 +441,7 @@
         new AudioSource(
                 mAudioSource,
                 mSampleRate,
-                AudioSystem::CHANNEL_IN_MONO);
+                mAudioChannels);
 
     status_t err = audioSource->initCheck();
 
@@ -561,6 +562,74 @@
     return OK;
 }
 
+void StagefrightRecorder::clipVideoFrameRate() {
+    LOGV("clipVideoFrameRate: encoder %d", mVideoEncoder);
+    int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.fps.min", mVideoEncoder);
+    int maxFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.fps.max", mVideoEncoder);
+    if (mFrameRate < minFrameRate) {
+        LOGW("Intended video encoding frame rate (%d fps) is too small"
+             " and will be set to (%d fps)", mFrameRate, minFrameRate);
+        mFrameRate = minFrameRate;
+    } else if (mFrameRate > maxFrameRate) {
+        LOGW("Intended video encoding frame rate (%d fps) is too large"
+             " and will be set to (%d fps)", mFrameRate, maxFrameRate);
+        mFrameRate = maxFrameRate;
+    }
+}
+
+void StagefrightRecorder::clipVideoBitRate() {
+    LOGV("clipVideoBitRate: encoder %d", mVideoEncoder);
+    int minBitRate = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.bps.min", mVideoEncoder);
+    int maxBitRate = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.bps.max", mVideoEncoder);
+    if (mVideoBitRate < minBitRate) {
+        LOGW("Intended video encoding bit rate (%d bps) is too small"
+             " and will be set to (%d bps)", mVideoBitRate, minBitRate);
+        mVideoBitRate = minBitRate;
+    } else if (mVideoBitRate > maxBitRate) {
+        LOGW("Intended video encoding bit rate (%d bps) is too large"
+             " and will be set to (%d bps)", mVideoBitRate, maxBitRate);
+        mVideoBitRate = maxBitRate;
+    }
+}
+
+void StagefrightRecorder::clipVideoFrameWidth() {
+    LOGV("clipVideoFrameWidth: encoder %d", mVideoEncoder);
+    int minFrameWidth = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.width.min", mVideoEncoder);
+    int maxFrameWidth = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.width.max", mVideoEncoder);
+    if (mVideoWidth < minFrameWidth) {
+        LOGW("Intended video encoding frame width (%d) is too small"
+             " and will be set to (%d)", mVideoWidth, minFrameWidth);
+        mVideoWidth = minFrameWidth;
+    } else if (mVideoWidth > maxFrameWidth) {
+        LOGW("Intended video encoding frame width (%d) is too large"
+             " and will be set to (%d)", mVideoWidth, maxFrameWidth);
+        mVideoWidth = maxFrameWidth;
+    }
+}
+
+void StagefrightRecorder::clipVideoFrameHeight() {
+    LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder);
+    int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.height.min", mVideoEncoder);
+    int maxFrameHeight = mEncoderProfiles->getVideoEncoderParamByName(
+                        "enc.vid.height.max", mVideoEncoder);
+    if (mVideoHeight < minFrameHeight) {
+        LOGW("Intended video encoding frame height (%d) is too small"
+             " and will be set to (%d)", mVideoHeight, minFrameHeight);
+        mVideoHeight = minFrameHeight;
+    } else if (mVideoHeight > maxFrameHeight) {
+        LOGW("Intended video encoding frame height (%d) is too large"
+             " and will be set to (%d)", mVideoHeight, maxFrameHeight);
+        mVideoHeight = maxFrameHeight;
+    }
+}
+
 status_t StagefrightRecorder::startMPEG4Recording() {
     mWriter = new MPEG4Writer(dup(mOutputFd));
 
@@ -587,6 +656,11 @@
     if (mVideoSource == VIDEO_SOURCE_DEFAULT
             || mVideoSource == VIDEO_SOURCE_CAMERA) {
 
+        clipVideoBitRate();
+        clipVideoFrameRate();
+        clipVideoFrameWidth();
+        clipVideoFrameHeight();
+
         int64_t token = IPCThreadState::self()->clearCallingIdentity();
         if (mCamera == 0) {
             mCamera = Camera::connect(0);
@@ -748,6 +822,7 @@
     mAudioBitRate  = 12200;
     mInterleaveDurationUs = 0;
     mIFramesInterval = 1;
+    mEncoderProfiles = MediaProfiles::getInstance();
 
     mOutputFd = -1;
     mFlags = 0;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index baf33cf..7de96f6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -26,6 +26,7 @@
 class Camera;
 struct MediaSource;
 struct MediaWriter;
+class MediaProfiles;
 
 struct StagefrightRecorder : public MediaRecorderBase {
     StagefrightRecorder();
@@ -84,6 +85,8 @@
     int mOutputFd;
     int32_t mFlags;
 
+    MediaProfiles *mEncoderProfiles;
+
     status_t startMPEG4Recording();
     status_t startAMRRecording();
     status_t startAACRecording();
@@ -96,6 +99,10 @@
     status_t setParamInterleaveDuration(int32_t durationUs);
     status_t setParamIFramesInterval(int32_t interval);
     status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration);
+    void clipVideoBitRate();
+    void clipVideoFrameRate();
+    void clipVideoFrameWidth();
+    void clipVideoFrameHeight();
 
     StagefrightRecorder(const StagefrightRecorder &);
     StagefrightRecorder &operator=(const StagefrightRecorder &);
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 9717aa6..d6020a6 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -33,15 +33,25 @@
 
 AudioSource::AudioSource(
         int inputSource, uint32_t sampleRate, uint32_t channels)
-    : mRecord(new AudioRecord(
-                inputSource, sampleRate, AudioSystem::PCM_16_BIT, channels)),
-      mInitCheck(mRecord->initCheck()),
-      mStarted(false),
+    : mStarted(false),
       mCollectStats(false),
       mTotalReadTimeUs(0),
       mTotalReadBytes(0),
       mTotalReads(0),
       mGroup(NULL) {
+
+    LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
+    uint32_t flags = AudioRecord::RECORD_AGC_ENABLE |
+                     AudioRecord::RECORD_NS_ENABLE  |
+                     AudioRecord::RECORD_IIR_ENABLE;
+
+    mRecord = new AudioRecord(
+                inputSource, sampleRate, AudioSystem::PCM_16_BIT,
+                channels > 1? AudioSystem::CHANNEL_IN_STEREO: AudioSystem::CHANNEL_IN_MONO,
+                4 * kMaxBufferSize / sizeof(int16_t), /* Enable ping-pong buffers */
+                flags);
+
+    mInitCheck = mRecord->initCheck();
 }
 
 AudioSource::~AudioSource() {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 6727c73..f29e7fb 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -267,6 +267,7 @@
             mFetching = false;
         }
     } else {
+        Mutex::Autolock autoLock(mLock);
         restartPrefetcherIfNecessary_l();
     }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6be41b4..5a01d79 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -347,7 +347,7 @@
 
         quirks |= kRequiresAllocateBufferOnInputPorts;
         quirks |= kRequiresAllocateBufferOnOutputPorts;
-        if (!strncmp(componentName, "OMX.TI.video.encoder", 20)) {
+        if (!strncmp(componentName, "OMX.TI.Video.encoder", 20)) {
             quirks |= kAvoidMemcopyInputRecordingFrames;
         }
     }
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 3de8c1d..0dacb53 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -22,11 +22,13 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "TimedEventQueue"
 #include <utils/Log.h>
+#include <utils/threads.h>
 
 #include "include/TimedEventQueue.h"
 
 #include <sys/prctl.h>
 #include <sys/time.h>
+#include <sys/resource.h>
 
 #include <media/stagefright/MediaDebug.h>
 
@@ -206,6 +208,7 @@
     vm->AttachCurrentThread(&env, NULL);
 #endif
 
+    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_FOREGROUND);
     static_cast<TimedEventQueue *>(me)->threadEntry();
 
 #ifdef ANDROID_SIMULATOR
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index ea131e8..c99da59 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -102,7 +102,7 @@
             OMX_IN OMX_U32 nData1,
             OMX_IN OMX_U32 nData2,
             OMX_IN OMX_PTR pEventData);
-        
+
     OMX_ERRORTYPE OnEmptyBufferDone(
             node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
 
@@ -115,20 +115,19 @@
     virtual ~OMX();
 
 private:
-    Mutex mLock;
-
-    OMXMaster *mMaster;
-
     struct CallbackDispatcher;
-    sp<CallbackDispatcher> mDispatcher;
 
+    Mutex mLock;
+    OMXMaster *mMaster;
     int32_t mNodeCounter;
 
     KeyedVector<wp<IBinder>, OMXNodeInstance *> mLiveNodes;
     KeyedVector<node_id, OMXNodeInstance *> mNodeIDToInstance;
+    KeyedVector<node_id, sp<CallbackDispatcher> > mDispatchers;
 
     node_id makeNodeID(OMXNodeInstance *instance);
     OMXNodeInstance *findInstance(node_id node);
+    sp<CallbackDispatcher> findDispatcher(node_id node);
 
     void invalidateNodeID_l(node_id node);
 
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 132e31b..ad5b0f9 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -43,7 +43,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 struct OMX::CallbackDispatcher : public RefBase {
-    CallbackDispatcher(OMX *owner);
+    CallbackDispatcher(OMXNodeInstance *owner);
 
     void post(const omx_message &msg);
 
@@ -53,7 +53,7 @@
 private:
     Mutex mLock;
 
-    OMX *mOwner;
+    OMXNodeInstance *mOwner;
     bool mDone;
     Condition mQueueChanged;
     List<omx_message> mQueue;
@@ -69,7 +69,7 @@
     CallbackDispatcher &operator=(const CallbackDispatcher &);
 };
 
-OMX::CallbackDispatcher::CallbackDispatcher(OMX *owner)
+OMX::CallbackDispatcher::CallbackDispatcher(OMXNodeInstance *owner)
     : mOwner(owner),
       mDone(false) {
     pthread_attr_t attr;
@@ -101,12 +101,11 @@
 }
 
 void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {
-    OMXNodeInstance *instance = mOwner->findInstance(msg.node);
-    if (instance == NULL) {
+    if (mOwner == NULL) {
         LOGV("Would have dispatched a message to a node that's already gone.");
         return;
     }
-    instance->onMessage(msg);
+    mOwner->onMessage(msg);
 }
 
 // static
@@ -145,7 +144,6 @@
 
 OMX::OMX()
     : mMaster(new OMXMaster),
-      mDispatcher(new CallbackDispatcher(this)),
       mNodeCounter(0) {
 }
 
@@ -226,6 +224,7 @@
     }
 
     *node = makeNodeID(instance);
+    mDispatchers.add(*node, new CallbackDispatcher(instance));
 
     instance->setHandle(*node, handle);
 
@@ -341,7 +340,7 @@
     msg.u.event_data.data1 = nData1;
     msg.u.event_data.data2 = nData2;
 
-    mDispatcher->post(msg);
+    findDispatcher(node)->post(msg);
 
     return OMX_ErrorNone;
 }
@@ -355,7 +354,7 @@
     msg.node = node;
     msg.u.buffer_data.buffer = pBuffer;
 
-    mDispatcher->post(msg);
+    findDispatcher(node)->post(msg);
 
     return OMX_ErrorNone;
 }
@@ -375,7 +374,7 @@
     msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
     msg.u.extended_buffer_data.data_ptr = pBuffer->pBuffer;
 
-    mDispatcher->post(msg);
+    findDispatcher(node)->post(msg);
 
     return OMX_ErrorNone;
 }
@@ -397,6 +396,14 @@
     return index < 0 ? NULL : mNodeIDToInstance.valueAt(index);
 }
 
+sp<OMX::CallbackDispatcher> OMX::findDispatcher(node_id node) {
+    Mutex::Autolock autoLock(mLock);
+
+    ssize_t index = mDispatchers.indexOfKey(node);
+
+    return index < 0 ? NULL : mDispatchers.valueAt(index);
+}
+
 void OMX::invalidateNodeID(node_id node) {
     Mutex::Autolock autoLock(mLock);
     invalidateNodeID_l(node);
diff --git a/native/include/android/input.h b/native/include/android/input.h
index ee2f664..193cbf3cb 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -394,6 +394,18 @@
  * in the java.lang.System.nanoTime() time base. */
 int64_t motion_event_get_event_time(const input_event_t* motion_event);
 
+/* Get the X coordinate offset.
+ * For touch events on the screen, this is the delta that was added to the raw
+ * screen coordinates to adjust for the absolute position of the containing windows
+ * and views. */
+float motion_event_get_x_offset(const input_event_t* motion_event);
+
+/* Get the precision of the Y coordinates being reported.
+ * For touch events on the screen, this is the delta that was added to the raw
+ * screen coordinates to adjust for the absolute position of the containing windows
+ * and views. */
+float motion_event_get_y_offset(const input_event_t* motion_event);
+
 /* Get the precision of the X coordinates being reported.
  * You can multiply this number with an X coordinate sample to find the
  * actual hardware value of the X coordinate. */
@@ -414,17 +426,17 @@
  * going up and down since the start of the current gesture. */
 int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index);
 
-/* Get the original raw X coordinate of this event.  For touch
- * events on the screen, this is the original location of the event
+/* Get the original raw X coordinate of this event.
+ * For touch events on the screen, this is the original location of the event
  * on the screen, before it had been adjusted for the containing window
  * and views. */
-float motion_event_get_raw_x(const input_event_t* motion_event);
+float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index);
 
-/* Get the original raw X coordinate of this event.  For touch
- * events on the screen, this is the original location of the event
+/* Get the original raw X coordinate of this event.
+ * For touch events on the screen, this is the original location of the event
  * on the screen, before it had been adjusted for the containing window
  * and views. */
-float motion_event_get_raw_y(const input_event_t* motion_event);
+float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index);
 
 /* Get the current X coordinate of this event for the given pointer index.
  * Whole numbers are pixels; the value may have a fraction for input devices
@@ -461,6 +473,24 @@
 int64_t motion_event_get_historical_event_time(input_event_t* motion_event,
         size_t history_index);
 
+/* Get the historical raw X coordinate of this event for the given pointer index that
+ * occurred between this event and the previous motion event.
+ * For touch events on the screen, this is the original location of the event
+ * on the screen, before it had been adjusted for the containing window
+ * and views.
+ * Whole numbers are pixels; the value may have a fraction for input devices
+ * that are sub-pixel precise. */
+float motion_event_get_historical_raw_x(const input_event_t* motion_event, size_t pointer_index);
+
+/* Get the historical raw Y coordinate of this event for the given pointer index that
+ * occurred between this event and the previous motion event.
+ * For touch events on the screen, this is the original location of the event
+ * on the screen, before it had been adjusted for the containing window
+ * and views.
+ * Whole numbers are pixels; the value may have a fraction for input devices
+ * that are sub-pixel precise. */
+float motion_event_get_historical_raw_y(const input_event_t* motion_event, size_t pointer_index);
+
 /* Get the historical X coordinate of this event for the given pointer index that
  * occurred between this event and the previous motion event.
  * Whole numbers are pixels; the value may have a fraction for input devices
diff --git a/policy/Android.mk b/policy/Android.mk
index a887142..47d8fb8 100644
--- a/policy/Android.mk
+++ b/policy/Android.mk
@@ -4,9 +4,11 @@
 # ============================================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-            $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
             
 LOCAL_MODULE := android.policy
 
 include $(BUILD_JAVA_LIBRARY)
+
+# additionally, build unit tests in a separate .apk
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/policy/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/AccountUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
diff --git a/policy/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/GlobalActions.java
rename to policy/src/com/android/internal/policy/impl/GlobalActions.java
diff --git a/policy/com/android/internal/policy/impl/IconUtilities.java b/policy/src/com/android/internal/policy/impl/IconUtilities.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/IconUtilities.java
rename to policy/src/com/android/internal/policy/impl/IconUtilities.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardScreen.java b/policy/src/com/android/internal/policy/impl/KeyguardScreen.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardScreen.java
rename to policy/src/com/android/internal/policy/impl/KeyguardScreen.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java b/policy/src/com/android/internal/policy/impl/KeyguardScreenCallback.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardScreenCallback.java
rename to policy/src/com/android/internal/policy/impl/KeyguardScreenCallback.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
rename to policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardViewBase.java
rename to policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewCallback.java b/policy/src/com/android/internal/policy/impl/KeyguardViewCallback.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardViewCallback.java
rename to policy/src/com/android/internal/policy/impl/KeyguardViewCallback.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardViewManager.java
rename to policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardViewMediator.java
rename to policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewProperties.java b/policy/src/com/android/internal/policy/impl/KeyguardViewProperties.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardViewProperties.java
rename to policy/src/com/android/internal/policy/impl/KeyguardViewProperties.java
diff --git a/policy/com/android/internal/policy/impl/KeyguardWindowController.java b/policy/src/com/android/internal/policy/impl/KeyguardWindowController.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/KeyguardWindowController.java
rename to policy/src/com/android/internal/policy/impl/KeyguardWindowController.java
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
rename to policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
rename to policy/src/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
diff --git a/policy/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/LockScreen.java
rename to policy/src/com/android/internal/policy/impl/LockScreen.java
diff --git a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
diff --git a/policy/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/PatternUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
diff --git a/policy/com/android/internal/policy/impl/PhoneLayoutInflater.java b/policy/src/com/android/internal/policy/impl/PhoneLayoutInflater.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/PhoneLayoutInflater.java
rename to policy/src/com/android/internal/policy/impl/PhoneLayoutInflater.java
diff --git a/policy/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/PhoneWindow.java
rename to policy/src/com/android/internal/policy/impl/PhoneWindow.java
diff --git a/policy/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/PhoneWindowManager.java
rename to policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
diff --git a/policy/com/android/internal/policy/impl/Policy.java b/policy/src/com/android/internal/policy/impl/Policy.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/Policy.java
rename to policy/src/com/android/internal/policy/impl/Policy.java
diff --git a/policy/com/android/internal/policy/impl/PowerDialog.java b/policy/src/com/android/internal/policy/impl/PowerDialog.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/PowerDialog.java
rename to policy/src/com/android/internal/policy/impl/PowerDialog.java
diff --git a/policy/com/android/internal/policy/impl/RecentApplicationsBackground.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/RecentApplicationsBackground.java
rename to policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java
diff --git a/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/RecentApplicationsDialog.java
rename to policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
diff --git a/policy/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/ShortcutManager.java
rename to policy/src/com/android/internal/policy/impl/ShortcutManager.java
diff --git a/policy/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
similarity index 100%
rename from policy/com/android/internal/policy/impl/SimUnlockScreen.java
rename to policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
diff --git a/policy/com/android/internal/policy/impl/package.html b/policy/src/com/android/internal/policy/impl/package.html
similarity index 100%
rename from policy/com/android/internal/policy/impl/package.html
rename to policy/src/com/android/internal/policy/impl/package.html
diff --git a/common/tests/Android.mk b/policy/tests/Android.mk
similarity index 63%
rename from common/tests/Android.mk
rename to policy/tests/Android.mk
index 74255521..ffb60b1 100644
--- a/common/tests/Android.mk
+++ b/policy/tests/Android.mk
@@ -1,10 +1,10 @@
-# Copyright (C) 2009 The Android Open Source Project
+# Copyright 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
+#     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,
@@ -12,17 +12,18 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH := $(call my-dir)
-
+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
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.policy android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := FrameworkPolicyTests
 
 include $(BUILD_PACKAGE)
+
diff --git a/common/tests/AndroidManifest.xml b/policy/tests/AndroidManifest.xml
similarity index 63%
rename from common/tests/AndroidManifest.xml
rename to policy/tests/AndroidManifest.xml
index 151ec20..dbdabfa 100644
--- a/common/tests/AndroidManifest.xml
+++ b/policy/tests/AndroidManifest.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- 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.
@@ -15,16 +15,17 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.common.tests"
-        android:sharedUserId="com.android.uid.test">
+          package="com.android.frameworks.policy.tests">
 
     <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" />
-
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+    
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.frameworks.policy.tests"
+        android:label="Framework policy tests" />
 </manifest>
diff --git a/tests/framework-tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java b/policy/tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
similarity index 97%
rename from tests/framework-tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
rename to policy/tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
index 1e57bd2..bdfe652 100644
--- a/tests/framework-tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
+++ b/policy/tests/src/com/android/internal/policy/impl/LockPatternKeyguardViewTest.java
@@ -55,8 +55,8 @@
         boolean isLockPatternEnabled = true;
         public boolean isPermanentlyLocked = false;
 
-        public MockLockPatternUtils() {
-            super(null);
+        public MockLockPatternUtils(Context context) {
+            super(context);
         }
 
         @Override
@@ -149,7 +149,7 @@
         @Override
         View createUnlockScreenFor(UnlockMode unlockMode) {
             final MockKeyguardScreen newView = new MockKeyguardScreen(getContext());
-            if (mInjectedUnlockScreens == null)  mInjectedUnlockScreens = Lists.newArrayList();
+            if (mInjectedUnlockScreens == null) mInjectedUnlockScreens = Lists.newArrayList();
             mInjectedUnlockScreens.add(newView);
             return newView;
         }
@@ -197,7 +197,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         mUpdateMonitor = new MockUpdateMonitor(getContext());
-        mLockPatternUtils = new MockLockPatternUtils();
+        mLockPatternUtils = new MockLockPatternUtils(getContext());
 
         mLPKV = new TestableLockPatternKeyguardView(getContext(), mUpdateMonitor,
                 mLockPatternUtils, new KeyguardWindowController() {
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 5cf61bd..e6c32d9 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -210,9 +210,6 @@
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
-        shutdownIfNoPower();
-        shutdownIfOverTemp();
-
         mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
         if (mAcOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
@@ -221,6 +218,19 @@
         } else {
             mPlugType = BATTERY_PLUGGED_NONE;
         }
+        
+        // Let the battery stats keep track of the current level.
+        try {
+            mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
+                    mPlugType, mBatteryLevel, mBatteryTemperature,
+                    mBatteryVoltage);
+        } catch (RemoteException e) {
+            // Should never happen.
+        }
+        
+        shutdownIfNoPower();
+        shutdownIfOverTemp();
+
         if (mBatteryStatus != mLastBatteryStatus ||
                 mBatteryHealth != mLastBatteryHealth ||
                 mBatteryPresent != mLastBatteryPresent ||
@@ -263,16 +273,6 @@
                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
             }
-            if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) {
-                // If the battery level has changed and we are on battery, update the current level.
-                // This is used for discharge cycle tracking so this shouldn't be updated while the
-                // battery is charging.
-                try {
-                    mBatteryStats.recordCurrentLevel(mBatteryLevel);
-                } catch (RemoteException e) {
-                    // Should never happen.
-                }
-            }
             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
                     mPlugType == BATTERY_PLUGGED_NONE) {
                 // We want to make sure we log discharge cycle outliers
@@ -342,11 +342,6 @@
         Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        try {
-            mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel);
-        } catch (RemoteException e) {
-            // Should never happen.
-        }
 
         int icon = getIcon(mBatteryLevel);
 
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 9493161..431cc39 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -44,6 +44,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.text.format.Time;
@@ -64,11 +65,13 @@
 
     private static final int MSG_UPDATE_TWILIGHT = 0;
     private static final int MSG_ENABLE_LOCATION_UPDATES = 1;
+    private static final int MSG_GET_NEW_LOCATION_UPDATE = 2;
 
-    private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS;
+    private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
     private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20;
     private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000;
-    private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 15 * DateUtils.MINUTE_IN_MILLIS;
     private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS;
 
     private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE";
@@ -215,6 +218,21 @@
         }
     };
 
+    private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
+                if (!intent.getBooleanExtra("state", false)) {
+                    // Airplane mode is now off!
+                    mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
+                }
+            } else {
+                // Time zone has changed!
+                mHandler.sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE);
+            }
+        }
+    };
+
     // A LocationListener to initialize the network location provider. The location updates
     // are handled through the passive location provider.
     private final LocationListener mEmptyLocationListener =  new LocationListener() {
@@ -304,6 +322,9 @@
                 new IntentFilter(Intent.ACTION_DOCK_EVENT));
         mContext.registerReceiver(mBatteryReceiver,
                 new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+        IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        mContext.registerReceiver(mUpdateLocationReceiver, filter);
 
         PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
@@ -586,7 +607,9 @@
 
         boolean mPassiveListenerEnabled;
         boolean mNetworkListenerEnabled;
-
+        boolean mDidFirstInit;
+        long mLastNetworkRegisterTime = -MIN_LOCATION_UPDATE_MS;
+        
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -599,6 +622,25 @@
                         }
                     }
                     break;
+                case MSG_GET_NEW_LOCATION_UPDATE:
+                    if (!mNetworkListenerEnabled) {
+                        // Don't do anything -- we are still trying to get a
+                        // location.
+                        return;
+                    }
+                    if ((mLastNetworkRegisterTime+MIN_LOCATION_UPDATE_MS)
+                            >= SystemClock.elapsedRealtime()) {
+                        // Don't do anything -- it hasn't been long enough
+                        // since we last requested an update.
+                        return;
+                    }
+                    
+                    // Unregister the current location monitor, so we can
+                    // register a new one for it to get an immediate update.
+                    mNetworkListenerEnabled = false;
+                    mLocationManager.removeUpdates(mEmptyLocationListener);
+                    
+                    // Fall through to re-register listener.
                 case MSG_ENABLE_LOCATION_UPDATES:
                     // enable network provider to receive at least location updates for a given
                     // distance.
@@ -613,17 +655,21 @@
                     }
                     if (!mNetworkListenerEnabled && networkLocationEnabled) {
                         mNetworkListenerEnabled = true;
+                        mLastNetworkRegisterTime = SystemClock.elapsedRealtime();
                         mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                                 LOCATION_UPDATE_MS, 0, mEmptyLocationListener);
 
-                        if (mLocation == null) {
-                            retrieveLocation();
-                        }
-                        synchronized (mLock) {
-                            if (isDoingNightMode() && mLocation != null
-                                    && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                                updateTwilightLocked();
-                                updateLocked(0, 0);
+                        if (!mDidFirstInit) {
+                            mDidFirstInit = true;
+                            if (mLocation == null) {
+                                retrieveLocation();
+                            }
+                            synchronized (mLock) {
+                                if (isDoingNightMode() && mLocation != null
+                                        && mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+                                    updateTwilightLocked();
+                                    updateLocked(0, 0);
+                                }
                             }
                         }
                     }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 8d6ad93..46a68ac 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -679,9 +679,9 @@
             /* Configuration changed on a running access point */
             if(enable && (wifiConfig != null)) {
                 try {
-                    persistApConfiguration(wifiConfig);
                     nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
                                              SOFTAP_IFACE);
+                    persistApConfiguration(wifiConfig);
                     return true;
                 } catch(Exception e) {
                     Slog.e(TAG, "Exception in nwService during AP restart");
@@ -717,7 +717,6 @@
                 wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
                 wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
             }
-            persistApConfiguration(wifiConfig);
 
             if (!mWifiStateTracker.loadDriver()) {
                 Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
@@ -734,6 +733,8 @@
                 return false;
             }
 
+            persistApConfiguration(wifiConfig);
+
         } else {
 
             try {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0946ba2..4d18191 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -97,6 +97,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -647,6 +648,15 @@
             = new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
 
     /**
+     * Fingerprints (String.hashCode()) of stack traces that we've
+     * already logged DropBox entries for.  Guarded by itself.  If
+     * something (rogue user app) forces this over
+     * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
+     */
+    private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
+    private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
+
+    /**
      * Intent broadcast that we have tried to start, but are
      * waiting for its application's process to be created.  We only
      * need one (instead of a list) because we always process broadcasts
@@ -9354,27 +9364,53 @@
     }
 
     public void handleApplicationStrictModeViolation(
-        IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
+        IBinder app, int violationMask, ApplicationErrorReport.CrashInfo crashInfo) {
         ProcessRecord r = findAppProcess(app);
-        // TODO: implement
-        Log.w(TAG, "handleApplicationStrictModeViolation.");
 
-        AppErrorResult result = new AppErrorResult();
-        synchronized (this) {
-            final long origId = Binder.clearCallingIdentity();
-
-            Message msg = Message.obtain();
-            msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
-            HashMap<String, Object> data = new HashMap<String, Object>();
-            data.put("result", result);
-            data.put("app", r);
-            msg.obj = data;
-            mHandler.sendMessage(msg);
-
-            Binder.restoreCallingIdentity(origId);
+        if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
+            Integer stackFingerprint = crashInfo.stackTrace.hashCode();
+            boolean logIt = true;
+            synchronized (mAlreadyLoggedViolatedStacks) {
+                if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
+                    logIt = false;
+                    // TODO: sub-sample into EventLog for these, with
+                    // the crashInfo.durationMillis?  Then we'd get
+                    // the relative pain numbers, without logging all
+                    // the stack traces repeatedly.  We'd want to do
+                    // likewise in the client code, which also does
+                    // dup suppression, before the Binder call.
+                } else {
+                    if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {
+                        mAlreadyLoggedViolatedStacks.clear();
+                    }
+                    mAlreadyLoggedViolatedStacks.add(stackFingerprint);
+                }
+            }
+            if (logIt) {
+                addErrorToDropBox("strictmode", r, null, null, null, null, null, crashInfo);
+            }
         }
-        int res = result.get();
-        Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+
+        if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
+            AppErrorResult result = new AppErrorResult();
+            synchronized (this) {
+                final long origId = Binder.clearCallingIdentity();
+
+                Message msg = Message.obtain();
+                msg.what = SHOW_STRICT_MODE_VIOLATION_MSG;
+                HashMap<String, Object> data = new HashMap<String, Object>();
+                data.put("result", result);
+                data.put("app", r);
+                data.put("violationMask", violationMask);
+                data.put("crashInfo", crashInfo);
+                msg.obj = data;
+                mHandler.sendMessage(msg);
+
+                Binder.restoreCallingIdentity(origId);
+            }
+            int res = result.get();
+            Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+        }
     }
 
     /**
@@ -9502,6 +9538,9 @@
             sb.append("Subject: ").append(subject).append("\n");
         }
         sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+        if (crashInfo != null && crashInfo.durationMillis != -1) {
+            sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n");
+        }
         sb.append("\n");
 
         // Do the rest in a worker thread to avoid blocking the caller on I/O
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index 3a1aad6..a769c05 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -80,9 +80,6 @@
                 DISMISS_TIMEOUT);
     }
 
-    public void onStop() {
-    }
-
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             synchronized (mProc) {
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index ba13c51..37da6f7 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -59,7 +59,7 @@
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
         synchronized (mStats) {
-            mStats.writeLocked();
+            mStats.shutdownLocked();
         }
     }
     
@@ -96,7 +96,6 @@
     public void noteStartWakelock(int uid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            Slog.i("battery", "Start wake lock: " + uid + " " + name);
             mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
         }
     }
@@ -104,7 +103,6 @@
     public void noteStopWakelock(int uid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            Slog.i("battery", "Stop wake lock: " + uid + " " + name);
             mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
         }
     }
@@ -126,14 +124,14 @@
     public void noteStartGps(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStartGps(uid);
+            mStats.noteStartGpsLocked(uid);
         }
     }
     
     public void noteStopGps(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStopGps(uid);
+            mStats.noteStopGpsLocked(uid);
         }
     }
         
@@ -323,14 +321,10 @@
         return mStats.isOnBattery();
     }
     
-    public void setOnBattery(boolean onBattery, int level) {
+    public void setBatteryState(int status, int health, int plugType, int level,
+            int temp, int volt) {
         enforceCallingPermission();
-        mStats.setOnBattery(onBattery, level);
-    }
-    
-    public void recordCurrentLevel(int level) {
-        enforceCallingPermission();
-        mStats.recordCurrentLevel(level);
+        mStats.setBatteryState(status, health, plugType, level, temp, volt);
     }
     
     public long getAwakeTimeBattery() {
@@ -361,7 +355,8 @@
                 for (String arg : args) {
                     if ("--checkin".equals(arg)) {
                         isCheckin = true;
-                        break;
+                    } else if ("--reset".equals(arg)) {
+                        mStats.resetAllStatsLocked();
                     }
                 }
             }
diff --git a/services/java/com/android/server/am/StrictModeViolationDialog.java b/services/java/com/android/server/am/StrictModeViolationDialog.java
index f4329e2..fe76d18 100644
--- a/services/java/com/android/server/am/StrictModeViolationDialog.java
+++ b/services/java/com/android/server/am/StrictModeViolationDialog.java
@@ -81,9 +81,6 @@
                 DISMISS_TIMEOUT);
     }
 
-    public void onStop() {
-    }
-
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             synchronized (mProc) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 53262ae..ab3922f 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -16,9 +16,20 @@
 
 #define LOG_TAG "InputManager-JNI"
 
+//#define LOG_NDEBUG 0
+
+// Log debug messages about InputReaderPolicy
+#define DEBUG_INPUT_READER_POLICY 1
+
+// Log debug messages about InputDispatcherPolicy
+#define DEBUG_INPUT_DISPATCHER_POLICY 1
+
+
 #include "JNIHelp.h"
 #include "jni.h"
 #include <android_runtime/AndroidRuntime.h>
+#include <ui/InputReader.h>
+#include <ui/InputDispatcher.h>
 #include <ui/InputManager.h>
 #include <ui/InputTransport.h>
 #include <utils/Log.h>
@@ -30,73 +41,7 @@
 
 namespace android {
 
-class InputDispatchPolicy : public InputDispatchPolicyInterface {
-public:
-    InputDispatchPolicy(JNIEnv* env, jobject callbacks);
-    virtual ~InputDispatchPolicy();
-
-    void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
-    void setDisplayOrientation(int32_t displayId, int32_t orientation);
-
-    virtual bool getDisplayInfo(int32_t displayId,
-            int32_t* width, int32_t* height, int32_t* orientation);
-
-    virtual void notifyConfigurationChanged(nsecs_t when,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig);
-
-    virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen);
-
-    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);
-
-    virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
-            bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
-    virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
-            bool rolled);
-    virtual int32_t interceptTouch(nsecs_t when);
-
-    virtual bool filterTouchEvents();
-    virtual bool filterJumpyTouchEvents();
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
-
-    virtual bool allowKeyRepeat();
-    virtual nsecs_t getKeyRepeatTimeout();
-
-    virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets);
-    virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets);
-
-private:
-    bool isScreenOn();
-    bool isScreenBright();
-
-private:
-    jobject mCallbacks;
-
-    int32_t mFilterTouchEvents;
-    int32_t mFilterJumpyTouchEvents;
-
-    Mutex mDisplayLock;
-    int32_t mDisplayWidth, mDisplayHeight;
-    int32_t mDisplayOrientation;
-
-    inline JNIEnv* threadEnv() const {
-        return AndroidRuntime::getJNIEnv();
-    }
-};
-
-
-// globals
-
-static sp<EventHub> gEventHub;
-static sp<InputDispatchPolicy> gInputDispatchPolicy;
-static sp<InputManager> gInputManager;
-
-// JNI
+// ----------------------------------------------------------------------------
 
 static struct {
     jclass clazz;
@@ -128,8 +73,508 @@
     jfieldID height;
 } gVirtualKeyDefinitionClassInfo;
 
+// ----------------------------------------------------------------------------
+
+class NativeInputManager : public virtual RefBase,
+    public virtual InputReaderPolicyInterface,
+    public virtual InputDispatcherPolicyInterface {
+protected:
+    virtual ~NativeInputManager();
+
+public:
+    NativeInputManager(jobject callbacksObj);
+
+    inline sp<InputManager> getInputManager() const { return mInputManager; }
+
+    void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
+    void setDisplayOrientation(int32_t displayId, int32_t orientation);
+
+    /* --- InputReaderPolicyInterface implementation --- */
+
+    virtual bool getDisplayInfo(int32_t displayId,
+            int32_t* width, int32_t* height, int32_t* orientation);
+    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);
+    virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
+            bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
+    virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
+            bool rolled);
+    virtual int32_t interceptTouch(nsecs_t when);
+    virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
+    virtual bool filterTouchEvents();
+    virtual bool filterJumpyTouchEvents();
+    virtual void getVirtualKeyDefinitions(const String8& deviceName,
+            Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions);
+    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
+
+    /* --- InputDispatcherPolicyInterface implementation --- */
+
+    virtual void notifyConfigurationChanged(nsecs_t when);
+    virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
+    virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel);
+    virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
+    virtual nsecs_t getKeyRepeatTimeout();
+    virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+            Vector<InputTarget>& outTargets);
+    virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            Vector<InputTarget>& outTargets);
+
+private:
+    sp<InputManager> mInputManager;
+
+    jobject mCallbacksObj;
+
+    // Cached filtering policies.
+    int32_t mFilterTouchEvents;
+    int32_t mFilterJumpyTouchEvents;
+
+    // Cached display state.  (lock mDisplayLock)
+    Mutex mDisplayLock;
+    int32_t mDisplayWidth, mDisplayHeight;
+    int32_t mDisplayOrientation;
+
+    // Callbacks.
+    bool isScreenOn();
+    bool isScreenBright();
+
+    static inline JNIEnv* jniEnv() {
+        return AndroidRuntime::getJNIEnv();
+    }
+
+    static bool isAppSwitchKey(int32_t keyCode);
+    static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName);
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::NativeInputManager(jobject callbacksObj) :
+    mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
+    JNIEnv* env = jniEnv();
+
+    mCallbacksObj = env->NewGlobalRef(callbacksObj);
+
+    sp<EventHub> eventHub = new EventHub();
+    mInputManager = new InputManager(eventHub, this, this);
+}
+
+NativeInputManager::~NativeInputManager() {
+    JNIEnv* env = jniEnv();
+
+    env->DeleteGlobalRef(mCallbacksObj);
+}
+
+bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
+    return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
+}
+
+bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+        return true;
+    }
+    return false;
+}
+
+void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
+    if (displayId == 0) {
+        AutoMutex _l(mDisplayLock);
+
+        mDisplayWidth = width;
+        mDisplayHeight = height;
+    }
+}
+
+void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orientation) {
+    if (displayId == 0) {
+        AutoMutex _l(mDisplayLock);
+
+        mDisplayOrientation = orientation;
+    }
+}
+
+bool NativeInputManager::getDisplayInfo(int32_t displayId,
+        int32_t* width, int32_t* height, int32_t* orientation) {
+    bool result = false;
+    if (displayId == 0) {
+        AutoMutex _l(mDisplayLock);
+
+        if (mDisplayWidth > 0) {
+            *width = mDisplayWidth;
+            *height = mDisplayHeight;
+            *orientation = mDisplayOrientation;
+            result = true;
+        }
+    }
+    return result;
+}
+
+bool NativeInputManager::isScreenOn() {
+    JNIEnv* env = jniEnv();
+
+    jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn);
+    if (checkExceptionFromCallback(env, "isScreenOn")) {
+        return true;
+    }
+    return result;
+}
+
+bool NativeInputManager::isScreenBright() {
+    JNIEnv* env = jniEnv();
+
+    jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
+    if (checkExceptionFromCallback(env, "isScreenBright")) {
+        return true;
+    }
+    return result;
+}
+
+void NativeInputManager::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) {
+#if DEBUG_INPUT_READER_POLICY
+    LOGD("virtualKeyFeedback - when=%lld, deviceId=%d, action=%d, flags=%d, keyCode=%d, "
+            "scanCode=%d, metaState=%d, downTime=%lld",
+            when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback,
+            when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
+    checkExceptionFromCallback(env, "virtualKeyFeedback");
+}
+
+int32_t NativeInputManager::interceptKey(nsecs_t when,
+        int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
+#if DEBUG_INPUT_READER_POLICY
+    LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
+            "policyFlags=%d",
+            when, deviceId, down, keyCode, scanCode, policyFlags);
+#endif
+
+    const int32_t WM_ACTION_PASS_TO_USER = 1;
+    const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
+    const int32_t WM_ACTION_GO_TO_SLEEP = 4;
+
+    JNIEnv* env = jniEnv();
+
+    bool isScreenOn = this->isScreenOn();
+    bool isScreenBright = this->isScreenBright();
+
+    jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
+            deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
+    if (checkExceptionFromCallback(env, "hackInterceptKey")) {
+        wmActions = 0;
+    }
+
+    int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
+    if (! isScreenOn) {
+        // Key presses and releases wake the device.
+        actions |= InputReaderPolicyInterface::ACTION_WOKE_HERE;
+    }
+
+    if (! isScreenBright) {
+        // Key presses and releases brighten the screen if dimmed.
+        actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+    }
+
+    if (wmActions & WM_ACTION_GO_TO_SLEEP) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when);
+        checkExceptionFromCallback(env, "goToSleep");
+    }
+
+    if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
+        checkExceptionFromCallback(env, "pokeUserActivityForKey");
+    }
+
+    if (wmActions & WM_ACTION_PASS_TO_USER) {
+        actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+
+        if (down && isAppSwitchKey(keyCode)) {
+            env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
+            checkExceptionFromCallback(env, "notifyAppSwitchComing");
+
+            actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
+        }
+    }
+    return actions;
+}
+
+int32_t NativeInputManager::interceptTouch(nsecs_t when) {
+#if DEBUG_INPUT_READER_POLICY
+    LOGD("interceptTouch - when=%lld", when);
+#endif
+
+    int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
+    if (isScreenOn()) {
+        // Only dispatch touch events when the device is awake.
+        actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+    }
+
+    if (! isScreenBright()) {
+        // Brighten the screen if dimmed.
+        actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+    }
+
+    return actions;
+}
+
+int32_t NativeInputManager::interceptTrackball(nsecs_t when,
+        bool buttonChanged, bool buttonDown, bool rolled) {
+#if DEBUG_INPUT_READER_POLICY
+    LOGD("interceptTrackball - when=%lld, buttonChanged=%d, buttonDown=%d, rolled=%d",
+            when, buttonChanged, buttonDown, rolled);
+#endif
+
+    int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
+    if (isScreenOn()) {
+        // Only dispatch trackball events when the device is awake.
+        actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+    }
+
+    if (! isScreenBright()) {
+        // Brighten the screen if dimmed.
+        actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+    }
+
+    return actions;
+}
+
+int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
+        int32_t switchValue) {
+#if DEBUG_INPUT_READER_POLICY
+    LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d",
+            when, switchCode, switchValue);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    switch (switchCode) {
+    case SW_LID:
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
+                when, switchValue == 0);
+        checkExceptionFromCallback(env, "notifyLidSwitchChanged");
+        break;
+    }
+
+    return InputReaderPolicyInterface::ACTION_NONE;
+}
+
+bool NativeInputManager::filterTouchEvents() {
+    if (mFilterTouchEvents < 0) {
+        JNIEnv* env = jniEnv();
+
+        jboolean result = env->CallBooleanMethod(mCallbacksObj,
+                gCallbacksClassInfo.filterTouchEvents);
+        if (checkExceptionFromCallback(env, "filterTouchEvents")) {
+            result = false;
+        }
+
+        mFilterTouchEvents = result ? 1 : 0;
+    }
+    return mFilterTouchEvents;
+}
+
+bool NativeInputManager::filterJumpyTouchEvents() {
+    if (mFilterJumpyTouchEvents < 0) {
+        JNIEnv* env = jniEnv();
+
+        jboolean result = env->CallBooleanMethod(mCallbacksObj,
+                gCallbacksClassInfo.filterJumpyTouchEvents);
+        if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) {
+            result = false;
+        }
+
+        mFilterJumpyTouchEvents = result ? 1 : 0;
+    }
+    return mFilterJumpyTouchEvents;
+}
+
+void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
+        Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions) {
+    JNIEnv* env = jniEnv();
+
+    jstring deviceNameStr = env->NewStringUTF(deviceName.string());
+    if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
+        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+                gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
+        if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
+            jsize length = env->GetArrayLength(result);
+            for (jsize i = 0; i < length; i++) {
+                jobject item = env->GetObjectArrayElement(result, i);
+
+                outVirtualKeyDefinitions.add();
+                outVirtualKeyDefinitions.editTop().scanCode =
+                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
+                outVirtualKeyDefinitions.editTop().centerX =
+                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
+                outVirtualKeyDefinitions.editTop().centerY =
+                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
+                outVirtualKeyDefinitions.editTop().width =
+                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
+                outVirtualKeyDefinitions.editTop().height =
+                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
+
+                env->DeleteLocalRef(item);
+            }
+            env->DeleteLocalRef(result);
+        }
+        env->DeleteLocalRef(deviceNameStr);
+    }
+}
+
+void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
+    JNIEnv* env = jniEnv();
+
+    jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+            gCallbacksClassInfo.getExcludedDeviceNames));
+    if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
+        jsize length = env->GetArrayLength(result);
+        for (jsize i = 0; i < length; i++) {
+            jstring item = jstring(env->GetObjectArrayElement(result, i));
+
+            const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
+            outExcludedDeviceNames.add(String8(deviceNameChars));
+            env->ReleaseStringUTFChars(item, deviceNameChars);
+
+            env->DeleteLocalRef(item);
+        }
+        env->DeleteLocalRef(result);
+    }
+}
+
+void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyConfigurationChanged - when=%lld", when);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    InputConfiguration config;
+    mInputManager->getInputConfiguration(& config);
+
+    env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
+            when, config.touchScreen, config.keyboard, config.navigation);
+    checkExceptionFromCallback(env, "notifyConfigurationChanged");
+}
+
+void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
+#endif
+
+    // TODO
+}
+
+void NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyInputChannelANR - inputChannel='%s'",
+            inputChannel->getName().string());
+#endif
+
+    // TODO
+}
+
+void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
+            inputChannel->getName().string());
+#endif
+
+    // TODO
+}
+
+nsecs_t NativeInputManager::getKeyRepeatTimeout() {
+    if (! isScreenOn()) {
+        // Disable key repeat when the screen is off.
+        return -1;
+    } else {
+        // TODO use ViewConfiguration.getLongPressTimeout()
+        return milliseconds_to_nanoseconds(500);
+    }
+}
+
+void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+        Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("getKeyEventTargets - policyFlags=%d", policyFlags);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
+    if (! keyEventObj) {
+        LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
+    } else {
+        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+                gCallbacksClassInfo.getKeyEventTargets,
+                keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
+        if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) {
+            jsize length = env->GetArrayLength(result);
+            for (jsize i = 0; i < length; i++) {
+                jobject item = env->GetObjectArrayElement(result, i);
+                if (! item) {
+                    break; // found null element indicating end of used portion of the array
+                }
+
+                outTargets.add();
+                android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+
+                env->DeleteLocalRef(item);
+            }
+            env->DeleteLocalRef(result);
+        }
+        env->DeleteLocalRef(keyEventObj);
+    }
+}
+
+void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+        Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("getMotionEventTargets - policyFlags=%d", policyFlags);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
+    if (! motionEventObj) {
+        LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
+    } else {
+        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+                gCallbacksClassInfo.getMotionEventTargets,
+                motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
+        if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) {
+            jsize length = env->GetArrayLength(result);
+            for (jsize i = 0; i < length; i++) {
+                jobject item = env->GetObjectArrayElement(result, i);
+                if (! item) {
+                    break; // found null element indicating end of used portion of the array
+                }
+
+                outTargets.add();
+                android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+
+                env->DeleteLocalRef(item);
+            }
+            env->DeleteLocalRef(result);
+        }
+        android_view_MotionEvent_recycle(env, motionEventObj);
+        env->DeleteLocalRef(motionEventObj);
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+
+static sp<NativeInputManager> gNativeInputManager;
+
 static bool checkInputManagerUnitialized(JNIEnv* env) {
-    if (gInputManager == NULL) {
+    if (gNativeInputManager == NULL) {
         LOGE("Input manager not initialized.");
         jniThrowRuntimeException(env, "Input manager not initialized.");
         return true;
@@ -139,16 +584,11 @@
 
 static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
         jobject callbacks) {
-    if (gEventHub == NULL) {
-        gEventHub = new EventHub();
-    }
-
-    if (gInputDispatchPolicy == NULL) {
-        gInputDispatchPolicy = new InputDispatchPolicy(env, callbacks);
-    }
-
-    if (gInputManager == NULL) {
-        gInputManager = new InputManager(gEventHub, gInputDispatchPolicy);
+    if (gNativeInputManager == NULL) {
+        gNativeInputManager = new NativeInputManager(callbacks);
+    } else {
+        LOGE("Input manager already initialized.");
+        jniThrowRuntimeException(env, "Input manager already initialized.");
     }
 }
 
@@ -157,7 +597,7 @@
         return;
     }
 
-    status_t result = gInputManager->start();
+    status_t result = gNativeInputManager->getInputManager()->start();
     if (result) {
         jniThrowRuntimeException(env, "Input manager could not be started.");
     }
@@ -173,7 +613,7 @@
     // to be passed in like this, not sure which is better but leaving it like this
     // keeps the window manager in direct control of when display transitions propagate down
     // to the input dispatcher
-    gInputDispatchPolicy->setDisplaySize(displayId, width, height);
+    gNativeInputManager->setDisplaySize(displayId, width, height);
 }
 
 static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
@@ -182,7 +622,7 @@
         return;
     }
 
-    gInputDispatchPolicy->setDisplayOrientation(displayId, orientation);
+    gNativeInputManager->setDisplayOrientation(displayId, orientation);
 }
 
 static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
@@ -191,7 +631,8 @@
         return KEY_STATE_UNKNOWN;
     }
 
-    return gInputManager->getScanCodeState(deviceId, deviceClasses, scanCode);
+    return gNativeInputManager->getInputManager()->getScanCodeState(
+            deviceId, deviceClasses, scanCode);
 }
 
 static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
@@ -200,7 +641,8 @@
         return KEY_STATE_UNKNOWN;
     }
 
-    return gInputManager->getKeyCodeState(deviceId, deviceClasses, keyCode);
+    return gNativeInputManager->getInputManager()->getKeyCodeState(
+            deviceId, deviceClasses, keyCode);
 }
 
 static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
@@ -209,7 +651,7 @@
         return KEY_STATE_UNKNOWN;
     }
 
-    return gInputManager->getSwitchState(deviceId, deviceClasses, sw);
+    return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw);
 }
 
 static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
@@ -223,7 +665,7 @@
     jsize numCodes = env->GetArrayLength(keyCodes);
     jboolean result;
     if (numCodes == env->GetArrayLength(outFlags)) {
-        result = gInputManager->hasKeys(numCodes, codes, flags);
+        result = gNativeInputManager->getInputManager()->hasKeys(numCodes, codes, flags);
     } else {
         result = JNI_FALSE;
     }
@@ -243,7 +685,9 @@
     LOGW("Input channel object '%s' was disposed without first being unregistered with "
             "the input manager!", inputChannel->getName().string());
 
-    gInputManager->unregisterInputChannel(inputChannel);
+    if (gNativeInputManager != NULL) {
+        gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
+    }
 }
 
 static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
@@ -259,7 +703,7 @@
         return;
     }
 
-    status_t status = gInputManager->registerInputChannel(inputChannel);
+    status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel);
     if (status) {
         jniThrowRuntimeException(env, "Failed to register input channel.  "
                 "Check logs for details.");
@@ -285,13 +729,15 @@
 
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
 
-    status_t status = gInputManager->unregisterInputChannel(inputChannel);
+    status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
     if (status) {
         jniThrowRuntimeException(env, "Failed to unregister input channel.  "
                 "Check logs for details.");
     }
 }
 
+// ----------------------------------------------------------------------------
+
 static JNINativeMethod gInputManagerMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
@@ -334,7 +780,7 @@
             gInputManagerMethods, NELEM(gInputManagerMethods));
     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
 
-    // Policy
+    // Callbacks
 
     FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
 
@@ -407,340 +853,4 @@
     return 0;
 }
 
-// static functions
-
-static bool isAppSwitchKey(int32_t keyCode) {
-    return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
-}
-
-static bool checkException(JNIEnv* env, const char* methodName) {
-    if (env->ExceptionCheck()) {
-        LOGE("An exception was thrown by an InputDispatchPolicy callback '%s'.", methodName);
-        LOGE_EX(env);
-        env->ExceptionClear();
-        return true;
-    }
-    return false;
-}
-
-
-// InputDispatchPolicy implementation
-
-InputDispatchPolicy::InputDispatchPolicy(JNIEnv* env, jobject callbacks) :
-        mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
-        mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
-    mCallbacks = env->NewGlobalRef(callbacks);
-}
-
-InputDispatchPolicy::~InputDispatchPolicy() {
-    JNIEnv* env = threadEnv();
-
-    env->DeleteGlobalRef(mCallbacks);
-}
-
-void InputDispatchPolicy::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
-    if (displayId == 0) {
-        AutoMutex _l(mDisplayLock);
-
-        mDisplayWidth = width;
-        mDisplayHeight = height;
-    }
-}
-
-void InputDispatchPolicy::setDisplayOrientation(int32_t displayId, int32_t orientation) {
-    if (displayId == 0) {
-        AutoMutex _l(mDisplayLock);
-
-        mDisplayOrientation = orientation;
-    }
-}
-
-bool InputDispatchPolicy::getDisplayInfo(int32_t displayId,
-        int32_t* width, int32_t* height, int32_t* orientation) {
-    bool result = false;
-    if (displayId == 0) {
-        AutoMutex _l(mDisplayLock);
-
-        if (mDisplayWidth > 0) {
-            *width = mDisplayWidth;
-            *height = mDisplayHeight;
-            *orientation = mDisplayOrientation;
-            result = true;
-        }
-    }
-    return result;
-}
-
-bool InputDispatchPolicy::isScreenOn() {
-    JNIEnv* env = threadEnv();
-
-    jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenOn);
-    if (checkException(env, "isScreenOn")) {
-        return true;
-    }
-    return result;
-}
-
-bool InputDispatchPolicy::isScreenBright() {
-    JNIEnv* env = threadEnv();
-
-    jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenBright);
-    if (checkException(env, "isScreenBright")) {
-        return true;
-    }
-    return result;
-}
-
-void InputDispatchPolicy::notifyConfigurationChanged(nsecs_t when,
-        int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) {
-    JNIEnv* env = threadEnv();
-
-    env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyConfigurationChanged,
-            when, touchScreenConfig, keyboardConfig, navigationConfig);
-    checkException(env, "notifyConfigurationChanged");
-}
-
-void InputDispatchPolicy::notifyLidSwitchChanged(nsecs_t when, bool lidOpen) {
-    JNIEnv* env = threadEnv();
-    env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyLidSwitchChanged,
-            when, lidOpen);
-    checkException(env, "notifyLidSwitchChanged");
-}
-
-void InputDispatchPolicy::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) {
-    JNIEnv* env = threadEnv();
-
-    env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.virtualKeyFeedback,
-            when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
-    checkException(env, "virtualKeyFeedback");
-}
-
-int32_t InputDispatchPolicy::interceptKey(nsecs_t when,
-        int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
-    const int32_t WM_ACTION_PASS_TO_USER = 1;
-    const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
-    const int32_t WM_ACTION_GO_TO_SLEEP = 4;
-
-    JNIEnv* env = threadEnv();
-
-    bool isScreenOn = this->isScreenOn();
-    bool isScreenBright = this->isScreenBright();
-
-    jint wmActions = env->CallIntMethod(mCallbacks, gCallbacksClassInfo.hackInterceptKey,
-            deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
-    if (checkException(env, "hackInterceptKey")) {
-        wmActions = 0;
-    }
-
-    int32_t actions = ACTION_NONE;
-    if (! isScreenOn) {
-        // Key presses and releases wake the device.
-        actions |= ACTION_WOKE_HERE;
-    }
-
-    if (! isScreenBright) {
-        // Key presses and releases brighten the screen if dimmed.
-        actions |= ACTION_BRIGHT_HERE;
-    }
-
-    if (wmActions & WM_ACTION_GO_TO_SLEEP) {
-        env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.goToSleep, when);
-        checkException(env, "goToSleep");
-    }
-
-    if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-        env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.pokeUserActivityForKey, when);
-        checkException(env, "pokeUserActivityForKey");
-    }
-
-    if (wmActions & WM_ACTION_PASS_TO_USER) {
-        actions |= ACTION_DISPATCH;
-    }
-
-    if (! (wmActions & WM_ACTION_PASS_TO_USER)) {
-        if (down && isAppSwitchKey(keyCode)) {
-            env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyAppSwitchComing);
-            checkException(env, "notifyAppSwitchComing");
-
-            actions |= ACTION_APP_SWITCH_COMING;
-        }
-    }
-    return actions;
-}
-
-int32_t InputDispatchPolicy::interceptTouch(nsecs_t when) {
-    if (! isScreenOn()) {
-        // Touch events do not wake the device.
-        return ACTION_NONE;
-    }
-
-    return ACTION_DISPATCH;
-}
-
-int32_t InputDispatchPolicy::interceptTrackball(nsecs_t when,
-        bool buttonChanged, bool buttonDown, bool rolled) {
-    if (! isScreenOn()) {
-        // Trackball motions and button presses do not wake the device.
-        return ACTION_NONE;
-    }
-
-    return ACTION_DISPATCH;
-}
-
-bool InputDispatchPolicy::filterTouchEvents() {
-    if (mFilterTouchEvents < 0) {
-        JNIEnv* env = threadEnv();
-
-        jboolean result = env->CallBooleanMethod(mCallbacks,
-                gCallbacksClassInfo.filterTouchEvents);
-        if (checkException(env, "filterTouchEvents")) {
-            result = false;
-        }
-
-        mFilterTouchEvents = result ? 1 : 0;
-    }
-    return mFilterTouchEvents;
-}
-
-bool InputDispatchPolicy::filterJumpyTouchEvents() {
-    if (mFilterJumpyTouchEvents < 0) {
-        JNIEnv* env = threadEnv();
-
-        jboolean result = env->CallBooleanMethod(mCallbacks,
-                gCallbacksClassInfo.filterJumpyTouchEvents);
-        if (checkException(env, "filterJumpyTouchEvents")) {
-            result = false;
-        }
-
-        mFilterJumpyTouchEvents = result ? 1 : 0;
-    }
-    return mFilterJumpyTouchEvents;
-}
-
-void InputDispatchPolicy::getVirtualKeyDefinitions(const String8& deviceName,
-        Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
-    JNIEnv* env = threadEnv();
-
-    jstring deviceNameStr = env->NewStringUTF(deviceName.string());
-    if (! checkException(env, "getVirtualKeyDefinitions")) {
-        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
-                gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
-        if (! checkException(env, "getVirtualKeyDefinitions") && result) {
-            jsize length = env->GetArrayLength(result);
-            for (jsize i = 0; i < length; i++) {
-                jobject item = env->GetObjectArrayElement(result, i);
-
-                outVirtualKeyDefinitions.add();
-                outVirtualKeyDefinitions.editTop().scanCode =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
-                outVirtualKeyDefinitions.editTop().centerX =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
-                outVirtualKeyDefinitions.editTop().centerY =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
-                outVirtualKeyDefinitions.editTop().width =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
-                outVirtualKeyDefinitions.editTop().height =
-                        int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
-
-                env->DeleteLocalRef(item);
-            }
-            env->DeleteLocalRef(result);
-        }
-        env->DeleteLocalRef(deviceNameStr);
-    }
-}
-
-void InputDispatchPolicy::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
-    JNIEnv* env = threadEnv();
-
-    jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
-            gCallbacksClassInfo.getExcludedDeviceNames));
-    if (! checkException(env, "getExcludedDeviceNames") && result) {
-        jsize length = env->GetArrayLength(result);
-        for (jsize i = 0; i < length; i++) {
-            jstring item = jstring(env->GetObjectArrayElement(result, i));
-
-            const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
-            outExcludedDeviceNames.add(String8(deviceNameChars));
-            env->ReleaseStringUTFChars(item, deviceNameChars);
-
-            env->DeleteLocalRef(item);
-        }
-        env->DeleteLocalRef(result);
-    }
-}
-
-bool InputDispatchPolicy::allowKeyRepeat() {
-    // Disable key repeat when the screen is off.
-    return isScreenOn();
-}
-
-nsecs_t InputDispatchPolicy::getKeyRepeatTimeout() {
-    // TODO use ViewConfiguration.getLongPressTimeout()
-    return milliseconds_to_nanoseconds(500);
-}
-
-void InputDispatchPolicy::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-        Vector<InputTarget>& outTargets) {
-    JNIEnv* env = threadEnv();
-
-    jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
-    if (! keyEventObj) {
-        LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
-    } else {
-        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
-                gCallbacksClassInfo.getKeyEventTargets,
-                keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
-        if (! checkException(env, "getKeyEventTargets") && result) {
-            jsize length = env->GetArrayLength(result);
-            for (jsize i = 0; i < length; i++) {
-                jobject item = env->GetObjectArrayElement(result, i);
-                if (! item) {
-                    break; // found null element indicating end of used portion of the array
-                }
-
-                outTargets.add();
-                android_view_InputTarget_toNative(env, item, & outTargets.editTop());
-
-                env->DeleteLocalRef(item);
-            }
-            env->DeleteLocalRef(result);
-        }
-        env->DeleteLocalRef(keyEventObj);
-    }
-}
-
-void InputDispatchPolicy::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-        Vector<InputTarget>& outTargets) {
-    JNIEnv* env = threadEnv();
-
-    jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
-    if (! motionEventObj) {
-        LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
-    } else {
-        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
-                gCallbacksClassInfo.getMotionEventTargets,
-                motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
-        if (! checkException(env, "getMotionEventTargets") && result) {
-            jsize length = env->GetArrayLength(result);
-            for (jsize i = 0; i < length; i++) {
-                jobject item = env->GetObjectArrayElement(result, i);
-                if (! item) {
-                    break; // found null element indicating end of used portion of the array
-                }
-
-                outTargets.add();
-                android_view_InputTarget_toNative(env, item, & outTargets.editTop());
-
-                env->DeleteLocalRef(item);
-            }
-            env->DeleteLocalRef(result);
-        }
-        android_view_MotionEvent_recycle(env, motionEventObj);
-        env->DeleteLocalRef(motionEventObj);
-    }
-}
-
 } /* namespace android */
diff --git a/tests/framework-tests/Android.mk b/tests/framework-tests/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/tests/framework-tests/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/tests/framework-tests/README b/tests/framework-tests/README
deleted file mode 100644
index 7b46b25..0000000
--- a/tests/framework-tests/README
+++ /dev/null
@@ -1,5 +0,0 @@
-This package contains tests which need to access package-private members in the framework code.
-To do this, the tests must be loaded in the same class loader as the classes which they are
-testing. This package is loaded in the boot classpath.
-
-Run these tests via AndroidTests -> FrameworkTests.
diff --git a/tests/framework-tests/src/Android.mk b/tests/framework-tests/src/Android.mk
deleted file mode 100644
index f537b52..0000000
--- a/tests/framework-tests/src/Android.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_MODULE := framework-tests
-
-LOCAL_JAVA_LIBRARIES := android.policy android.test.runner
-
-include $(BUILD_JAVA_LIBRARY)
diff --git a/tests/framework-tests/src/android/test/FrameworkTests.java b/tests/framework-tests/src/android/test/FrameworkTests.java
deleted file mode 100644
index cb3f493..0000000
--- a/tests/framework-tests/src/android/test/FrameworkTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.test;
-
-import com.android.internal.os.LoggingPrintStreamTest;
-import junit.framework.TestSuite;
-import com.android.internal.http.multipart.MultipartTest;
-import com.android.internal.policy.impl.LockPatternKeyguardViewTest;
-
-/**
- * Tests that are loaded in the boot classpath along with the Android framework
- * classes. This enables you to access package-private members in the framework
- * classes; doing so is not possible when the test classes are loaded in an
- * application classloader.
- */
-public class FrameworkTests {
-    public static TestSuite suite() {
-        TestSuite suite = new TestSuite(FrameworkTests.class.getName());
-
-        suite.addTestSuite(MultipartTest.class);
-        suite.addTestSuite(LoggingPrintStreamTest.class);
-        suite.addTestSuite(LockPatternKeyguardViewTest.class);
-
-        return suite;
-    }
-}
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 499a7ac..b339a2c 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -4,6 +4,9 @@
 # Android Asset Packaging Tool
 #
 
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -53,3 +56,4 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
+endif # TARGET_BUILD_APPS
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 1e8b395..83057b8 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1000,7 +1000,7 @@
             }
 
             // Bluetooth-related compatibility logic
-            if (!specBluetoothFeature && hasBluetoothPermission) {
+            if (!specBluetoothFeature && hasBluetoothPermission && (targetSdk > 4)) {
                 // if app takes a Bluetooth permission but does not request the Bluetooth
                 // feature, we infer that it meant to
                 printf("uses-feature:'android.hardware.bluetooth'\n");
diff --git a/tools/aidl/Android.mk b/tools/aidl/Android.mk
index 944aeb6..2ad0728 100644
--- a/tools/aidl/Android.mk
+++ b/tools/aidl/Android.mk
@@ -2,6 +2,9 @@
 #
 # Copies files into the directory structure described by a manifest
 
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -21,4 +24,4 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
-
+endif # TARGET_BUILD_APPS
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 78a9674..8b2cfc6 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1483,18 +1483,19 @@
      * disconnect or stop command was initiated.
      */
     public synchronized boolean disconnectAndStop() {
+        boolean ret = true;;
         if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
             // Take down any open network notifications
             setNotificationVisible(false, 0, false, 0);
 
-            mRunState = RUN_STATE_STOPPING;
             if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
-                return stopDriver();
+                ret = stopDriver();
             } else {
-                return disconnect();
+                ret = disconnect();
             }
+            mRunState = RUN_STATE_STOPPING;
         }
-        return true;
+        return ret;
     }
 
     public synchronized boolean restart() {
@@ -1605,7 +1606,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean scan(boolean forceActive) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.scanCommand(forceActive);
@@ -1621,7 +1622,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setScanResultHandling(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED) {
             return false;
         }
         return WifiNative.setScanResultHandlingCommand(mode);
@@ -1635,7 +1636,7 @@
      * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
      */
     public synchronized String scanResults() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return null;
         }
         return WifiNative.scanResultsCommand();
@@ -1647,7 +1648,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setScanMode(boolean isScanModeActive) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         if (mIsScanModeActive != isScanModeActive) {
@@ -1662,7 +1663,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean disconnect() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.disconnectCommand();
@@ -1674,7 +1675,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean reconnectCommand() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.reconnectCommand();
@@ -1738,7 +1739,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean reassociate() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.reassociateCommand();
@@ -1837,7 +1838,7 @@
      * @return RSSI value, -1 on failure
      */
     public synchronized int getRssi() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return -1;
         }
         return WifiNative.getRssiApproxCommand();
@@ -1849,7 +1850,7 @@
      * @return RSSI value, -1 on failure
      */
     public synchronized int getRssiApprox() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return -1;
         }
         return WifiNative.getRssiApproxCommand();
@@ -1861,7 +1862,7 @@
      * @return link speed, -1 on failure
      */
     public synchronized int getLinkSpeed() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return -1;
         }
         return WifiNative.getLinkSpeedCommand();
@@ -1873,7 +1874,7 @@
      * @return MAC address, null on failure
      */
     public synchronized String getMacAddress() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return null;
         }
         return WifiNative.getMacAddressCommand();
@@ -1897,7 +1898,9 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean stopDriver() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
+        /* Driver stop should not happen only when supplicant event
+         * DRIVER_STOPPED has already been handled */
+        if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
             return false;
         }
         return WifiNative.stopDriverCommand();
@@ -1909,7 +1912,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean startPacketFiltering() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.startPacketFiltering();
@@ -1921,7 +1924,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean stopPacketFiltering() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.stopPacketFiltering();
@@ -1935,7 +1938,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setPowerMode(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.setPowerModeCommand(mode);
@@ -1948,7 +1951,7 @@
      * the number of channels is invalid.
      */
     public synchronized boolean setNumAllowedChannels() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         try {
@@ -1973,7 +1976,7 @@
      * {@code numChannels} is outside the valid range.
      */
     public synchronized boolean setNumAllowedChannels(int numChannels) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         mNumAllowedChannels = numChannels;
@@ -1986,7 +1989,7 @@
      * @return channel count, -1 on failure
      */
     public synchronized int getNumAllowedChannels() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return -1;
         }
         return WifiNative.getNumAllowedChannelsCommand();
@@ -2002,7 +2005,7 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public synchronized boolean setBluetoothCoexistenceMode(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return false;
         }
         return WifiNative.setBluetoothCoexistenceModeCommand(mode);
@@ -2016,7 +2019,7 @@
      * @param isBluetoothPlaying whether to enable or disable this mode
      */
     public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
             return;
         }
         WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);