Merge "Added support for the GL_TEXTURE_EXTERNAL target" into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 53319e4..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"
@@ -176783,7 +176804,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="downTime" type="long">
@@ -176883,7 +176904,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="false"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -188125,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/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/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 317e547..e2f5ada 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,65 +17,61 @@
 
 package android.hardware;
 
-/** 
- * Class representing a sensor. Use {@link SensorManager#getSensorList}
- * to get the list of available Sensors.
+/**
+ * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
+ * the list of available Sensors.
  */
 public class Sensor {
 
-    /** 
-     * A constant describing an accelerometer sensor type.
-     * See {@link android.hardware.SensorEvent SensorEvent}
-     * for more details.
+    /**
+     * A constant describing an accelerometer sensor type. See
+     * {@link android.hardware.SensorEvent SensorEvent} for more details.
      */
-    public static final int TYPE_ACCELEROMETER  = 1;
+    public static final int TYPE_ACCELEROMETER = 1;
 
-    /** 
-     * A constant describing a magnetic field sensor type.
-     * See {@link android.hardware.SensorEvent SensorEvent}
-     * for more details.
+    /**
+     * A constant describing a magnetic field sensor type. See
+     * {@link android.hardware.SensorEvent SensorEvent} for more details.
      */
     public static final int TYPE_MAGNETIC_FIELD = 2;
-    
-    /** 
-     * A constant describing an orientation sensor type.
-     * See {@link android.hardware.SensorEvent SensorEvent}
-     * for more details.
+
+    /**
+     * A constant describing an orientation sensor type. See
+     * {@link android.hardware.SensorEvent SensorEvent} for more details.
+     *
      * @deprecated use {@link android.hardware.SensorManager#getOrientation
-     *  SensorManager.getOrientation()} instead.
+     *             SensorManager.getOrientation()} instead.
      */
     @Deprecated
-    public static final int TYPE_ORIENTATION    = 3;
+    public static final int TYPE_ORIENTATION = 3;
 
     /** A constant describing a gyroscope sensor type */
-    public static final int TYPE_GYROSCOPE      = 4;
+    public static final int TYPE_GYROSCOPE = 4;
+
     /**
-     * A constant describing an light sensor type.
-     * See {@link android.hardware.SensorEvent SensorEvent}
-     * for more details.
+     * A constant describing an light sensor type. See
+     * {@link android.hardware.SensorEvent SensorEvent} for more details.
      */
-    public static final int TYPE_LIGHT          = 5;
+    public static final int TYPE_LIGHT = 5;
 
     /** A constant describing a pressure sensor type */
-    public static final int TYPE_PRESSURE       = 6;
+    public static final int TYPE_PRESSURE = 6;
 
     /** A constant describing a temperature sensor type */
-    public static final int TYPE_TEMPERATURE    = 7;
+    public static final int TYPE_TEMPERATURE = 7;
 
     /**
-     * A constant describing an proximity sensor type.
-     * See {@link android.hardware.SensorEvent SensorEvent}
-     * for more details.
+     * A constant describing an proximity sensor type. See
+     * {@link android.hardware.SensorEvent SensorEvent} for more details.
      */
-    public static final int TYPE_PROXIMITY      = 8;
+    public static final int TYPE_PROXIMITY = 8;
 
-    
-    /** 
+    /**
      * A constant describing all sensor types.
      */
-    public static final int TYPE_ALL             = -1;
+    public static final int TYPE_ALL = -1;
 
-    /* Some of these fields are set only by the native bindings in 
+    /* Some of these fields are set only by the native bindings in
      * SensorManager.
      */
     private String  mName;
@@ -87,8 +83,8 @@
     private float   mResolution;
     private float   mPower;
     private int     mLegacyType;
-    
-    
+
+
     Sensor() {
     }
 
@@ -105,51 +101,51 @@
     public String getVendor() {
         return mVendor;
     }
-    
+
     /**
      * @return generic type of this sensor.
      */
     public int getType() {
         return mType;
     }
-    
+
     /**
      * @return version of the sensor's module.
      */
     public int getVersion() {
         return mVersion;
     }
-    
+
     /**
      * @return maximum range of the sensor in the sensor's unit.
      */
     public float getMaximumRange() {
         return mMaxRange;
     }
-    
+
     /**
      * @return resolution of the sensor in the sensor's unit.
      */
     public float getResolution() {
         return mResolution;
     }
-    
+
     /**
      * @return the power in mA used by this sensor while in use
      */
     public float getPower() {
         return mPower;
     }
-    
+
     int getHandle() {
         return mHandle;
     }
-    
+
     void setRange(float max, float res) {
         mMaxRange = max;
         mResolution = res;
     }
-    
+
     void setLegacyType(int legacyType) {
         mLegacyType = legacyType;
     }
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 9a9f0bf..dfefe7e 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -17,26 +17,36 @@
 package android.hardware;
 
 /**
- * This class represents a sensor event and holds informations such as the
- * sensor type (eg: accelerometer, orientation, etc...), the time-stamp, 
- * accuracy and of course the sensor's {@link SensorEvent#values data}.
+ * <p>
+ * This class represents a {@link android.hardware.Sensor Sensor} event and
+ * holds informations such as the sensor's type, the time-stamp, accuracy and of
+ * course the sensor's {@link SensorEvent#values data}.
+ * </p>
  *
- * <p><u>Definition of the coordinate system used by the SensorEvent API.</u><p>
- * 
- * <pre>
- * The coordinate space is defined relative to the screen of the phone 
- * in its default orientation. The axes are not swapped when the device's
- * screen orientation changes.
- * 
- * The OpenGL ES coordinate system is used. The origin is in the
- * lower-left corner  with respect to the screen, with the X axis horizontal
- * and pointing  right, the Y axis vertical and pointing up and the Z axis
- * pointing outside the front face of the screen. In this system, coordinates
- * behind the screen have negative Z values.
- * 
+ * <p>
+ * <u>Definition of the coordinate system used by the SensorEvent API.</u>
+ * </p>
+ *
+ * <p>
+ * The coordinate space is defined relative to the screen of the phone in its
+ * default orientation. The axes are not swapped when the device's screen
+ * orientation changes.
+ * </p>
+ *
+ * <p>
+ * The OpenGL ES coordinate system is used. The origin is in the lower-left
+ * corner with respect to the screen, with the X axis horizontal and pointing
+ * right, the Y axis vertical and pointing up and the Z axis pointing outside
+ * the front face of the screen. In this system, coordinates behind the screen
+ * have negative Z values.
+ * </p>
+ *
+ * <p>
  * <b>Note:</b> This coordinate system is different from the one used in the
- * Android 2D APIs where the origin is in the top-left corner. 
+ * Android 2D APIs where the origin is in the top-left corner.
+ * </p>
  *
+ * <pre>
  *   x<0         x>0
  *                ^
  *                |
@@ -60,100 +70,125 @@
 
 public class SensorEvent {
     /**
+     * <p>
      * The length and contents of the values array vary depending on which
-     * sensor type is being monitored (see also {@link SensorEvent} for a 
-     * definition of the coordinate system used):
-     * 
-     * <p>{@link android.hardware.Sensor#TYPE_ORIENTATION Sensor.TYPE_ORIENTATION}:<p>
-     *  All values are angles in degrees.
-     * 
-     * <p>values[0]: Azimuth, angle between the magnetic north direction and
-     * the Y axis, around the Z axis (0 to 359).
-     * 0=North, 90=East, 180=South, 270=West
+     * {@link android.hardware.Sensor sensor} type is being monitored (see also
+     * {@link SensorEvent} for a definition of the coordinate system used):
+     * </p>
      *
-     * <p>values[1]: Pitch, rotation around X axis (-180 to 180), 
-     * with positive values when the z-axis moves <b>toward</b> the y-axis.
+     * <h3>{@link android.hardware.Sensor#TYPE_ORIENTATION
+     * Sensor.TYPE_ORIENTATION}:</h3> All values are angles in degrees.
      *
-     * <p>values[2]: Roll, rotation around Y axis (-90 to 90), with 
-     * positive values  when the x-axis moves <b>toward</b> the z-axis.
-     * 
-     * <p><b>Important note:</b> For historical reasons the roll angle is
-     * positive in the clockwise direction (mathematically speaking, it
-     * should be positive in the counter-clockwise direction).
+     * <ul>
+     * <p>
+     * values[0]: Azimuth, angle between the magnetic north direction and the Y
+     * axis, around the Z axis (0 to 359). 0=North, 90=East, 180=South, 270=West
      *
-     * <p><b>Note:</b> This definition is different from <b>yaw, pitch and 
-     * roll</b> used in aviation where the X axis is along the long side of
-     * the plane (tail to nose).
+     * <p>
+     * values[1]: Pitch, rotation around X axis (-180 to 180), with positive
+     * values when the z-axis moves <b>toward</b> the y-axis.
      *
-     * <p><b>Note:</b> This sensor type exists for legacy reasons, please use
-     * {@link android.hardware.SensorManager#getRotationMatrix 
-     *      getRotationMatrix()} in conjunction with
-     * {@link android.hardware.SensorManager#remapCoordinateSystem 
-     *      remapCoordinateSystem()} and
-     * {@link android.hardware.SensorManager#getOrientation getOrientation()}
-     * to compute these values instead.
+     * <p>
+     * values[2]: Roll, rotation around Y axis (-90 to 90), with positive values
+     * when the x-axis moves <b>toward</b> the z-axis.
+     * </ul>
      *
-     * <p>{@link android.hardware.Sensor#TYPE_ACCELEROMETER Sensor.TYPE_ACCELEROMETER}:<p>
-     *  All values are in SI units (m/s^2) and measure the acceleration applied
-     *  to the phone minus the force of gravity.
-     *  
-     *  <p>values[0]: Acceleration minus Gx on the x-axis 
-     *  <p>values[1]: Acceleration minus Gy on the y-axis 
-     *  <p>values[2]: Acceleration minus Gz on the z-axis
-     *  
-     *  <p><u>Examples</u>:
-     *    <li>When the device lies flat on a table and is pushed on its left 
-     *    side toward the right, the x acceleration value is positive.</li>
-     *    
-     *    <li>When the device lies flat on a table, the acceleration value is 
-     *    +9.81, which correspond to the acceleration of the device (0 m/s^2) 
-     *    minus the force of gravity (-9.81 m/s^2).</li>
-     *    
-     *    <li>When the device lies flat on a table and is pushed toward the sky
-     *    with an acceleration of A m/s^2, the acceleration value is equal to 
-     *    A+9.81 which correspond to the acceleration of the 
-     *    device (+A m/s^2) minus the force of gravity (-9.81 m/s^2).</li>
-     * 
-     * 
-     * <p>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD Sensor.TYPE_MAGNETIC_FIELD}:<p>
-     *  All values are in micro-Tesla (uT) and measure the ambient magnetic
-     *  field in the X, Y and Z axis.
+     * <p>
+     * <b>Important note:</b> For historical reasons the roll angle is positive
+     * in the clockwise direction (mathematically speaking, it should be
+     * positive in the counter-clockwise direction).
      *
-     * <p>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:<p>
+     * <p>
+     * <b>Note:</b> This definition is different from <b>yaw, pitch and roll</b>
+     * used in aviation where the X axis is along the long side of the plane
+     * (tail to nose).
      *
-     *  <p>values[0]: Ambient light level in SI lux units
+     * <p>
+     * <b>Note:</b> This sensor type exists for legacy reasons, please use
+     * {@link android.hardware.SensorManager#getRotationMatrix
+     * getRotationMatrix()} in conjunction with
+     * {@link android.hardware.SensorManager#remapCoordinateSystem
+     * remapCoordinateSystem()} and
+     * {@link android.hardware.SensorManager#getOrientation getOrientation()} to
+     * compute these values instead.
      *
-     * <p>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:<p>
+     * <h3>{@link android.hardware.Sensor#TYPE_ACCELEROMETER
+     * Sensor.TYPE_ACCELEROMETER}:</h3>
+     * All values are in SI units (m/s^2) and measure the acceleration applied
+     * to the phone minus the force of gravity.
      *
-     *  <p>values[0]: Proximity sensor distance measured in centimeters
+     * <ul>
+     * <p>
+     * values[0]: Acceleration minus Gx on the x-axis
+     * <p>
+     * values[1]: Acceleration minus Gy on the y-axis
+     * <p>
+     * values[2]: Acceleration minus Gz on the z-axis
+     * </ul>
      *
-     *  <p> Note that some proximity sensors only support a binary "close" or "far" measurement.
-     *   In this case, the sensor should report its maxRange value in the "far" state and a value
-     *   less than maxRange in the "near" state.
+     * <p>
+     * <u>Examples</u>:
+     * <ul>
+     * <li>When the device lies flat on a table and is pushed on its left side
+     * toward the right, the x acceleration value is positive.</li>
+     *
+     * <li>When the device lies flat on a table, the acceleration value is
+     * +9.81, which correspond to the acceleration of the device (0 m/s^2) minus
+     * the force of gravity (-9.81 m/s^2).</li>
+     *
+     * <li>When the device lies flat on a table and is pushed toward the sky
+     * with an acceleration of A m/s^2, the acceleration value is equal to
+     * A+9.81 which correspond to the acceleration of the device (+A m/s^2)
+     * minus the force of gravity (-9.81 m/s^2).</li>
+     * </ul>
+     *
+     *
+     * <h3>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD
+     * Sensor.TYPE_MAGNETIC_FIELD}:</h3>
+     * All values are in micro-Tesla (uT) and measure the ambient magnetic field
+     * in the X, Y and Z axis.
+     *
+     * <h3>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h3>
+     *
+     * <ul>
+     * <p>
+     * values[0]: Ambient light level in SI lux units
+     * </ul>
+     *
+     * <h3>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
+     * </h3>
+     *
+     * <ul>
+     * <p>
+     * values[0]: Proximity sensor distance measured in centimeters
+     * </ul>
+     *
+     * <p>
+     * Note that some proximity sensors only support a binary "close" or "far"
+     * measurement. In this case, the sensor should report its maxRange value in
+     * the "far" state and a value less than maxRange in the "near" state.
      */
     public final float[] values;
 
     /**
-     * The sensor that generated this event.
-     * See {@link android.hardware.SensorManager SensorManager}
-     * for details.
+     * The sensor that generated this event. See
+     * {@link android.hardware.SensorManager SensorManager} for details.
      */
-    public Sensor sensor;
-    
+   public Sensor sensor;
+
     /**
-     * The accuracy of this event.
-     * See {@link android.hardware.SensorManager SensorManager}
-     * for details.
+     * The accuracy of this event. See {@link android.hardware.SensorManager
+     * SensorManager} for details.
      */
     public int accuracy;
-    
-    
+
+
     /**
      * The time in nanosecond at which the event happened
      */
     public long timestamp;
 
-    
+
     SensorEvent(int size) {
         values = new float[size];
     }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 98172e6..f60e2d7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -41,9 +41,11 @@
 import java.util.List;
 
 /**
- * Class that lets you access the device's sensors. Get an instance of this
- * class by calling {@link android.content.Context#getSystemService(java.lang.String)
- * Context.getSystemService()} with an argument of {@link android.content.Context#SENSOR_SERVICE}.
+ * SensorManager lets you access the device's {@link android.hardware.Sensor
+ * sensors}. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String)
+ * Context.getSystemService()} with the argument
+ * {@link android.content.Context#SENSOR_SERVICE}.
  */
 public class SensorManager
 {
@@ -53,172 +55,220 @@
     /* NOTE: sensor IDs must be a power of 2 */
 
     /**
-     * A constant describing an orientation sensor.
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing an orientation sensor. See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_ORIENTATION = 1 << 0;
 
     /**
-     * A constant describing an accelerometer.
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing an accelerometer. See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_ACCELEROMETER = 1 << 1;
 
     /**
-     * A constant describing a temperature sensor
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing a temperature sensor See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_TEMPERATURE = 1 << 2;
 
     /**
-     * A constant describing a magnetic sensor
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing a magnetic sensor See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_MAGNETIC_FIELD = 1 << 3;
 
     /**
-     * A constant describing an ambient light sensor
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing an ambient light sensor See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_LIGHT = 1 << 4;
 
     /**
-     * A constant describing a proximity sensor
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing a proximity sensor See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_PROXIMITY = 1 << 5;
 
     /**
-     * A constant describing a Tricorder
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing a Tricorder See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_TRICORDER = 1 << 6;
 
     /**
-     * A constant describing an orientation sensor.
-     * See {@link android.hardware.SensorListener SensorListener} for more details.
+     * A constant describing an orientation sensor. See
+     * {@link android.hardware.SensorListener SensorListener} for more details.
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_ORIENTATION_RAW = 1 << 7;
 
-    /** A constant that includes all sensors
+    /**
+     * A constant that includes all sensors
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_ALL = 0x7F;
 
-    /** Smallest sensor ID 
+    /**
+     * Smallest sensor ID
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_MIN = SENSOR_ORIENTATION;
 
-    /** Largest sensor ID
+    /**
+     * Largest sensor ID
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int SENSOR_MAX = ((SENSOR_ALL + 1)>>1);
 
 
-    /** Index of the X value in the array returned by
+    /**
+     * Index of the X value in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int DATA_X = 0;
-    /** Index of the Y value in the array returned by
+
+    /**
+     * Index of the Y value in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int DATA_Y = 1;
-    /** Index of the Z value in the array returned by
+
+    /**
+     * Index of the Z value in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int DATA_Z = 2;
 
-    /** Offset to the untransformed values in the array returned by
+    /**
+     * Offset to the untransformed values in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int RAW_DATA_INDEX = 3;
 
-    /** Index of the untransformed X value in the array returned by
+    /**
+     * Index of the untransformed X value in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int RAW_DATA_X = 3;
-    /** Index of the untransformed Y value in the array returned by
+
+    /**
+     * Index of the untransformed Y value in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int RAW_DATA_Y = 4;
-    /** Index of the untransformed Z value in the array returned by
+
+    /**
+     * Index of the untransformed Z value in the array returned by
      * {@link android.hardware.SensorListener#onSensorChanged}
+     * 
      * @deprecated use {@link android.hardware.Sensor Sensor} instead.
      */
     @Deprecated
     public static final int RAW_DATA_Z = 5;
 
-
     /** Standard gravity (g) on Earth. This value is equivalent to 1G */
     public static final float STANDARD_GRAVITY = 9.80665f;
 
-    /** values returned by the accelerometer in various locations in the universe.
-     * all values are in SI units (m/s^2) */
+    /** Sun's gravity in SI units (m/s^2) */
     public static final float GRAVITY_SUN             = 275.0f;
+    /** Mercury's gravity in SI units (m/s^2) */
     public static final float GRAVITY_MERCURY         = 3.70f;
+    /** Venus' gravity in SI units (m/s^2) */
     public static final float GRAVITY_VENUS           = 8.87f;
+    /** Earth's gravity in SI units (m/s^2) */
     public static final float GRAVITY_EARTH           = 9.80665f;
+    /** The Moon's gravity in SI units (m/s^2) */
     public static final float GRAVITY_MOON            = 1.6f;
+    /** Mars' gravity in SI units (m/s^2) */
     public static final float GRAVITY_MARS            = 3.71f;
+    /** Jupiter's gravity in SI units (m/s^2) */
     public static final float GRAVITY_JUPITER         = 23.12f;
+    /** Saturn's gravity in SI units (m/s^2) */
     public static final float GRAVITY_SATURN          = 8.96f;
+    /** Uranus' gravity in SI units (m/s^2) */
     public static final float GRAVITY_URANUS          = 8.69f;
+    /** Neptune's gravity in SI units (m/s^2) */
     public static final float GRAVITY_NEPTUNE         = 11.0f;
+    /** Pluto's gravity in SI units (m/s^2) */
     public static final float GRAVITY_PLUTO           = 0.6f;
+    /** Gravity (estimate) on the first Death Star in Empire units (m/s^2) */
     public static final float GRAVITY_DEATH_STAR_I    = 0.000000353036145f;
+    /** Gravity on the island */
     public static final float GRAVITY_THE_ISLAND      = 4.815162342f;
 
 
     /** Maximum magnetic field on Earth's surface */
     public static final float MAGNETIC_FIELD_EARTH_MAX = 60.0f;
-
     /** Minimum magnetic field on Earth's surface */
     public static final float MAGNETIC_FIELD_EARTH_MIN = 30.0f;
 
-
-    /** Various luminance values during the day (lux) */
+    
+    /** Maximum luminance of sunlight in lux */
     public static final float LIGHT_SUNLIGHT_MAX = 120000.0f;
+    /** luminance of sunlight in lux */
     public static final float LIGHT_SUNLIGHT     = 110000.0f;
+    /** luminance in shade in lux */
     public static final float LIGHT_SHADE        = 20000.0f;
+    /** luminance under an overcast sky in lux */
     public static final float LIGHT_OVERCAST     = 10000.0f;
+    /** luminance at sunrise in lux */
     public static final float LIGHT_SUNRISE      = 400.0f;
+    /** luminance under a cloudy sky in lux */
     public static final float LIGHT_CLOUDY       = 100.0f;
-    /** Various luminance values during the night (lux) */
+    /** luminance at night with full moon in lux */
     public static final float LIGHT_FULLMOON     = 0.25f;
+    /** luminance at night with no moon in lux*/
     public static final float LIGHT_NO_MOON      = 0.001f;
 
+    
     /** get sensor data as fast as possible */
     public static final int SENSOR_DELAY_FASTEST = 0;
     /** rate suitable for games */
@@ -229,16 +279,22 @@
     public static final int SENSOR_DELAY_NORMAL = 3;
 
 
-    /** The values returned by this sensor cannot be trusted, calibration
-     * is needed or the environment doesn't allow readings */
+    /**
+     * The values returned by this sensor cannot be trusted, calibration is
+     * needed or the environment doesn't allow readings
+     */
     public static final int SENSOR_STATUS_UNRELIABLE = 0;
 
-    /** This sensor is reporting data with low accuracy, calibration with the
-     * environment is needed */
+    /**
+     * This sensor is reporting data with low accuracy, calibration with the
+     * environment is needed
+     */
     public static final int SENSOR_STATUS_ACCURACY_LOW = 1;
 
-    /** This sensor is reporting data with an average level of accuracy,
-     * calibration with the environment may improve the readings */
+    /**
+     * This sensor is reporting data with an average level of accuracy,
+     * calibration with the environment may improve the readings
+     */
     public static final int SENSOR_STATUS_ACCURACY_MEDIUM = 2;
 
     /** This sensor is reporting data with maximum accuracy */
@@ -586,9 +642,10 @@
         return 0;
     }
 
-    /** @return available sensors.
+    /**
+     * @return available sensors.
      * @deprecated This method is deprecated, use
-     * {@link SensorManager#getSensorList(int)} instead
+     *             {@link SensorManager#getSensorList(int)} instead
      */
     @Deprecated
     public int getSensors() {
@@ -612,12 +669,14 @@
     }
 
     /**
-     * Use this method to get the list of available sensors of a certain
-     * type. Make multiple calls to get sensors of different types or use
-     * {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all
-     * the sensors.
-     *
-     * @param type of sensors requested
+     * Use this method to get the list of available sensors of a certain type.
+     * Make multiple calls to get sensors of different types or use
+     * {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all the
+     * sensors.
+     * 
+     * @param type
+     *        of sensors requested
+     * 
      * @return a list of sensors matching the asked type.
      */
     public List<Sensor> getSensorList(int type) {
@@ -644,13 +703,14 @@
     }
 
     /**
-     * Use this method to get the default sensor for a given type. Note that
-     * the returned sensor could be a composite sensor, and its data could be
+     * Use this method to get the default sensor for a given type. Note that the
+     * returned sensor could be a composite sensor, and its data could be
      * averaged or filtered. If you need to access the raw sensors use
      * {@link SensorManager#getSensorList(int) getSensorList}.
-     *
-     *
-     * @param type of sensors requested
+     * 
+     * @param type
+     *        of sensors requested
+     * 
      * @return the default sensors matching the asked type.
      */
     public Sensor getDefaultSensor(int type) {
@@ -659,17 +719,21 @@
         return l.isEmpty() ? null : l.get(0);
     }
 
-
     /**
      * Registers a listener for given sensors.
+     * 
      * @deprecated This method is deprecated, use
-     * {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
-     * instead.
-     *
-     * @param listener sensor listener object
-     * @param sensors a bit masks of the sensors to register to
-     *
-     * @return true if the sensor is supported and successfully enabled
+     *             {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
+     *             instead.
+     * 
+     * @param listener
+     *        sensor listener object
+     * 
+     * @param sensors
+     *        a bit masks of the sensors to register to
+     * 
+     * @return <code>true</code> if the sensor is supported and successfully
+     *         enabled
      */
     @Deprecated
     public boolean registerListener(SensorListener listener, int sensors) {
@@ -678,18 +742,26 @@
 
     /**
      * Registers a SensorListener for given sensors.
+     * 
      * @deprecated This method is deprecated, use
-     * {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
-     * instead.
-     *
-     * @param listener sensor listener object
-     * @param sensors a bit masks of the sensors to register to
-     * @param rate rate of events. This is only a hint to the system. events
-     * may be received faster or slower than the specified rate. Usually events
-     * are received faster. The value must be one of {@link #SENSOR_DELAY_NORMAL},
-     * {@link #SENSOR_DELAY_UI}, {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
-     *
-     * @return true if the sensor is supported and successfully enabled
+     *             {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
+     *             instead.
+     * 
+     * @param listener
+     *        sensor listener object
+     * 
+     * @param sensors
+     *        a bit masks of the sensors to register to
+     * 
+     * @param rate
+     *        rate of events. This is only a hint to the system. events may be
+     *        received faster or slower than the specified rate. Usually events
+     *        are received faster. The value must be one of
+     *        {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
+     *        {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
+     * 
+     * @return <code>true</code> if the sensor is supported and successfully
+     *         enabled
      */
     @Deprecated
     public boolean registerListener(SensorListener listener, int sensors, int rate) {
@@ -747,12 +819,16 @@
 
     /**
      * Unregisters a listener for the sensors with which it is registered.
+     * 
      * @deprecated This method is deprecated, use
-     * {@link SensorManager#unregisterListener(SensorEventListener, Sensor)}
-     * instead.
-     *
-     * @param listener a SensorListener object
-     * @param sensors a bit masks of the sensors to unregister from
+     *             {@link SensorManager#unregisterListener(SensorEventListener, Sensor)}
+     *             instead.
+     * 
+     * @param listener
+     *        a SensorListener object
+     * 
+     * @param sensors
+     *        a bit masks of the sensors to unregister from
      */
     @Deprecated
     public void unregisterListener(SensorListener listener, int sensors) {
@@ -815,11 +891,13 @@
 
     /**
      * Unregisters a listener for all sensors.
+     * 
      * @deprecated This method is deprecated, use
-     * {@link SensorManager#unregisterListener(SensorEventListener)}
-     * instead.
-     *
-     * @param listener a SensorListener object
+     *             {@link SensorManager#unregisterListener(SensorEventListener)}
+     *             instead.
+     * 
+     * @param listener
+     *        a SensorListener object
      */
     @Deprecated
     public void unregisterListener(SensorListener listener) {
@@ -828,10 +906,12 @@
 
     /**
      * Unregisters a listener for the sensors with which it is registered.
-     *
-     * @param listener a SensorEventListener object
-     * @param sensor the sensor to unregister from
-     *
+     * 
+     * @param listener
+     *        a SensorEventListener object
+     * @param sensor
+     *        the sensor to unregister from
+     * 
      */
     public void unregisterListener(SensorEventListener listener, Sensor sensor) {
         unregisterListener((Object)listener, sensor);
@@ -839,50 +919,68 @@
 
     /**
      * Unregisters a listener for all sensors.
-     *
-     * @param listener a SensorListener object
-     *
+     * 
+     * @param listener
+     *        a SensorListener object
+     * 
      */
     public void unregisterListener(SensorEventListener listener) {
         unregisterListener((Object)listener);
     }
 
-
     /**
-     * Registers a {@link android.hardware.SensorEventListener SensorEventListener}
-     * for the given sensor.
-     *
-     * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object.
-     * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
-     * @param rate The rate {@link android.hardware.SensorEvent sensor events} are delivered at.
-     * This is only a hint to the system. Events may be received faster or
-     * slower than the specified rate. Usually events are received faster. The value must be
-     * one of {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI}, {@link #SENSOR_DELAY_GAME},
-     * or {@link #SENSOR_DELAY_FASTEST}.
-     *
-     * @return true if the sensor is supported and successfully enabled.
-     *
+     * Registers a {@link android.hardware.SensorEventListener
+     * SensorEventListener} for the given sensor.
+     * 
+     * @param listener
+     *        A {@link android.hardware.SensorEventListener SensorEventListener}
+     *        object.
+     * 
+     * @param sensor
+     *        The {@link android.hardware.Sensor Sensor} to register to.
+     * 
+     * @param rate
+     *        The rate {@link android.hardware.SensorEvent sensor events} are
+     *        delivered at. This is only a hint to the system. Events may be
+     *        received faster or slower than the specified rate. Usually events
+     *        are received faster. The value must be one of
+     *        {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
+     *        {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
+     * 
+     * @return <code>true</code> if the sensor is supported and successfully
+     *         enabled.
+     * 
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
         return registerListener(listener, sensor, rate, null);
     }
 
     /**
-     * Registers a {@link android.hardware.SensorEventListener SensorEventListener}
-     * for the given sensor.
-     *
-     * @param listener A {@link android.hardware.SensorEventListener SensorEventListener} object.
-     * @param sensor The {@link android.hardware.Sensor Sensor} to register to.
-     * @param rate The rate {@link android.hardware.SensorEvent sensor events} are delivered at.
-     * This is only a hint to the system. Events may be received faster or
-     * slower than the specified rate. Usually events are received faster. The value must be one
-     * of {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI}, {@link #SENSOR_DELAY_GAME}, or
-     * {@link #SENSOR_DELAY_FASTEST}.
-     * @param handler The {@link android.os.Handler Handler} the
-     * {@link android.hardware.SensorEvent sensor events} will be delivered to.
-     *
+     * Registers a {@link android.hardware.SensorEventListener
+     * SensorEventListener} for the given sensor.
+     * 
+     * @param listener
+     *        A {@link android.hardware.SensorEventListener SensorEventListener}
+     *        object.
+     * 
+     * @param sensor
+     *        The {@link android.hardware.Sensor Sensor} to register to.
+     * 
+     * @param rate
+     *        The rate {@link android.hardware.SensorEvent sensor events} are
+     *        delivered at. This is only a hint to the system. Events may be
+     *        received faster or slower than the specified rate. Usually events
+     *        are received faster. The value must be one of
+     *        {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
+     *        {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
+     * 
+     * @param handler
+     *        The {@link android.os.Handler Handler} the
+     *        {@link android.hardware.SensorEvent sensor events} will be
+     *        delivered to.
+     * 
      * @return true if the sensor is supported and successfully enabled.
-     *
+     * 
      */
     public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
             Handler handler) {
@@ -1003,95 +1101,126 @@
     }
 
     /**
-     * Computes the inclination matrix <b>I</b> as well as the rotation
-     * matrix <b>R</b> transforming a vector from the
-     * device coordinate system to the world's coordinate system which is
-     * defined as a direct orthonormal basis, where:
+     * <p>
+     * Computes the inclination matrix <b>I</b> as well as the rotation matrix
+     * <b>R</b> transforming a vector from the device coordinate system to the
+     * world's coordinate system which is defined as a direct orthonormal basis,
+     * where:
+     * </p>
      * 
+     * <ul>
      * <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
      * the ground at the device's current location and roughly points East).</li>
      * <li>Y is tangential to the ground at the device's current location and
      * points towards the magnetic North Pole.</li>
      * <li>Z points towards the sky and is perpendicular to the ground.</li>
+     * </ul>
      * <p>
      * <hr>
-     * <p>By definition:
-     * <p>[0 0 g] = <b>R</b> * <b>gravity</b> (g = magnitude of gravity)
-     * <p>[0 m 0] = <b>I</b> * <b>R</b> * <b>geomagnetic</b>
-     * (m = magnitude of geomagnetic field)
-     * <p><b>R</b> is the identity matrix when the device is aligned with the
+     * <p>
+     * By definition:
+     * <p>
+     * [0 0 g] = <b>R</b> * <b>gravity</b> (g = magnitude of gravity)
+     * <p>
+     * [0 m 0] = <b>I</b> * <b>R</b> * <b>geomagnetic</b> (m = magnitude of
+     * geomagnetic field)
+     * <p>
+     * <b>R</b> is the identity matrix when the device is aligned with the
      * world's coordinate system, that is, when the device's X axis points
      * toward East, the Y axis points to the North Pole and the device is facing
      * the sky.
-     *
-     * <p><b>I</b> is a rotation matrix transforming the geomagnetic
-     * vector into the same coordinate space as gravity (the world's coordinate
-     * space). <b>I</b> is a simple rotation around the X axis.
-     * The inclination angle in radians can be computed with
-     * {@link #getInclination}.
+     * 
+     * <p>
+     * <b>I</b> is a rotation matrix transforming the geomagnetic vector into
+     * the same coordinate space as gravity (the world's coordinate space).
+     * <b>I</b> is a simple rotation around the X axis. The inclination angle in
+     * radians can be computed with {@link #getInclination}.
      * <hr>
      * 
-     * <p> Each matrix is returned either as a 3x3 or 4x4 row-major matrix
-     * depending on the length of the passed array:
-     * <p><u>If the array length is 16:</u>
+     * <p>
+     * Each matrix is returned either as a 3x3 or 4x4 row-major matrix depending
+     * on the length of the passed array:
+     * <p>
+     * <u>If the array length is 16:</u>
+     * 
      * <pre>
      *   /  M[ 0]   M[ 1]   M[ 2]   M[ 3]  \
      *   |  M[ 4]   M[ 5]   M[ 6]   M[ 7]  |
      *   |  M[ 8]   M[ 9]   M[10]   M[11]  |
      *   \  M[12]   M[13]   M[14]   M[15]  /
      *</pre>
-     * This matrix is ready to be used by OpenGL ES's 
-     * {@link javax.microedition.khronos.opengles.GL10#glLoadMatrixf(float[], int) 
-     * glLoadMatrixf(float[], int)}. 
-     * <p>Note that because OpenGL matrices are column-major matrices you must
-     * transpose the matrix before using it. However, since the matrix is a 
+     * 
+     * This matrix is ready to be used by OpenGL ES's
+     * {@link javax.microedition.khronos.opengles.GL10#glLoadMatrixf(float[], int)
+     * glLoadMatrixf(float[], int)}.
+     * <p>
+     * Note that because OpenGL matrices are column-major matrices you must
+     * transpose the matrix before using it. However, since the matrix is a
      * rotation matrix, its transpose is also its inverse, conveniently, it is
      * often the inverse of the rotation that is needed for rendering; it can
      * therefore be used with OpenGL ES directly.
      * <p>
      * Also note that the returned matrices always have this form:
+     * 
      * <pre>
      *   /  M[ 0]   M[ 1]   M[ 2]   0  \
      *   |  M[ 4]   M[ 5]   M[ 6]   0  |
      *   |  M[ 8]   M[ 9]   M[10]   0  |
      *   \      0       0       0   1  /
      *</pre>
-     * <p><u>If the array length is 9:</u>
+     * 
+     * <p>
+     * <u>If the array length is 9:</u>
+     * 
      * <pre>
      *   /  M[ 0]   M[ 1]   M[ 2]  \
      *   |  M[ 3]   M[ 4]   M[ 5]  |
      *   \  M[ 6]   M[ 7]   M[ 8]  /
      *</pre>
-     *
+     * 
      * <hr>
-     * <p>The inverse of each matrix can be computed easily by taking its
+     * <p>
+     * The inverse of each matrix can be computed easily by taking its
      * transpose.
-     *
-     * <p>The matrices returned by this function are meaningful only when the
-     * device is not free-falling and it is not close to the magnetic north.
-     * If the device is accelerating, or placed into a strong magnetic field,
-     * the returned matrices may be inaccurate.
-     *
-     * @param R is an array of 9 floats holding the rotation matrix <b>R</b>
-     * when this function returns. R can be null.<p>
-     * @param I is an array of 9 floats holding the rotation matrix <b>I</b>
-     * when this function returns. I can be null.<p>
-     * @param gravity is an array of 3 floats containing the gravity vector
-     * expressed in the device's coordinate. You can simply use the
-     * {@link android.hardware.SensorEvent#values values}
-     * returned by a {@link android.hardware.SensorEvent SensorEvent} of a
-     * {@link android.hardware.Sensor Sensor} of type
-     * {@link android.hardware.Sensor#TYPE_ACCELEROMETER TYPE_ACCELEROMETER}.<p>
-     * @param geomagnetic is an array of 3 floats containing the geomagnetic
-     * vector expressed in the device's coordinate. You can simply use the
-     * {@link android.hardware.SensorEvent#values values}
-     * returned by a {@link android.hardware.SensorEvent SensorEvent} of a
-     * {@link android.hardware.Sensor Sensor} of type
-     * {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD TYPE_MAGNETIC_FIELD}.
-     * @return
-     *   true on success<p>
-     *   false on failure (for instance, if the device is in free fall).
-     *   On failure the output matrices are not modified.
+     * 
+     * <p>
+     * The matrices returned by this function are meaningful only when the
+     * device is not free-falling and it is not close to the magnetic north. If
+     * the device is accelerating, or placed into a strong magnetic field, the
+     * returned matrices may be inaccurate.
+     * 
+     * @param R
+     *        is an array of 9 floats holding the rotation matrix <b>R</b> when
+     *        this function returns. R can be null.
+     *        <p>
+     * 
+     * @param I
+     *        is an array of 9 floats holding the rotation matrix <b>I</b> when
+     *        this function returns. I can be null.
+     *        <p>
+     * 
+     * @param gravity
+     *        is an array of 3 floats containing the gravity vector expressed in
+     *        the device's coordinate. You can simply use the
+     *        {@link android.hardware.SensorEvent#values values} returned by a
+     *        {@link android.hardware.SensorEvent SensorEvent} of a
+     *        {@link android.hardware.Sensor Sensor} of type
+     *        {@link android.hardware.Sensor#TYPE_ACCELEROMETER
+     *        TYPE_ACCELEROMETER}.
+     *        <p>
+     * 
+     * @param geomagnetic
+     *        is an array of 3 floats containing the geomagnetic vector
+     *        expressed in the device's coordinate. You can simply use the
+     *        {@link android.hardware.SensorEvent#values values} returned by a
+     *        {@link android.hardware.SensorEvent SensorEvent} of a
+     *        {@link android.hardware.Sensor Sensor} of type
+     *        {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD
+     *        TYPE_MAGNETIC_FIELD}.
+     * 
+     * @return <code>true</code> on success, <code>false</code> on failure (for
+     *         instance, if the device is in free fall). On failure the output
+     *         matrices are not modified.
      */
 
     public static boolean getRotationMatrix(float[] R, float[] I,
@@ -1160,7 +1289,9 @@
     /**
      * Computes the geomagnetic inclination angle in radians from the
      * inclination matrix <b>I</b> returned by {@link #getRotationMatrix}.
-     * @param I inclination matrix see {@link #getRotationMatrix}.
+     * 
+     * @param I
+     *        inclination matrix see {@link #getRotationMatrix}.
      * @return The geomagnetic inclination angle in radians.
      */
     public static float getInclination(float[] I) {
@@ -1172,52 +1303,76 @@
     }
 
     /**
-     * Rotates the supplied rotation matrix so it is expressed in a
-     * different coordinate system. This is typically used when an application
-     * needs to compute the three orientation angles of the device (see
+     * <p>
+     * Rotates the supplied rotation matrix so it is expressed in a different
+     * coordinate system. This is typically used when an application needs to
+     * compute the three orientation angles of the device (see
      * {@link #getOrientation}) in a different coordinate system.
+     * </p>
      * 
-     * <p>When the rotation matrix is used for drawing (for instance with 
-     * OpenGL ES), it usually <b>doesn't need</b> to be transformed by this 
-     * function, unless the screen is physically rotated, in which case you
-     * can use {@link android.view.Display#getRotation() Display.getRotation()}
-     * to retrieve the current rotation of the screen.  Note that because the
-     * user is generally free to rotate their screen, you often should
-     * consider the rotation in deciding the parameters to use here.
-     *
-     * <p><u>Examples:</u><p>
-     *
-     * <li>Using the camera (Y axis along the camera's axis) for an augmented 
-     * reality application where the rotation angles are needed: </li><p>
-     *
-     * <code>remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);</code><p>
-     *
+     * <p>
+     * When the rotation matrix is used for drawing (for instance with OpenGL
+     * ES), it usually <b>doesn't need</b> to be transformed by this function,
+     * unless the screen is physically rotated, in which case you can use
+     * {@link android.view.Display#getRotation() Display.getRotation()} to
+     * retrieve the current rotation of the screen. Note that because the user
+     * is generally free to rotate their screen, you often should consider the
+     * rotation in deciding the parameters to use here.
+     * </p>
+     * 
+     * <p>
+     * <u>Examples:</u>
+     * <p>
+     * 
+     * <ul>
+     * <li>Using the camera (Y axis along the camera's axis) for an augmented
+     * reality application where the rotation angles are needed:</li>
+     * 
+     * <p>
+     * <ul>
+     * <code>remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);</code>
+     * </ul>
+     * </p>
+     * 
      * <li>Using the device as a mechanical compass when rotation is
-     * {@link android.view.Surface#ROTATION_90 Surface.ROTATION_90}:</li><p>
-     *
-     * <code>remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);</code><p>
-     *
-     * Beware of the above example. This call is needed only to account for
-     * a rotation from its natural orientation when calculating the
-     * rotation angles (see {@link #getOrientation}).
-     * If the rotation matrix is also used for rendering, it may not need to 
-     * be transformed, for instance if your {@link android.app.Activity
-     * Activity} is running in landscape mode.
-     *
-     * <p>Since the resulting coordinate system is orthonormal, only two axes
-     * need to be specified.
-     *
-     * @param inR the rotation matrix to be transformed. Usually it is the
-     * matrix returned by {@link #getRotationMatrix}.
-     * @param X defines on which world axis and direction the X axis of the
-     *        device is mapped.
-     * @param Y defines on which world axis and direction the Y axis of the
-     *        device is mapped.
-     * @param outR the transformed rotation matrix. inR and outR can be the same
+     * {@link android.view.Surface#ROTATION_90 Surface.ROTATION_90}:</li>
+     * 
+     * <p>
+     * <ul>
+     * <code>remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);</code>
+     * </ul>
+     * </p>
+     * 
+     * Beware of the above example. This call is needed only to account for a
+     * rotation from its natural orientation when calculating the rotation
+     * angles (see {@link #getOrientation}). If the rotation matrix is also used
+     * for rendering, it may not need to be transformed, for instance if your
+     * {@link android.app.Activity Activity} is running in landscape mode.
+     * </ul>
+     * 
+     * <p>
+     * Since the resulting coordinate system is orthonormal, only two axes need
+     * to be specified.
+     * 
+     * @param inR
+     *        the rotation matrix to be transformed. Usually it is the matrix
+     *        returned by {@link #getRotationMatrix}.
+     * 
+     * @param X
+     *        defines on which world axis and direction the X axis of the device
+     *        is mapped.
+     * 
+     * @param Y
+     *        defines on which world axis and direction the Y axis of the device
+     *        is mapped.
+     * 
+     * @param outR
+     *        the transformed rotation matrix. inR and outR can be the same
      *        array, but it is not recommended for performance reason.
-     * @return true on success. false if the input parameters are incorrect, for
-     * instance if X and Y define the same axis. Or if inR and outR don't have 
-     * the same length.
+     * 
+     * @return <code>true</code> on success. <code>false</code> if the input
+     *         parameters are incorrect, for instance if X and Y define the same
+     *         axis. Or if inR and outR don't have the same length.
      */
 
     public static boolean remapCoordinateSystem(float[] inR, int X, int Y,
@@ -1301,19 +1456,24 @@
 
     /**
      * Computes the device's orientation based on the rotation matrix.
-     * <p> When it returns, the array values is filled with the result:
+     * <p>
+     * When it returns, the array values is filled with the result:
+     * <ul>
      * <li>values[0]: <i>azimuth</i>, rotation around the Z axis.</li>
      * <li>values[1]: <i>pitch</i>, rotation around the X axis.</li>
      * <li>values[2]: <i>roll</i>, rotation around the Y axis.</li>
+     * </ul>
      * <p>
      * All three angles above are in <b>radians</b> and <b>positive</b> in the
      * <b>counter-clockwise</b> direction.
-     *
-     * @param R rotation matrix see {@link #getRotationMatrix}.
-     * @param values an array of 3 floats to hold the result.
+     * 
+     * @param R
+     *        rotation matrix see {@link #getRotationMatrix}.
+     * @param values
+     *        an array of 3 floats to hold the result.
      * @return The array values passed as argument.
      */
-    public static float[] getOrientation(float[] R, float values[]) {
+   public static float[] getOrientation(float[] R, float values[]) {
         /*
          * 4x4 (length=16) case:
          *   /  R[ 0]   R[ 1]   R[ 2]   0  \
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/MessageQueue.java b/core/java/android/os/MessageQueue.java
index df30c76..adb11c8 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -341,11 +341,4 @@
         }
     }
     */
-
-    void poke()
-    {
-        synchronized (this) {
-            nativeWake();
-        }
-    }
 }
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index 66a83b8..e5ebc69 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -26,7 +26,7 @@
  * to the ViewRoot through a Binder transaction as part of registering the Window.
  * @hide
  */
-public class InputChannel implements Parcelable {
+public final class InputChannel implements Parcelable {
     private static final String TAG = "InputChannel";
     
     public static final Parcelable.Creator<InputChannel> CREATOR
diff --git a/core/java/android/view/InputTarget.java b/core/java/android/view/InputTarget.java
index e56e03c..6ff7305 100644
--- a/core/java/android/view/InputTarget.java
+++ b/core/java/android/view/InputTarget.java
@@ -25,7 +25,7 @@
  * These parameters are used by the native input dispatching code.
  * @hide
  */
-public class InputTarget {
+public final class InputTarget {
     public InputChannel mInputChannel;
     public int mFlags;
     public long mTimeoutNanos;
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/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-de/strings.xml b/core/res/res/values-de/strings.xml
index 218054d..11095c0 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -132,7 +132,7 @@
     <string name="turn_off_radio" msgid="8198784949987062346">"Funk ausschalten"</string>
     <string name="screen_lock" msgid="799094655496098153">"Display-Sperre"</string>
     <string name="power_off" msgid="4266614107412865048">"Ausschalten"</string>
-    <string name="shutdown_progress" msgid="2281079257329981203">"Fährt herunter..."</string>
+    <string name="shutdown_progress" msgid="2281079257329981203">"Wird heruntergefahren..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Ihr Telefon wird heruntergefahren."</string>
     <string name="recent_tasks_title" msgid="3691764623638127888">"Zuletzt verwendet"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Keine zuletzt verwendeten Anwendungen"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 33cb9fb..d0b3b8a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -734,7 +734,7 @@
     <string name="chooseActivity" msgid="1009246475582238425">"Seleziona un\'azione"</string>
     <string name="noApplications" msgid="1691104391758345586">"Nessuna applicazione è in grado di svolgere questa azione."</string>
     <string name="aerr_title" msgid="653922989522758100">"Spiacenti."</string>
-    <string name="aerr_application" msgid="4683614104336409186">"Interruzione imprevista dell\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> (processo<xliff:g id="PROCESS">%2$s</xliff:g>). Riprova."</string>
+    <string name="aerr_application" msgid="4683614104336409186">"Interruzione imprevista dell\'applicazione <xliff:g id="APPLICATION">%1$s</xliff:g> (processo <xliff:g id="PROCESS">%2$s</xliff:g>). Riprova."</string>
     <string name="aerr_process" msgid="1551785535966089511">"Interruzione imprevista del processo <xliff:g id="PROCESS">%1$s</xliff:g>. Riprova."</string>
     <string name="anr_title" msgid="3100070910664756057">"Spiacenti."</string>
     <string name="anr_activity_application" msgid="3538242413112507636">"L\'attività <xliff:g id="ACTIVITY">%1$s</xliff:g> (nell\'applicazione <xliff:g id="APPLICATION">%2$s</xliff:g>) non risponde."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index afe7b67..8aaf761 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -148,7 +148,7 @@
     <string name="safeMode" msgid="2788228061547930246">"안전 모드"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 시스템"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"요금이 부과되는 서비스"</string>
-    <string name="permgroupdesc_costMoney" msgid="8193824940620517189">"응용프로그램이 요금이 부과될 수 있는 작업을 할 수 있도록 합니다."</string>
+    <string name="permgroupdesc_costMoney" msgid="8193824940620517189">"애플리케이션이 요금이 부과될 수 있는 작업을 할 수 있도록 합니다."</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"메시지"</string>
     <string name="permgroupdesc_messages" msgid="7045736972019211994">"SMS, 이메일 및 기타 메시지를 읽고 씁니다."</string>
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"개인정보"</string>
@@ -156,7 +156,7 @@
     <string name="permgrouplab_location" msgid="635149742436692049">"위치"</string>
     <string name="permgroupdesc_location" msgid="2430258821648348660">"실제 위치 모니터링"</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"네트워크 통신"</string>
-    <string name="permgroupdesc_network" msgid="5035763698958415998">"응용프로그램이 다양한 네트워크 기능에 액세스할 수 있도록 합니다."</string>
+    <string name="permgroupdesc_network" msgid="5035763698958415998">"애플리케이션이 다양한 네트워크 기능에 액세스할 수 있도록 합니다."</string>
     <string name="permgrouplab_accounts" msgid="3359646291125325519">"계정"</string>
     <string name="permgroupdesc_accounts" msgid="4948732641827091312">"사용 가능한 계정에 액세스합니다."</string>
     <string name="permgrouplab_hardwareControls" msgid="7998214968791599326">"하드웨어 제어"</string>
@@ -166,261 +166,261 @@
     <string name="permgrouplab_systemTools" msgid="4652191644082714048">"시스템 도구"</string>
     <string name="permgroupdesc_systemTools" msgid="8162102602190734305">"시스템을 하위 수준에서 액세스하고 제어합니다."</string>
     <string name="permgrouplab_developmentTools" msgid="3446164584710596513">"개발 도구"</string>
-    <string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"응용프로그램 개발자에게만 필요한 기능입니다."</string>
+    <string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"애플리케이션 개발자에게만 필요한 기능입니다."</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"저장"</string>
     <string name="permgroupdesc_storage" msgid="9203302214915355774">"SD 카드에 액세스합니다."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"상태 표시줄 사용 중지 또는 수정"</string>
-    <string name="permdesc_statusBar" msgid="1365473595331989732">"응용프로그램이 상태 표시줄을 사용 중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 합니다."</string>
+    <string name="permdesc_statusBar" msgid="1365473595331989732">"애플리케이션이 상태 표시줄을 사용 중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 합니다."</string>
     <string name="permlab_expandStatusBar" msgid="1148198785937489264">"상태 표시줄 확장/축소"</string>
-    <string name="permdesc_expandStatusBar" msgid="7088604400110768665">"응용프로그램이 상태 표시줄을 확장하거나 축소할 수 있도록 합니다."</string>
+    <string name="permdesc_expandStatusBar" msgid="7088604400110768665">"애플리케이션이 상태 표시줄을 확장하거나 축소할 수 있도록 합니다."</string>
     <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"발신전화 가로채기"</string>
-    <string name="permdesc_processOutgoingCalls" msgid="2228988201852654461">"응용프로그램이 발신전화를 처리하고 전화를 걸 번호를 변경할 수 있도록 합니다. 이 경우 악성 응용프로그램이 발신전화를 모니터링하거나, 다른 방향으로 돌리거나, 중단시킬 수 있습니다."</string>
+    <string name="permdesc_processOutgoingCalls" msgid="2228988201852654461">"애플리케이션이 발신전화를 처리하고 전화를 걸 번호를 변경할 수 있도록 합니다. 이 경우 악성 애플리케이션이 발신전화를 모니터링하거나, 다른 방향으로 돌리거나, 중단시킬 수 있습니다."</string>
     <string name="permlab_receiveSms" msgid="2697628268086208535">"SMS 수신"</string>
-    <string name="permdesc_receiveSms" msgid="6298292335965966117">"응용프로그램이 SMS 메시지를 받고 처리할 수 있도록 합니다. 이 경우 악성 응용프로그램이 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string>
+    <string name="permdesc_receiveSms" msgid="6298292335965966117">"애플리케이션이 SMS 메시지를 받고 처리할 수 있도록 합니다. 이 경우 악성 애플리케이션이 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string>
     <string name="permlab_receiveMms" msgid="8894700916188083287">"MMS 수신"</string>
-    <string name="permdesc_receiveMms" msgid="4563346832000174373">"응용프로그램이 MMS 메시지를 받고 처리할 수 있도록 합니다. 이 경우 악성 응용프로그램이 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string>
+    <string name="permdesc_receiveMms" msgid="4563346832000174373">"애플리케이션이 MMS 메시지를 받고 처리할 수 있도록 합니다. 이 경우 악성 애플리케이션이 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string>
     <string name="permlab_sendSms" msgid="5600830612147671529">"SMS 메시지 보내기"</string>
-    <string name="permdesc_sendSms" msgid="1946540351763502120">"응용프로그램이 SMS 메시지를 보낼 수 있도록 합니다. 이 경우 악성 응용프로그램이 사용자의 확인 없이 메시지를 전송하여 요금을 부과할 수 있습니다."</string>
+    <string name="permdesc_sendSms" msgid="1946540351763502120">"애플리케이션이 SMS 메시지를 보낼 수 있도록 합니다. 이 경우 악성 애플리케이션이 사용자의 확인 없이 메시지를 전송하여 요금을 부과할 수 있습니다."</string>
     <string name="permlab_readSms" msgid="4085333708122372256">"SMS 또는 MMS 읽기"</string>
-    <string name="permdesc_readSms" msgid="3002170087197294591">"응용프로그램이 휴대전화 또는 SIM 카드에 저장된 SMS 메시지를 읽을 수 있도록 합니다. 이 경우 악성 응용프로그램이 기밀 메시지를 읽을 수 있습니다."</string>
+    <string name="permdesc_readSms" msgid="3002170087197294591">"애플리케이션이 휴대전화 또는 SIM 카드에 저장된 SMS 메시지를 읽을 수 있도록 합니다. 이 경우 악성 애플리케이션이 기밀 메시지를 읽을 수 있습니다."</string>
     <string name="permlab_writeSms" msgid="6881122575154940744">"SMS 또는 MMS 편집"</string>
-    <string name="permdesc_writeSms" msgid="6299398896177548095">"응용프로그램이 휴대전화 또는 SIM 카드에 저장된 SMS 메시지에 쓸 수 있도록 합니다. 단, 악성 응용프로그램이 이 기능을 이용하여 메시지를 삭제할 수 있습니다."</string>
+    <string name="permdesc_writeSms" msgid="6299398896177548095">"애플리케이션이 휴대전화 또는 SIM 카드에 저장된 SMS 메시지에 쓸 수 있도록 합니다. 단, 악성 애플리케이션이 이 기능을 이용하여 메시지를 삭제할 수 있습니다."</string>
     <string name="permlab_receiveWapPush" msgid="8258226427716551388">"WAP 수신"</string>
-    <string name="permdesc_receiveWapPush" msgid="5979623826128082171">"응용프로그램이 WAP 메시지를 받고 처리할 수 있도록 합니다. 이 경우 악성 응용프로그램이 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string>
-    <string name="permlab_getTasks" msgid="5005277531132573353">"실행 중인 응용프로그램 검색"</string>
-    <string name="permdesc_getTasks" msgid="7048711358713443341">"응용프로그램이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있도록 합니다. 이 경우 악성 응용프로그램이 다른 응용프로그램에 대한 개인 정보를 검색할 수 있습니다."</string>
-    <string name="permlab_reorderTasks" msgid="5669588525059921549">"실행 중인 응용프로그램 순서 재지정"</string>
-    <string name="permdesc_reorderTasks" msgid="126252774270522835">"응용프로그램이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 합니다. 이 경우 악성 응용프로그램이 사용자의 조작 없이 앞으로 이동할 수 있습니다."</string>
-    <string name="permlab_setDebugApp" msgid="4339730312925176742">"응용프로그램 디버깅 사용"</string>
-    <string name="permdesc_setDebugApp" msgid="5584310661711990702">"응용프로그램이 다른 응용프로그램에 대해 디버깅을 사용할 수 있도록 합니다. 이 경우 악성 응용프로그램이 다른 응용프로그램을 중지시킬 수 있습니다."</string>
+    <string name="permdesc_receiveWapPush" msgid="5979623826128082171">"애플리케이션이 WAP 메시지를 받고 처리할 수 있도록 합니다. 이 경우 악성 애플리케이션이 메시지를 모니터링하거나 사용자가 보기 전에 삭제할 수 있습니다."</string>
+    <string name="permlab_getTasks" msgid="5005277531132573353">"실행 중인 애플리케이션 검색"</string>
+    <string name="permdesc_getTasks" msgid="7048711358713443341">"애플리케이션이 현재 실행 중이거나 최근에 실행된 작업에 대한 정보를 검색할 수 있도록 합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션에 대한 개인 정보를 검색할 수 있습니다."</string>
+    <string name="permlab_reorderTasks" msgid="5669588525059921549">"실행 중인 애플리케이션 순서 재지정"</string>
+    <string name="permdesc_reorderTasks" msgid="126252774270522835">"애플리케이션이 작업을 포그라운드나 백그라운드로 이동할 수 있도록 합니다. 이 경우 악성 애플리케이션이 사용자의 조작 없이 앞으로 이동할 수 있습니다."</string>
+    <string name="permlab_setDebugApp" msgid="4339730312925176742">"애플리케이션 디버깅 사용"</string>
+    <string name="permdesc_setDebugApp" msgid="5584310661711990702">"애플리케이션이 다른 애플리케이션에 대해 디버깅을 사용할 수 있도록 합니다. 이 경우 악성 애플리케이션이 다른 애플리케이션을 중지시킬 수 있습니다."</string>
     <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI 설정 변경"</string>
-    <string name="permdesc_changeConfiguration" msgid="3465121501528064399">"응용프로그램이 로케일 또는 전체 글꼴 크기와 같은 현재 구성을 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_changeConfiguration" msgid="3465121501528064399">"애플리케이션이 로케일 또는 전체 글꼴 크기와 같은 현재 구성을 변경할 수 있도록 합니다."</string>
     <string name="permlab_enableCarMode" msgid="5684504058192921098">"차량 모드 사용"</string>
-    <string name="permdesc_enableCarMode" msgid="5673461159384850628">"응용프로그램이 차량 모드를 사용할 수 있도록 합니다."</string>
+    <string name="permdesc_enableCarMode" msgid="5673461159384850628">"애플리케이션이 차량 모드를 사용할 수 있도록 합니다."</string>
     <string name="permlab_killBackgroundProcesses" msgid="8373714752793061963">"백그라운드 프로세스 종료"</string>
-    <string name="permdesc_killBackgroundProcesses" msgid="2908829602869383753">"메모리가 부족하지 않은 경우에도 응용프로그램이 다른 응용프로그램의 백그라운드 프로세스를 중단할 수 있도록 합니다."</string>
-    <string name="permlab_forceStopPackages" msgid="1447830113260156236">"다른 응용프로그램 강제 종료"</string>
-    <string name="permdesc_forceStopPackages" msgid="7263036616161367402">"응용프로그램이 다른 응용프로그램을 강제로 종료할 수 있도록 합니다."</string>
-    <string name="permlab_forceBack" msgid="1804196839880393631">"강제로 응용프로그램 닫기"</string>
-    <string name="permdesc_forceBack" msgid="6534109744159919013">"응용프로그램이 포그라운드에 있는 활동을 강제로 닫고 되돌아갈 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_killBackgroundProcesses" msgid="2908829602869383753">"메모리가 부족하지 않은 경우에도 애플리케이션이 다른 애플리케이션의 백그라운드 프로세스를 중단할 수 있도록 합니다."</string>
+    <string name="permlab_forceStopPackages" msgid="1447830113260156236">"다른 애플리케이션 강제 종료"</string>
+    <string name="permdesc_forceStopPackages" msgid="7263036616161367402">"애플리케이션이 다른 애플리케이션을 강제로 종료할 수 있도록 합니다."</string>
+    <string name="permlab_forceBack" msgid="1804196839880393631">"강제로 애플리케이션 닫기"</string>
+    <string name="permdesc_forceBack" msgid="6534109744159919013">"애플리케이션이 포그라운드에 있는 활동을 강제로 닫고 되돌아갈 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_dump" msgid="1681799862438954752">"시스템 내부 상태 검색"</string>
-    <string name="permdesc_dump" msgid="2198776174276275220">"응용프로그램이 시스템의 내부 상태를 검색할 수 있도록 합니다. 단, 악성 응용프로그램이 이 기능을 이용하여 일반적으로 필요하지 않은 다양한 개인정보와 보안정보를 검색할 수 있습니다."</string>
+    <string name="permdesc_dump" msgid="2198776174276275220">"애플리케이션이 시스템의 내부 상태를 검색할 수 있도록 합니다. 단, 악성 애플리케이션이 이 기능을 이용하여 일반적으로 필요하지 않은 다양한 개인정보와 보안정보를 검색할 수 있습니다."</string>
     <string name="permlab_shutdown" msgid="7185747824038909016">"부분 종료"</string>
     <string name="permdesc_shutdown" msgid="7046500838746291775">"작업 관리자를 종료 상태로 설정합니다. 전체 종료를 수행하지는 않습니다."</string>
-    <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"응용프로그램 전환 방지"</string>
-    <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"사용자가 다른 응용프로그램으로 전환하지 못하게 합니다."</string>
-    <string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"실행 중인 모든 응용프로그램 모니터링 및 제어"</string>
-    <string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"응용프로그램이 시스템에서 활동이 시작되는 방식을 모니터링하고 제어할 수 있도록 합니다. 단, 악성 응용프로그램이 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 휴대전화 사용 시에는 필요하지 않습니다."</string>
+    <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"애플리케이션 전환 방지"</string>
+    <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"사용자가 다른 애플리케이션으로 전환하지 못하게 합니다."</string>
+    <string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"실행 중인 모든 애플리케이션 모니터링 및 제어"</string>
+    <string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"애플리케이션이 시스템에서 활동이 시작되는 방식을 모니터링하고 제어할 수 있도록 합니다. 단, 악성 애플리케이션이 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 휴대전화 사용 시에는 필요하지 않습니다."</string>
     <string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"패키지 제거 브로드캐스트 보내기"</string>
-    <string name="permdesc_broadcastPackageRemoved" msgid="3453286591439891260">"응용프로그램이 응용프로그램 패키지가 삭제되었다는 알림을 브로드캐스트할 수 있도록 합니다. 이 경우 악성 응용프로그램이 실행 중인 다른 응용프로그램을 중지시킬 수 있습니다."</string>
+    <string name="permdesc_broadcastPackageRemoved" msgid="3453286591439891260">"애플리케이션이 애플리케이션 패키지가 삭제되었다는 알림을 브로드캐스트할 수 있도록 합니다. 이 경우 악성 애플리케이션이 실행 중인 다른 애플리케이션을 중지시킬 수 있습니다."</string>
     <string name="permlab_broadcastSmsReceived" msgid="5689095009030336593">"SMS 수신 브로드캐스트 보내기"</string>
-    <string name="permdesc_broadcastSmsReceived" msgid="9122419277306740155">"응용프로그램이 SMS 메시지를 받았다는 알림을 브로드캐스트할 수 있도록 합니다. 이 경우 악성 응용프로그램이 수신된 SMS 메시지처럼 위장할 수 있습니다."</string>
+    <string name="permdesc_broadcastSmsReceived" msgid="9122419277306740155">"애플리케이션이 SMS 메시지를 받았다는 알림을 브로드캐스트할 수 있도록 합니다. 이 경우 악성 애플리케이션이 수신된 SMS 메시지처럼 위장할 수 있습니다."</string>
     <string name="permlab_broadcastWapPush" msgid="3145347413028582371">"WAP-PUSH-수신 브로드캐스트 보내기"</string>
-    <string name="permdesc_broadcastWapPush" msgid="3955303669461378091">"응용프로그램이 WAP PUSH 메시지를 받았다는 알림을 브로드캐스트할 수 있도록 합니다. 이 경우 악성 응용프로그램이 MMS 메시지를 받은 것처럼 위장하거나 웹페이지의 콘텐츠를 악성 변종으로 몰래 바꿀 수 있습니다."</string>
+    <string name="permdesc_broadcastWapPush" msgid="3955303669461378091">"애플리케이션이 WAP PUSH 메시지를 받았다는 알림을 브로드캐스트할 수 있도록 합니다. 이 경우 악성 애플리케이션이 MMS 메시지를 받은 것처럼 위장하거나 웹페이지의 콘텐츠를 악성 변종으로 몰래 바꿀 수 있습니다."</string>
     <string name="permlab_setProcessLimit" msgid="2451873664363662666">"실행 중인 프로세스 수 제한"</string>
-    <string name="permdesc_setProcessLimit" msgid="7824786028557379539">"응용프로그램이 실행할 최대 프로세스 수를 제어할 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
-    <string name="permlab_setAlwaysFinish" msgid="5342837862439543783">"모든 백그라운드 응용프로그램이 닫히도록 하기"</string>
-    <string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"응용프로그램이 백그라운드로 이동한 활동을 항상 바로 종료할지 여부를 제어할 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_setProcessLimit" msgid="7824786028557379539">"애플리케이션이 실행할 최대 프로세스 수를 제어할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
+    <string name="permlab_setAlwaysFinish" msgid="5342837862439543783">"모든 백그라운드 애플리케이션이 닫히도록 하기"</string>
+    <string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"애플리케이션이 백그라운드로 이동한 활동을 항상 바로 종료할지 여부를 제어할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_batteryStats" msgid="7863923071360031652">"배터리 통계 수정"</string>
-    <string name="permdesc_batteryStats" msgid="5847319823772230560">"수집된 배터리 통계를 수정할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_batteryStats" msgid="5847319823772230560">"수집된 배터리 통계를 수정할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_backup" msgid="470013022865453920">"시스템 백업 및 복원 관리"</string>
-    <string name="permdesc_backup" msgid="4837493065154256525">"응용프로그램이 시스템의 백업 및 복원 매커니즘을 제어할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_backup" msgid="4837493065154256525">"애플리케이션이 시스템의 백업 및 복원 매커니즘을 제어할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_internalSystemWindow" msgid="2148563628140193231">"인증되지 않은 창 표시"</string>
-    <string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"내부 시스템 사용자 인터페이스에서 사용하는 창을 만들 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"내부 시스템 사용자 인터페이스에서 사용하는 창을 만들 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_systemAlertWindow" msgid="3372321942941168324">"시스템 수준 경고 표시"</string>
-    <string name="permdesc_systemAlertWindow" msgid="5109622689323490558">"응용프로그램이 시스템 경고 창을 표시할 수 있도록 합니다. 이 경우 악성 응용프로그램이 휴대전화 화면 전체를 차지할 수 있습니다."</string>
+    <string name="permdesc_systemAlertWindow" msgid="5109622689323490558">"애플리케이션이 시스템 경고 창을 표시할 수 있도록 합니다. 이 경우 악성 애플리케이션이 휴대전화 화면 전체를 차지할 수 있습니다."</string>
     <string name="permlab_setAnimationScale" msgid="2805103241153907174">"전체 애니메이션 속도 수정"</string>
-    <string name="permdesc_setAnimationScale" msgid="7181522138912391988">"응용프로그램이 언제든지 전체 애니메이션 속도를 빠르게 또는 느리게 변경할 수 있도록 합니다."</string>
-    <string name="permlab_manageAppTokens" msgid="17124341698093865">"응용프로그램 토큰 관리"</string>
-    <string name="permdesc_manageAppTokens" msgid="977127907524195988">"응용프로그램이 일반적인 Z-순서를 무시하여 자체 토큰을 만들고 관리할 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_setAnimationScale" msgid="7181522138912391988">"애플리케이션이 언제든지 전체 애니메이션 속도를 빠르게 또는 느리게 변경할 수 있도록 합니다."</string>
+    <string name="permlab_manageAppTokens" msgid="17124341698093865">"애플리케이션 토큰 관리"</string>
+    <string name="permdesc_manageAppTokens" msgid="977127907524195988">"애플리케이션이 일반적인 Z-순서를 무시하여 자체 토큰을 만들고 관리할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_injectEvents" msgid="1378746584023586600">"키 및 컨트롤 버튼 누르기"</string>
-    <string name="permdesc_injectEvents" msgid="3946098050410874715">"응용프로그램이 입력 이벤트(예: 키 누름)를 다른 응용프로그램에 전달할 수 있도록 합니다. 이 경우 악성 응용프로그램이 휴대전화를 완전히 제어할 수 있습니다."</string>
+    <string name="permdesc_injectEvents" msgid="3946098050410874715">"애플리케이션이 입력 이벤트(예: 키 누름)를 다른 애플리케이션에 전달할 수 있도록 합니다. 이 경우 악성 애플리케이션이 휴대전화를 완전히 제어할 수 있습니다."</string>
     <string name="permlab_readInputState" msgid="469428900041249234">"사용자가 입력한 내용 및 수행한 작업 기록"</string>
-    <string name="permdesc_readInputState" msgid="5132879321450325445">"응용프로그램이 다른 응용프로그램과 상호작용할 때에도 사용자가 누르는 키(예: 비밀번호 입력)를 볼 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_readInputState" msgid="5132879321450325445">"애플리케이션이 다른 애플리케이션과 상호작용할 때에도 사용자가 누르는 키(예: 비밀번호 입력)를 볼 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"입력 방법 연결"</string>
-    <string name="permdesc_bindInputMethod" msgid="3734838321027317228">"권한을 가진 프로그램이 입력 방법에 대한 최상위 인터페이스를 사용하도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_bindInputMethod" msgid="3734838321027317228">"권한을 가진 프로그램이 입력 방법에 대한 최상위 인터페이스를 사용하도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_bindWallpaper" msgid="8716400279937856462">"배경화면 연결"</string>
-    <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"권한을 가진 프로그램이 배경화면에 대한 최상위 인터페이스를 사용하도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"권한을 가진 프로그램이 배경화면에 대한 최상위 인터페이스를 사용하도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_bindDeviceAdmin" msgid="8704986163711455010">"기기 관리자와 상호 작용"</string>
-    <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"보유자가 기기 관리자에게 인텐트를 보낼 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
+    <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"보유자가 기기 관리자에게 인텐트를 보낼 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"화면 방향 변경"</string>
-    <string name="permdesc_setOrientation" msgid="6335814461615851863">"응용프로그램이 언제든지 화면 회전을 변경할 수 있도록 합니다. 일반 응용프로그램에는 절대로 필요하지 않습니다."</string>
-    <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"응용프로그램에 Linux 시그널 보내기"</string>
-    <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"응용프로그램이 제공된 시그널을 모든 영구 프로세스로 보내도록 요청할 수 있도록 합니다."</string>
-    <string name="permlab_persistentActivity" msgid="8659652042401085862">"응용프로그램이 항상 실행되도록 설정"</string>
-    <string name="permdesc_persistentActivity" msgid="5037199778265006008">"응용프로그램이 자신의 일부 구성 요소를 지속가능으로 설정하여 다른 응용프로그램에 사용할 수 없도록 합니다."</string>
-    <string name="permlab_deletePackages" msgid="3343439331576348805">"응용프로그램 삭제"</string>
-    <string name="permdesc_deletePackages" msgid="3634943677518723314">"응용프로그램이 Android 패키지를 삭제할 수 있도록 합니다. 이 경우 악성 응용프로그램이 중요한 응용프로그램을 삭제할 수 있습니다."</string>
-    <string name="permlab_clearAppUserData" msgid="2192134353540277878">"다른 응용프로그램의 데이터 삭제"</string>
-    <string name="permdesc_clearAppUserData" msgid="7546345080434325456">"응용프로그램이 사용자 데이터를 지울 수 있도록 합니다."</string>
-    <string name="permlab_deleteCacheFiles" msgid="1518556602634276725">"다른 응용프로그램의 캐시 삭제"</string>
-    <string name="permdesc_deleteCacheFiles" msgid="2283074077168165971">"응용프로그램이 캐시 파일을 삭제할 수 있도록 합니다."</string>
-    <string name="permlab_getPackageSize" msgid="4799785352306641460">"응용프로그램 저장공간 계산"</string>
-    <string name="permdesc_getPackageSize" msgid="5557253039670753437">"응용프로그램이 해당 코드, 데이터 및 캐시 크기를 검색할 수 있도록 합니다."</string>
-    <string name="permlab_installPackages" msgid="335800214119051089">"응용프로그램 직접 설치"</string>
-    <string name="permdesc_installPackages" msgid="526669220850066132">"응용프로그램이 새로운 또는 업데이트된 Android 패키지를 설치할 수 있도록 합니다. 이 경우 악성 응용프로그램이 임의의 강력한 권한으로 새 응용프로그램을 추가할 수 있습니다."</string>
-    <string name="permlab_clearAppCache" msgid="4747698311163766540">"모든 응용프로그램 캐시 데이터 삭제"</string>
-    <string name="permdesc_clearAppCache" msgid="7740465694193671402">"응용프로그램이 응용프로그램 캐시 디렉토리에 있는 파일을 삭제하여 휴대전화의 저장공간을 늘릴 수 있도록 합니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string>
-    <string name="permlab_movePackage" msgid="728454979946503926">"응용프로그램 리소스 이동"</string>
-    <string name="permdesc_movePackage" msgid="6323049291923925277">"응용프로그램이 응용프로그램 리소스를 내부에서 외부 미디어로 또는 그 반대로 이동할 수 있도록 합니다."</string>
+    <string name="permdesc_setOrientation" msgid="6335814461615851863">"애플리케이션이 언제든지 화면 회전을 변경할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
+    <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"애플리케이션에 Linux 시그널 보내기"</string>
+    <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"애플리케이션이 제공된 시그널을 모든 영구 프로세스로 보내도록 요청할 수 있도록 합니다."</string>
+    <string name="permlab_persistentActivity" msgid="8659652042401085862">"애플리케이션이 항상 실행되도록 설정"</string>
+    <string name="permdesc_persistentActivity" msgid="5037199778265006008">"애플리케이션이 자신의 일부 구성 요소를 지속가능으로 설정하여 다른 애플리케이션에 사용할 수 없도록 합니다."</string>
+    <string name="permlab_deletePackages" msgid="3343439331576348805">"애플리케이션 삭제"</string>
+    <string name="permdesc_deletePackages" msgid="3634943677518723314">"애플리케이션이 Android 패키지를 삭제할 수 있도록 합니다. 이 경우 악성 애플리케이션이 중요한 애플리케이션을 삭제할 수 있습니다."</string>
+    <string name="permlab_clearAppUserData" msgid="2192134353540277878">"다른 애플리케이션의 데이터 삭제"</string>
+    <string name="permdesc_clearAppUserData" msgid="7546345080434325456">"애플리케이션이 사용자 데이터를 지울 수 있도록 합니다."</string>
+    <string name="permlab_deleteCacheFiles" msgid="1518556602634276725">"다른 애플리케이션의 캐시 삭제"</string>
+    <string name="permdesc_deleteCacheFiles" msgid="2283074077168165971">"애플리케이션이 캐시 파일을 삭제할 수 있도록 합니다."</string>
+    <string name="permlab_getPackageSize" msgid="4799785352306641460">"애플리케이션 저장공간 계산"</string>
+    <string name="permdesc_getPackageSize" msgid="5557253039670753437">"애플리케이션이 해당 코드, 데이터 및 캐시 크기를 검색할 수 있도록 합니다."</string>
+    <string name="permlab_installPackages" msgid="335800214119051089">"애플리케이션 직접 설치"</string>
+    <string name="permdesc_installPackages" msgid="526669220850066132">"애플리케이션이 새로운 또는 업데이트된 Android 패키지를 설치할 수 있도록 합니다. 이 경우 악성 애플리케이션이 임의의 강력한 권한으로 새 애플리케이션을 추가할 수 있습니다."</string>
+    <string name="permlab_clearAppCache" msgid="4747698311163766540">"모든 애플리케이션 캐시 데이터 삭제"</string>
+    <string name="permdesc_clearAppCache" msgid="7740465694193671402">"애플리케이션이 애플리케이션 캐시 디렉토리에 있는 파일을 삭제하여 휴대전화의 저장공간을 늘릴 수 있도록 합니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string>
+    <string name="permlab_movePackage" msgid="728454979946503926">"애플리케이션 리소스 이동"</string>
+    <string name="permdesc_movePackage" msgid="6323049291923925277">"애플리케이션이 애플리케이션 리소스를 내부에서 외부 미디어로 또는 그 반대로 이동할 수 있도록 합니다."</string>
     <string name="permlab_readLogs" msgid="4811921703882532070">"시스템 로그 파일 읽기"</string>
-    <string name="permdesc_readLogs" msgid="2257937955580475902">"응용프로그램이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 응용프로그램은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string>
+    <string name="permdesc_readLogs" msgid="2257937955580475902">"애플리케이션이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 애플리케이션은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"진단 그룹 소유의 리소스 읽기/쓰기"</string>
-    <string name="permdesc_diagnostic" msgid="3121238373951637049">"응용프로그램이 진단 그룹 소유의 리소스(예: /dev에 있는 파일)를 읽고 쓸 수 있도록 합니다. 이 기능은 시스템 안정성 및 보안에 영향을 미칠 수 있으므로 제조업체 또는 사업자가 하드웨어 관련 진단을 수행하는 경우에만 사용해야 합니다."</string>
-    <string name="permlab_changeComponentState" msgid="79425198834329406">"응용프로그램 구성 요소 사용 또는 사용 안함"</string>
-    <string name="permdesc_changeComponentState" msgid="4569107043246700630">"응용프로그램이 다른 응용프로그램 구성 요소 사용 여부를 변경할 수 있도록 합니다. 이 경우 악성 응용프로그램이 중요한 휴대전화 기능을 사용하지 않도록 설정할 수 있습니다. 이 권한을 설정할 경우 응용프로그램 구성 요소가 사용 불가능하게 되거나 일관성이 맞지 않거나 불안정해질 수 있으므로 주의해야 합니다."</string>
-    <string name="permlab_setPreferredApplications" msgid="3393305202145172005">"기본 응용프로그램 설정"</string>
-    <string name="permdesc_setPreferredApplications" msgid="760008293501937546">"응용프로그램이 기본 응용프로그램을 수정할 수 있도록 합니다. 이 경우 악성 응용프로그램이 사용자의 개인 정보를 수집하기 위해 기존 응용프로그램으로 위장하도록 실행되는 응용프로그램을 몰래 변경할 수 있습니다."</string>
+    <string name="permdesc_diagnostic" msgid="3121238373951637049">"애플리케이션이 진단 그룹 소유의 리소스(예: /dev에 있는 파일)를 읽고 쓸 수 있도록 합니다. 이 기능은 시스템 안정성 및 보안에 영향을 미칠 수 있으므로 제조업체 또는 사업자가 하드웨어 관련 진단을 수행하는 경우에만 사용해야 합니다."</string>
+    <string name="permlab_changeComponentState" msgid="79425198834329406">"애플리케이션 구성 요소 사용 또는 사용 안함"</string>
+    <string name="permdesc_changeComponentState" msgid="4569107043246700630">"애플리케이션이 다른 애플리케이션 구성 요소 사용 여부를 변경할 수 있도록 합니다. 이 경우 악성 애플리케이션이 중요한 휴대전화 기능을 사용하지 않도록 설정할 수 있습니다. 이 권한을 설정할 경우 애플리케이션 구성 요소가 사용 불가능하게 되거나 일관성이 맞지 않거나 불안정해질 수 있으므로 주의해야 합니다."</string>
+    <string name="permlab_setPreferredApplications" msgid="3393305202145172005">"기본 애플리케이션 설정"</string>
+    <string name="permdesc_setPreferredApplications" msgid="760008293501937546">"애플리케이션이 기본 애플리케이션을 수정할 수 있도록 합니다. 이 경우 악성 애플리케이션이 사용자의 개인 정보를 수집하기 위해 기존 애플리케이션으로 위장하도록 실행되는 애플리케이션을 몰래 변경할 수 있습니다."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"전체 시스템 설정 수정"</string>
-    <string name="permdesc_writeSettings" msgid="838789419871034696">"응용프로그램이 시스템의 설정 데이터를 수정할 수 있도록 합니다. 이 경우 악성 응용프로그램이 시스템 구성을 손상시킬 수 있습니다."</string>
+    <string name="permdesc_writeSettings" msgid="838789419871034696">"애플리케이션이 시스템의 설정 데이터를 수정할 수 있도록 합니다. 이 경우 악성 애플리케이션이 시스템 구성을 손상시킬 수 있습니다."</string>
     <string name="permlab_writeSecureSettings" msgid="204676251876718288">"보안 시스템 설정 수정"</string>
-    <string name="permdesc_writeSecureSettings" msgid="5497873143539034724">"응용프로그램이 시스템의 보안 설정값 데이터를 수정할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_writeSecureSettings" msgid="5497873143539034724">"애플리케이션이 시스템의 보안 설정값 데이터를 수정할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_writeGservices" msgid="2149426664226152185">"Google 서비스 지도 수정"</string>
-    <string name="permdesc_writeGservices" msgid="6602362746516676175">"응용프로그램이 Google 서비스 지도를 수정할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_writeGservices" msgid="6602362746516676175">"애플리케이션이 Google 서비스 지도를 수정할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"부팅할 때 자동 시작"</string>
-    <string name="permdesc_receiveBootCompleted" msgid="698336728415008796">"응용프로그램이 시스템 부팅이 끝난 후 바로 시작할 수 있도록 합니다. 이 경우 휴대전화가 시작하는 데 시간이 오래 걸리고 응용프로그램이 항상 실행되어 전체 휴대전화 속도가 느려질 수 있습니다."</string>
+    <string name="permdesc_receiveBootCompleted" msgid="698336728415008796">"애플리케이션이 시스템 부팅이 끝난 후 바로 시작할 수 있도록 합니다. 이 경우 휴대전화가 시작하는 데 시간이 오래 걸리고 애플리케이션이 항상 실행되어 전체 휴대전화 속도가 느려질 수 있습니다."</string>
     <string name="permlab_broadcastSticky" msgid="7919126372606881614">"스티키 브로드캐스트 보내기"</string>
-    <string name="permdesc_broadcastSticky" msgid="1920045289234052219">"응용프로그램이 브로드캐스트가 끝난 후에도 유지되는 스티키 브로드캐스트(Sticky Broadcast)를 보낼 수 있도록 합니다. 이 경우 악성 응용프로그램이 휴대전화가 메모리를 너무 많이 사용하도록 하여 속도를 저하시키거나 불안정하게 만들 수 있습니다."</string>
+    <string name="permdesc_broadcastSticky" msgid="1920045289234052219">"애플리케이션이 브로드캐스트가 끝난 후에도 유지되는 스티키 브로드캐스트(Sticky Broadcast)를 보낼 수 있도록 합니다. 이 경우 악성 애플리케이션이 휴대전화가 메모리를 너무 많이 사용하도록 하여 속도를 저하시키거나 불안정하게 만들 수 있습니다."</string>
     <string name="permlab_readContacts" msgid="6219652189510218240">"연락처 데이터 읽기"</string>
-    <string name="permdesc_readContacts" msgid="3371591512896545975">"응용프로그램이 휴대전화에 저장된 모든 연락처(주소) 데이터를 읽을 수 있도록 합니다. 이 경우 악성 응용프로그램이 데이터를 다른 사람에게 보낼 수 있습니다."</string>
+    <string name="permdesc_readContacts" msgid="3371591512896545975">"애플리케이션이 휴대전화에 저장된 모든 연락처(주소) 데이터를 읽을 수 있도록 합니다. 이 경우 악성 애플리케이션이 데이터를 다른 사람에게 보낼 수 있습니다."</string>
     <string name="permlab_writeContacts" msgid="644616215860933284">"연락처 데이터 작성"</string>
-    <string name="permdesc_writeContacts" msgid="3924383579108183601">"응용프로그램이 휴대전화에 저장된 연락처(주소) 데이터를 수정할 수 있도록 합니다. 이 경우 악성 응용프로그램이 연락처 데이터를 지우거나 수정할 수 있습니다."</string>
+    <string name="permdesc_writeContacts" msgid="3924383579108183601">"애플리케이션이 휴대전화에 저장된 연락처(주소) 데이터를 수정할 수 있도록 합니다. 이 경우 악성 애플리케이션이 연락처 데이터를 지우거나 수정할 수 있습니다."</string>
     <string name="permlab_writeOwnerData" msgid="4892555913849295393">"소유자 데이터 작성"</string>
-    <string name="permdesc_writeOwnerData" msgid="2344055317969787124">"응용프로그램이 휴대전화에 저장된 소유자 데이터를 수정할 수 있도록 합니다. 단, 악성 응용프로그램이 이 기능을 이용하여 소유자 데이터를 지우거나 수정할 수 있습니다."</string>
+    <string name="permdesc_writeOwnerData" msgid="2344055317969787124">"애플리케이션이 휴대전화에 저장된 소유자 데이터를 수정할 수 있도록 합니다. 단, 악성 애플리케이션이 이 기능을 이용하여 소유자 데이터를 지우거나 수정할 수 있습니다."</string>
     <string name="permlab_readOwnerData" msgid="6668525984731523563">"소유자 데이터 읽기"</string>
-    <string name="permdesc_readOwnerData" msgid="3088486383128434507">"응용프로그램이 휴대전화에 저장된 휴대전화 소유자 데이터를 읽을 수 있도록 합니다. 이 경우 악성 응용프로그램이 휴대전화 소유자 데이터를 읽을 수 있습니다."</string>
+    <string name="permdesc_readOwnerData" msgid="3088486383128434507">"애플리케이션이 휴대전화에 저장된 휴대전화 소유자 데이터를 읽을 수 있도록 합니다. 이 경우 악성 애플리케이션이 휴대전화 소유자 데이터를 읽을 수 있습니다."</string>
     <string name="permlab_readCalendar" msgid="6898987798303840534">"캘린더 일정 읽기"</string>
-    <string name="permdesc_readCalendar" msgid="5533029139652095734">"응용프로그램이 휴대전화에 저장된 모든 캘린더 일정을 읽을 수 있도록 합니다. 이 경우 악성 응용프로그램이 캘린더 일정을 다른 사람에게 보낼 수 있습니다."</string>
+    <string name="permdesc_readCalendar" msgid="5533029139652095734">"애플리케이션이 휴대전화에 저장된 모든 캘린더 일정을 읽을 수 있도록 합니다. 이 경우 악성 애플리케이션이 캘린더 일정을 다른 사람에게 보낼 수 있습니다."</string>
     <string name="permlab_writeCalendar" msgid="3894879352594904361">"캘린더 일정 추가/수정 및 참석자에게 이메일 전송"</string>
-    <string name="permdesc_writeCalendar" msgid="2988871373544154221">"응용프로그램이 캘린더에 일정을 추가하거나 변경할 수 있도록 합니다. 이렇게 하면 참석자에게 이메일을 보낼 수 있습니다. 악성 응용프로그램이 이를 사용하여 캘린더 일정을 삭제, 수정하거나 참석자에게 이메일을 보낼 수 있습니다."</string>
+    <string name="permdesc_writeCalendar" msgid="2988871373544154221">"애플리케이션이 캘린더에 일정을 추가하거나 변경할 수 있도록 합니다. 이렇게 하면 참석자에게 이메일을 보낼 수 있습니다. 악성 애플리케이션이 이를 사용하여 캘린더 일정을 삭제, 수정하거나 참석자에게 이메일을 보낼 수 있습니다."</string>
     <string name="permlab_accessMockLocation" msgid="8688334974036823330">"테스트를 위해 위치 정보제공자로 가장"</string>
-    <string name="permdesc_accessMockLocation" msgid="7648286063459727252">"테스트용 가짜 위치 정보 제공자를 만듭니다. 단, 악성 응용프로그램이 이 기능을 이용하여 GPS, 네트워크 공급자 같은 실제 위치 정보제공자에서 반환한 위치 및/또는 상태를 덮어쓸 수 있습니다."</string>
+    <string name="permdesc_accessMockLocation" msgid="7648286063459727252">"테스트용 가짜 위치 정보 제공자를 만듭니다. 단, 악성 애플리케이션이 이 기능을 이용하여 GPS, 네트워크 공급자 같은 실제 위치 정보제공자에서 반환한 위치 및/또는 상태를 덮어쓸 수 있습니다."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"추가 위치 제공업체 명령에 액세스"</string>
-    <string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"추가적인 위치 제공 명령을 사용합니다. 단, 악성 응용프로그램이 이 기능을 이용하여 GPS 또는 기타 위치 소스의 작동을 방해할 수 있습니다."</string>
+    <string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"추가적인 위치 제공 명령을 사용합니다. 단, 악성 애플리케이션이 이 기능을 이용하여 GPS 또는 기타 위치 소스의 작동을 방해할 수 있습니다."</string>
     <string name="permlab_installLocationProvider" msgid="6578101199825193873">"위치 정보 공급자 설치 권한"</string>
-    <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"테스트용 가짜 위치 정보제공자를 만듭니다. 단, 악성 응용프로그램이 이 기능을 이용하여 GPS, 네트워크 공급업체 같은 실제 위치 소스에서 반환한 위치 및/또는 상태를 덮어쓰거나 사용자의 위치를 모니터링하여 외부 소스로 보고할 수 있습니다."</string>
+    <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"테스트용 가짜 위치 정보제공자를 만듭니다. 단, 악성 애플리케이션이 이 기능을 이용하여 GPS, 네트워크 공급업체 같은 실제 위치 소스에서 반환한 위치 및/또는 상태를 덮어쓰거나 사용자의 위치를 모니터링하여 외부 소스로 보고할 수 있습니다."</string>
     <string name="permlab_accessFineLocation" msgid="8116127007541369477">"자세한 (GPS) 위치"</string>
-    <string name="permdesc_accessFineLocation" msgid="7411213317434337331">"GPS 등의 자세한 위치 정보가 사용 가능한 경우 휴대전화에서 이를 사용합니다. 이 경우 악성 응용프로그램이 사용자의 위치를 확인하고 추가 배터리 전원을 소비할 수 있습니다."</string>
+    <string name="permdesc_accessFineLocation" msgid="7411213317434337331">"GPS 등의 자세한 위치 정보가 사용 가능한 경우 휴대전화에서 이를 사용합니다. 이 경우 악성 애플리케이션이 사용자의 위치를 확인하고 추가 배터리 전원을 소비할 수 있습니다."</string>
     <string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"네트워크 기반의 대략적인 위치"</string>
-    <string name="permdesc_accessCoarseLocation" msgid="8235655958070862293">"휴대전화의 대략적인 위치를 측정하기 위해 셀룰러 네트워크 데이터베이스와 같은 광범위한 위치 정보를 사용합니다. 이 경우 악성 응용프로그램이 사용자의 위치를 대략적으로 측정할 수 있습니다."</string>
+    <string name="permdesc_accessCoarseLocation" msgid="8235655958070862293">"휴대전화의 대략적인 위치를 측정하기 위해 셀룰러 네트워크 데이터베이스와 같은 광범위한 위치 정보를 사용합니다. 이 경우 악성 애플리케이션이 사용자의 위치를 대략적으로 측정할 수 있습니다."</string>
     <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"SurfaceFlinger 액세스"</string>
-    <string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"응용프로그램이 SurfaceFlinger의 하위 수준 기능을 사용할 수 있도록 합니다."</string>
+    <string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"애플리케이션이 SurfaceFlinger의 하위 수준 기능을 사용할 수 있도록 합니다."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"프레임 버퍼 읽기"</string>
-    <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"응용프로그램이 프레임 버퍼의 내용을 읽을 수 있도록 합니다."</string>
+    <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"애플리케이션이 프레임 버퍼의 내용을 읽을 수 있도록 합니다."</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"오디오 설정 변경"</string>
-    <string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"응용프로그램이 볼륨 및 경로 지정 같은 전체 오디오 설정을 수정할 수 있도록 합니다."</string>
+    <string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"애플리케이션이 볼륨 및 경로 지정 같은 전체 오디오 설정을 수정할 수 있도록 합니다."</string>
     <string name="permlab_recordAudio" msgid="3876049771427466323">"오디오 녹음"</string>
-    <string name="permdesc_recordAudio" msgid="6493228261176552356">"응용프로그램이 오디오 레코드 경로에 액세스할 수 있도록 합니다."</string>
+    <string name="permdesc_recordAudio" msgid="6493228261176552356">"애플리케이션이 오디오 레코드 경로에 액세스할 수 있도록 합니다."</string>
     <string name="permlab_camera" msgid="8059288807274039014">"사진 촬영"</string>
-    <string name="permdesc_camera" msgid="9013476258810982546">"응용프로그램이 카메라로 사진을 찍을 수 있도록 합니다. 이 경우 응용프로그램이 카메라에 보여지는 화면을 언제든지 수집할 수 있습니다."</string>
+    <string name="permdesc_camera" msgid="9013476258810982546">"애플리케이션이 카메라로 사진을 찍을 수 있도록 합니다. 이 경우 애플리케이션이 카메라에 보여지는 화면을 언제든지 수집할 수 있습니다."</string>
     <string name="permlab_brick" msgid="8337817093326370537">"휴대전화를 영구적으로 사용 중지"</string>
-    <string name="permdesc_brick" msgid="5569526552607599221">"응용프로그램이 휴대전화를 영구적으로 사용 중지할 수 있게 합니다. 이 기능은 매우 위험합니다."</string>
+    <string name="permdesc_brick" msgid="5569526552607599221">"애플리케이션이 휴대전화를 영구적으로 사용 중지할 수 있게 합니다. 이 기능은 매우 위험합니다."</string>
     <string name="permlab_reboot" msgid="2898560872462638242">"휴대전화 강제로 다시 부팅"</string>
-    <string name="permdesc_reboot" msgid="7914933292815491782">"응용프로그램이 휴대전화를 강제로 다시 부팅할 수 있도록 합니다."</string>
+    <string name="permdesc_reboot" msgid="7914933292815491782">"애플리케이션이 휴대전화를 강제로 다시 부팅할 수 있도록 합니다."</string>
     <string name="permlab_mount_unmount_filesystems" msgid="1761023272170956541">"파일시스템 마운트 및 마운트 해제"</string>
-    <string name="permdesc_mount_unmount_filesystems" msgid="6253263792535859767">"응용프로그램이 이동식 저장소의 파일 시스템을 마운트하고 마운트 해제할 수 있도록 합니다."</string>
+    <string name="permdesc_mount_unmount_filesystems" msgid="6253263792535859767">"애플리케이션이 이동식 저장소의 파일 시스템을 마운트하고 마운트 해제할 수 있도록 합니다."</string>
     <string name="permlab_mount_format_filesystems" msgid="5523285143576718981">"외부 저장소 포맷"</string>
-    <string name="permdesc_mount_format_filesystems" msgid="574060044906047386">"응용프로그램이 이동식 저장소를 포맷할 수 있도록 합니다."</string>
+    <string name="permdesc_mount_format_filesystems" msgid="574060044906047386">"애플리케이션이 이동식 저장소를 포맷할 수 있도록 합니다."</string>
     <string name="permlab_asec_access" msgid="1070364079249834666">"보안 저장소에 대한 정보 가져오기"</string>
-    <string name="permdesc_asec_access" msgid="7691616292170590244">"응용프로그램이 보안 저장소의 정보를 가져올 수 있도록 합니다."</string>
+    <string name="permdesc_asec_access" msgid="7691616292170590244">"애플리케이션이 보안 저장소의 정보를 가져올 수 있도록 합니다."</string>
     <string name="permlab_asec_create" msgid="7312078032326928899">"보안 저장소 만들기"</string>
-    <string name="permdesc_asec_create" msgid="7041802322759014035">"응용프로그램이 보안 저장소를 만들 수 있도록 합니다."</string>
+    <string name="permdesc_asec_create" msgid="7041802322759014035">"애플리케이션이 보안 저장소를 만들 수 있도록 합니다."</string>
     <string name="permlab_asec_destroy" msgid="7787322878955261006">"보안 저장소 제거"</string>
-    <string name="permdesc_asec_destroy" msgid="5740754114967893169">"응용프로그램이 보안 저장소를 제거할 수 있도록 합니다."</string>
+    <string name="permdesc_asec_destroy" msgid="5740754114967893169">"애플리케이션이 보안 저장소를 제거할 수 있도록 합니다."</string>
     <string name="permlab_asec_mount_unmount" msgid="7517449694667828592">"보안 저장소 마운트/마운트 해제"</string>
-    <string name="permdesc_asec_mount_unmount" msgid="5438078121718738625">"응용프로그램이 보안 저장소를 마운트/마운트 해제할 수 있도록 합니다."</string>
+    <string name="permdesc_asec_mount_unmount" msgid="5438078121718738625">"애플리케이션이 보안 저장소를 마운트/마운트 해제할 수 있도록 합니다."</string>
     <string name="permlab_asec_rename" msgid="5685344390439934495">"보안 저장소 이름 바꾸기"</string>
-    <string name="permdesc_asec_rename" msgid="1387881770708872470">"응용프로그램이 보안 저장소의 이름을 바꿀 수 있도록 합니다."</string>
+    <string name="permdesc_asec_rename" msgid="1387881770708872470">"애플리케이션이 보안 저장소의 이름을 바꿀 수 있도록 합니다."</string>
     <string name="permlab_vibrate" msgid="7768356019980849603">"진동 제어"</string>
-    <string name="permdesc_vibrate" msgid="2886677177257789187">"응용프로그램이 진동을 제어할 수 있도록 합니다."</string>
+    <string name="permdesc_vibrate" msgid="2886677177257789187">"애플리케이션이 진동을 제어할 수 있도록 합니다."</string>
     <string name="permlab_flashlight" msgid="2155920810121984215">"카메라 플래시 제어"</string>
-    <string name="permdesc_flashlight" msgid="6433045942283802309">"응용프로그램이 카메라 플래시를 제어할 수 있도록 합니다."</string>
+    <string name="permdesc_flashlight" msgid="6433045942283802309">"애플리케이션이 카메라 플래시를 제어할 수 있도록 합니다."</string>
     <string name="permlab_hardware_test" msgid="4148290860400659146">"하드웨어 테스트"</string>
-    <string name="permdesc_hardware_test" msgid="3668894686500081699">"응용프로그램이 하드웨어를 테스트할 목적으로 다양한 주변장치를 제어할 수 있도록 합니다."</string>
+    <string name="permdesc_hardware_test" msgid="3668894686500081699">"애플리케이션이 하드웨어를 테스트할 목적으로 다양한 주변장치를 제어할 수 있도록 합니다."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"전화번호 자동 연결"</string>
-    <string name="permdesc_callPhone" msgid="3369867353692722456">"응용프로그램이 사용자의 조작 없이 전화번호로 전화를 걸 수 있도록 합니다. 이 경우 악성 응용프로그램으로 인해 예상치 못한 통화 요금이 부과될 수 있습니다. 이 권한으로 응용프로그램이 비상 전화를 걸게 할 수는 없습니다."</string>
+    <string name="permdesc_callPhone" msgid="3369867353692722456">"애플리케이션이 사용자의 조작 없이 전화번호로 전화를 걸 수 있도록 합니다. 이 경우 악성 애플리케이션으로 인해 예상치 못한 통화 요금이 부과될 수 있습니다. 이 권한으로 애플리케이션이 비상 전화를 걸게 할 수는 없습니다."</string>
     <string name="permlab_callPrivileged" msgid="4198349211108497879">"모든 전화번호 자동 연결"</string>
-    <string name="permdesc_callPrivileged" msgid="244405067160028452">"응용프로그램이 사용자의 조작 없이 비상 번호를 포함한 전화번호로 전화를 걸 수 있도록 합니다. 이 경우 악성 응용프로그램이 응급 서비스를 불필요하게 또는 불법적으로 호출할 수 있습니다."</string>
+    <string name="permdesc_callPrivileged" msgid="244405067160028452">"애플리케이션이 사용자의 조작 없이 비상 번호를 포함한 전화번호로 전화를 걸 수 있도록 합니다. 이 경우 악성 애플리케이션이 응급 서비스를 불필요하게 또는 불법적으로 호출할 수 있습니다."</string>
     <string name="permlab_performCdmaProvisioning" msgid="5604848095315421425">"직접 CDMA 전화 설정 시작"</string>
-    <string name="permdesc_performCdmaProvisioning" msgid="6457447676108355905">"응용프로그램이 CDMA 프로비저닝을 시작할 수 있도록 합니다. 이 경우 악성 응용프로그램이 불필요하게 CDMA 프로비저닝을 시작할 수 있습니다."</string>
+    <string name="permdesc_performCdmaProvisioning" msgid="6457447676108355905">"애플리케이션이 CDMA 프로비저닝을 시작할 수 있도록 합니다. 이 경우 악성 애플리케이션이 불필요하게 CDMA 프로비저닝을 시작할 수 있습니다."</string>
     <string name="permlab_locationUpdates" msgid="7785408253364335740">"위치 업데이트 알림 제어"</string>
-    <string name="permdesc_locationUpdates" msgid="2300018303720930256">"무선의 위치 업데이트 알림을 사용하거나 사용 중지할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_locationUpdates" msgid="2300018303720930256">"무선의 위치 업데이트 알림을 사용하거나 사용 중지할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_checkinProperties" msgid="7855259461268734914">"체크인 속성 액세스"</string>
-    <string name="permdesc_checkinProperties" msgid="7150307006141883832">"체크인 서비스에서 업로드한 속성에 대한 읽기/쓰기 접근을 허용합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_checkinProperties" msgid="7150307006141883832">"체크인 서비스에서 업로드한 속성에 대한 읽기/쓰기 접근을 허용합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_bindGadget" msgid="776905339015863471">"위젯 선택"</string>
-    <string name="permdesc_bindGadget" msgid="2098697834497452046">"응용프로그램이 어떤 응용프로그램에서 어떤 위젯을 사용할 수 있는 지를 시스템에 알릴 수 있도록 합니다. 이 권한을 갖는 응용프로그램은 개인 정보에 대한 액세스 권한을 다른 응용프로그램에 부여할 수 있습니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_bindGadget" msgid="2098697834497452046">"애플리케이션이 어떤 애플리케이션에서 어떤 위젯을 사용할 수 있는 지를 시스템에 알릴 수 있도록 합니다. 이 권한을 갖는 애플리케이션은 개인 정보에 대한 액세스 권한을 다른 애플리케이션에 부여할 수 있습니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="permlab_modifyPhoneState" msgid="8423923777659292228">"휴대전화 상태 수정"</string>
-    <string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"응용프로그램이 장치의 휴대전화 기능을 제어할 수 있도록 합니다. 이 권한을 갖는 응용프로그램은 사용자에게 알리지 않고 네트워크를 전환하거나 휴대전화 무선 기능을 켜고 끄는 등의 작업을 수행할 수 있습니다."</string>
+    <string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"애플리케이션이 장치의 휴대전화 기능을 제어할 수 있도록 합니다. 이 권한을 갖는 애플리케이션은 사용자에게 알리지 않고 네트워크를 전환하거나 휴대전화 무선 기능을 켜고 끄는 등의 작업을 수행할 수 있습니다."</string>
     <string name="permlab_readPhoneState" msgid="2326172951448691631">"휴대전화 상태 및 ID 읽기"</string>
-    <string name="permdesc_readPhoneState" msgid="188877305147626781">"응용프로그램이 장치의 휴대전화 기능에 접근할 수 있도록 합니다. 이 권한을 갖는 응용프로그램은 휴대전화의 전화번호 및 일련번호, 통화가 활성인지 여부, 해당 통화가 연결된 번호 등을 확인할 수 있습니다."</string>
+    <string name="permdesc_readPhoneState" msgid="188877305147626781">"애플리케이션이 장치의 휴대전화 기능에 접근할 수 있도록 합니다. 이 권한을 갖는 애플리케이션은 휴대전화의 전화번호 및 일련번호, 통화가 활성인지 여부, 해당 통화가 연결된 번호 등을 확인할 수 있습니다."</string>
     <string name="permlab_wakeLock" msgid="573480187941496130">"휴대전화가 절전 모드로 전환되지 않도록 설정"</string>
-    <string name="permdesc_wakeLock" msgid="7584036471227467099">"응용프로그램이 휴대전화가 절전 모드로 전환되지 않도록 합니다."</string>
+    <string name="permdesc_wakeLock" msgid="7584036471227467099">"애플리케이션이 휴대전화가 절전 모드로 전환되지 않도록 합니다."</string>
     <string name="permlab_devicePower" msgid="4928622470980943206">"휴대전화 전원 켜고 끄기"</string>
-    <string name="permdesc_devicePower" msgid="4577331933252444818">"응용프로그램이 휴대전화를 켜거나 끌 수 있도록 합니다."</string>
+    <string name="permdesc_devicePower" msgid="4577331933252444818">"애플리케이션이 휴대전화를 켜거나 끌 수 있도록 합니다."</string>
     <string name="permlab_factoryTest" msgid="3715225492696416187">"출고 테스트 모드로 실행"</string>
     <string name="permdesc_factoryTest" msgid="8136644990319244802">"휴대전화 하드웨어에 대한 완전한 액세스를 허용하는 하위 수준의 제조업체 테스트로 실행됩니다. 휴대전화가 제조업체 테스트 모드로 실행 중일 때만 사용할 수 있습니다."</string>
     <string name="permlab_setWallpaper" msgid="6627192333373465143">"배경화면 설정"</string>
-    <string name="permdesc_setWallpaper" msgid="6417041752170585837">"응용프로그램이 시스템 배경화면을 설정할 수 있도록 합니다."</string>
+    <string name="permdesc_setWallpaper" msgid="6417041752170585837">"애플리케이션이 시스템 배경화면을 설정할 수 있도록 합니다."</string>
     <string name="permlab_setWallpaperHints" msgid="3600721069353106851">"배경화면 크기 힌트 설정"</string>
-    <string name="permdesc_setWallpaperHints" msgid="6019479164008079626">"응용프로그램이 시스템 배경화면 크기 힌트를 설정할 수 있도록 합니다."</string>
+    <string name="permdesc_setWallpaperHints" msgid="6019479164008079626">"애플리케이션이 시스템 배경화면 크기 힌트를 설정할 수 있도록 합니다."</string>
     <string name="permlab_masterClear" msgid="2315750423139697397">"시스템을 기본값으로 재설정"</string>
-    <string name="permdesc_masterClear" msgid="5033465107545174514">"응용프로그램이 모든 데이터, 구성 및 설치된 응용프로그램을 지워서 시스템을 완전히 초기화할 수 있도록 합니다."</string>
+    <string name="permdesc_masterClear" msgid="5033465107545174514">"애플리케이션이 모든 데이터, 구성 및 설치된 애플리케이션을 지워서 시스템을 완전히 초기화할 수 있도록 합니다."</string>
     <string name="permlab_setTime" msgid="2021614829591775646">"시간 설정"</string>
-    <string name="permdesc_setTime" msgid="667294309287080045">"응용프로그램이 휴대전화 시계의 시간을 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_setTime" msgid="667294309287080045">"애플리케이션이 휴대전화 시계의 시간을 변경할 수 있도록 합니다."</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"표준시간대 설정"</string>
-    <string name="permdesc_setTimeZone" msgid="1902540227418179364">"응용프로그램이 휴대전화의 표준시간대를 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_setTimeZone" msgid="1902540227418179364">"애플리케이션이 휴대전화의 표준시간대를 변경할 수 있도록 합니다."</string>
     <string name="permlab_accountManagerService" msgid="4829262349691386986">"AccountManagerService로 활동"</string>
-    <string name="permdesc_accountManagerService" msgid="6056903274106394752">"응용프로그램이 AccountAuthenticators으로 전화를 걸 수 있도록 합니다."</string>
+    <string name="permdesc_accountManagerService" msgid="6056903274106394752">"애플리케이션이 AccountAuthenticators으로 전화를 걸 수 있도록 합니다."</string>
     <string name="permlab_getAccounts" msgid="4549918644233460103">"알려진 계정 검색"</string>
-    <string name="permdesc_getAccounts" msgid="6839262446413155394">"응용프로그램이 휴대전화에 알려진 계정 목록을 가져올 수 있도록 합니다."</string>
+    <string name="permdesc_getAccounts" msgid="6839262446413155394">"애플리케이션이 휴대전화에 알려진 계정 목록을 가져올 수 있도록 합니다."</string>
     <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"계정 인증자로 활동"</string>
-    <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"응용프로그램이 계정 만들기, 비밀번호 가져오기 및 설정 등과 같은 AccountManager의 계정 인증자 기능을 사용할 수 있도록 합니다."</string>
+    <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"애플리케이션이 계정 만들기, 비밀번호 가져오기 및 설정 등과 같은 AccountManager의 계정 인증자 기능을 사용할 수 있도록 합니다."</string>
     <string name="permlab_manageAccounts" msgid="4440380488312204365">"계정 목록 관리"</string>
-    <string name="permdesc_manageAccounts" msgid="8804114016661104517">"응용프로그램이 계정 추가, 삭제 및 비밀번호 삭제 등의 작업을 수행할 수 있도록 합니다."</string>
+    <string name="permdesc_manageAccounts" msgid="8804114016661104517">"애플리케이션이 계정 추가, 삭제 및 비밀번호 삭제 등의 작업을 수행할 수 있도록 합니다."</string>
     <string name="permlab_useCredentials" msgid="6401886092818819856">"계정의 인증 자격증명 사용"</string>
-    <string name="permdesc_useCredentials" msgid="7416570544619546974">"응용프로그램이 인증 토큰을 요청하도록 합니다."</string>
+    <string name="permdesc_useCredentials" msgid="7416570544619546974">"애플리케이션이 인증 토큰을 요청하도록 합니다."</string>
     <string name="permlab_accessNetworkState" msgid="6865575199464405769">"네트워크 상태 보기"</string>
-    <string name="permdesc_accessNetworkState" msgid="558721128707712766">"응용프로그램이 모든 네트워크의 상태를 볼 수 있도록 합니다."</string>
+    <string name="permdesc_accessNetworkState" msgid="558721128707712766">"애플리케이션이 모든 네트워크의 상태를 볼 수 있도록 합니다."</string>
     <string name="permlab_createNetworkSockets" msgid="9121633680349549585">"인터넷에 최대한 액세스"</string>
-    <string name="permdesc_createNetworkSockets" msgid="4593339106921772192">"응용프로그램이 네트워크 소켓을 만들 수 있도록 합니다."</string>
+    <string name="permdesc_createNetworkSockets" msgid="4593339106921772192">"애플리케이션이 네트워크 소켓을 만들 수 있도록 합니다."</string>
     <string name="permlab_writeApnSettings" msgid="7823599210086622545">"액세스포인트 이름(APN) 설정 쓰기"</string>
-    <string name="permdesc_writeApnSettings" msgid="7443433457842966680">"응용프로그램이 APN의 프록시 및 포트 같은 APN 설정을 수정할 수 있도록 합니다."</string>
+    <string name="permdesc_writeApnSettings" msgid="7443433457842966680">"애플리케이션이 APN의 프록시 및 포트 같은 APN 설정을 수정할 수 있도록 합니다."</string>
     <string name="permlab_changeNetworkState" msgid="958884291454327309">"네트워크 연결 변경"</string>
-    <string name="permdesc_changeNetworkState" msgid="4199958910396387075">"응용프로그램이 네트워크 연결 상태를 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_changeNetworkState" msgid="4199958910396387075">"애플리케이션이 네트워크 연결 상태를 변경할 수 있도록 합니다."</string>
     <string name="permlab_changeTetherState" msgid="2702121155761140799">"테러링 연결 변경"</string>
-    <string name="permdesc_changeTetherState" msgid="8905815579146349568">"응용프로그램이 테더링된 네트워크의 연결 상태를 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_changeTetherState" msgid="8905815579146349568">"애플리케이션이 테더링된 네트워크의 연결 상태를 변경할 수 있도록 합니다."</string>
     <string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"백그라운드 데이터 사용 설정 변경"</string>
-    <string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"응용프로그램이 백그라운드 데이터 사용 설정을 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_changeBackgroundDataSetting" msgid="1001482853266638864">"애플리케이션이 백그라운드 데이터 사용 설정을 변경할 수 있도록 합니다."</string>
     <string name="permlab_accessWifiState" msgid="8100926650211034400">"Wi-Fi 상태 보기"</string>
-    <string name="permdesc_accessWifiState" msgid="485796529139236346">"응용프로그램이 Wi-Fi의 상태에 대한 정보를 볼 수 있도록 합니다."</string>
+    <string name="permdesc_accessWifiState" msgid="485796529139236346">"애플리케이션이 Wi-Fi의 상태에 대한 정보를 볼 수 있도록 합니다."</string>
     <string name="permlab_changeWifiState" msgid="7280632711057112137">"Wi-Fi 상태 변경"</string>
-    <string name="permdesc_changeWifiState" msgid="2950383153656873267">"응용프로그램이 Wi-Fi 액세스포인트에 연결하거나 연결을 끊고, 구성된 Wi-Fi 네트워크를 변경할 수 있도록 합니다."</string>
+    <string name="permdesc_changeWifiState" msgid="2950383153656873267">"애플리케이션이 Wi-Fi 액세스포인트에 연결하거나 연결을 끊고, 구성된 Wi-Fi 네트워크를 변경할 수 있도록 합니다."</string>
     <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"Wi-Fi 멀티캐스트 수신 허용"</string>
-    <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"응용프로그램이 휴대기기로 직접 주소가 지정되지 않은 패킷을 받을 수 있도록 합니다. 이 기능은 가까운 곳에서 제공되는 서비스를 검색할 때 유용하며 비멀티캐스트 모드보다 전원을 더 많이 소비합니다."</string>
+    <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"애플리케이션이 휴대기기로 직접 주소가 지정되지 않은 패킷을 받을 수 있도록 합니다. 이 기능은 가까운 곳에서 제공되는 서비스를 검색할 때 유용하며 비멀티캐스트 모드보다 전원을 더 많이 소비합니다."</string>
     <string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"Bluetooth 관리"</string>
-    <string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"응용프로그램이 로컬 Bluetooth 휴대전화를 구성한 다음 원격 장치를 검색하여 페어링할 수 있도록 합니다."</string>
+    <string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"애플리케이션이 로컬 Bluetooth 휴대전화를 구성한 다음 원격 장치를 검색하여 페어링할 수 있도록 합니다."</string>
     <string name="permlab_bluetooth" msgid="8361038707857018732">"Bluetooth 연결 만들기"</string>
-    <string name="permdesc_bluetooth" msgid="762515380679392945">"응용프로그램이 로컬 Bluetooth 전화의 구성을 보고 페어링된 장치에 연결하며 연결을 수락할 수 있도록 합니다."</string>
+    <string name="permdesc_bluetooth" msgid="762515380679392945">"애플리케이션이 로컬 Bluetooth 전화의 구성을 보고 페어링된 장치에 연결하며 연결을 수락할 수 있도록 합니다."</string>
     <string name="permlab_disableKeyguard" msgid="4977406164311535092">"키 잠금 사용 중지"</string>
-    <string name="permdesc_disableKeyguard" msgid="3189763479326302017">"응용프로그램이 키 잠금 및 관련 비밀번호 보안을 사용 중지할 수 있도록 합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용 중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다."</string>
+    <string name="permdesc_disableKeyguard" msgid="3189763479326302017">"애플리케이션이 키 잠금 및 관련 비밀번호 보안을 사용 중지할 수 있도록 합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용 중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다."</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"동기화 설정 읽기"</string>
-    <string name="permdesc_readSyncSettings" msgid="5315925706353341823">"응용프로그램이 주소록에 동기화를 사용할지 여부와 같은 동기화 설정을 읽을 수 있도록 합니다."</string>
+    <string name="permdesc_readSyncSettings" msgid="5315925706353341823">"애플리케이션이 주소록에 동기화를 사용할지 여부와 같은 동기화 설정을 읽을 수 있도록 합니다."</string>
     <string name="permlab_writeSyncSettings" msgid="6297138566442486462">"동기화 설정 쓰기"</string>
-    <string name="permdesc_writeSyncSettings" msgid="2498201614431360044">"응용프로그램이 주소록에 대해 동기화를 사용할지 여부 등의 동기화 설정을 수정할 수 있도록 합니다."</string>
+    <string name="permdesc_writeSyncSettings" msgid="2498201614431360044">"애플리케이션이 주소록에 대해 동기화를 사용할지 여부 등의 동기화 설정을 수정할 수 있도록 합니다."</string>
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"동기화 통계 읽기"</string>
-    <string name="permdesc_readSyncStats" msgid="7511448343374465000">"응용프로그램이 동기화 통계(예: 실행된 동기화 기록)을 읽을 수 있도록 합니다."</string>
+    <string name="permdesc_readSyncStats" msgid="7511448343374465000">"애플리케이션이 동기화 통계(예: 실행된 동기화 기록)을 읽을 수 있도록 합니다."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"가입된 피드 읽기"</string>
-    <string name="permdesc_subscribedFeedsRead" msgid="3622200625634207660">"응용프로그램이 현재 동기화된 피드에 대한 세부정보를 가져올 수 있도록 합니다."</string>
+    <string name="permdesc_subscribedFeedsRead" msgid="3622200625634207660">"애플리케이션이 현재 동기화된 피드에 대한 세부정보를 가져올 수 있도록 합니다."</string>
     <string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"가입 피드 작성"</string>
-    <string name="permdesc_subscribedFeedsWrite" msgid="8121607099326533878">"응용프로그램이 현재 동기화된 피드를 수정할 수 있도록 합니다. 이 경우 악성 응용프로그램이 동기화된 피드를 변경할 수 있습니다."</string>
+    <string name="permdesc_subscribedFeedsWrite" msgid="8121607099326533878">"애플리케이션이 현재 동기화된 피드를 수정할 수 있도록 합니다. 이 경우 악성 애플리케이션이 동기화된 피드를 변경할 수 있습니다."</string>
     <string name="permlab_readDictionary" msgid="432535716804748781">"사용자 정의 사전 읽기"</string>
-    <string name="permdesc_readDictionary" msgid="1082972603576360690">"응용프로그램이 사용자 사전에 보관되어 있는 비공개 단어, 이름 및 구문을 읽도록 합니다."</string>
+    <string name="permdesc_readDictionary" msgid="1082972603576360690">"애플리케이션이 사용자 사전에 보관되어 있는 비공개 단어, 이름 및 구문을 읽도록 합니다."</string>
     <string name="permlab_writeDictionary" msgid="6703109511836343341">"사용자정의 사전에 작성"</string>
-    <string name="permdesc_writeDictionary" msgid="2241256206524082880">"응용프로그램이 사용자 사전에 새 단어를 입력할 수 있도록 합니다."</string>
+    <string name="permdesc_writeDictionary" msgid="2241256206524082880">"애플리케이션이 사용자 사전에 새 단어를 입력할 수 있도록 합니다."</string>
     <string name="permlab_sdcardWrite" msgid="8079403759001777291">"SD 카드 콘텐츠 수정/삭제"</string>
-    <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"응용프로그램이 SD 카드에 쓸 수 있도록 합니다."</string>
+    <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"애플리케이션이 SD 카드에 쓸 수 있도록 합니다."</string>
     <string name="permlab_cache_filesystem" msgid="5656487264819669824">"캐시 파일시스템 액세스"</string>
-    <string name="permdesc_cache_filesystem" msgid="1624734528435659906">"응용프로그램이 캐시 파일시스템을 읽고 쓸 수 있도록 합니다."</string>
+    <string name="permdesc_cache_filesystem" msgid="1624734528435659906">"애플리케이션이 캐시 파일시스템을 읽고 쓸 수 있도록 합니다."</string>
     <string name="policylab_limitPassword" msgid="4307861496302850201">"비밀번호 제한"</string>
     <string name="policydesc_limitPassword" msgid="1719877245692318299">"사용할 수 있는 비밀번호 유형을 제한합니다."</string>
     <string name="policylab_watchLogin" msgid="7374780712664285321">"로그인 시도 보기"</string>
@@ -571,8 +571,8 @@
     <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string>
     <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
     <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
-    <string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
-    <string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
+    <string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="AMPM">%P</xliff:g> <xliff:g id="HOUR">%-l</xliff:g>:00"</string>
+    <string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="AMPM">%p</xliff:g> <xliff:g id="HOUR">%-l</xliff:g>:00"</string>
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"알림 없음"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string>
@@ -593,11 +593,11 @@
     <string name="save_password_label" msgid="6860261758665825069">"확인"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"도움말: 축소/확대하려면 두 번 누릅니다."</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브라우저의 기록 및 북마크 읽기"</string>
-    <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"응용프로그램이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string>
+    <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"애플리케이션이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브라우저의 기록 및 북마크 쓰기"</string>
-    <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"응용프로그램이 휴대전화에 저장된 브라우저 기록 또는 북마크를 수정할 수 있도록 허용합니다. 이 경우 악성 응용프로그램이 브라우저의 데이터를 지우거나 수정할 수 있습니다."</string>
+    <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"애플리케이션이 휴대전화에 저장된 브라우저 기록 또는 북마크를 수정할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 브라우저의 데이터를 지우거나 수정할 수 있습니다."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"브라우저 위치 정보 수정 권한"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"응용프로그램이 브라우저의 위치 정보 권한을 수정할 수 있도록 합니다. 악성 응용프로그램이 이를 사용하여 임의의 웹사이트에 위치 정보를 보낼 수도 있습니다."</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"애플리케이션이 브라우저의 위치 정보 권한을 수정할 수 있도록 합니다. 악성 애플리케이션이 이를 사용하여 임의의 웹사이트에 위치 정보를 보낼 수도 있습니다."</string>
     <string name="save_password_message" msgid="767344687139195790">"브라우저에 이 비밀번호를 저장하시겠습니까?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"나중에"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"저장"</string>
@@ -728,18 +728,18 @@
     <string name="dialog_alert_title" msgid="2049658708609043103">"주의"</string>
     <string name="capital_on" msgid="1544682755514494298">"사용"</string>
     <string name="capital_off" msgid="6815870386972805832">"사용 안함"</string>
-    <string name="whichApplication" msgid="4533185947064773386">"작업을 수행할 때 사용하는 응용프로그램"</string>
+    <string name="whichApplication" msgid="4533185947064773386">"작업을 수행할 때 사용하는 애플리케이션"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"이 작업에 대해 기본값으로 사용"</string>
-    <string name="clearDefaultHintMsg" msgid="4815455344600932173">"홈 설정 &gt; 응용프로그램 &gt; 응용프로그램 관리에서 기본값을 지웁니다."</string>
+    <string name="clearDefaultHintMsg" msgid="4815455344600932173">"홈 설정 &gt; 애플리케이션 &gt; 애플리케이션 관리에서 기본값을 지웁니다."</string>
     <string name="chooseActivity" msgid="1009246475582238425">"작업 선택"</string>
-    <string name="noApplications" msgid="1691104391758345586">"작업을 수행할 수 있는 응용프로그램이 없습니다."</string>
+    <string name="noApplications" msgid="1691104391758345586">"작업을 수행할 수 있는 애플리케이션이 없습니다."</string>
     <string name="aerr_title" msgid="653922989522758100">"죄송합니다."</string>
-    <string name="aerr_application" msgid="4683614104336409186">"<xliff:g id="APPLICATION">%1$s</xliff:g> 응용프로그램(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 예상치 않게 중지되었습니다. 다시 시도해 주세요."</string>
+    <string name="aerr_application" msgid="4683614104336409186">"<xliff:g id="APPLICATION">%1$s</xliff:g> 애플리케이션(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 예상치 않게 중지되었습니다. 다시 시도해 주세요."</string>
     <string name="aerr_process" msgid="1551785535966089511">"<xliff:g id="PROCESS">%1$s</xliff:g> 프로세스가 예상치 않게 중지되었습니다. 다시 시도해 주세요."</string>
     <string name="anr_title" msgid="3100070910664756057">"죄송합니다."</string>
-    <string name="anr_activity_application" msgid="3538242413112507636">"<xliff:g id="APPLICATION">%2$s</xliff:g> 활동(<xliff:g id="ACTIVITY">%1$s</xliff:g> 응용프로그램)이 응답하지 않습니다."</string>
+    <string name="anr_activity_application" msgid="3538242413112507636">"<xliff:g id="APPLICATION">%2$s</xliff:g> 활동(<xliff:g id="ACTIVITY">%1$s</xliff:g> 애플리케이션)이 응답하지 않습니다."</string>
     <string name="anr_activity_process" msgid="5420826626009561014">"<xliff:g id="ACTIVITY">%1$s</xliff:g> 활동(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 응답하지 않습니다."</string>
-    <string name="anr_application_process" msgid="4185842666452210193">"<xliff:g id="APPLICATION">%1$s</xliff:g> 응용프로그램(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 응답하지 않습니다."</string>
+    <string name="anr_application_process" msgid="4185842666452210193">"<xliff:g id="APPLICATION">%1$s</xliff:g> 애플리케이션(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)이 응답하지 않습니다."</string>
     <string name="anr_process" msgid="1246866008169975783">"<xliff:g id="PROCESS">%1$s</xliff:g> 프로세스가 응답하지 않습니다."</string>
     <string name="force_close" msgid="3653416315450806396">"닫기"</string>
     <string name="report" msgid="4060218260984795706">"신고"</string>
@@ -768,7 +768,7 @@
     <item quantity="other" msgid="7915895323644292768">"개방형 Wi-Fi 네트워크 사용 가능"</item>
   </plurals>
     <string name="select_character" msgid="3365550120617701745">"문자 삽입"</string>
-    <string name="sms_control_default_app_name" msgid="7630529934366549163">"알 수 없는 응용프로그램"</string>
+    <string name="sms_control_default_app_name" msgid="7630529934366549163">"알 수 없는 애플리케이션"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS 메시지를 보내는 중"</string>
     <string name="sms_control_message" msgid="1289331457999236205">"여러 개의 SMS 메시지를 보내는 중입니다. 계속하려면 \'확인\'을 선택하고 전송을 중지하려면 \'취소\'를 선택하세요."</string>
     <string name="sms_control_yes" msgid="2532062172402615953">"확인"</string>
@@ -792,7 +792,7 @@
     <string name="usb_storage_stop_button_mount" msgid="7060218034900696029">"USB 저장소 사용 안함"</string>
     <string name="usb_storage_stop_error_message" msgid="143881914840412108">"USB 저장소를 사용하지 않도록 설정하는 동안 문제가 발생했습니다. USB 호스트와 연결을 해제했는지 확인한 다음 다시 시도하세요."</string>
     <string name="dlg_confirm_kill_storage_users_title" msgid="963039033470478697">"USB 저장소 사용"</string>
-    <string name="dlg_confirm_kill_storage_users_text" msgid="3202838234780505886">"USB 저장소를 사용 설정하면 사용 중인 일부 응용프로그램이 중지되고 USB 저장소를 사용 중지할 때까지 사용할 수 없게 됩니다."</string>
+    <string name="dlg_confirm_kill_storage_users_text" msgid="3202838234780505886">"USB 저장소를 사용 설정하면 사용 중인 일부 애플리케이션이 중지되고 USB 저장소를 사용 중지할 때까지 사용할 수 없게 됩니다."</string>
     <string name="dlg_error_title" msgid="8048999973837339174">"USB 작업 실패"</string>
     <string name="dlg_ok" msgid="7376953167039865701">"확인"</string>
     <string name="extmedia_format_title" msgid="8663247929551095854">"SD 카드 포맷"</string>
@@ -818,9 +818,9 @@
     <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD 카드가 없습니다.  SD 카드를 넣으세요."</string>
     <string name="activity_list_empty" msgid="4168820609403385789">"일치하는 활동이 없습니다."</string>
     <string name="permlab_pkgUsageStats" msgid="8787352074326748892">"구성 요소 사용 통계 업데이트"</string>
-    <string name="permdesc_pkgUsageStats" msgid="891553695716752835">"수집된 구성요소 사용 통계를 수정할 수 있는 권한을 부여합니다. 일반 응용프로그램은 이 권한을 사용하지 않습니다."</string>
-    <string name="permlab_copyProtectedData" msgid="1660908117394854464">"기본 컨테이너 서비스를 호출하여 콘텐츠를 복사할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
-    <string name="permdesc_copyProtectedData" msgid="537780957633976401">"기본 컨테이너 서비스를 호출하여 콘텐츠를 복사할 수 있도록 합니다. 일반 응용프로그램에서는 사용하지 않습니다."</string>
+    <string name="permdesc_pkgUsageStats" msgid="891553695716752835">"수집된 구성요소 사용 통계를 수정할 수 있는 권한을 부여합니다. 일반 애플리케이션은 이 권한을 사용하지 않습니다."</string>
+    <string name="permlab_copyProtectedData" msgid="1660908117394854464">"기본 컨테이너 서비스를 호출하여 콘텐츠를 복사할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
+    <string name="permdesc_copyProtectedData" msgid="537780957633976401">"기본 컨테이너 서비스를 호출하여 콘텐츠를 복사할 수 있도록 합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"확대/축소하려면 두 번 탭하세요."</string>
     <string name="gadget_host_error_inflating" msgid="2613287218853846830">"위젯을 생성하는 과정(inflate)에 오류가 발생했습니다."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"이동"</string>
@@ -833,7 +833,7 @@
     <string name="create_contact_using" msgid="4947405226788104538">"전화번호부에"\n"<xliff:g id="NUMBER">%s</xliff:g> 추가"</string>
     <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"선택함"</string>
     <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"선택 안함"</string>
-    <string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"현재 이후로 하나 이상의 다음 응용프로그램이 계정에 대한 액세스 권한을 요청합니다."</string>
+    <string name="grant_credentials_permission_message_header" msgid="6824538733852821001">"현재 이후로 하나 이상의 다음 애플리케이션이 계정에 대한 액세스 권한을 요청합니다."</string>
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"요청을 허용하시겠습니까?"</string>
     <string name="grant_permissions_header_text" msgid="2722567482180797717">"액세스 요청"</string>
     <string name="allow" msgid="7225948811296386551">"허용"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 02afb16..6acf4a6 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -780,7 +780,7 @@
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string>
     <string name="usb_storage_activity_title" msgid="2399289999608900443">"USB-masselagring"</string>
     <string name="usb_storage_title" msgid="5901459041398751495">"USB koblet til"</string>
-    <string name="usb_storage_message" msgid="4796759646167247178">"Du har koblet telefonen til datamaskinen via USB. Velg knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og SD-kortet i Android-telefonen."</string>
+    <string name="usb_storage_message" msgid="4796759646167247178">"Du har koblet telefonen til datamaskinen via USB. Velg knappen nedenfor hvis du vil kopiere filer mellom datamaskinen og minnekortet i telefonen."</string>
     <string name="usb_storage_button_mount" msgid="1052259930369508235">"Slå på USB-lagring"</string>
     <string name="usb_storage_error_message" msgid="2534784751603345363">"Det oppsto et problem med å bruke minnekortet ditt for USB-lagring."</string>
     <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB tilkoblet"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b7c5bbc0..834a84c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -587,7 +587,7 @@
     <string name="factorytest_not_system" msgid="4435201656767276723">"只有在 /system/app 中安装的包支持 FACTORY_TEST 操作。"</string>
     <string name="factorytest_no_action" msgid="872991874799998561">"未发现支持 FACTORY_TEST 操作的包。"</string>
     <string name="factorytest_reboot" msgid="6320168203050791643">"重新启动"</string>
-    <string name="js_dialog_title" msgid="8143918455087008109">"“<xliff:g id="TITLE">%s</xliff:g>”处的页面表明:"</string>
+    <string name="js_dialog_title" msgid="8143918455087008109">"来自“<xliff:g id="TITLE">%s</xliff:g>”的提示:"</string>
     <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"是否从该页面导航至它处?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"选择“确定”继续,或选择“取消”留在当前页面。"</string>
     <string name="save_password_label" msgid="6860261758665825069">"确认"</string>
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/include/media/AudioTrack.h b/include/media/AudioTrack.h
index c46df1e..ef537f4 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -102,6 +102,17 @@
 
     typedef void (*callback_t)(int event, void* user, void *info);
 
+    /* Returns the minimum frame count required for the successful creation of
+     * an AudioTrack object.
+     * Returned status (from utils/Errors.h) can be:
+     *  - NO_ERROR: successful operation
+     *  - NO_INIT: audio server or audio hardware not initialized
+     */
+
+     static status_t getMinFrameCount(int* frameCount,
+                                      int streamType      =-1,
+                                      uint32_t sampleRate = 0);
+
     /* Constructs an uninitialized AudioTrack. No connection with
      * AudioFlinger takes place.
      */
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 6a5c8a8..979d6e8 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -58,8 +58,6 @@
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
- *
- * TODO This enumeration should probably be split up or relabeled for clarity.
  */
 enum {
     /* These flags originate in RawEvents and are generally set in the key map. */
@@ -73,7 +71,9 @@
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
 
-    /* These flags are set by the input dispatch policy as it intercepts each event. */
+    POLICY_FLAG_RAW_MASK = 0x0000ffff,
+
+    /* These flags are set by the input reader policy as it intercepts each event. */
 
     // Indicates that the screen was off when the event was received and the event
     // should wake the device.
@@ -85,6 +85,37 @@
 };
 
 /*
+ * Describes the basic configuration of input devices that are present.
+ */
+struct InputConfiguration {
+    enum {
+        TOUCHSCREEN_UNDEFINED = 0,
+        TOUCHSCREEN_NOTOUCH = 1,
+        TOUCHSCREEN_STYLUS = 2,
+        TOUCHSCREEN_FINGER = 3
+    };
+
+    enum {
+        KEYBOARD_UNDEFINED = 0,
+        KEYBOARD_NOKEYS = 1,
+        KEYBOARD_QWERTY = 2,
+        KEYBOARD_12KEY = 3
+    };
+
+    enum {
+        NAVIGATION_UNDEFINED = 0,
+        NAVIGATION_NONAV = 1,
+        NAVIGATION_DPAD = 2,
+        NAVIGATION_TRACKBALL = 3,
+        NAVIGATION_WHEEL = 4
+    };
+
+    int32_t touchScreen;
+    int32_t keyboard;
+    int32_t navigation;
+};
+
+/*
  * Pointer coordinate data.
  */
 struct PointerCoords {
@@ -117,6 +148,9 @@
     int32_t mNature;
 };
 
+/*
+ * Key events.
+ */
 class KeyEvent : public InputEvent {
 public:
     virtual ~KeyEvent() { }
@@ -162,6 +196,9 @@
     nsecs_t mEventTime;
 };
 
+/*
+ * Motion events.
+ */
 class MotionEvent : public InputEvent {
 public:
     virtual ~MotionEvent() { }
@@ -174,6 +211,10 @@
 
     inline int32_t getMetaState() const { return mMetaState; }
 
+    inline float getXOffset() const { return mXOffset; }
+
+    inline float getYOffset() const { return mYOffset; }
+
     inline float getXPrecision() const { return mXPrecision; }
 
     inline float getYPrecision() const { return mYPrecision; }
@@ -186,18 +227,22 @@
 
     inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
 
-    inline float getRawX() const { return mRawX; }
-
-    inline float getRawY() const { return mRawY; }
-
-    inline float getX(size_t pointerIndex) const {
+    inline float getRawX(size_t pointerIndex) const {
         return getCurrentPointerCoords(pointerIndex).x;
     }
 
-    inline float getY(size_t pointerIndex) const {
+    inline float getRawY(size_t pointerIndex) const {
         return getCurrentPointerCoords(pointerIndex).y;
     }
 
+    inline float getX(size_t pointerIndex) const {
+        return getRawX(pointerIndex) + mXOffset;
+    }
+
+    inline float getY(size_t pointerIndex) const {
+        return getRawY(pointerIndex) + mYOffset;
+    }
+
     inline float getPressure(size_t pointerIndex) const {
         return getCurrentPointerCoords(pointerIndex).pressure;
     }
@@ -212,14 +257,22 @@
         return mSampleEventTimes[historicalIndex];
     }
 
-    inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
+    inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const {
         return getHistoricalPointerCoords(pointerIndex, historicalIndex).x;
     }
 
-    inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
+    inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const {
         return getHistoricalPointerCoords(pointerIndex, historicalIndex).y;
     }
 
+    inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
+        return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset;
+    }
+
+    inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
+        return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset;
+    }
+
     inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
         return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure;
     }
@@ -234,8 +287,8 @@
             int32_t action,
             int32_t edgeFlags,
             int32_t metaState,
-            float rawX,
-            float rawY,
+            float xOffset,
+            float yOffset,
             float xPrecision,
             float yPrecision,
             nsecs_t downTime,
@@ -250,12 +303,19 @@
 
     void offsetLocation(float xOffset, float yOffset);
 
+    // Low-level accessors.
+    inline const int32_t* getPointerIds() const { return mPointerIds.array(); }
+    inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); }
+    inline const PointerCoords* getSamplePointerCoords() const {
+            return mSamplePointerCoords.array();
+    }
+
 private:
     int32_t mAction;
     int32_t mEdgeFlags;
     int32_t mMetaState;
-    float mRawX;
-    float mRawY;
+    float mXOffset;
+    float mYOffset;
     float mXPrecision;
     float mYPrecision;
     nsecs_t mDownTime;
diff --git a/include/ui/InputDispatchPolicy.h b/include/ui/InputDispatchPolicy.h
deleted file mode 100644
index 3546813..0000000
--- a/include/ui/InputDispatchPolicy.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUT_DISPATCH_POLICY_H
-#define _UI_INPUT_DISPATCH_POLICY_H
-
-/**
- * Native input dispatch policy.
- */
-
-#include <ui/Input.h>
-#include <utils/Errors.h>
-#include <utils/Vector.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-
-namespace android {
-
-class InputChannel;
-
-/*
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- */
-struct InputTarget {
-    enum {
-        /* This flag indicates that subsequent event delivery should be held until the
-         * current event is delivered to this target or a timeout occurs. */
-        FLAG_SYNC = 0x01,
-
-        /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
-         * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */
-        FLAG_OUTSIDE = 0x02,
-
-        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
-         * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
-         * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */
-        FLAG_CANCEL = 0x04
-    };
-
-    // The input channel to be targeted.
-    sp<InputChannel> inputChannel;
-
-    // Flags for the input target.
-    int32_t flags;
-
-    // The timeout for event delivery to this target in nanoseconds.  Or -1 if none.
-    nsecs_t timeout;
-
-    // The x and y offset to add to a MotionEvent as it is delivered.
-    // (ignored for KeyEvents)
-    float xOffset, yOffset;
-};
-
-/*
- * Input dispatch policy interface.
- *
- * The input dispatch policy is used by the input dispatcher to interact with the
- * Window Manager and other system components.  This separation of concerns keeps
- * the input dispatcher relatively free of special case logic such as is required
- * to determine the target of iput events, when to wake the device, how to interact
- * with key guard, and when to transition to the home screen.
- *
- * The actual implementation is partially supported by callbacks into the DVM
- * via JNI.  This class is also mocked in the input dispatcher unit tests since
- * it is an ideal test seam.
- */
-class InputDispatchPolicyInterface : public virtual RefBase {
-protected:
-    InputDispatchPolicyInterface() { }
-    virtual ~InputDispatchPolicyInterface() { }
-
-public:
-    enum {
-        ROTATION_0 = 0,
-        ROTATION_90 = 1,
-        ROTATION_180 = 2,
-        ROTATION_270 = 3
-    };
-
-    enum {
-        // The input dispatcher should do nothing and discard the input unless other
-        // flags are set.
-        ACTION_NONE = 0,
-
-        // The input dispatcher should dispatch the input to the application.
-        ACTION_DISPATCH = 0x00000001,
-
-        // The input dispatcher should perform special filtering in preparation for
-        // a pending app switch.
-        ACTION_APP_SWITCH_COMING = 0x00000002,
-
-        // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it
-        // passes through the dispatch pipeline.
-        ACTION_WOKE_HERE = 0x00000004,
-
-        // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
-        // passes through the dispatch pipeline.
-        ACTION_BRIGHT_HERE = 0x00000008
-    };
-
-    enum {
-        TOUCHSCREEN_UNDEFINED = 0,
-        TOUCHSCREEN_NOTOUCH = 1,
-        TOUCHSCREEN_STYLUS = 2,
-        TOUCHSCREEN_FINGER = 3
-    };
-
-    enum {
-        KEYBOARD_UNDEFINED = 0,
-        KEYBOARD_NOKEYS = 1,
-        KEYBOARD_QWERTY = 2,
-        KEYBOARD_12KEY = 3
-    };
-
-    enum {
-        NAVIGATION_UNDEFINED = 0,
-        NAVIGATION_NONAV = 1,
-        NAVIGATION_DPAD = 2,
-        NAVIGATION_TRACKBALL = 3,
-        NAVIGATION_WHEEL = 4
-    };
-
-    struct VirtualKeyDefinition {
-        int32_t scanCode;
-
-        // configured position data, specified in display coords
-        int32_t centerX;
-        int32_t centerY;
-        int32_t width;
-        int32_t height;
-    };
-
-    /* Gets information about the display with the specified id.
-     * Returns true if the display info is available, false otherwise.
-     */
-    virtual bool getDisplayInfo(int32_t displayId,
-            int32_t* width, int32_t* height, int32_t* orientation) = 0;
-
-    virtual void notifyConfigurationChanged(nsecs_t when,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0;
-
-    virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen) = 0;
-
-    virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
-            int32_t action, int32_t flags, int32_t keyCode,
-            int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
-
-    virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
-            bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0;
-
-    virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
-            bool rolled) = 0;
-
-    virtual int32_t interceptTouch(nsecs_t when) = 0;
-
-    virtual bool allowKeyRepeat() = 0;
-    virtual nsecs_t getKeyRepeatTimeout() = 0;
-
-    virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets) = 0;
-    virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
-            Vector<InputTarget>& outTargets) = 0;
-
-    /* Determine whether to turn on some hacks we have to improve the touch interaction with a
-     * certain device whose screen currently is not all that good.
-     */
-    virtual bool filterTouchEvents() = 0;
-
-    /* Determine whether to turn on some hacks to improve touch interaction with another device
-     * where touch coordinate data can get corrupted.
-     */
-    virtual bool filterJumpyTouchEvents() = 0;
-
-    virtual void getVirtualKeyDefinitions(const String8& deviceName,
-            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
-    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_DISPATCH_POLICY_H
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index bde07f2..511ad20 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -18,7 +18,6 @@
 #define _UI_INPUT_DISPATCHER_H
 
 #include <ui/Input.h>
-#include <ui/InputDispatchPolicy.h>
 #include <ui/InputTransport.h>
 #include <utils/KeyedVector.h>
 #include <utils/Vector.h>
@@ -35,6 +34,118 @@
 
 namespace android {
 
+/*
+ * Constants used to report the outcome of input event injection.
+ */
+enum {
+    /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
+    INPUT_EVENT_INJECTION_PENDING = -1,
+
+    /* Injection succeeded. */
+    INPUT_EVENT_INJECTION_SUCCEEDED = 0,
+
+    /* Injection failed because the injector did not have permission to inject
+     * into the application with input focus. */
+    INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
+
+    /* Injection failed because there were no available input targets. */
+    INPUT_EVENT_INJECTION_FAILED = 2,
+
+    /* Injection failed due to a timeout. */
+    INPUT_EVENT_INJECTION_TIMED_OUT = 3
+};
+
+
+/*
+ * An input target specifies how an input event is to be dispatched to a particular window
+ * including the window's input channel, control flags, a timeout, and an X / Y offset to
+ * be added to input event coordinates to compensate for the absolute position of the
+ * window area.
+ */
+struct InputTarget {
+    enum {
+        /* This flag indicates that subsequent event delivery should be held until the
+         * current event is delivered to this target or a timeout occurs. */
+        FLAG_SYNC = 0x01,
+
+        /* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
+         * this target and so should instead be delivered as an ACTION_OUTSIDE to this target. */
+        FLAG_OUTSIDE = 0x02,
+
+        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
+         * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
+         * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL. */
+        FLAG_CANCEL = 0x04
+    };
+
+    // The input channel to be targeted.
+    sp<InputChannel> inputChannel;
+
+    // Flags for the input target.
+    int32_t flags;
+
+    // The timeout for event delivery to this target in nanoseconds.  Or -1 if none.
+    nsecs_t timeout;
+
+    // The x and y offset to add to a MotionEvent as it is delivered.
+    // (ignored for KeyEvents)
+    float xOffset, yOffset;
+};
+
+
+/*
+ * Input dispatcher policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class InputDispatcherPolicyInterface : public virtual RefBase {
+protected:
+    InputDispatcherPolicyInterface() { }
+    virtual ~InputDispatcherPolicyInterface() { }
+
+public:
+    /* Notifies the system that a configuration change has occurred. */
+    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
+
+    /* Notifies the system that an input channel is unrecoverably broken. */
+    virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
+
+    /* Notifies the system that an input channel is not responding.
+     * Returns true and a new timeout value if the dispatcher should keep waiting.
+     * Otherwise returns false. */
+    virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+            nsecs_t& outNewTimeout) = 0;
+
+    /* Notifies the system that an input channel recovered from ANR. */
+    virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
+
+    /* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
+    virtual nsecs_t getKeyRepeatTimeout() = 0;
+
+    /* Gets the input targets for a key event.
+     * If the event is being injected, injectorPid and injectorUid should specify the
+     * process id and used id of the injecting application, otherwise they should both
+     * be -1.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
+    virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid,
+            Vector<InputTarget>& outTargets) = 0;
+
+    /* Gets the input targets for a motion event.
+     * If the event is being injected, injectorPid and injectorUid should specify the
+     * process id and used id of the injecting application, otherwise they should both
+     * be -1.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
+    virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid,
+            Vector<InputTarget>& outTargets) = 0;
+};
+
+
 /* Notifies the system about input events generated by the input reader.
  * The dispatcher is expected to be mostly asynchronous. */
 class InputDispatcherInterface : public virtual RefBase {
@@ -51,14 +162,10 @@
     virtual void dispatchOnce() = 0;
 
     /* Notifies the dispatcher about new events.
-     * The dispatcher will process most of these events asynchronously although some
-     * policy processing may occur synchronously.
      *
      * These methods should only be called on the input reader thread.
      */
-    virtual void notifyConfigurationChanged(nsecs_t eventTime,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0;
-    virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) = 0;
+    virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
     virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
@@ -68,6 +175,17 @@
             uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime) = 0;
 
+    /* Injects an input event and optionally waits for sync.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+
     /* Registers or unregister input channels that may be used as targets for input events.
      *
      * These methods may be called on any thread (usually by the input manager).
@@ -76,19 +194,33 @@
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 };
 
-/* Dispatches events. */
+/* Dispatches events to input targets.  Some functions of the input dispatcher, such as
+ * identifying input targets, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ *     Because the policy can potentially block or cause re-entrance into the input dispatcher,
+ *     the input dispatcher never calls into the policy while holding its internal locks.
+ *     The implementation is also carefully designed to recover from scenarios such as an
+ *     input channel becoming unregistered while identifying input targets or processing timeouts.
+ *
+ *     Methods marked 'Locked' must be called with the lock acquired.
+ *
+ *     Methods marked 'LockedInterruptible' must be called with the lock acquired but
+ *     may during the course of their execution release the lock, call into the policy, and
+ *     then reacquire the lock.  The caller is responsible for recovering gracefully.
+ *
+ *     A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
+ */
 class InputDispatcher : public InputDispatcherInterface {
 protected:
     virtual ~InputDispatcher();
 
 public:
-    explicit InputDispatcher(const sp<InputDispatchPolicyInterface>& policy);
+    explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
 
     virtual void dispatchOnce();
 
-    virtual void notifyConfigurationChanged(nsecs_t eventTime,
-            int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig);
-    virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen);
+    virtual void notifyConfigurationChanged(nsecs_t eventTime);
     virtual void notifyAppSwitchComing(nsecs_t eventTime);
     virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature,
             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
@@ -98,6 +230,9 @@
             uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime);
 
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
@@ -119,12 +254,17 @@
         int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
+
+        int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
+        int32_t injectorPid;     // -1 if not injected
+        int32_t injectorUid;     // -1 if not injected
+
+        bool dispatchInProgress; // initially false, set to true while dispatching
+
+        inline bool isInjected() { return injectorPid >= 0; }
     };
 
     struct ConfigurationChangedEntry : EventEntry {
-        int32_t touchScreenConfig;
-        int32_t keyboardConfig;
-        int32_t navigationConfig;
     };
 
     struct KeyEntry : EventEntry {
@@ -165,6 +305,7 @@
         MotionSample* lastSample;
     };
 
+    // Tracks the progress of dispatching a particular event to a particular connection.
     struct DispatchEntry : Link<DispatchEntry> {
         EventEntry* eventEntry; // the event to dispatch
         int32_t targetFlags;
@@ -189,6 +330,37 @@
         MotionSample* tailMotionSample;
     };
 
+    // A command entry captures state and behavior for an action to be performed in the
+    // dispatch loop after the initial processing has taken place.  It is essentially
+    // a kind of continuation used to postpone sensitive policy interactions to a point
+    // in the dispatch loop where it is safe to release the lock (generally after finishing
+    // the critical parts of the dispatch cycle).
+    //
+    // The special thing about commands is that they can voluntarily release and reacquire
+    // the dispatcher lock at will.  Initially when the command starts running, the
+    // dispatcher lock is held.  However, if the command needs to call into the policy to
+    // do some work, it can release the lock, do the work, then reacquire the lock again
+    // before returning.
+    //
+    // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+    // never calls into the policy while holding its lock.
+    //
+    // Commands are implicitly 'LockedInterruptible'.
+    struct CommandEntry;
+    typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
+
+    class Connection;
+    struct CommandEntry : Link<CommandEntry> {
+        CommandEntry();
+        ~CommandEntry();
+
+        Command command;
+
+        // parameters for the command (usage varies by command)
+        sp<Connection> connection;
+    };
+
+    // Generic queue implementation.
     template <typename T>
     struct Queue {
         T head;
@@ -238,21 +410,28 @@
     public:
         Allocator();
 
-        ConfigurationChangedEntry* obtainConfigurationChangedEntry();
-        KeyEntry* obtainKeyEntry();
-        MotionEntry* obtainMotionEntry();
+        ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
+        KeyEntry* obtainKeyEntry(nsecs_t eventTime,
+                int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+                int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+                int32_t repeatCount, nsecs_t downTime);
+        MotionEntry* obtainMotionEntry(nsecs_t eventTime,
+                int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+                int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+                nsecs_t downTime, uint32_t pointerCount,
+                const int32_t* pointerIds, const PointerCoords* pointerCoords);
         DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
+        CommandEntry* obtainCommandEntry(Command command);
 
         void releaseEventEntry(EventEntry* entry);
         void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
         void releaseKeyEntry(KeyEntry* entry);
         void releaseMotionEntry(MotionEntry* entry);
         void releaseDispatchEntry(DispatchEntry* entry);
+        void releaseCommandEntry(CommandEntry* entry);
 
         void appendMotionSample(MotionEntry* motionEntry,
-                nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords);
-        void freeMotionSample(MotionSample* sample);
-        void freeMotionSampleList(MotionSample* head);
+                nsecs_t eventTime, const PointerCoords* pointerCoords);
 
     private:
         Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
@@ -260,6 +439,9 @@
         Pool<MotionEntry> mMotionEntryPool;
         Pool<MotionSample> mMotionSamplePool;
         Pool<DispatchEntry> mDispatchEntryPool;
+        Pool<CommandEntry> mCommandEntryPool;
+
+        void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
     };
 
     /* Manages the dispatch state associated with a single input channel. */
@@ -291,7 +473,9 @@
 
         explicit Connection(const sp<InputChannel>& inputChannel);
 
-        inline const char* getInputChannelName() { return inputChannel->getName().string(); }
+        inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
+
+        const char* getStatusLabel() const;
 
         // Finds a DispatchEntry in the outbound queue associated with the specified event.
         // Returns NULL if not found.
@@ -321,24 +505,36 @@
         }
 
         status_t initialize();
+
+        void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
     };
 
-    sp<InputDispatchPolicyInterface> mPolicy;
+    sp<InputDispatcherPolicyInterface> mPolicy;
 
     Mutex mLock;
 
-    Queue<EventEntry> mInboundQueue;
     Allocator mAllocator;
-
     sp<PollLoop> mPollLoop;
 
+    Queue<EventEntry> mInboundQueue;
+    Queue<CommandEntry> mCommandQueue;
+
     // All registered connections mapped by receive pipe file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
 
     // Active connections are connections that have a non-empty outbound queue.
+    // We don't use a ref-counted pointer here because we explicitly abort connections
+    // during unregistration which causes the connection's outbound queue to be cleared
+    // and the connection itself to be deactivated.
     Vector<Connection*> mActiveConnections;
 
-    // Pool of key and motion event objects used only to ask the input dispatch policy
+    // List of connections that have timed out.  Only used by dispatchOnce()
+    // We don't use a ref-counted pointer here because it is not possible for a connection
+    // to be unregistered while processing timed out connections since we hold the lock for
+    // the duration.
+    Vector<Connection*> mTimedOutConnections;
+
+    // Preallocated key and motion event objects used only to ask the input dispatcher policy
     // for the targets of an event that is to be dispatched.
     KeyEvent mReusableKeyEvent;
     MotionEvent mReusableMotionEvent;
@@ -347,6 +543,14 @@
     // If there is a synchronous event dispatch in progress, the current input targets will
     // remain unchanged until the dispatch has completed or been aborted.
     Vector<InputTarget> mCurrentInputTargets;
+    bool mCurrentInputTargetsValid; // false while targets are being recomputed
+
+    // Event injection and synchronization.
+    Condition mInjectionResultAvailableCondition;
+    Condition mFullySynchronizedCondition;
+    bool isFullySynchronizedLocked();
+    EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
+    void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
@@ -357,26 +561,41 @@
 
     void resetKeyRepeatLocked();
 
+    // Deferred command processing.
+    bool runCommandsLockedInterruptible();
+    CommandEntry* postCommandLocked(Command command);
+
     // Process events that have just been dequeued from the head of the input queue.
-    void processConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry);
-    void processKeyLocked(nsecs_t currentTime, KeyEntry* entry);
-    void processKeyRepeatLocked(nsecs_t currentTime);
-    void processMotionLocked(nsecs_t currentTime, MotionEntry* entry);
+    void processConfigurationChangedLockedInterruptible(
+            nsecs_t currentTime, ConfigurationChangedEntry* entry);
+    void processKeyLockedInterruptible(
+            nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout);
+    void processKeyRepeatLockedInterruptible(
+            nsecs_t currentTime, nsecs_t keyRepeatTimeout);
+    void processMotionLockedInterruptible(
+            nsecs_t currentTime, MotionEntry* entry);
 
     // Identify input targets for an event and dispatch to them.
-    void identifyInputTargetsAndDispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry);
-    void identifyInputTargetsAndDispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry);
-    void dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, EventEntry* entry,
-            bool resumeWithAppendedMotionSample);
+    void identifyInputTargetsAndDispatchKeyLockedInterruptible(
+            nsecs_t currentTime, KeyEntry* entry);
+    void identifyInputTargetsAndDispatchMotionLockedInterruptible(
+            nsecs_t currentTime, MotionEntry* entry);
+    void dispatchEventToCurrentInputTargetsLocked(
+            nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
 
     // Manage the dispatch cycle for a single connection.
-    void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
+    // These methods are deliberately not Interruptible because doing all of the work
+    // with the mutex held makes it easier to ensure that connection invariants are maintained.
+    // If needed, the methods post commands to run later once the critical bits are done.
+    void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             EventEntry* eventEntry, const InputTarget* inputTarget,
             bool resumeWithAppendedMotionSample);
-    void startDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
-    void finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
-    bool timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
-    bool abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
+    void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
+    void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+            const sp<Connection>& connection, nsecs_t newTimeout);
+    void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
             bool broken);
     static bool handleReceiveCallback(int receiveFd, int events, void* data);
 
@@ -385,11 +604,19 @@
     void deactivateConnectionLocked(Connection* connection);
 
     // Interesting events that we might like to log or tell the framework about.
-    void onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection);
-    void onDispatchCycleFinishedLocked(nsecs_t currentTime, Connection* connection,
-            bool recoveredFromANR);
-    void onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection);
-    void onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection);
+    void onDispatchCycleStartedLocked(
+            nsecs_t currentTime, const sp<Connection>& connection);
+    void onDispatchCycleFinishedLocked(
+            nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
+    void onDispatchCycleANRLocked(
+            nsecs_t currentTime, const sp<Connection>& connection);
+    void onDispatchCycleBrokenLocked(
+            nsecs_t currentTime, const sp<Connection>& connection);
+
+    // Outbound policy interactions.
+    void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
+    void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
 };
 
 /* Enqueues and dispatches input events, endlessly. */
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index eb27513..7509dd2 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -23,7 +23,6 @@
 
 #include <ui/EventHub.h>
 #include <ui/Input.h>
-#include <ui/InputDispatchPolicy.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 #include <utils/Timers.h>
@@ -32,9 +31,14 @@
 
 namespace android {
 
-class InputReader;
-class InputDispatcher;
+class InputChannel;
+
+class InputReaderInterface;
+class InputReaderPolicyInterface;
 class InputReaderThread;
+
+class InputDispatcherInterface;
+class InputDispatcherPolicyInterface;
 class InputDispatcherThread;
 
 /*
@@ -74,8 +78,20 @@
     /* Unregisters an input channel. */
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
 
+    /* Injects an input event and optionally waits for sync.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
+     * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
+     */
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+
+    /* Gets input device configuration. */
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+
     /*
-     * Query current input state.
+     * Queries current input state.
      *   deviceId may be -1 to search for the device automatically, filtered by class.
      *   deviceClasses may be -1 to ignore device class while searching.
      */
@@ -86,7 +102,7 @@
     virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
             int32_t sw) const = 0;
 
-    /* Determine whether physical keys exist for the given framework-domain key codes. */
+    /* Determines whether physical keys exist for the given framework-domain key codes. */
     virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
 };
 
@@ -95,12 +111,15 @@
     virtual ~InputManager();
 
 public:
-    /*
-     * Creates an input manager that reads events from the given
-     * event hub and applies the given input dispatch policy.
-     */
-    InputManager(const sp<EventHubInterface>& eventHub,
-            const sp<InputDispatchPolicyInterface>& policy);
+    InputManager(
+            const sp<EventHubInterface>& eventHub,
+            const sp<InputReaderPolicyInterface>& readerPolicy,
+            const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
+
+    // (used for testing purposes)
+    InputManager(
+            const sp<InputReaderInterface>& reader,
+            const sp<InputDispatcherInterface>& dispatcher);
 
     virtual status_t start();
     virtual status_t stop();
@@ -108,6 +127,10 @@
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
+    virtual int32_t injectInputEvent(const InputEvent* event,
+            int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+
+    virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
             int32_t scanCode) const;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
@@ -117,16 +140,13 @@
     virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
 
 private:
-    sp<EventHubInterface> mEventHub;
-    sp<InputDispatchPolicyInterface> mPolicy;
-
-    sp<InputDispatcher> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
-
-    sp<InputReader> mReader;
+    sp<InputReaderInterface> mReader;
     sp<InputReaderThread> mReaderThread;
 
-    void configureExcludedDevices();
+    sp<InputDispatcherInterface> mDispatcher;
+    sp<InputDispatcherThread> mDispatcherThread;
+
+    void initialize();
 };
 
 } // namespace android
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 7e7a64c..d76b8fe 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -19,7 +19,6 @@
 
 #include <ui/EventHub.h>
 #include <ui/Input.h>
-#include <ui/InputDispatchPolicy.h>
 #include <ui/InputDispatcher.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
@@ -35,21 +34,6 @@
  * (This is limited by our use of BitSet32 to track pointer assignments.) */
 #define MAX_POINTER_ID 32
 
-/** Amount that trackball needs to move in order to generate a key event. */
-#define TRACKBALL_MOVEMENT_THRESHOLD 6
-
-/* Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value. */
-#define JUMPY_EPSILON_DIVISOR 212
-
-/* Number of jumpy points to drop for touchscreens that need it. */
-#define JUMPY_TRANSITION_DROPS 3
-#define JUMPY_DROP_LIMIT 3
-
-/* Maximum squared distance for averaging.
- * If moving farther than this, turn of averaging to avoid lag in response. */
-#define AVERAGING_DISTANCE_LIMIT (75 * 75)
-
 /* Maximum number of historical samples to average. */
 #define AVERAGING_HISTORY_SIZE 5
 
@@ -335,8 +319,129 @@
 };
 
 
-/* Processes raw input events and sends cooked event data to an input dispatcher
- * in accordance with the input dispatch policy. */
+/*
+ * Input reader policy interface.
+ *
+ * The input reader policy is used by the input reader to interact with the Window Manager
+ * and other system components.
+ *
+ * The actual implementation is partially supported by callbacks into the DVM
+ * via JNI.  This interface is also mocked in the unit tests.
+ */
+class InputReaderPolicyInterface : public virtual RefBase {
+protected:
+    InputReaderPolicyInterface() { }
+    virtual ~InputReaderPolicyInterface() { }
+
+public:
+    /* Display orientations. */
+    enum {
+        ROTATION_0 = 0,
+        ROTATION_90 = 1,
+        ROTATION_180 = 2,
+        ROTATION_270 = 3
+    };
+
+    /* Actions returned by interceptXXX methods. */
+    enum {
+        // The input dispatcher should do nothing and discard the input unless other
+        // flags are set.
+        ACTION_NONE = 0,
+
+        // The input dispatcher should dispatch the input to the application.
+        ACTION_DISPATCH = 0x00000001,
+
+        // The input dispatcher should perform special filtering in preparation for
+        // a pending app switch.
+        ACTION_APP_SWITCH_COMING = 0x00000002,
+
+        // The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it
+        // passes through the dispatch pipeline.
+        ACTION_WOKE_HERE = 0x00000004,
+
+        // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
+        // passes through the dispatch pipeline.
+        ACTION_BRIGHT_HERE = 0x00000008
+    };
+
+    /* Describes a virtual key. */
+    struct VirtualKeyDefinition {
+        int32_t scanCode;
+
+        // configured position data, specified in display coords
+        int32_t centerX;
+        int32_t centerY;
+        int32_t width;
+        int32_t height;
+    };
+
+    /* Gets information about the display with the specified id.
+     * Returns true if the display info is available, false otherwise.
+     */
+    virtual bool getDisplayInfo(int32_t displayId,
+            int32_t* width, int32_t* height, int32_t* orientation) = 0;
+
+    /* Provides feedback for a virtual key.
+     */
+    virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
+            int32_t action, int32_t flags, int32_t keyCode,
+            int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
+
+    /* Intercepts a key event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
+            bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0;
+
+    /* Intercepts a trackball event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
+            bool rolled) = 0;
+
+    /* Intercepts a touch event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Returns a policy action constant such as ACTION_DISPATCH.
+     */
+    virtual int32_t interceptTouch(nsecs_t when) = 0;
+
+    /* Intercepts a switch event.
+     * The policy can use this method as an opportunity to perform power management functions
+     * and early event preprocessing.
+     *
+     * Switches are not dispatched to applications so this method should
+     * usually return ACTION_NONE.
+     */
+    virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) = 0;
+
+    /* Determines whether to turn on some hacks we have to improve the touch interaction with a
+     * certain device whose screen currently is not all that good.
+     */
+    virtual bool filterTouchEvents() = 0;
+
+    /* Determines whether to turn on some hacks to improve touch interaction with another device
+     * where touch coordinate data can get corrupted.
+     */
+    virtual bool filterJumpyTouchEvents() = 0;
+
+    /* Gets the configured virtual key definitions for an input device. */
+    virtual void getVirtualKeyDefinitions(const String8& deviceName,
+            Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
+
+    /* Gets the excluded device names for the platform. */
+    virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
+};
+
+
+/* Processes raw input events and sends cooked event data to an input dispatcher. */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -355,16 +460,42 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const = 0;
+
+    /* Gets the current input device configuration.
+     *
+     * This method may be called on any thread (usually by the input manager).
+     */
+    virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const = 0;
+
+    /*
+     * Query current input state.
+     *   deviceId may be -1 to search for the device automatically, filtered by class.
+     *   deviceClasses may be -1 to ignore device class while searching.
+     */
+    virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t scanCode) const = 0;
+    virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t keyCode) const = 0;
+    virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
+            int32_t sw) const = 0;
+
+    /* Determine whether physical keys exist for the given framework-domain key codes. */
+    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
 };
 
+
 /* The input reader reads raw event data from the event hub and processes it into input events
- * that it sends to the input dispatcher.  Some functions of the input reader are controlled
- * by the input dispatch policy, such as early event filtering in low power states.
+ * that it sends to the input dispatcher.  Some functions of the input reader, such as early
+ * event filtering in low power states, are controlled by a separate policy object.
+ *
+ * IMPORTANT INVARIANT:
+ *     Because the policy can potentially block or cause re-entrance into the input reader,
+ *     the input reader never calls into the policy while holding its internal locks.
  */
 class InputReader : public InputReaderInterface {
 public:
     InputReader(const sp<EventHubInterface>& eventHub,
-            const sp<InputDispatchPolicyInterface>& policy,
+            const sp<InputReaderPolicyInterface>& policy,
             const sp<InputDispatcherInterface>& dispatcher);
     virtual ~InputReader();
 
@@ -372,6 +503,17 @@
 
     virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const;
 
+    virtual void getCurrentInputConfiguration(InputConfiguration* outConfiguration) const;
+
+    virtual int32_t getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t scanCode) const;
+    virtual int32_t getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+            int32_t keyCode) const;
+    virtual int32_t getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
+            int32_t sw) const;
+
+    virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
+
 private:
     // Lock that must be acquired while manipulating state that may be concurrently accessed
     // from other threads by input state query methods.  It should be held for as short a
@@ -383,15 +525,18 @@
     //     (but not other internal device state)
     mutable Mutex mExportedStateLock;
 
-    // current virtual key information
-    int32_t mGlobalVirtualKeyCode;
-    int32_t mGlobalVirtualScanCode;
+    // current virtual key information (lock mExportedStateLock)
+    int32_t mExportedVirtualKeyCode;
+    int32_t mExportedVirtualScanCode;
+
+    // current input configuration (lock mExportedStateLock)
+    InputConfiguration mExportedInputConfiguration;
 
     // combined key meta state
     int32_t mGlobalMetaState;
 
     sp<EventHubInterface> mEventHub;
-    sp<InputDispatchPolicyInterface> mPolicy;
+    sp<InputReaderPolicyInterface> mPolicy;
     sp<InputDispatcherInterface> mDispatcher;
 
     KeyedVector<int32_t, InputDevice*> mDevices;
@@ -414,7 +559,7 @@
     // input policy processing and dispatch
     void onKey(nsecs_t when, InputDevice* device, bool down,
             int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
-    void onSwitch(nsecs_t when, InputDevice* device, bool down, int32_t code);
+    void onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode, int32_t switchValue);
     void onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device);
     void onMultiTouchScreenStateChanged(nsecs_t when, InputDevice* device);
     void onTouchScreenChanged(nsecs_t when, InputDevice* device, bool havePointerIds);
@@ -445,13 +590,17 @@
     void configureVirtualKeys(InputDevice* device);
     void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name,
             InputDevice::AbsoluteAxisInfo* out);
+    void configureExcludedDevices();
 
     // global meta state management for all devices
     void resetGlobalMetaState();
     int32_t globalMetaState();
 
     // virtual key management
-    void updateGlobalVirtualKeyState();
+    void updateExportedVirtualKeyState();
+
+    // input configuration management
+    void updateExportedInputConfiguration();
 };
 
 
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index 9537523..7b182f3 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -62,7 +62,7 @@
      * Returns OK on success.
      */
     static status_t openInputChannelPair(const String8& name,
-            InputChannel** outServerChannel, InputChannel** outClientChannel);
+            sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
 
     inline String8 getName() const { return mName; }
     inline int32_t getAshmemFd() const { return mAshmemFd; }
@@ -72,7 +72,8 @@
     /* Sends a signal to the other endpoint.
      *
      * Returns OK on success.
-     * Errors probably indicate that the channel is broken.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
      */
     status_t sendSignal(char signal);
 
@@ -81,6 +82,7 @@
      *
      * Returns OK on success.
      * Returns WOULD_BLOCK if there is no signal present.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
     status_t receiveSignal(char* outSignal);
@@ -298,7 +300,7 @@
      * Returns INVALID_OPERATION if there is no currently published event.
      * Returns NO_MEMORY if the event could not be created.
      */
-    status_t consume(InputEventFactoryInterface* factory, InputEvent** event);
+    status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent);
 
     /* Sends a finished signal to the publisher to inform it that the current message is
      * finished processing.
diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h
index c9d951f..a95fb17 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -114,8 +114,10 @@
     };
 
     Mutex mLock;
-    Condition mAwake;
     bool mPolling;
+    uint32_t mWaiters;
+    Condition mAwake;
+    Condition mResume;
 
     int mWakeReadPipeFd;
     int mWakeWritePipeFd;
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index d40ae16..ec851bd 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -115,10 +115,10 @@
 
 
     //! insert an array at a given index
-            ssize_t         insertArrayAt(const TYPE* array, size_t index, size_t numItems);
+            ssize_t         insertArrayAt(const TYPE* array, size_t index, size_t length);
 
     //! append an array at the end of this vector
-            ssize_t         appendArray(const TYPE* array, size_t numItems);
+            ssize_t         appendArray(const TYPE* array, size_t length);
 
             /*! 
              * add/insert/replace items
@@ -126,7 +126,7 @@
              
     //! insert one or several items initialized with their default constructor
     inline  ssize_t         insertAt(size_t index, size_t numItems = 1);
-    //! insert on onr several items initialized from a prototype item
+    //! insert one or several items initialized from a prototype item
             ssize_t         insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
     //! pop the top of the stack (removes the last element). No-op if the stack's empty
     inline  void            pop();
@@ -265,13 +265,13 @@
 }
 
 template<class TYPE> inline
-ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t numItems) {
-    return VectorImpl::insertAt(array, index, numItems);
+ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {
+    return VectorImpl::insertArrayAt(array, index, length);
 }
 
 template<class TYPE> inline
-ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t numItems) {
-    return VectorImpl::add(array, numItems);
+ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {
+    return VectorImpl::appendArray(array, length);
 }
 
 template<class TYPE> inline
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
index 46a7bc2..c4ec2ff 100644
--- a/include/utils/VectorImpl.h
+++ b/include/utils/VectorImpl.h
@@ -65,9 +65,11 @@
             size_t          capacity() const;
             ssize_t         setCapacity(size_t size);
 
-            /*! append/insert another vector */
+            /*! append/insert another vector or array */
             ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
             ssize_t         appendVector(const VectorImpl& vector);
+            ssize_t         insertArrayAt(const void* array, size_t index, size_t length);
+            ssize_t         appendArray(const void* array, size_t length);
             
             /*! add/insert/replace items */
             ssize_t         insertAt(size_t where, size_t numItems = 1);
@@ -76,7 +78,7 @@
             void            push();
             void            push(const void* item);
             ssize_t         add();
-            ssize_t         add(const void* item, size_t numItems = 1);
+            ssize_t         add(const void* item);
             ssize_t         replaceAt(size_t index);
             ssize_t         replaceAt(const void* item, size_t index);
 
@@ -184,8 +186,8 @@
             void            push(const void* item);
             ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
             ssize_t         appendVector(const VectorImpl& vector);
-            ssize_t         insertArrayAt(const void* array, size_t index, size_t numItems);
-            ssize_t         appendArray(const void* array, size_t numItems);
+            ssize_t         insertArrayAt(const void* array, size_t index, size_t length);
+            ssize_t         appendArray(const void* array, size_t length);
             ssize_t         insertAt(size_t where, size_t numItems = 1);
             ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);
             ssize_t         replaceAt(size_t index);
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index d367708..0e6f2f5 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -50,8 +50,8 @@
         int32_t action,
         int32_t edgeFlags,
         int32_t metaState,
-        float rawX,
-        float rawY,
+        float xOffset,
+        float yOffset,
         float xPrecision,
         float yPrecision,
         nsecs_t downTime,
@@ -63,8 +63,8 @@
     mAction = action;
     mEdgeFlags = edgeFlags;
     mMetaState = metaState;
-    mRawX = rawX;
-    mRawY = rawY;
+    mXOffset = xOffset;
+    mYOffset = yOffset;
     mXPrecision = xPrecision;
     mYPrecision = yPrecision;
     mDownTime = downTime;
@@ -83,13 +83,8 @@
 }
 
 void MotionEvent::offsetLocation(float xOffset, float yOffset) {
-    if (xOffset != 0 || yOffset != 0) {
-        for (size_t i = 0; i < mSamplePointerCoords.size(); i++) {
-            PointerCoords& pointerCoords = mSamplePointerCoords.editItemAt(i);
-            pointerCoords.x += xOffset;
-            pointerCoords.y += yOffset;
-        }
-    }
+    mXOffset += xOffset;
+    mYOffset += yOffset;
 }
 
 } // namespace android
@@ -163,6 +158,14 @@
     return reinterpret_cast<const MotionEvent*>(motion_event)->getEventTime();
 }
 
+float motion_event_get_x_offset(const input_event_t* motion_event) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getXOffset();
+}
+
+float motion_event_get_y_offset(const input_event_t* motion_event) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getYOffset();
+}
+
 float motion_event_get_x_precision(const input_event_t* motion_event) {
     return reinterpret_cast<const MotionEvent*>(motion_event)->getXPrecision();
 }
@@ -179,12 +182,12 @@
     return reinterpret_cast<const MotionEvent*>(motion_event)->getPointerId(pointer_index);
 }
 
-float motion_event_get_raw_x(const input_event_t* motion_event) {
-    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawX();
+float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawX(pointer_index);
 }
 
-float motion_event_get_raw_y(const input_event_t* motion_event) {
-    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawY();
+float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getRawY(pointer_index);
 }
 
 float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index) {
@@ -213,6 +216,18 @@
             history_index);
 }
 
+float motion_event_get_historical_raw_x(input_event_t* motion_event, size_t pointer_index,
+        size_t history_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalRawX(
+            pointer_index, history_index);
+}
+
+float motion_event_get_historical_raw_y(input_event_t* motion_event, size_t pointer_index,
+        size_t history_index) {
+    return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalRawY(
+            pointer_index, history_index);
+}
+
 float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index,
         size_t history_index) {
     return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalX(
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 0ccde0d..2ad3382 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -19,9 +19,15 @@
 // Log debug messages about the dispatch cycle.
 #define DEBUG_DISPATCH_CYCLE 1
 
+// Log debug messages about registrations.
+#define DEBUG_REGISTRATION 1
+
 // Log debug messages about performance statistics.
 #define DEBUG_PERFORMANCE_STATISTICS 1
 
+// Log debug messages about input event injection.
+#define DEBUG_INJECTION 1
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
 
@@ -40,9 +46,13 @@
             || keyCode == KEYCODE_DPAD_RIGHT;
 }
 
+static inline nsecs_t now() {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
 // --- InputDispatcher ---
 
-InputDispatcher::InputDispatcher(const sp<InputDispatchPolicyInterface>& policy) :
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
     mPolicy(policy) {
     mPollLoop = new PollLoop();
 
@@ -55,6 +65,8 @@
     mInboundQueue.tail.eventTime = LONG_LONG_MAX;
 
     mKeyRepeatState.lastKeyEntry = NULL;
+
+    mCurrentInputTargetsValid = false;
 }
 
 InputDispatcher::~InputDispatcher() {
@@ -72,57 +84,58 @@
 }
 
 void InputDispatcher::dispatchOnce() {
-    bool allowKeyRepeat = mPolicy->allowKeyRepeat();
+    nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
 
+    bool skipPoll = false;
     nsecs_t currentTime;
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
         AutoMutex _l(mLock);
-        currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        currentTime = now();
 
         // Reset the key repeat timer whenever we disallow key events, even if the next event
         // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
         // out of sleep.
         // XXX we should handle resetting input state coming out of sleep more generally elsewhere
-        if (! allowKeyRepeat) {
+        if (keyRepeatTimeout < 0) {
             resetKeyRepeatLocked();
         }
 
-        // Process timeouts for all connections and determine if there are any synchronous
-        // event dispatches pending.
+        // Detect and process timeouts for all connections and determine if there are any
+        // synchronous event dispatches pending.  This step is entirely non-interruptible.
         bool hasPendingSyncTarget = false;
-        for (size_t i = 0; i < mActiveConnections.size(); ) {
+        size_t activeConnectionCount = mActiveConnections.size();
+        for (size_t i = 0; i < activeConnectionCount; i++) {
             Connection* connection = mActiveConnections.itemAt(i);
 
-            nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
-            if (connectionTimeoutTime <= currentTime) {
-                bool deactivated = timeoutDispatchCycleLocked(currentTime, connection);
-                if (deactivated) {
-                    // Don't increment i because the connection has been removed
-                    // from mActiveConnections (hence, deactivated).
-                    continue;
-                }
-            }
-
-            if (connectionTimeoutTime < nextWakeupTime) {
-                nextWakeupTime = connectionTimeoutTime;
-            }
-
             if (connection->hasPendingSyncTarget()) {
                 hasPendingSyncTarget = true;
             }
 
-            i += 1;
+            nsecs_t connectionTimeoutTime  = connection->nextTimeoutTime;
+            if (connectionTimeoutTime <= currentTime) {
+                mTimedOutConnections.add(connection);
+            } else if (connectionTimeoutTime < nextWakeupTime) {
+                nextWakeupTime = connectionTimeoutTime;
+            }
         }
 
+        size_t timedOutConnectionCount = mTimedOutConnections.size();
+        for (size_t i = 0; i < timedOutConnectionCount; i++) {
+            Connection* connection = mTimedOutConnections.itemAt(i);
+            timeoutDispatchCycleLocked(currentTime, connection);
+            skipPoll = true;
+        }
+        mTimedOutConnections.clear();
+
         // If we don't have a pending sync target, then we can begin delivering a new event.
         // (Otherwise we wait for dispatch to complete for that target.)
         if (! hasPendingSyncTarget) {
             if (mInboundQueue.isEmpty()) {
                 if (mKeyRepeatState.lastKeyEntry) {
                     if (currentTime >= mKeyRepeatState.nextRepeatTime) {
-                        processKeyRepeatLocked(currentTime);
-                        return; // dispatched once
+                        processKeyRepeatLockedInterruptible(currentTime, keyRepeatTimeout);
+                        skipPoll = true;
                     } else {
                         if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) {
                             nextWakeupTime = mKeyRepeatState.nextRepeatTime;
@@ -130,31 +143,30 @@
                     }
                 }
             } else {
-                // Inbound queue has at least one entry.  Dequeue it and begin dispatching.
-                // Note that we do not hold the lock for this process because dispatching may
-                // involve making many callbacks.
-                EventEntry* entry = mInboundQueue.dequeueAtHead();
+                // Inbound queue has at least one entry.
+                // Start processing it but leave it on the queue until later so that the
+                // input reader can keep appending samples onto a motion event between the
+                // time we started processing it and the time we finally enqueue dispatch
+                // entries for it.
+                EventEntry* entry = mInboundQueue.head.next;
 
                 switch (entry->type) {
                 case EventEntry::TYPE_CONFIGURATION_CHANGED: {
                     ConfigurationChangedEntry* typedEntry =
                             static_cast<ConfigurationChangedEntry*>(entry);
-                    processConfigurationChangedLocked(currentTime, typedEntry);
-                    mAllocator.releaseConfigurationChangedEntry(typedEntry);
+                    processConfigurationChangedLockedInterruptible(currentTime, typedEntry);
                     break;
                 }
 
                 case EventEntry::TYPE_KEY: {
                     KeyEntry* typedEntry = static_cast<KeyEntry*>(entry);
-                    processKeyLocked(currentTime, typedEntry);
-                    mAllocator.releaseKeyEntry(typedEntry);
+                    processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout);
                     break;
                 }
 
                 case EventEntry::TYPE_MOTION: {
                     MotionEntry* typedEntry = static_cast<MotionEntry*>(entry);
-                    processMotionLocked(currentTime, typedEntry);
-                    mAllocator.releaseMotionEntry(typedEntry);
+                    processMotionLockedInterruptible(currentTime, typedEntry);
                     break;
                 }
 
@@ -162,30 +174,73 @@
                     assert(false);
                     break;
                 }
-                return; // dispatched once
+
+                // Dequeue and release the event entry that we just processed.
+                mInboundQueue.dequeue(entry);
+                mAllocator.releaseEventEntry(entry);
+                skipPoll = true;
             }
         }
+
+        // Run any deferred commands.
+        skipPoll |= runCommandsLockedInterruptible();
+
+        // Wake up synchronization waiters, if needed.
+        if (isFullySynchronizedLocked()) {
+            mFullySynchronizedCondition.broadcast();
+        }
     } // release lock
 
+    // If we dispatched anything, don't poll just now.  Wait for the next iteration.
+    // Contents may have shifted during flight.
+    if (skipPoll) {
+        return;
+    }
+
     // Wait for callback or timeout or wake.
     nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
     int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
     mPollLoop->pollOnce(timeoutMillis);
 }
 
-void InputDispatcher::processConfigurationChangedLocked(nsecs_t currentTime,
-        ConfigurationChangedEntry* entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
-    LOGD("processConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, "
-            "keyboardConfig=%d, navigationConfig=%d", entry->eventTime,
-            entry->touchScreenConfig, entry->keyboardConfig, entry->navigationConfig);
-#endif
+bool InputDispatcher::runCommandsLockedInterruptible() {
+    if (mCommandQueue.isEmpty()) {
+        return false;
+    }
 
-    mPolicy->notifyConfigurationChanged(entry->eventTime, entry->touchScreenConfig,
-            entry->keyboardConfig, entry->navigationConfig);
+    do {
+        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
+
+        Command command = commandEntry->command;
+        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+
+        commandEntry->connection.clear();
+        mAllocator.releaseCommandEntry(commandEntry);
+    } while (! mCommandQueue.isEmpty());
+    return true;
 }
 
-void InputDispatcher::processKeyLocked(nsecs_t currentTime, KeyEntry* entry) {
+InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
+    CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command);
+    mCommandQueue.enqueueAtTail(commandEntry);
+    return commandEntry;
+}
+
+void InputDispatcher::processConfigurationChangedLockedInterruptible(
+        nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+    mLock.unlock();
+
+    mPolicy->notifyConfigurationChanged(entry->eventTime);
+
+    mLock.lock();
+}
+
+void InputDispatcher::processKeyLockedInterruptible(
+        nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processKey - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, "
             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
@@ -209,7 +264,7 @@
         } else {
             // Not a repeat.  Save key down state in case we do see a repeat later.
             resetKeyRepeatLocked();
-            mKeyRepeatState.nextRepeatTime = entry->eventTime + mPolicy->getKeyRepeatTimeout();
+            mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
         }
         mKeyRepeatState.lastKeyEntry = entry;
         entry->refCount += 1;
@@ -217,10 +272,11 @@
         resetKeyRepeatLocked();
     }
 
-    identifyInputTargetsAndDispatchKeyLocked(currentTime, entry);
+    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::processKeyRepeatLocked(nsecs_t currentTime) {
+void InputDispatcher::processKeyRepeatLockedInterruptible(
+        nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
     // TODO Old WindowManagerServer code sniffs the input queue for following key up
     //      events and drops the repeat if one is found.  We should do something similar.
     //      One good place to do it is in notifyKey as soon as the key up enters the
@@ -229,30 +285,25 @@
     // Synthesize a key repeat after the repeat timeout expired.
     // We reuse the previous key entry if otherwise unreferenced.
     KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+    uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
+        entry->eventTime = currentTime;
+        entry->downTime = currentTime;
+        entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
     } else {
-        KeyEntry* newEntry = mAllocator.obtainKeyEntry();
-        newEntry->deviceId = entry->deviceId;
-        newEntry->nature = entry->nature;
-        newEntry->policyFlags = entry->policyFlags;
-        newEntry->action = entry->action;
-        newEntry->flags = entry->flags;
-        newEntry->keyCode = entry->keyCode;
-        newEntry->scanCode = entry->scanCode;
-        newEntry->metaState = entry->metaState;
-        newEntry->repeatCount = entry->repeatCount + 1;
+        KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime,
+                entry->deviceId, entry->nature, policyFlags,
+                entry->action, entry->flags, entry->keyCode, entry->scanCode,
+                entry->metaState, entry->repeatCount + 1, currentTime);
 
         mKeyRepeatState.lastKeyEntry = newEntry;
         mAllocator.releaseKeyEntry(entry);
 
         entry = newEntry;
     }
-    entry->eventTime = currentTime;
-    entry->downTime = currentTime;
-    entry->policyFlags = 0;
 
-    mKeyRepeatState.nextRepeatTime = currentTime + mPolicy->getKeyRepeatTimeout();
+    mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
 
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, "
@@ -263,10 +314,11 @@
             entry->repeatCount, entry->downTime);
 #endif
 
-    identifyInputTargetsAndDispatchKeyLocked(currentTime, entry);
+    identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::processMotionLocked(nsecs_t currentTime, MotionEntry* entry) {
+void InputDispatcher::processMotionLockedInterruptible(
+        nsecs_t currentTime, MotionEntry* entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("processMotion - eventTime=%lld, deviceId=0x%x, nature=0x%x, policyFlags=0x%x, action=0x%x, "
             "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
@@ -296,54 +348,75 @@
     }
 #endif
 
-    identifyInputTargetsAndDispatchMotionLocked(currentTime, entry);
+    identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry);
 }
 
-void InputDispatcher::identifyInputTargetsAndDispatchKeyLocked(
+void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
         nsecs_t currentTime, KeyEntry* entry) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("identifyInputTargetsAndDispatchKey");
 #endif
 
+    entry->dispatchInProgress = true;
+    mCurrentInputTargetsValid = false;
+    mLock.unlock();
+
     mReusableKeyEvent.initialize(entry->deviceId, entry->nature, entry->action, entry->flags,
             entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
             entry->downTime, entry->eventTime);
 
     mCurrentInputTargets.clear();
-    mPolicy->getKeyEventTargets(& mReusableKeyEvent, entry->policyFlags,
+    int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
+            entry->policyFlags, entry->injectorPid, entry->injectorUid,
             mCurrentInputTargets);
 
+    mLock.lock();
+    mCurrentInputTargetsValid = true;
+
+    setInjectionResultLocked(entry, injectionResult);
+
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 }
 
-void InputDispatcher::identifyInputTargetsAndDispatchMotionLocked(
+void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
         nsecs_t currentTime, MotionEntry* entry) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("identifyInputTargetsAndDispatchMotion");
 #endif
 
+    entry->dispatchInProgress = true;
+    mCurrentInputTargetsValid = false;
+    mLock.unlock();
+
     mReusableMotionEvent.initialize(entry->deviceId, entry->nature, entry->action,
             entry->edgeFlags, entry->metaState,
-            entry->firstSample.pointerCoords[0].x, entry->firstSample.pointerCoords[0].y,
-            entry->xPrecision, entry->yPrecision,
+            0, 0, entry->xPrecision, entry->yPrecision,
             entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds,
             entry->firstSample.pointerCoords);
 
     mCurrentInputTargets.clear();
-    mPolicy->getMotionEventTargets(& mReusableMotionEvent, entry->policyFlags,
+    int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
+            entry->policyFlags, entry->injectorPid, entry->injectorUid,
             mCurrentInputTargets);
 
+    mLock.lock();
+    mCurrentInputTargetsValid = true;
+
+    setInjectionResultLocked(entry, injectionResult);
+
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 }
 
 void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
         EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("dispatchEventToCurrentInputTargets, "
+    LOGD("dispatchEventToCurrentInputTargets - "
             "resumeWithAppendedMotionSample=%s",
             resumeWithAppendedMotionSample ? "true" : "false");
 #endif
 
+    assert(eventEntry->dispatchInProgress); // should already have been set to true
+
     for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
 
@@ -351,7 +424,7 @@
                 inputTarget.inputChannel->getReceivePipeFd());
         if (connectionIndex >= 0) {
             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
-            prepareDispatchCycleLocked(currentTime, connection.get(), eventEntry, & inputTarget,
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
                     resumeWithAppendedMotionSample);
         } else {
             LOGW("Framework requested delivery of an input event to channel '%s' but it "
@@ -361,11 +434,11 @@
     }
 }
 
-void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
-        EventEntry* eventEntry, const InputTarget* inputTarget,
+void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ prepareDispatchCycle, flags=%d, timeout=%lldns, "
+    LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
             "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout,
             inputTarget->xOffset, inputTarget->yOffset,
@@ -377,7 +450,7 @@
     // not responding.
     if (connection->status != Connection::STATUS_NORMAL) {
         LOGV("channel '%s' ~ Dropping event because the channel status is %s",
-                connection->status == Connection::STATUS_BROKEN ? "BROKEN" : "NOT RESPONDING");
+                connection->getStatusLabel());
         return;
     }
 
@@ -488,12 +561,13 @@
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
     if (wasEmpty) {
-        activateConnectionLocked(connection);
+        activateConnectionLocked(connection.get());
         startDispatchCycleLocked(currentTime, connection);
     }
 }
 
-void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ startDispatchCycle",
             connection->getInputChannelName());
@@ -623,22 +697,24 @@
     connection->lastDispatchTime = currentTime;
 
     nsecs_t timeout = dispatchEntry->timeout;
-    connection->nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
+    connection->setNextTimeoutTime(currentTime, timeout);
 
     // Notify other system components.
     onDispatchCycleStartedLocked(currentTime, connection);
 }
 
-void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ finishDispatchCycle: %01.1fms since event, "
+    LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
             "%01.1fms since dispatch",
             connection->getInputChannelName(),
             connection->getEventLatencyMillis(currentTime),
             connection->getDispatchLatencyMillis(currentTime));
 #endif
 
-    if (connection->status == Connection::STATUS_BROKEN) {
+    if (connection->status == Connection::STATUS_BROKEN
+            || connection->status == Connection::STATUS_ZOMBIE) {
         return;
     }
 
@@ -696,60 +772,77 @@
     }
 
     // Outbound queue is empty, deactivate the connection.
-    deactivateConnectionLocked(connection);
+    deactivateConnectionLocked(connection.get());
 }
 
-bool InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ timeoutDispatchCycle",
             connection->getInputChannelName());
 #endif
 
     if (connection->status != Connection::STATUS_NORMAL) {
-        return false;
+        return;
     }
 
     // Enter the not responding state.
     connection->status = Connection::STATUS_NOT_RESPONDING;
     connection->lastANRTime = currentTime;
-    bool deactivated = abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
 
     // Notify other system components.
+    // This enqueues a command which will eventually either call
+    // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
     onDispatchCycleANRLocked(currentTime, connection);
-    return deactivated;
 }
 
-bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
-        bool broken) {
+void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection, nsecs_t newTimeout) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ abortDispatchCycle, broken=%s",
-            connection->getInputChannelName(), broken ? "true" : "false");
+    LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked",
+            connection->getInputChannelName());
 #endif
 
-    if (connection->status == Connection::STATUS_BROKEN) {
-        return false;
+    if (connection->status != Connection::STATUS_NOT_RESPONDING) {
+        return;
     }
 
+    // Resume normal dispatch.
+    connection->status = Connection::STATUS_NORMAL;
+    connection->setNextTimeoutTime(currentTime, newTimeout);
+}
+
+void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
+        const sp<Connection>& connection, bool broken) {
+#if DEBUG_DISPATCH_CYCLE
+    LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
+            connection->getInputChannelName(), broken ? "true" : "false");
+#endif
+
     // Clear the pending timeout.
     connection->nextTimeoutTime = LONG_LONG_MAX;
 
     // Clear the outbound queue.
-    while (! connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
-        mAllocator.releaseDispatchEntry(dispatchEntry);
-    }
+    if (! connection->outboundQueue.isEmpty()) {
+        do {
+            DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+            mAllocator.releaseDispatchEntry(dispatchEntry);
+        } while (! connection->outboundQueue.isEmpty());
 
-    // Outbound queue is empty, deactivate the connection.
-    deactivateConnectionLocked(connection);
+        deactivateConnectionLocked(connection.get());
+    }
 
     // Handle the case where the connection appears to be unrecoverably broken.
+    // Ignore already broken or zombie connections.
     if (broken) {
-        connection->status = Connection::STATUS_BROKEN;
+        if (connection->status == Connection::STATUS_NORMAL
+                || connection->status == Connection::STATUS_NOT_RESPONDING) {
+            connection->status = Connection::STATUS_BROKEN;
 
-        // Notify other system components.
-        onDispatchCycleBrokenLocked(currentTime, connection);
+            // Notify other system components.
+            onDispatchCycleBrokenLocked(currentTime, connection);
+        }
     }
-    return true; /*deactivated*/
 }
 
 bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
@@ -765,13 +858,14 @@
             return false; // remove the callback
         }
 
-        nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        nsecs_t currentTime = now();
 
         sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
         if (events & (POLLERR | POLLHUP | POLLNVAL)) {
             LOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                     "events=0x%x", connection->getInputChannelName(), events);
-            d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+            d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+            d->runCommandsLockedInterruptible();
             return false; // remove the callback
         }
 
@@ -785,32 +879,27 @@
         if (status) {
             LOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                     connection->getInputChannelName(), status);
-            d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+            d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+            d->runCommandsLockedInterruptible();
             return false; // remove the callback
         }
 
-        d->finishDispatchCycleLocked(currentTime, connection.get());
+        d->finishDispatchCycleLocked(currentTime, connection);
+        d->runCommandsLockedInterruptible();
         return true;
     } // release lock
 }
 
-void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime, int32_t touchScreenConfig,
-        int32_t keyboardConfig, int32_t navigationConfig) {
+void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyConfigurationChanged - eventTime=%lld, touchScreenConfig=%d, "
-            "keyboardConfig=%d, navigationConfig=%d", eventTime,
-            touchScreenConfig, keyboardConfig, navigationConfig);
+    LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
 #endif
 
     bool wasEmpty;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry();
-        newEntry->eventTime = eventTime;
-        newEntry->touchScreenConfig = touchScreenConfig;
-        newEntry->keyboardConfig = keyboardConfig;
-        newEntry->navigationConfig = navigationConfig;
+        ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -821,16 +910,6 @@
     }
 }
 
-void InputDispatcher::notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) {
-#if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyLidSwitchChanged - eventTime=%lld, open=%s", eventTime,
-            lidOpen ? "true" : "false");
-#endif
-
-    // Send lid switch notification immediately and synchronously.
-    mPolicy->notifyLidSwitchChanged(eventTime, lidOpen);
-}
-
 void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime);
@@ -852,6 +931,9 @@
                     LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
                             keyEntry->keyCode, keyEntry->action);
                     mInboundQueue.dequeue(keyEntry);
+
+                    setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+
                     mAllocator.releaseKeyEntry(keyEntry);
                 } else {
                     // stop at last non-movement key
@@ -878,18 +960,10 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        KeyEntry* newEntry = mAllocator.obtainKeyEntry();
-        newEntry->eventTime = eventTime;
-        newEntry->deviceId = deviceId;
-        newEntry->nature = nature;
-        newEntry->policyFlags = policyFlags;
-        newEntry->action = action;
-        newEntry->flags = flags;
-        newEntry->keyCode = keyCode;
-        newEntry->scanCode = scanCode;
-        newEntry->metaState = metaState;
-        newEntry->repeatCount = 0;
-        newEntry->downTime = downTime;
+        int32_t repeatCount = 0;
+        KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
+                deviceId, nature, policyFlags, action, flags, keyCode, scanCode,
+                metaState, repeatCount, downTime);
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -942,20 +1016,33 @@
                 }
 
                 if (motionEntry->action != MOTION_EVENT_ACTION_MOVE
-                        || motionEntry->pointerCount != pointerCount) {
+                        || motionEntry->pointerCount != pointerCount
+                        || motionEntry->isInjected()) {
                     // Last motion event in the queue for this device is not compatible for
                     // appending new samples.  Stop here.
                     goto NoBatchingOrStreaming;
                 }
 
                 // The last motion event is a move and is compatible for appending.
-                // Do the batching magic and exit.
-                mAllocator.appendMotionSample(motionEntry, eventTime, pointerCount, pointerCoords);
+                // Do the batching magic.
+                mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
 #if DEBUG_BATCHING
                 LOGD("Appended motion sample onto batch for most recent "
                         "motion event for this device in the inbound queue.");
 #endif
-                return; // done
+
+                // Sanity check for special case because dispatch is interruptible.
+                // The dispatch logic is partially interruptible and releases its lock while
+                // identifying targets.  However, as soon as the targets have been identified,
+                // the dispatcher proceeds to write a dispatch entry into all relevant outbound
+                // queues and then promptly removes the motion entry from the queue.
+                //
+                // Consequently, we should never observe the case where the inbound queue contains
+                // an in-progress motion entry unless the current input targets are invalid
+                // (currently being computed).  Check for this!
+                assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid));
+
+                return; // done!
             }
 
             // STREAMING CASE
@@ -977,34 +1064,39 @@
             // Note: This code crucially depends on the invariant that an outbound queue always
             //       contains at most one synchronous event and it is always last (but it might
             //       not be first!).
-            for (size_t i = 0; i < mActiveConnections.size(); i++) {
-                Connection* connection = mActiveConnections.itemAt(i);
-                if (! connection->outboundQueue.isEmpty()) {
-                    DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
-                    if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
-                        if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
-                            goto NoBatchingOrStreaming;
-                        }
+            if (mCurrentInputTargetsValid) {
+                for (size_t i = 0; i < mActiveConnections.size(); i++) {
+                    Connection* connection = mActiveConnections.itemAt(i);
+                    if (! connection->outboundQueue.isEmpty()) {
+                        DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev;
+                        if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) {
+                            if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
+                                goto NoBatchingOrStreaming;
+                            }
 
-                        MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
-                                dispatchEntry->eventEntry);
-                        if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE
-                                || syncedMotionEntry->deviceId != deviceId
-                                || syncedMotionEntry->pointerCount != pointerCount) {
-                            goto NoBatchingOrStreaming;
-                        }
+                            MotionEntry* syncedMotionEntry = static_cast<MotionEntry*>(
+                                    dispatchEntry->eventEntry);
+                            if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE
+                                    || syncedMotionEntry->deviceId != deviceId
+                                    || syncedMotionEntry->pointerCount != pointerCount
+                                    || syncedMotionEntry->isInjected()) {
+                                goto NoBatchingOrStreaming;
+                            }
 
-                        // Found synced move entry.  Append sample and resume dispatch.
-                        mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
-                                pointerCount, pointerCoords);
-#if DEBUG_BATCHING
-                        LOGD("Appended motion sample onto batch for most recent synchronously "
-                                "dispatched motion event for this device in the outbound queues.");
-#endif
-                        nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
-                                true /*resumeWithAppendedMotionSample*/);
-                        return; // done!
+                            // Found synced move entry.  Append sample and resume dispatch.
+                            mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
+                                    pointerCoords);
+    #if DEBUG_BATCHING
+                            LOGD("Appended motion sample onto batch for most recent synchronously "
+                                    "dispatched motion event for this device in the outbound queues.");
+    #endif
+                            nsecs_t currentTime = now();
+                            dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
+                                    true /*resumeWithAppendedMotionSample*/);
+
+                            runCommandsLockedInterruptible();
+                            return; // done!
+                        }
                     }
                 }
             }
@@ -1013,24 +1105,10 @@
         }
 
         // Just enqueue a new motion event.
-        MotionEntry* newEntry = mAllocator.obtainMotionEntry();
-        newEntry->eventTime = eventTime;
-        newEntry->deviceId = deviceId;
-        newEntry->nature = nature;
-        newEntry->policyFlags = policyFlags;
-        newEntry->action = action;
-        newEntry->metaState = metaState;
-        newEntry->edgeFlags = edgeFlags;
-        newEntry->xPrecision = xPrecision;
-        newEntry->yPrecision = yPrecision;
-        newEntry->downTime = downTime;
-        newEntry->pointerCount = pointerCount;
-        newEntry->firstSample.eventTime = eventTime;
-        newEntry->lastSample = & newEntry->firstSample;
-        for (uint32_t i = 0; i < pointerCount; i++) {
-            newEntry->pointerIds[i] = pointerIds[i];
-            newEntry->firstSample.pointerCoords[i] = pointerCoords[i];
-        }
+        MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
+                deviceId, nature, policyFlags, action, metaState, edgeFlags,
+                xPrecision, yPrecision, downTime,
+                pointerCount, pointerIds, pointerCoords);
 
         wasEmpty = mInboundQueue.isEmpty();
         mInboundQueue.enqueueAtTail(newEntry);
@@ -1041,6 +1119,133 @@
     }
 }
 
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
+        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+    LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+            "sync=%d, timeoutMillis=%d",
+            event->getType(), injectorPid, injectorUid, sync, timeoutMillis);
+#endif
+
+    nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
+
+    EventEntry* injectedEntry;
+    bool wasEmpty;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        injectedEntry = createEntryFromInputEventLocked(event);
+        injectedEntry->refCount += 1;
+        injectedEntry->injectorPid = injectorPid;
+        injectedEntry->injectorUid = injectorUid;
+
+        wasEmpty = mInboundQueue.isEmpty();
+        mInboundQueue.enqueueAtTail(injectedEntry);
+
+    } // release lock
+
+    if (wasEmpty) {
+        mPollLoop->wake();
+    }
+
+    int32_t injectionResult;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (;;) {
+            injectionResult = injectedEntry->injectionResult;
+            if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+                break;
+            }
+
+            nsecs_t remainingTimeout = endTime - now();
+            if (remainingTimeout <= 0) {
+                injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                sync = false;
+                break;
+            }
+
+            mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+        }
+
+        if (sync) {
+            while (! isFullySynchronizedLocked()) {
+                nsecs_t remainingTimeout = endTime - now();
+                if (remainingTimeout <= 0) {
+                    injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+                    break;
+                }
+
+                mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout);
+            }
+        }
+
+        mAllocator.releaseEventEntry(injectedEntry);
+    } // release lock
+
+    return injectionResult;
+}
+
+void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+    if (entry->isInjected()) {
+#if DEBUG_INJECTION
+        LOGD("Setting input event injection result to %d.  "
+                "injectorPid=%d, injectorUid=%d",
+                 injectionResult, entry->injectorPid, entry->injectorUid);
+#endif
+
+        entry->injectionResult = injectionResult;
+        mInjectionResultAvailableCondition.broadcast();
+    }
+}
+
+bool InputDispatcher::isFullySynchronizedLocked() {
+    return mInboundQueue.isEmpty() && mActiveConnections.isEmpty();
+}
+
+InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
+        const InputEvent* event) {
+    switch (event->getType()) {
+    case INPUT_EVENT_TYPE_KEY: {
+        const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
+        uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+
+        KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
+                keyEvent->getDeviceId(), keyEvent->getNature(), policyFlags,
+                keyEvent->getAction(), keyEvent->getFlags(),
+                keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
+                keyEvent->getRepeatCount(), keyEvent->getDownTime());
+        return keyEntry;
+    }
+
+    case INPUT_EVENT_TYPE_MOTION: {
+        const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+        uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
+
+        const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+        const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+        size_t pointerCount = motionEvent->getPointerCount();
+
+        MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
+                motionEvent->getDeviceId(), motionEvent->getNature(), policyFlags,
+                motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
+                motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+                motionEvent->getDownTime(), uint32_t(pointerCount),
+                motionEvent->getPointerIds(), samplePointerCoords);
+        for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+            sampleEventTimes += 1;
+            samplePointerCoords += pointerCount;
+            mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords);
+        }
+        return motionEntry;
+    }
+
+    default:
+        assert(false);
+        return NULL;
+    }
+}
+
 void InputDispatcher::resetKeyRepeatLocked() {
     if (mKeyRepeatState.lastKeyEntry) {
         mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
@@ -1049,6 +1254,10 @@
 }
 
 status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+    LOGD("channel '%s' - Registered", inputChannel->getName().string());
+#endif
+
     int receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1069,6 +1278,8 @@
         }
 
         mConnectionsByReceiveFd.add(receiveFd, connection);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
@@ -1076,6 +1287,10 @@
 }
 
 status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+    LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
+#endif
+
     int32_t receiveFd;
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1093,8 +1308,10 @@
 
         connection->status = Connection::STATUS_ZOMBIE;
 
-        nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
+        nsecs_t currentTime = now();
+        abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
+
+        runCommandsLockedInterruptible();
     } // release lock
 
     mPollLoop->removeCallback(receiveFd);
@@ -1123,11 +1340,12 @@
     }
 }
 
-void InputDispatcher::onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleStartedLocked(
+        nsecs_t currentTime, const sp<Connection>& connection) {
 }
 
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
-        Connection* connection, bool recoveredFromANR) {
+void InputDispatcher::onDispatchCycleFinishedLocked(
+        nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
     if (recoveredFromANR) {
         LOGI("channel '%s' ~ Recovered from ANR.  %01.1fms since event, "
                 "%01.1fms since dispatch, %01.1fms since ANR",
@@ -1136,51 +1354,150 @@
                 connection->getDispatchLatencyMillis(currentTime),
                 connection->getANRLatencyMillis(currentTime));
 
-        // TODO tell framework
+        CommandEntry* commandEntry = postCommandLocked(
+                & InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
+        commandEntry->connection = connection;
     }
 }
 
-void InputDispatcher::onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleANRLocked(
+        nsecs_t currentTime, const sp<Connection>& connection) {
     LOGI("channel '%s' ~ Not responding!  %01.1fms since event, %01.1fms since dispatch",
             connection->getInputChannelName(),
             connection->getEventLatencyMillis(currentTime),
             connection->getDispatchLatencyMillis(currentTime));
 
-    // TODO tell framework
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
+    commandEntry->connection = connection;
 }
 
-void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection) {
+void InputDispatcher::onDispatchCycleBrokenLocked(
+        nsecs_t currentTime, const sp<Connection>& connection) {
     LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
             connection->getInputChannelName());
 
-    // TODO tell framework
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+    commandEntry->connection = connection;
 }
 
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<Connection> connection = commandEntry->connection;
+
+    if (connection->status != Connection::STATUS_ZOMBIE) {
+        mLock.unlock();
+
+        mPolicy->notifyInputChannelBroken(connection->inputChannel);
+
+        mLock.lock();
+    }
+}
+
+void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<Connection> connection = commandEntry->connection;
+
+    if (connection->status != Connection::STATUS_ZOMBIE) {
+        mLock.unlock();
+
+        nsecs_t newTimeout;
+        bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
+
+        mLock.lock();
+
+        nsecs_t currentTime = now();
+        if (resume) {
+            resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
+        } else {
+            abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
+        }
+    }
+}
+
+void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
+        CommandEntry* commandEntry) {
+    sp<Connection> connection = commandEntry->connection;
+
+    if (connection->status != Connection::STATUS_ZOMBIE) {
+        mLock.unlock();
+
+        mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
+
+        mLock.lock();
+    }
+}
+
+
 // --- InputDispatcher::Allocator ---
 
 InputDispatcher::Allocator::Allocator() {
 }
 
+void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
+        nsecs_t eventTime) {
+    entry->type = type;
+    entry->refCount = 1;
+    entry->dispatchInProgress = false;
+    entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    entry->injectorPid = -1;
+    entry->injectorUid = -1;
+}
+
 InputDispatcher::ConfigurationChangedEntry*
-InputDispatcher::Allocator::obtainConfigurationChangedEntry() {
+InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) {
     ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
-    entry->refCount = 1;
-    entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED;
+    initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime);
     return entry;
 }
 
-InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry() {
+InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime,
+        int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+        int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+        int32_t repeatCount, nsecs_t downTime) {
     KeyEntry* entry = mKeyEntryPool.alloc();
-    entry->refCount = 1;
-    entry->type = EventEntry::TYPE_KEY;
+    initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime);
+
+    entry->deviceId = deviceId;
+    entry->nature = nature;
+    entry->policyFlags = policyFlags;
+    entry->action = action;
+    entry->flags = flags;
+    entry->keyCode = keyCode;
+    entry->scanCode = scanCode;
+    entry->metaState = metaState;
+    entry->repeatCount = repeatCount;
+    entry->downTime = downTime;
     return entry;
 }
 
-InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry() {
+InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
+        int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
+        int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+        nsecs_t downTime, uint32_t pointerCount,
+        const int32_t* pointerIds, const PointerCoords* pointerCoords) {
     MotionEntry* entry = mMotionEntryPool.alloc();
-    entry->refCount = 1;
-    entry->type = EventEntry::TYPE_MOTION;
+    initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime);
+
+    entry->eventTime = eventTime;
+    entry->deviceId = deviceId;
+    entry->nature = nature;
+    entry->policyFlags = policyFlags;
+    entry->action = action;
+    entry->metaState = metaState;
+    entry->edgeFlags = edgeFlags;
+    entry->xPrecision = xPrecision;
+    entry->yPrecision = yPrecision;
+    entry->downTime = downTime;
+    entry->pointerCount = pointerCount;
+    entry->firstSample.eventTime = eventTime;
     entry->firstSample.next = NULL;
+    entry->lastSample = & entry->firstSample;
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        entry->pointerIds[i] = pointerIds[i];
+        entry->firstSample.pointerCoords[i] = pointerCoords[i];
+    }
     return entry;
 }
 
@@ -1192,6 +1509,12 @@
     return entry;
 }
 
+InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) {
+    CommandEntry* entry = mCommandEntryPool.alloc();
+    entry->command = command;
+    return entry;
+}
+
 void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
     switch (entry->type) {
     case EventEntry::TYPE_CONFIGURATION_CHANGED:
@@ -1231,7 +1554,11 @@
 void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
-        freeMotionSampleList(entry->firstSample.next);
+        for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
+            MotionSample* next = sample->next;
+            mMotionSamplePool.free(sample);
+            sample = next;
+        }
         mMotionEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -1243,11 +1570,16 @@
     mDispatchEntryPool.free(entry);
 }
 
+void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) {
+    mCommandEntryPool.free(entry);
+}
+
 void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
-        nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords) {
+        nsecs_t eventTime, const PointerCoords* pointerCoords) {
     MotionSample* sample = mMotionSamplePool.alloc();
     sample->eventTime = eventTime;
-    for (int32_t i = 0; i < pointerCount; i++) {
+    uint32_t pointerCount = motionEntry->pointerCount;
+    for (uint32_t i = 0; i < pointerCount; i++) {
         sample->pointerCoords[i] = pointerCoords[i];
     }
 
@@ -1256,18 +1588,6 @@
     motionEntry->lastSample = sample;
 }
 
-void InputDispatcher::Allocator::freeMotionSample(MotionSample* sample) {
-    mMotionSamplePool.free(sample);
-}
-
-void InputDispatcher::Allocator::freeMotionSampleList(MotionSample* head) {
-    while (head) {
-        MotionSample* next = head->next;
-        mMotionSamplePool.free(head);
-        head = next;
-    }
-}
-
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
@@ -1284,6 +1604,29 @@
     return inputPublisher.initialize();
 }
 
+void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
+    nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
+}
+
+const char* InputDispatcher::Connection::getStatusLabel() const {
+    switch (status) {
+    case STATUS_NORMAL:
+        return "NORMAL";
+
+    case STATUS_BROKEN:
+        return "BROKEN";
+
+    case STATUS_NOT_RESPONDING:
+        return "NOT_RESPONDING";
+
+    case STATUS_ZOMBIE:
+        return "ZOMBIE";
+
+    default:
+        return "UNKNOWN";
+    }
+}
+
 InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
         const EventEntry* eventEntry) const {
     for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev;
@@ -1295,6 +1638,14 @@
     return NULL;
 }
 
+// --- InputDispatcher::CommandEntry ---
+
+InputDispatcher::CommandEntry::CommandEntry() {
+}
+
+InputDispatcher::CommandEntry::~CommandEntry() {
+}
+
 
 // --- InputDispatcherThread ---
 
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index ab354a5..32c58b4 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -14,29 +14,30 @@
 
 namespace android {
 
-InputManager::InputManager(const sp<EventHubInterface>& eventHub,
-        const sp<InputDispatchPolicyInterface>& policy) :
-        mEventHub(eventHub), mPolicy(policy) {
-    mDispatcher = new InputDispatcher(policy);
-    mReader = new InputReader(eventHub, policy, mDispatcher);
+InputManager::InputManager(
+        const sp<EventHubInterface>& eventHub,
+        const sp<InputReaderPolicyInterface>& readerPolicy,
+        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
+    mDispatcher = new InputDispatcher(dispatcherPolicy);
+    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
+    initialize();
+}
 
-    mDispatcherThread = new InputDispatcherThread(mDispatcher);
-    mReaderThread = new InputReaderThread(mReader);
-
-    configureExcludedDevices();
+InputManager::InputManager(
+        const sp<InputReaderInterface>& reader,
+        const sp<InputDispatcherInterface>& dispatcher) :
+        mReader(reader),
+        mDispatcher(dispatcher) {
+    initialize();
 }
 
 InputManager::~InputManager() {
     stop();
 }
 
-void InputManager::configureExcludedDevices() {
-    Vector<String8> excludedDeviceNames;
-    mPolicy->getExcludedDeviceNames(excludedDeviceNames);
-
-    for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
-        mEventHub->addExcludedDevice(excludedDeviceNames[i]);
-    }
+void InputManager::initialize() {
+    mReaderThread = new InputReaderThread(mReader);
+    mDispatcherThread = new InputDispatcherThread(mDispatcher);
 }
 
 status_t InputManager::start() {
@@ -79,36 +80,31 @@
     return mDispatcher->unregisterInputChannel(inputChannel);
 }
 
-int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode)
-    const {
-    int32_t vkKeyCode, vkScanCode;
-    if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) {
-        if (vkScanCode == scanCode) {
-            return KEY_STATE_VIRTUAL;
-        }
-    }
-
-    return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode);
+int32_t InputManager::injectInputEvent(const InputEvent* event,
+        int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
+    return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
 }
 
-int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, int32_t keyCode)
-    const {
-    int32_t vkKeyCode, vkScanCode;
-    if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) {
-        if (vkKeyCode == keyCode) {
-            return KEY_STATE_VIRTUAL;
-        }
-    }
+void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
+    mReader->getCurrentInputConfiguration(outConfiguration);
+}
 
-    return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
+int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t scanCode) const {
+    return mReader->getCurrentScanCodeState(deviceId, deviceClasses, scanCode);
+}
+
+int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t keyCode) const {
+    return mReader->getCurrentKeyCodeState(deviceId, deviceClasses, keyCode);
 }
 
 int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
-    return mEventHub->getSwitchState(deviceId, deviceClasses, sw);
+    return mReader->getCurrentSwitchState(deviceId, deviceClasses, sw);
 }
 
 bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
-    return mEventHub->hasKeys(numCodes, keyCodes, outFlags);
+    return mReader->hasKeys(numCodes, keyCodes, outFlags);
 }
 
 } // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 796abd0f..1824054 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -19,6 +19,9 @@
 // Log debug messages about pointers.
 #define DEBUG_POINTERS 1
 
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
 #include <cutils/log.h>
 #include <ui/InputReader.h>
 
@@ -27,6 +30,22 @@
 #include <errno.h>
 #include <limits.h>
 
+/** Amount that trackball needs to move in order to generate a key event. */
+#define TRACKBALL_MOVEMENT_THRESHOLD 6
+
+/* Slop distance for jumpy pointer detection.
+ * The vertical range of the screen divided by this is our epsilon value. */
+#define JUMPY_EPSILON_DIVISOR 212
+
+/* Number of jumpy points to drop for touchscreens that need it. */
+#define JUMPY_TRANSITION_DROPS 3
+#define JUMPY_DROP_LIMIT 3
+
+/* Maximum squared distance for averaging.
+ * If moving farther than this, turn of averaging to avoid lag in response. */
+#define AVERAGING_DISTANCE_LIMIT (75 * 75)
+
+
 namespace android {
 
 // --- Static Functions ---
@@ -41,6 +60,14 @@
     return a < b ? a : b;
 }
 
+template<typename T>
+inline static void swap(T& a, T& b) {
+    T temp = a;
+    a = b;
+    b = temp;
+}
+
+
 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
     int32_t mask;
     switch (keyCode) {
@@ -89,7 +116,7 @@
         sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
 
 int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
-    if (orientation != InputDispatchPolicyInterface::ROTATION_0) {
+    if (orientation != InputReaderPolicyInterface::ROTATION_0) {
         for (int i = 0; i < keyCodeRotationMapSize; i++) {
             if (keyCode == keyCodeRotationMap[i][0]) {
                 return keyCodeRotationMap[i][orientation];
@@ -172,6 +199,12 @@
     jumpyTouchFilter.jumpyPointsDropped = 0;
 }
 
+struct PointerDistanceHeapElement {
+    uint32_t currentPointerIndex : 8;
+    uint32_t lastPointerIndex : 8;
+    uint64_t distance : 48; // squared distance
+};
+
 void InputDevice::TouchScreenState::calculatePointerIds() {
     uint32_t currentPointerCount = currentTouch.pointerCount;
     uint32_t lastPointerCount = lastTouch.pointerCount;
@@ -198,11 +231,7 @@
         // We build a heap of squared euclidean distances between current and last pointers
         // associated with the current and last pointer indices.  Then, we find the best
         // match (by distance) for each current pointer.
-        struct {
-            uint32_t currentPointerIndex : 8;
-            uint32_t lastPointerIndex : 8;
-            uint64_t distance : 48; // squared distance
-        } heap[MAX_POINTERS * MAX_POINTERS];
+        PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
 
         uint32_t heapSize = 0;
         for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
@@ -217,23 +246,45 @@
                 uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
 
                 // Insert new element into the heap (sift up).
+                heap[heapSize].currentPointerIndex = currentPointerIndex;
+                heap[heapSize].lastPointerIndex = lastPointerIndex;
+                heap[heapSize].distance = distance;
                 heapSize += 1;
-                uint32_t insertionIndex = heapSize;
-                while (insertionIndex > 1) {
-                    uint32_t parentIndex = (insertionIndex - 1) / 2;
-                    if (distance < heap[parentIndex].distance) {
-                        heap[insertionIndex] = heap[parentIndex];
-                        insertionIndex = parentIndex;
-                    } else {
-                        break;
-                    }
-                }
-                heap[insertionIndex].currentPointerIndex = currentPointerIndex;
-                heap[insertionIndex].lastPointerIndex = lastPointerIndex;
-                heap[insertionIndex].distance = distance;
             }
         }
 
+        // Heapify
+        for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+            startIndex -= 1;
+            for (uint32_t parentIndex = startIndex; ;) {
+                uint32_t childIndex = parentIndex * 2 + 1;
+                if (childIndex >= heapSize) {
+                    break;
+                }
+
+                if (childIndex + 1 < heapSize
+                        && heap[childIndex + 1].distance < heap[childIndex].distance) {
+                    childIndex += 1;
+                }
+
+                if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                    break;
+                }
+
+                swap(heap[parentIndex], heap[childIndex]);
+                parentIndex = childIndex;
+            }
+        }
+
+#if DEBUG_POINTER_ASSIGNMENT
+        LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+        for (size_t i = 0; i < heapSize; i++) {
+            LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                    i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                    heap[i].distance);
+        }
+#endif
+
         // Pull matches out by increasing order of distance.
         // To avoid reassigning pointers that have already been matched, the loop keeps track
         // of which last and current pointers have been matched using the matchedXXXBits variables.
@@ -246,7 +297,7 @@
             for (;;) {
                 if (first) {
                     // The first time through the loop, we just consume the root element of
-                    // the heap (the one with smalled distance).
+                    // the heap (the one with smallest distance).
                     first = false;
                 } else {
                     // Previous iterations consumed the root element of the heap.
@@ -254,10 +305,10 @@
                     heapSize -= 1;
                     assert(heapSize > 0);
 
-                    // Sift down to find where the element at index heapSize needs to be moved.
-                    uint32_t rootIndex = 0;
-                    for (;;) {
-                        uint32_t childIndex = rootIndex * 2 + 1;
+                    // Sift down.
+                    heap[0] = heap[heapSize];
+                    for (uint32_t parentIndex = 0; ;) {
+                        uint32_t childIndex = parentIndex * 2 + 1;
                         if (childIndex >= heapSize) {
                             break;
                         }
@@ -267,14 +318,22 @@
                             childIndex += 1;
                         }
 
-                        if (heap[heapSize].distance < heap[childIndex].distance) {
+                        if (heap[parentIndex].distance <= heap[childIndex].distance) {
                             break;
                         }
 
-                        heap[rootIndex] = heap[childIndex];
-                        rootIndex = childIndex;
+                        swap(heap[parentIndex], heap[childIndex]);
+                        parentIndex = childIndex;
                     }
-                    heap[rootIndex] = heap[heapSize];
+
+#if DEBUG_POINTER_ASSIGNMENT
+                    LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+                    for (size_t i = 0; i < heapSize; i++) {
+                        LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                                heap[i].distance);
+                    }
+#endif
                 }
 
                 uint32_t currentPointerIndex = heap[0].currentPointerIndex;
@@ -290,6 +349,11 @@
                 currentTouch.pointers[currentPointerIndex].id = id;
                 currentTouch.idToIndex[id] = currentPointerIndex;
                 usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+                        lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
                 break;
             }
         }
@@ -304,6 +368,11 @@
                 currentTouch.idToIndex[id] = currentPointerIndex;
                 usedIdBits.markBit(id);
 
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+                        currentPointerIndex, id);
+#endif
+
                 if (--i == 0) break; // done
                 matchedCurrentBits.markBit(currentPointerIndex);
             }
@@ -677,12 +746,13 @@
 // --- InputReader ---
 
 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
-        const sp<InputDispatchPolicyInterface>& policy,
+        const sp<InputReaderPolicyInterface>& policy,
         const sp<InputDispatcherInterface>& dispatcher) :
         mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher) {
+    configureExcludedDevices();
     resetGlobalMetaState();
     resetDisplayProperties();
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
 }
 
 InputReader::~InputReader() {
@@ -925,7 +995,7 @@
     InputDevice* device = getNonIgnoredDevice(rawEvent->deviceId);
     if (! device) return;
 
-    onSwitch(rawEvent->when, device, rawEvent->value != 0, rawEvent->scanCode);
+    onSwitch(rawEvent->when, device, rawEvent->scanCode, rawEvent->value);
 }
 
 void InputReader::onKey(nsecs_t when, InputDevice* device,
@@ -974,7 +1044,7 @@
     }
 
     int32_t keyEventFlags = KEY_EVENT_FLAG_FROM_SYSTEM;
-    if (policyActions & InputDispatchPolicyInterface::ACTION_WOKE_HERE) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
         keyEventFlags = keyEventFlags | KEY_EVENT_FLAG_WOKE_HERE;
     }
 
@@ -984,12 +1054,12 @@
             device->keyboard.current.downTime);
 }
 
-void InputReader::onSwitch(nsecs_t when, InputDevice* device, bool down,
-        int32_t code) {
-    switch (code) {
-    case SW_LID:
-        mDispatcher->notifyLidSwitchChanged(when, ! down);
-    }
+void InputReader::onSwitch(nsecs_t when, InputDevice* device, int32_t switchCode,
+        int32_t switchValue) {
+    int32_t policyActions = mPolicy->interceptSwitch(when, switchCode, switchValue);
+
+    uint32_t policyFlags = 0;
+    applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags);
 }
 
 void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
@@ -1191,8 +1261,10 @@
 
         int32_t x = device->touchScreen.currentTouch.pointers[0].x;
         int32_t y = device->touchScreen.currentTouch.pointers[0].y;
-        if (device->touchScreen.isPointInsideDisplay(x, y)) {
-            // Pointer moved inside the display area.  Send key cancellation.
+        if (device->touchScreen.isPointInsideDisplay(x, y)
+                || device->touchScreen.currentTouch.pointerCount != 1) {
+            // Pointer moved inside the display area or another pointer also went down.
+            // Send key cancellation.
             device->touchScreen.currentVirtualKey.down = false;
 
 #if DEBUG_VIRTUAL_KEYS
@@ -1210,7 +1282,7 @@
             device->touchScreen.lastTouch.clear();
             return false; // not consumed
         }
-    } else if (device->touchScreen.currentTouch.pointerCount > 0
+    } else if (device->touchScreen.currentTouch.pointerCount == 1
             && device->touchScreen.lastTouch.pointerCount == 0) {
         int32_t x = device->touchScreen.currentTouch.pointers[0].x;
         int32_t y = device->touchScreen.currentTouch.pointers[0].y;
@@ -1256,7 +1328,7 @@
     nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
     int32_t metaState = globalMetaState();
 
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
 
     mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
             keyCode, scanCode, metaState, downTime);
@@ -1333,8 +1405,8 @@
         int32_t motionEventAction) {
     int32_t orientedWidth, orientedHeight;
     switch (mDisplayOrientation) {
-    case InputDispatchPolicyInterface::ROTATION_90:
-    case InputDispatchPolicyInterface::ROTATION_270:
+    case InputReaderPolicyInterface::ROTATION_90:
+    case InputReaderPolicyInterface::ROTATION_270:
         orientedWidth = mDisplayHeight;
         orientedHeight = mDisplayWidth;
         break;
@@ -1369,20 +1441,20 @@
                 * device->touchScreen.precalculated.sizeScale;
 
         switch (mDisplayOrientation) {
-        case InputDispatchPolicyInterface::ROTATION_90: {
+        case InputReaderPolicyInterface::ROTATION_90: {
             float xTemp = x;
             x = y;
-            y = mDisplayHeight - xTemp;
+            y = mDisplayWidth - xTemp;
             break;
         }
-        case InputDispatchPolicyInterface::ROTATION_180: {
+        case InputReaderPolicyInterface::ROTATION_180: {
             x = mDisplayWidth - x;
             y = mDisplayHeight - y;
             break;
         }
-        case InputDispatchPolicyInterface::ROTATION_270: {
+        case InputReaderPolicyInterface::ROTATION_270: {
             float xTemp = x;
-            x = mDisplayWidth - y;
+            x = mDisplayHeight - y;
             y = xTemp;
             break;
         }
@@ -1438,7 +1510,7 @@
 
     uint32_t fields = device->trackball.accumulator.fields;
     bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
-    bool deltaChanged = (fields & DELTA_FIELDS) == DELTA_FIELDS;
+    bool deltaChanged = fields & DELTA_FIELDS;
 
     bool down;
     if (downChanged) {
@@ -1474,27 +1546,27 @@
 
     int32_t pointerId = 0;
     PointerCoords pointerCoords;
-    pointerCoords.x = device->trackball.accumulator.relX
-            * device->trackball.precalculated.xScale;
-    pointerCoords.y = device->trackball.accumulator.relY
-            * device->trackball.precalculated.yScale;
+    pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X
+            ? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0;
+    pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y
+            ? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0;
     pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
     pointerCoords.size = 0;
 
     float temp;
     switch (mDisplayOrientation) {
-    case InputDispatchPolicyInterface::ROTATION_90:
+    case InputReaderPolicyInterface::ROTATION_90:
         temp = pointerCoords.x;
         pointerCoords.x = pointerCoords.y;
         pointerCoords.y = - temp;
         break;
 
-    case InputDispatchPolicyInterface::ROTATION_180:
+    case InputReaderPolicyInterface::ROTATION_180:
         pointerCoords.x = - pointerCoords.x;
         pointerCoords.y = - pointerCoords.y;
         break;
 
-    case InputDispatchPolicyInterface::ROTATION_270:
+    case InputReaderPolicyInterface::ROTATION_270:
         temp = pointerCoords.x;
         pointerCoords.x = - pointerCoords.y;
         pointerCoords.y = temp;
@@ -1514,51 +1586,30 @@
     resetGlobalMetaState();
 
     // Reset virtual keys, just in case.
-    updateGlobalVirtualKeyState();
+    updateExportedVirtualKeyState();
+
+    // Update input configuration.
+    updateExportedInputConfiguration();
 
     // Enqueue configuration changed.
-    // XXX This stuff probably needs to be tracked elsewhere in an input device registry
-    //     of some kind that can be asynchronously updated and queried.  (Same as above?)
-    int32_t touchScreenConfig = InputDispatchPolicyInterface::TOUCHSCREEN_NOTOUCH;
-    int32_t keyboardConfig = InputDispatchPolicyInterface::KEYBOARD_NOKEYS;
-    int32_t navigationConfig = InputDispatchPolicyInterface::NAVIGATION_NONAV;
-
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        int32_t deviceClasses = device->classes;
-
-        if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
-            touchScreenConfig = InputDispatchPolicyInterface::TOUCHSCREEN_FINGER;
-        }
-        if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) {
-            keyboardConfig = InputDispatchPolicyInterface::KEYBOARD_QWERTY;
-        }
-        if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) {
-            navigationConfig = InputDispatchPolicyInterface::NAVIGATION_TRACKBALL;
-        } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) {
-            navigationConfig = InputDispatchPolicyInterface::NAVIGATION_DPAD;
-        }
-    }
-
-    mDispatcher->notifyConfigurationChanged(when, touchScreenConfig,
-            keyboardConfig, navigationConfig);
+    mDispatcher->notifyConfigurationChanged(when);
 }
 
 bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when,
         int32_t policyActions, uint32_t* policyFlags) {
-    if (policyActions & InputDispatchPolicyInterface::ACTION_APP_SWITCH_COMING) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) {
         mDispatcher->notifyAppSwitchComing(when);
     }
 
-    if (policyActions & InputDispatchPolicyInterface::ACTION_WOKE_HERE) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_WOKE_HERE) {
         *policyFlags |= POLICY_FLAG_WOKE_HERE;
     }
 
-    if (policyActions & InputDispatchPolicyInterface::ACTION_BRIGHT_HERE) {
+    if (policyActions & InputReaderPolicyInterface::ACTION_BRIGHT_HERE) {
         *policyFlags |= POLICY_FLAG_BRIGHT_HERE;
     }
 
-    return policyActions & InputDispatchPolicyInterface::ACTION_DISPATCH;
+    return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
 }
 
 void InputReader::resetDisplayProperties() {
@@ -1706,7 +1757,7 @@
 void InputReader::configureVirtualKeys(InputDevice* device) {
     device->touchScreen.virtualKeys.clear();
 
-    Vector<InputDispatchPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
+    Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions;
     mPolicy->getVirtualKeyDefinitions(device->name, virtualKeyDefinitions);
     if (virtualKeyDefinitions.size() == 0) {
         return;
@@ -1720,7 +1771,7 @@
     int32_t touchScreenHeight = device->touchScreen.parameters.yAxis.range;
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
-        const InputDispatchPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
+        const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition =
                 virtualKeyDefinitions[i];
 
         device->touchScreen.virtualKeys.add();
@@ -1779,6 +1830,15 @@
     LOGI("  %s: unknown axis values, setting to zero", name);
 }
 
+void InputReader::configureExcludedDevices() {
+    Vector<String8> excludedDeviceNames;
+    mPolicy->getExcludedDeviceNames(excludedDeviceNames);
+
+    for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
+        mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+    }
+}
+
 void InputReader::resetGlobalMetaState() {
     mGlobalMetaState = -1;
 }
@@ -1796,7 +1856,7 @@
     return mGlobalMetaState;
 }
 
-void InputReader::updateGlobalVirtualKeyState() {
+void InputReader::updateExportedVirtualKeyState() {
     int32_t keyCode = -1, scanCode = -1;
 
     for (size_t i = 0; i < mDevices.size(); i++) {
@@ -1809,20 +1869,96 @@
         }
     }
 
-    {
+    { // acquire exported state lock
         AutoMutex _l(mExportedStateLock);
 
-        mGlobalVirtualKeyCode = keyCode;
-        mGlobalVirtualScanCode = scanCode;
-    }
+        mExportedVirtualKeyCode = keyCode;
+        mExportedVirtualScanCode = scanCode;
+    } // release exported state lock
 }
 
 bool InputReader::getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const {
-    AutoMutex _l(mExportedStateLock);
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
 
-    *outKeyCode = mGlobalVirtualKeyCode;
-    *outScanCode = mGlobalVirtualScanCode;
-    return mGlobalVirtualKeyCode != -1;
+        *outKeyCode = mExportedVirtualKeyCode;
+        *outScanCode = mExportedVirtualScanCode;
+        return mExportedVirtualKeyCode != -1;
+    } // release exported state lock
+}
+
+void InputReader::updateExportedInputConfiguration() {
+    int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+    int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+    int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        int32_t deviceClasses = device->classes;
+
+        if (deviceClasses & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
+            touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+        }
+        if (deviceClasses & INPUT_DEVICE_CLASS_ALPHAKEY) {
+            keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
+        }
+        if (deviceClasses & INPUT_DEVICE_CLASS_TRACKBALL) {
+            navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+        } else if (deviceClasses & INPUT_DEVICE_CLASS_DPAD) {
+            navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+        }
+    }
+
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        mExportedInputConfiguration.touchScreen = touchScreenConfig;
+        mExportedInputConfiguration.keyboard = keyboardConfig;
+        mExportedInputConfiguration.navigation = navigationConfig;
+    } // release exported state lock
+}
+
+void InputReader::getCurrentInputConfiguration(InputConfiguration* outConfiguration) const {
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        *outConfiguration = mExportedInputConfiguration;
+    } // release exported state lock
+}
+
+int32_t InputReader::getCurrentScanCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t scanCode) const {
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        if (mExportedVirtualScanCode == scanCode) {
+            return KEY_STATE_VIRTUAL;
+        }
+    } // release exported state lock
+
+    return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode);
+}
+
+int32_t InputReader::getCurrentKeyCodeState(int32_t deviceId, int32_t deviceClasses,
+        int32_t keyCode) const {
+    { // acquire exported state lock
+        AutoMutex _l(mExportedStateLock);
+
+        if (mExportedVirtualKeyCode == keyCode) {
+            return KEY_STATE_VIRTUAL;
+        }
+    } // release exported state lock
+
+    return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
+}
+
+int32_t InputReader::getCurrentSwitchState(int32_t deviceId, int32_t deviceClasses,
+        int32_t sw) const {
+    return mEventHub->getSwitchState(deviceId, deviceClasses, sw);
+}
+
+bool InputReader::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
+    return mEventHub->hasKeys(numCodes, keyCodes, outFlags);
 }
 
 
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index a24180f..86bbd37 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -8,13 +8,13 @@
 //#define LOG_NDEBUG 0
 
 // Log debug messages about channel signalling (send signal, receive signal)
-#define DEBUG_CHANNEL_SIGNALS 1
+#define DEBUG_CHANNEL_SIGNALS 0
 
 // Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 1
+#define DEBUG_CHANNEL_LIFECYCLE 0
 
 // Log debug messages about transport actions (initialize, reset, publish, ...)
-#define DEBUG_TRANSPORT_ACTIONS 1
+#define DEBUG_TRANSPORT_ACTIONS 0
 
 
 #include <cutils/ashmem.h>
@@ -70,7 +70,7 @@
 }
 
 status_t InputChannel::openInputChannelPair(const String8& name,
-        InputChannel** outServerChannel, InputChannel** outClientChannel) {
+        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
     status_t result;
 
     int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
@@ -107,12 +107,12 @@
                     } else {
                         String8 serverChannelName = name;
                         serverChannelName.append(" (server)");
-                        *outServerChannel = new InputChannel(serverChannelName,
+                        outServerChannel = new InputChannel(serverChannelName,
                                 serverAshmemFd, reverse[0], forward[1]);
 
                         String8 clientChannelName = name;
                         clientChannelName.append(" (client)");
-                        *outClientChannel = new InputChannel(clientChannelName,
+                        outClientChannel = new InputChannel(clientChannelName,
                                 clientAshmemFd, forward[0], reverse[1]);
                         return OK;
                     }
@@ -125,8 +125,8 @@
         ::close(serverAshmemFd);
     }
 
-    *outServerChannel = NULL;
-    *outClientChannel = NULL;
+    outServerChannel.clear();
+    outClientChannel.clear();
     return result;
 }
 
@@ -155,6 +155,13 @@
         return OK;
     }
 
+    if (nRead == 0) { // check for EOF
+#if DEBUG_CHANNEL_SIGNALS
+        LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
+#endif
+        return DEAD_OBJECT;
+    }
+
     if (errno == EAGAIN) {
 #if DEBUG_CHANNEL_SIGNALS
         LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
@@ -535,13 +542,13 @@
     return OK;
 }
 
-status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** event) {
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
 #if DEBUG_TRANSPORT_ACTIONS
     LOGD("channel '%s' consumer ~ consume",
             mChannel->getName().string());
 #endif
 
-    *event = NULL;
+    *outEvent = NULL;
 
     int ashmemFd = mChannel->getAshmemFd();
     int result = ashmem_pin_region(ashmemFd, 0, 0);
@@ -583,7 +590,7 @@
 
         populateKeyEvent(keyEvent);
 
-        *event = keyEvent;
+        *outEvent = keyEvent;
         break;
     }
 
@@ -593,7 +600,7 @@
 
         populateMotionEvent(motionEvent);
 
-        *event = motionEvent;
+        *outEvent = motionEvent;
         break;
     }
 
@@ -655,8 +662,8 @@
             mSharedMessage->motion.action,
             mSharedMessage->motion.edgeFlags,
             mSharedMessage->motion.metaState,
-            mSharedMessage->motion.sampleData[0].coords[0].x,
-            mSharedMessage->motion.sampleData[0].coords[0].y,
+            mSharedMessage->motion.xOffset,
+            mSharedMessage->motion.yOffset,
             mSharedMessage->motion.xPrecision,
             mSharedMessage->motion.yPrecision,
             mSharedMessage->motion.downTime,
@@ -676,9 +683,6 @@
             motionEvent->addSample(sampleData->eventTime, sampleData->coords);
         }
     }
-
-    motionEvent->offsetLocation(mSharedMessage->motion.xOffset,
-            mSharedMessage->motion.yOffset);
 }
 
 } // namespace android
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 1ff896b..46d7493 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -3,7 +3,9 @@
 include $(CLEAR_VARS)
 
 test_src_files := \
-    InputDispatcher_test.cpp
+    InputChannel_test.cpp \
+    InputDispatcher_test.cpp \
+    InputPublisherAndConsumer_test.cpp
 
 shared_libraries := \
 	libcutils \
diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp
new file mode 100644
index 0000000..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/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/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 4b61131..2118f8f 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -41,6 +41,35 @@
 #define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))
 
 namespace android {
+// ---------------------------------------------------------------------------
+
+// static
+status_t AudioTrack::getMinFrameCount(
+        int* frameCount,
+        int streamType,
+        uint32_t sampleRate)
+{
+    int afSampleRate;
+    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+        return NO_INIT;
+    }
+    int afFrameCount;
+    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+        return NO_INIT;
+    }
+    uint32_t afLatency;
+    if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
+        return NO_INIT;
+    }
+
+    // Ensure that buffer depth covers at least audio hardware latency
+    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
+    if (minBufCount < 2) minBufCount = 2;
+
+    *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
+              afFrameCount * minBufCount * sampleRate / afSampleRate;
+    return NO_ERROR;
+}
 
 // ---------------------------------------------------------------------------
 
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/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/InputManager.java b/services/java/com/android/server/InputManager.java
index 72c4166..8d9bb29 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -81,6 +81,10 @@
     private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
     private static native void nativeRegisterInputChannel(InputChannel inputChannel);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+    private static native int nativeInjectKeyEvent(KeyEvent event, int nature,
+            int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
+    private static native int nativeInjectMotionEvent(MotionEvent event, int nature,
+            int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
     
     // Device class as defined by EventHub.
     private static final int CLASS_KEYBOARD = 0x00000001;
@@ -90,6 +94,12 @@
     private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
     private static final int CLASS_DPAD = 0x00000020;
     
+    // Input event injection constants defined in InputDispatcher.h.
+    static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
+    static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
+    static final int INPUT_EVENT_INJECTION_FAILED = 2;
+    static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
+    
     public InputManager(Context context,
             WindowManagerService windowManagerService,
             WindowManagerPolicy windowManagerPolicy,
@@ -215,37 +225,63 @@
         nativeUnregisterInputChannel(inputChannel);
     }
     
-    // TBD where this really belongs, duplicate copy in WindowManagerService
-    static final int INJECT_FAILED = 0;
-    static final int INJECT_SUCCEEDED = 1;
-    static final int INJECT_NO_PERMISSION = -1;
-    
     /**
      * Injects a key event into the event system on behalf of an application.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
      * @param event The event to inject.
      * @param nature The nature of the event.
+     * @param injectorPid The pid of the injecting application.
+     * @param injectorUid The uid of the injecting application.
      * @param sync If true, waits for the event to be completed before returning.
-     * @param pid The pid of the injecting application.
-     * @param uid The uid of the injecting application.
-     * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
+     * @param timeoutMillis The injection timeout in milliseconds.
+     * @return One of the INPUT_EVENT_INJECTION_XXX constants.
      */
-    public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) {
-        // TODO
-        return INJECT_FAILED;
+    public int injectKeyEvent(KeyEvent event, int nature, int injectorPid, int injectorUid,
+            boolean sync, int timeoutMillis) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (injectorPid < 0 || injectorUid < 0) {
+            throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
+        }
+        if (timeoutMillis <= 0) {
+            throw new IllegalArgumentException("timeoutMillis must be positive");
+        }
+        
+        return nativeInjectKeyEvent(event, nature, injectorPid, injectorUid,
+                sync, timeoutMillis);
     }
     
     /**
      * Injects a motion event into the event system on behalf of an application.
+     * This method may block even if sync is false because it must wait for previous events
+     * to be dispatched before it can determine whether input event injection will be
+     * permitted based on the current input focus.
      * @param event The event to inject.
      * @param nature The nature of the event.
      * @param sync If true, waits for the event to be completed before returning.
-     * @param pid The pid of the injecting application.
-     * @param uid The uid of the injecting application.
-     * @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
+     * @param injectorPid The pid of the injecting application.
+     * @param injectorUid The uid of the injecting application.
+     * @param sync If true, waits for the event to be completed before returning.
+     * @param timeoutMillis The injection timeout in milliseconds.
+     * @return One of the INPUT_EVENT_INJECTION_XXX constants.
      */
-    public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) {
-        // TODO
-        return INJECT_FAILED;
+    public int injectMotionEvent(MotionEvent event, int nature, int injectorPid, int injectorUid,
+            boolean sync, int timeoutMillis) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (injectorPid < 0 || injectorUid < 0) {
+            throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
+        }
+        if (timeoutMillis <= 0) {
+            throw new IllegalArgumentException("timeoutMillis must be positive");
+        }
+        
+        return nativeInjectMotionEvent(event, nature, injectorPid, injectorUid,
+                sync, timeoutMillis);
     }
     
     public void dump(PrintWriter pw) {
@@ -271,8 +307,6 @@
         private static final boolean DEBUG_VIRTUAL_KEYS = false;
         private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
         
-        private final InputTargetList mReusableInputTargetList = new InputTargetList();
-        
         @SuppressWarnings("unused")
         public boolean isScreenOn() {
             return mPowerManagerService.isScreenOn();
@@ -309,6 +343,21 @@
         }
         
         @SuppressWarnings("unused")
+        public void notifyInputChannelBroken(InputChannel inputChannel) {
+            mWindowManagerService.notifyInputChannelBroken(inputChannel);
+        }
+
+        @SuppressWarnings("unused")
+        public long notifyInputChannelANR(InputChannel inputChannel) {
+            return mWindowManagerService.notifyInputChannelANR(inputChannel);
+        }
+
+        @SuppressWarnings("unused")
+        public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+            mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel);
+        }
+        
+        @SuppressWarnings("unused")
         public int hackInterceptKey(int deviceId, int type, int scanCode,
                 int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
             RawInputEvent event = new RawInputEvent();
@@ -437,24 +486,23 @@
             return names.toArray(new String[names.size()]);
         }
         
+        // TODO All code related to target identification should be moved down into native.
         @SuppressWarnings("unused")
-        public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) {
-            mReusableInputTargetList.clear();
-            
-            mWindowManagerService.getKeyEventTargets(mReusableInputTargetList,
-                    event, nature, policyFlags);
-            
-            return mReusableInputTargetList.toNullTerminatedArray();
+        public int getKeyEventTargets(InputTargetList inputTargets,
+                KeyEvent event, int nature, int policyFlags,
+                int injectorPid, int injectorUid) {
+            inputTargets.clear();
+            return mWindowManagerService.getKeyEventTargetsTd(
+                    inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
         }
         
         @SuppressWarnings("unused")
-        public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) {
-            mReusableInputTargetList.clear();
-            
-            mWindowManagerService.getMotionEventTargets(mReusableInputTargetList,
-                    event, nature, policyFlags);
-            
-            return mReusableInputTargetList.toNullTerminatedArray();
+        public int getMotionEventTargets(InputTargetList inputTargets,
+                MotionEvent event, int nature, int policyFlags,
+                int injectorPid, int injectorUid) {
+            inputTargets.clear();
+            return mWindowManagerService.getMotionEventTargetsTd(
+                    inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
         }
     }
 }
diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java
index 1575612..83acc8f 100644
--- a/services/java/com/android/server/InputTargetList.java
+++ b/services/java/com/android/server/InputTargetList.java
@@ -29,7 +29,7 @@
  * 
  * @hide
  */
-public class InputTargetList {
+public final class InputTargetList {
     private InputTarget[] mArray;
     private int mCount;
     
@@ -55,7 +55,7 @@
             count -= 1;
             mArray[count].recycle();
         }
-        // mArray[0] could be set to null here but we do it in toNullTerminatedArray()
+        mArray[0] = null;
     }
     
     /**
@@ -91,7 +91,7 @@
         
         mArray[mCount] = inputTarget;
         mCount += 1;
-        // mArray[mCount] could be set to null here but we do it in toNullTerminatedArray()
+        mArray[mCount] = null;
     }
     
     /**
@@ -99,7 +99,6 @@
      * @return The input target array.
      */
     public InputTarget[] toNullTerminatedArray() {
-        mArray[mCount] = null;
         return mArray;
     }
 }
\ No newline at end of file
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/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 9bc3931..95ab5bc 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -203,6 +203,10 @@
     /** Adjustment to time to perform a dim, to make it more dramatic.
      */
     static final int DIM_DURATION_MULTIPLIER = 6;
+    
+    // Maximum number of milliseconds to wait for input event injection.
+    // FIXME is this value reasonable?
+    private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
 
     static final int INJECT_FAILED = 0;
     static final int INJECT_SUCCEEDED = 1;
@@ -447,8 +451,6 @@
     final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
     final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
 
-    //flag to detect fat touch events
-    boolean mFatTouch = false;
     Display mDisplay;
 
     H mH = new H();
@@ -5072,106 +5074,336 @@
         mPolicy.adjustConfigurationLw(config);
         return true;
     }
+    
+    /* Notifies the window manager about a broken input channel.
+     * 
+     * Called by the InputManager.
+     */
+    public void notifyInputChannelBroken(InputChannel inputChannel) {
+        synchronized (mWindowMap) {
+            WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+            if (windowState == null) {
+                return; // irrelevant
+            }
+            
+            Slog.i(TAG, "WINDOW DIED " + windowState);
+            removeWindowLocked(windowState.mSession, windowState);
+        }
+    }
+    
+    /* Notifies the window manager about a broken input channel.
+     * 
+     * Called by the InputManager.
+     */
+    public long notifyInputChannelANR(InputChannel inputChannel) {
+        IApplicationToken appToken;
+        synchronized (mWindowMap) {
+            WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+            if (windowState == null) {
+                return -2; // irrelevant, abort dispatching (-2)
+            }
+            
+            Slog.i(TAG, "Input event dispatching timed out sending to "
+                    + windowState.mAttrs.getTitle());
+            appToken = windowState.getAppToken();
+        }
+        
+        try {
+            // Notify the activity manager about the timeout and let it decide whether
+            // to abort dispatching or keep waiting.
+            boolean abort = appToken.keyDispatchingTimedOut();
+            if (abort) {
+                return -2; // abort dispatching
+            }
+            
+            // Return new timeout.
+            // We use -1 for infinite timeout to avoid clash with -2 magic number.
+            long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000;
+            return newTimeout < 0 ? -1 : newTimeout;
+        } catch (RemoteException ex) {
+            return -2; // abort dispatching
+        }
+    }
+
+    /* Notifies the window manager about a broken input channel.
+     * 
+     * Called by the InputManager.
+     */
+    public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+        // Nothing to do just now.
+        // Just wait for the user to dismiss the ANR dialog.
+        
+        // TODO We could try to automatically dismiss the ANR dialog on recovery
+        //      although that might be disorienting.
+    }
+    
+    private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+        int windowCount = mWindows.size();
+        for (int i = 0; i < windowCount; i++) {
+            WindowState windowState = (WindowState) mWindows.get(i);
+            if (windowState.mInputChannel == inputChannel) {
+                return windowState;
+            }
+        }
+        
+        return null;
+    }
 
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
     
-    public void getKeyEventTargets(InputTargetList inputTargets,
-            KeyEvent event, int nature, int policyFlags) {
+    private boolean checkInjectionPermissionTd(WindowState focus,
+            int injectorPid, int injectorUid) {
+        if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) {
+            if (mContext.checkPermission(
+                    android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Slog.w(TAG, "Permission denied: injecting key event from pid "
+                        + injectorPid + " uid " + injectorUid + " to window " + focus
+                        + " owned by uid " + focus.mSession.mUid);
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    /* Gets the input targets for a key event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    public int getKeyEventTargetsTd(InputTargetList inputTargets,
+            KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
         if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
 
         // TODO what do we do with mDisplayFrozen?
         // TODO what do we do with focus.mToken.paused?
         
         WindowState focus = getFocusedWindow();
-        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
         
-        addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
-    }
-    
-    // Target of Motion events
-    WindowState mTouchFocus;
-
-    // Windows above the target who would like to receive an "outside"
-    // touch event for any down events outside of them.
-    // (This is a linked list by way of WindowState.mNextOutsideTouch.)
-    WindowState mOutsideTouchTargets;
-    
-    private void clearTouchFocus() {
-        mTouchFocus = null;
-        mOutsideTouchTargets = null;
-    }
-    
-    public void getMotionEventTargets(InputTargetList inputTargets,
-            MotionEvent event, int nature, int policyFlags) {
-        if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) {
-            // More or less the same as for keys...
-            WindowState focus = getFocusedWindow();
-            wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-            
-            addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
-            return;
+        if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
+            return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         }
         
-        int action = event.getAction();
+        if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(),
+                event.getAction() == KeyEvent.ACTION_DOWN,
+                event.getRepeatCount(), event.getFlags())) {
+            // Policy consumed the event.
+            return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+        }
+        
+        if (focus == null) {
+            return InputManager.INPUT_EVENT_INJECTION_FAILED;
+        }
+        
+        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+        
+        addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
+        return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+    }
+    
+    /* Gets the input targets for a motion event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    public int getMotionEventTargetsTd(InputTargetList inputTargets,
+            MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
+        switch (nature) {
+            case InputQueue.INPUT_EVENT_NATURE_TRACKBALL:
+                return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags,
+                        injectorPid, injectorUid);
+                
+            case InputQueue.INPUT_EVENT_NATURE_TOUCH:
+                return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags,
+                        injectorPid, injectorUid);
+                
+            default:
+                return InputManager.INPUT_EVENT_INJECTION_FAILED;
+        }
+    }
+    
+    /* Gets the input targets for a trackball event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets,
+            MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
+        WindowState focus = getFocusedWindow();
+        
+        if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
+            return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+        }
+        
+        if (focus == null) {
+            return InputManager.INPUT_EVENT_INJECTION_FAILED;
+        }
+        
+        wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
+        
+        addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
+        return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+    }
+    
+    /* Set to true when a fat touch has been detected during the processing of a touch event.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Set to true whenever a fat touch is detected and reset to false on ACTION_UP.
+     */
+    private boolean mFatTouch;
+    
+    /* Set to true when we think the touch event.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Set to true on ACTION_DOWN and set to false on ACTION_UP.
+     */
+    private boolean mTouchDown;
+    
+    /* Current target of Motion events.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Initialized on ACTION_DOWN and cleared on ACTION_UP.
+     */
+    private WindowState mTouchFocus;
 
-        // TODO detect cheek presses somewhere... either here or in native code
+    /* Windows above the target that would like to receive an "outside" touch event
+     * for any down events outside of them.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Initialized on ACTION_DOWN and cleared immediately afterwards.
+     */
+    private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>();
+    
+    /* Wallpaper windows that are currently receiving touch events.
+     * 
+     * Only used by getMotionEventTargetsForTouchTd.
+     * Initialized on ACTION_DOWN and cleared on ACTION_UP.
+     */
+    private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>();
+    
+    /* Gets the input targets for a touch event.
+     * 
+     * Called by the InputManager on the InputDispatcher thread.
+     */
+    private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets,
+            MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
+        final int action = event.getAction();
+        
+        if (action == MotionEvent.ACTION_DOWN) {
+            updateTouchFocusBeforeDownTd(event, policyFlags);
+        } else {
+            updateTouchFocusBeforeNonDownTd(event, policyFlags);
+        }
+
+        boolean skipDelivery = false;
+        int touchTargetFlags = 0;
+        
+        int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
+        WindowState focusedTouchTarget = mTouchFocus;
+        if (focusedTouchTarget == null) {
+            // In this case we are either dropping the event, or have received
+            // a move or up without a down.  It is common to receive move
+            // events in such a way, since this means the user is moving the
+            // pointer without actually pressing down.  All other cases should
+            // be atypical, so let's log them.
+            if (action != MotionEvent.ACTION_MOVE) {
+                Slog.w(TAG, "No window to dispatch pointer action " + action);
+                injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED;
+            }
+        } else {
+            // We have a valid focused touch target.
+            if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) {
+                return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+            }
+            
+            wakeupIfNeeded(focusedTouchTarget, eventType(event));
+            
+            if ((focusedTouchTarget.mAttrs.flags &
+                    WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
+                // Target wants to ignore fat touch events
+                boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
+                
+                if (cheekPress) {
+                    if ((action == MotionEvent.ACTION_DOWN)) {
+                        mFatTouch = true;
+                        skipDelivery = true;
+                    } else {
+                        if (! mFatTouch) {
+                            // cancel the earlier event
+                            touchTargetFlags |= InputTarget.FLAG_CANCEL;
+                            mFatTouch = true;
+                        } else {
+                            skipDelivery = true;
+                        }
+                    }
+                }
+            }
+        }
+        
+        if (! skipDelivery) {
+            int outsideTargetCount = mOutsideTouchTargets.size();
+            for (int i = 0; i < outsideTargetCount; i++) {
+                WindowState outsideTouchTarget = mOutsideTouchTargets.get(i);
+                addInputTargetTd(inputTargets, outsideTouchTarget,
+                        InputTarget.FLAG_OUTSIDE | touchTargetFlags);
+            }
+            
+            int wallpaperTargetCount = mWallpaperTouchTargets.size();
+            for (int i = 0; i < wallpaperTargetCount; i++) {
+                WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i);
+                addInputTargetTd(inputTargets, wallpaperTouchTarget,
+                        touchTargetFlags);
+            }
+            
+            if (focusedTouchTarget != null) {
+                addInputTargetTd(inputTargets, focusedTouchTarget,
+                        InputTarget.FLAG_SYNC | touchTargetFlags);
+            }
+        }
+
+        if (action == MotionEvent.ACTION_UP) {
+            updateTouchFocusAfterUpTd(event, policyFlags);
+        }
+        
+        return injectionResult;
+    }
+    
+    private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) {
+        if (mTouchDown) {
+            // This is weird, we got a down, but we thought it was already down!
+            // XXX: We should probably send an ACTION_UP to the current target.
+            Slog.w(TAG, "Pointer down received while already down in: " + mTouchFocus);
+            updateTouchFocusAfterUpTd(event, policyFlags);
+        }
+        
+        mTouchDown = true;
+        mPowerManager.logPointerDownEvent();
         
         final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
-        
-        WindowState target = mTouchFocus;
-        
-        if (action == MotionEvent.ACTION_UP) {
-            // let go of our target
-            mPowerManager.logPointerUpEvent();
-            clearTouchFocus();
-        } else if (action == MotionEvent.ACTION_DOWN) {
-            // acquire a new target
-            mPowerManager.logPointerDownEvent();
-        
-            synchronized (mWindowMap) {
-                if (mTouchFocus != null) {
-                    // this is weird, we got a pen down, but we thought it was
-                    // already down!
-                    // XXX: We should probably send an ACTION_UP to the current
-                    // target.
-                    Slog.w(TAG, "Pointer down received while already down in: "
-                            + mTouchFocus);
-                    clearTouchFocus();
+        synchronized (mWindowMap) {
+            final int x = (int) event.getX();
+            final int y = (int) event.getY();
+
+            final ArrayList windows = mWindows;
+            final int N = windows.size();
+            WindowState topErrWindow = null;
+            final Rect tmpRect = mTempRect;
+            for (int i= N - 1; i >= 0; i--) {
+                WindowState child = (WindowState) windows.get(i);
+                //Slog.i(TAG, "Checking dispatch to: " + child);
+                
+                final int flags = child.mAttrs.flags;
+                if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
+                    if (topErrWindow == null) {
+                        topErrWindow = child;
+                    }
                 }
-
-                // ACTION_DOWN is special, because we need to lock next events to
-                // the window we'll land onto.
-                final int x = (int) event.getX();
-                final int y = (int) event.getY();
-
-                final ArrayList windows = mWindows;
-                final int N = windows.size();
-                WindowState topErrWindow = null;
-                final Rect tmpRect = mTempRect;
-                for (int i=N-1; i>=0; i--) {
-                    WindowState child = (WindowState)windows.get(i);
-                    //Slog.i(TAG, "Checking dispatch to: " + child);
-                    final int flags = child.mAttrs.flags;
-                    if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
-                        if (topErrWindow == null) {
-                            topErrWindow = child;
-                        }
-                    }
-                    if (!child.isVisibleLw()) {
-                        //Slog.i(TAG, "Not visible!");
-                        continue;
-                    }
-                    if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
-                        //Slog.i(TAG, "Not touchable!");
-                        if ((flags & WindowManager.LayoutParams
-                                .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
-                            child.mNextOutsideTouch = mOutsideTouchTargets;
-                            mOutsideTouchTargets = child;
-                        }
-                        continue;
-                    }
+                
+                if (!child.isVisibleLw()) {
+                    //Slog.i(TAG, "Not visible!");
+                    continue;
+                }
+                
+                if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
                     tmpRect.set(child.mFrame);
                     if (child.mTouchableInsets == ViewTreeObserver
                                 .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
@@ -5197,7 +5429,7 @@
                         |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
                     if (tmpRect.contains(x, y) || touchFlags == 0) {
                         //Slog.i(TAG, "Using this target!");
-                        if (!screenWasOff || (flags &
+                        if (! screenWasOff || (flags &
                                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
                             mTouchFocus = child;
                         } else {
@@ -5206,143 +5438,76 @@
                         }
                         break;
                     }
-
-                    if ((flags & WindowManager.LayoutParams
-                            .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
-                        child.mNextOutsideTouch = mOutsideTouchTargets;
-                        mOutsideTouchTargets = child;
-                        //Slog.i(TAG, "Adding to outside target list: " + child);
-                    }
                 }
 
-                // if there's an error window but it's not accepting
-                // focus (typically because it is not yet visible) just
-                // wait for it -- any other focused window may in fact
-                // be in ANR state.
-                if (topErrWindow != null && mTouchFocus != topErrWindow) {
-                    mTouchFocus = null;
+                if ((flags & WindowManager.LayoutParams
+                        .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
+                    //Slog.i(TAG, "Adding to outside target list: " + child);
+                    mOutsideTouchTargets.add(child);
                 }
             }
-            
-            target = mTouchFocus;
-        }
-        
-        if (target != null) {
-            wakeupIfNeeded(target, eventType(event));
-        }
-        
-        int targetFlags = 0;
-        if (target == null) {
-            // In this case we are either dropping the event, or have received
-            // a move or up without a down.  It is common to receive move
-            // events in such a way, since this means the user is moving the
-            // pointer without actually pressing down.  All other cases should
-            // be atypical, so let's log them.
-            if (action != MotionEvent.ACTION_MOVE) {
-                Slog.w(TAG, "No window to dispatch pointer action " + action);
-            }
-        } else {
-            if ((target.mAttrs.flags &
-                    WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
-                //target wants to ignore fat touch events
-                boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
-                //explicit flag to return without processing event further
-                boolean returnFlag = false;
-                if((action == MotionEvent.ACTION_DOWN)) {
-                    mFatTouch = false;
-                    if(cheekPress) {
-                        mFatTouch = true;
-                        returnFlag = true;
-                    }
-                } else {
-                    if(action == MotionEvent.ACTION_UP) {
-                        if(mFatTouch) {
-                            //earlier even was invalid doesnt matter if current up is cheekpress or not
-                            mFatTouch = false;
-                            returnFlag = true;
-                        } else if(cheekPress) {
-                            //cancel the earlier event
-                            targetFlags |= InputTarget.FLAG_CANCEL;
-                            action = MotionEvent.ACTION_CANCEL;
-                        }
-                    } else if(action == MotionEvent.ACTION_MOVE) {
-                        if(mFatTouch) {
-                            //two cases here
-                            //an invalid down followed by 0 or moves(valid or invalid)
-                            //a valid down,  invalid move, more moves. want to ignore till up
-                            returnFlag = true;
-                        } else if(cheekPress) {
-                            //valid down followed by invalid moves
-                            //an invalid move have to cancel earlier action
-                            targetFlags |= InputTarget.FLAG_CANCEL;
-                            action = MotionEvent.ACTION_CANCEL;
-                            if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
-                            //note that the subsequent invalid moves will not get here
-                            mFatTouch = true;
-                        }
-                    }
-                } //else if action
-                if(returnFlag) {
-                    return;
-                }
-            } //end if target
-        }        
-        
-        synchronized (mWindowMap) {
-            if (target != null && ! target.isVisibleLw()) {
-                target = null;
+
+            // If there's an error window but it's not accepting focus (typically because
+            // it is not yet visible) just wait for it -- any other focused window may in fact
+            // be in ANR state.
+            if (topErrWindow != null && mTouchFocus != topErrWindow) {
+                mTouchFocus = null;
             }
             
-            if (action == MotionEvent.ACTION_DOWN) {
-                while (mOutsideTouchTargets != null) {
-                    addInputTarget(inputTargets, mOutsideTouchTargets,
-                            InputTarget.FLAG_OUTSIDE | targetFlags);
-                    mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch;
-                }
+            // Drop the touch focus if the window is not visible.
+            if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
+                mTouchFocus = null;
             }
             
-            // If we sent an initial down to the wallpaper, then continue
-            // sending events until the final up.
-            // Alternately if we are on top of the wallpaper, then the wallpaper also
-            // gets to see this movement.
-            if (mSendingPointersToWallpaper ||
-                    (target != null && action == MotionEvent.ACTION_DOWN
-                            && mWallpaperTarget == target
-                            && target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) {
+            // Determine wallpaper targets.
+            if (mTouchFocus != null
+                    && mTouchFocus == mWallpaperTarget
+                    && mTouchFocus.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
                 int curTokenIndex = mWallpaperTokens.size();
                 while (curTokenIndex > 0) {
                     curTokenIndex--;
                     WindowToken token = mWallpaperTokens.get(curTokenIndex);
+                    
                     int curWallpaperIndex = token.windows.size();
                     while (curWallpaperIndex > 0) {
                         curWallpaperIndex--;
                         WindowState wallpaper = token.windows.get(curWallpaperIndex);
                         if ((wallpaper.mAttrs.flags &
-                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
-                            continue;
+                                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
+                            mWallpaperTouchTargets.add(wallpaper);
                         }
-                        
-                        switch (action) {
-                            case MotionEvent.ACTION_DOWN:
-                                mSendingPointersToWallpaper = true;
-                                break;
-                            case MotionEvent.ACTION_UP:
-                                mSendingPointersToWallpaper = false;
-                                break;
-                        }
-                        
-                        addInputTarget(inputTargets, wallpaper, targetFlags);
                     }
                 }
             }
-            
-            if (target != null) {
-                addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags);
+        }
+    }
+
+    private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) {
+        synchronized (mWindowMap) {
+            // Drop the touch focus if the window is not visible.
+            if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
+                mTouchFocus = null;
+                mWallpaperTouchTargets.clear();
             }
         }
     }
     
-    private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) {
+    private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) {
+        mFatTouch = false;
+        mTouchDown = false;
+        mTouchFocus = null;
+        mOutsideTouchTargets.clear();
+        mWallpaperTouchTargets.clear();
+        
+        mPowerManager.logPointerUpEvent();
+    }
+
+    /* Adds a window to a list of input targets.
+     * Do NOT call this method while holding any locks because the call to
+     * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager
+     * and create a deadlock hazard.
+     */
+    private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) {
         if (window.mInputChannel == null) {
             return;
         }
@@ -5874,8 +6039,8 @@
         
         final int result;
         if (ENABLE_NATIVE_INPUT_DISPATCH) {
-            result = mInputManager.injectKeyEvent(newEvent,
-                    InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid);
+            result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY,
+                    pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         } else {
             result = dispatchKey(newEvent, pid, uid);
             if (sync) {
@@ -5884,14 +6049,7 @@
         }
         
         Binder.restoreCallingIdentity(ident);
-        switch (result) {
-            case INJECT_NO_PERMISSION:
-                throw new SecurityException(
-                        "Injecting to another application requires INJECT_EVENTS permission");
-            case INJECT_SUCCEEDED:
-                return true;
-        }
-        return false;
+        return reportInjectionResult(result);
     }
 
     /**
@@ -5910,8 +6068,8 @@
         
         final int result;
         if (ENABLE_NATIVE_INPUT_DISPATCH) {
-            result = mInputManager.injectMotionEvent(ev,
-                    InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid);
+            result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH,
+                    pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         } else {
             result = dispatchPointer(null, ev, pid, uid);
             if (sync) {
@@ -5920,14 +6078,7 @@
         }
         
         Binder.restoreCallingIdentity(ident);
-        switch (result) {
-            case INJECT_NO_PERMISSION:
-                throw new SecurityException(
-                        "Injecting to another application requires INJECT_EVENTS permission");
-            case INJECT_SUCCEEDED:
-                return true;
-        }
-        return false;
+        return reportInjectionResult(result);
     }
 
     /**
@@ -5946,8 +6097,8 @@
         
         final int result;
         if (ENABLE_NATIVE_INPUT_DISPATCH) {
-            result = mInputManager.injectMotionEvent(ev,
-                    InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid);
+            result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL,
+                    pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
         } else {
             result = dispatchTrackball(null, ev, pid, uid);
             if (sync) {
@@ -5956,14 +6107,37 @@
         }
         
         Binder.restoreCallingIdentity(ident);
-        switch (result) {
-            case INJECT_NO_PERMISSION:
-                throw new SecurityException(
-                        "Injecting to another application requires INJECT_EVENTS permission");
-            case INJECT_SUCCEEDED:
-                return true;
+        return reportInjectionResult(result);
+    }
+    
+    private boolean reportInjectionResult(int result) {
+        if (ENABLE_NATIVE_INPUT_DISPATCH) {
+            switch (result) {
+                case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+                    Slog.w(TAG, "Input event injection permission denied.");
+                    throw new SecurityException(
+                            "Injecting to another application requires INJECT_EVENTS permission");
+                case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
+                    Slog.v(TAG, "Input event injection succeeded.");
+                    return true;
+                case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
+                    Slog.w(TAG, "Input event injection timed out.");
+                    return false;
+                case InputManager.INPUT_EVENT_INJECTION_FAILED:
+                default:
+                    Slog.w(TAG, "Input event injection failed.");
+                    return false;
+            }
+        } else {
+            switch (result) {
+                case INJECT_NO_PERMISSION:
+                    throw new SecurityException(
+                            "Injecting to another application requires INJECT_EVENTS permission");
+                case INJECT_SUCCEEDED:
+                    return true;
+            }
+            return false;
         }
-        return false;
     }
 
     private WindowState getFocusedWindow() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ec209eda..4d18191 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -9538,7 +9538,7 @@
             sb.append("Subject: ").append(subject).append("\n");
         }
         sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
-        if (crashInfo.durationMillis != -1) {
+        if (crashInfo != null && crashInfo.durationMillis != -1) {
             sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n");
         }
         sb.append("\n");
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/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 53262ae..1a6119a 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;
@@ -105,6 +50,9 @@
     jmethodID isScreenBright;
     jmethodID notifyConfigurationChanged;
     jmethodID notifyLidSwitchChanged;
+    jmethodID notifyInputChannelBroken;
+    jmethodID notifyInputChannelANR;
+    jmethodID notifyInputChannelRecoveredFromANR;
     jmethodID virtualKeyFeedback;
     jmethodID hackInterceptKey;
     jmethodID goToSleep;
@@ -128,8 +76,665 @@
     jfieldID height;
 } gVirtualKeyDefinitionClassInfo;
 
+static struct {
+    jclass clazz;
+
+    jmethodID ctor;
+    jfieldID mArray;
+} gInputTargetListClassInfo;
+
+// ----------------------------------------------------------------------------
+
+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);
+
+    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
+            jweak inputChannelObjWeak);
+    status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+
+    /* --- 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 bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+            nsecs_t& outNewTimeout);
+    virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
+    virtual nsecs_t getKeyRepeatTimeout();
+    virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+    virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+            int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+
+private:
+    sp<InputManager> mInputManager;
+
+    jobject mCallbacksObj;
+    jobject mReusableInputTargetListObj;
+
+    // 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();
+
+    // Weak references to all currently registered input channels by receive fd.
+    Mutex mInputChannelRegistryLock;
+    KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd;
+
+    jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
+
+    static inline JNIEnv* jniEnv() {
+        return AndroidRuntime::getJNIEnv();
+    }
+
+    static bool isAppSwitchKey(int32_t keyCode);
+    static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+    static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+            Vector<InputTarget>& outTargets);
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::NativeInputManager(jobject callbacksObj) :
+    mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+    mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
+    JNIEnv* env = jniEnv();
+
+    mCallbacksObj = env->NewGlobalRef(callbacksObj);
+
+    jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz,
+            gInputTargetListClassInfo.ctor);
+    mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj);
+    env->DeleteLocalRef(inputTargetListObj);
+
+    sp<EventHub> eventHub = new EventHub();
+    mInputManager = new InputManager(eventHub, this, this);
+}
+
+NativeInputManager::~NativeInputManager() {
+    JNIEnv* env = jniEnv();
+
+    env->DeleteGlobalRef(mCallbacksObj);
+    env->DeleteGlobalRef(mReusableInputTargetListObj);
+}
+
+bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
+    return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
+}
+
+bool NativeInputManager::checkAndClearExceptionFromCallback(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;
+    }
+}
+
+status_t NativeInputManager::registerInputChannel(JNIEnv* env,
+        const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
+    jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
+    if (! inputChannelObjWeak) {
+        LOGE("Could not create weak reference for input channel.");
+        LOGE_EX(env);
+        return NO_MEMORY;
+    }
+
+    status_t status;
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+
+        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
+                inputChannel->getReceivePipeFd());
+        if (index >= 0) {
+            LOGE("Input channel object '%s' has already been registered",
+                    inputChannel->getName().string());
+            status = INVALID_OPERATION;
+            goto DeleteWeakRef;
+        }
+
+        mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(),
+                inputChannelObjWeak);
+    }
+
+    status = mInputManager->registerInputChannel(inputChannel);
+    if (! status) {
+        return OK;
+    }
+
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+        mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
+    }
+
+DeleteWeakRef:
+    env->DeleteWeakGlobalRef(inputChannelObjWeak);
+    return status;
+}
+
+status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,
+        const sp<InputChannel>& inputChannel) {
+    jweak inputChannelObjWeak;
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+
+        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
+                inputChannel->getReceivePipeFd());
+        if (index < 0) {
+            LOGE("Input channel object '%s' is not currently registered",
+                    inputChannel->getName().string());
+            return INVALID_OPERATION;
+        }
+
+        inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+        mInputChannelObjWeakByReceiveFd.removeItemsAt(index);
+    }
+
+    env->DeleteWeakGlobalRef(inputChannelObjWeak);
+
+    return mInputManager->unregisterInputChannel(inputChannel);
+}
+
+jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
+        const sp<InputChannel>& inputChannel) {
+    {
+        AutoMutex _l(mInputChannelRegistryLock);
+
+        ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
+                inputChannel->getReceivePipeFd());
+        if (index < 0) {
+            return NULL;
+        }
+
+        jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+        return env->NewLocalRef(inputChannelObjWeak);
+    }
+}
+
+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 (checkAndClearExceptionFromCallback(env, "isScreenOn")) {
+        return true;
+    }
+    return result;
+}
+
+bool NativeInputManager::isScreenBright() {
+    JNIEnv* env = jniEnv();
+
+    jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
+    if (checkAndClearExceptionFromCallback(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);
+    checkAndClearExceptionFromCallback(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 (checkAndClearExceptionFromCallback(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);
+        checkAndClearExceptionFromCallback(env, "goToSleep");
+    }
+
+    if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
+        checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey");
+    }
+
+    if (wmActions & WM_ACTION_PASS_TO_USER) {
+        actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+
+        if (down && isAppSwitchKey(keyCode)) {
+            env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
+            checkAndClearExceptionFromCallback(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);
+        checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
+        break;
+    }
+
+    return InputReaderPolicyInterface::ACTION_NONE;
+}
+
+bool NativeInputManager::filterTouchEvents() {
+    if (mFilterTouchEvents < 0) {
+        JNIEnv* env = jniEnv();
+
+        jboolean result = env->CallBooleanMethod(mCallbacksObj,
+                gCallbacksClassInfo.filterTouchEvents);
+        if (checkAndClearExceptionFromCallback(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 (checkAndClearExceptionFromCallback(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 (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
+        jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+                gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
+        if (! checkAndClearExceptionFromCallback(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 (! checkAndClearExceptionFromCallback(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);
+    checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
+}
+
+void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+    if (inputChannelObjLocal) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken,
+                inputChannelObjLocal);
+        checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
+
+        env->DeleteLocalRef(inputChannelObjLocal);
+    }
+}
+
+bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
+        nsecs_t& outNewTimeout) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyInputChannelANR - inputChannel='%s'",
+            inputChannel->getName().string());
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jlong newTimeout;
+    jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+    if (inputChannelObjLocal) {
+        newTimeout = env->CallLongMethod(mCallbacksObj,
+                gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
+        if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
+            newTimeout = -2;
+        }
+
+        env->DeleteLocalRef(inputChannelObjLocal);
+    } else {
+        newTimeout = -2;
+    }
+
+    if (newTimeout == -2) {
+        return false; // abort
+    }
+
+    outNewTimeout = newTimeout;
+    return true; // resume
+}
+
+void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
+            inputChannel->getName().string());
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
+    if (inputChannelObjLocal) {
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
+                inputChannelObjLocal);
+        checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
+
+        env->DeleteLocalRef(inputChannelObjLocal);
+    }
+}
+
+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);
+    }
+}
+
+int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jint injectionResult;
+    jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
+    if (! keyEventObj) {
+        LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
+        injectionResult = INPUT_EVENT_INJECTION_FAILED;
+    } else {
+        jint injectionResult = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj,
+                keyEventObj, jint(keyEvent->getNature()), jint(policyFlags),
+                jint(injectorPid), jint(injectorUid));
+        if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) {
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+        } else {
+            populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+        }
+        env->DeleteLocalRef(keyEventObj);
+    }
+    return injectionResult;
+}
+
+int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+        int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+            policyFlags, injectorPid, injectorUid);
+#endif
+
+    JNIEnv* env = jniEnv();
+
+    jint injectionResult;
+    jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
+    if (! motionEventObj) {
+        LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
+        injectionResult = INPUT_EVENT_INJECTION_FAILED;
+    } else {
+        jint injectionResult = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj,
+                motionEventObj, jint(motionEvent->getNature()), jint(policyFlags),
+                jint(injectorPid), jint(injectorUid));
+        if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) {
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+        } else {
+            populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+        }
+        android_view_MotionEvent_recycle(env, motionEventObj);
+        env->DeleteLocalRef(motionEventObj);
+    }
+    return injectionResult;
+}
+
+void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+        Vector<InputTarget>& outTargets) {
+    jobjectArray inputTargetArray = jobjectArray(env->GetObjectField(
+            inputTargetListObj, gInputTargetListClassInfo.mArray));
+
+    jsize length = env->GetArrayLength(inputTargetArray);
+    for (jsize i = 0; i < length; i++) {
+        jobject item = env->GetObjectArrayElement(inputTargetArray, 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(inputTargetArray);
+}
+
+
+// ----------------------------------------------------------------------------
+
+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 +744,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 +757,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 +773,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 +782,7 @@
         return;
     }
 
-    gInputDispatchPolicy->setDisplayOrientation(displayId, orientation);
+    gNativeInputManager->setDisplayOrientation(displayId, orientation);
 }
 
 static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
@@ -191,7 +791,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 +801,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 +811,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 +825,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 +845,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->unregisterInputChannel(env, inputChannel);
+    }
 }
 
 static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
@@ -259,7 +863,9 @@
         return;
     }
 
-    status_t status = gInputManager->registerInputChannel(inputChannel);
+
+    status_t status = gNativeInputManager->registerInputChannel(
+            env, inputChannel, inputChannelObj);
     if (status) {
         jniThrowRuntimeException(env, "Failed to register input channel.  "
                 "Check logs for details.");
@@ -285,13 +891,43 @@
 
     android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
 
-    status_t status = gInputManager->unregisterInputChannel(inputChannel);
+    status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
     if (status) {
         jniThrowRuntimeException(env, "Failed to unregister input channel.  "
                 "Check logs for details.");
     }
 }
 
+static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz,
+        jobject keyEventObj, jint nature, jint injectorPid, jint injectorUid,
+        jboolean sync, jint timeoutMillis) {
+    if (checkInputManagerUnitialized(env)) {
+        return INPUT_EVENT_INJECTION_FAILED;
+    }
+
+    KeyEvent keyEvent;
+    android_view_KeyEvent_toNative(env, keyEventObj, nature, & keyEvent);
+
+    return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
+            injectorPid, injectorUid, sync, timeoutMillis);
+}
+
+static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz,
+        jobject motionEventObj, jint nature, jint injectorPid, jint injectorUid,
+        jboolean sync, jint timeoutMillis) {
+    if (checkInputManagerUnitialized(env)) {
+        return INPUT_EVENT_INJECTION_FAILED;
+    }
+
+    MotionEvent motionEvent;
+    android_view_MotionEvent_toNative(env, motionEventObj, nature, & motionEvent);
+
+    return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
+            injectorPid, injectorUid, sync, timeoutMillis);
+}
+
+// ----------------------------------------------------------------------------
+
 static JNINativeMethod gInputManagerMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
@@ -313,7 +949,11 @@
     { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
-            (void*) android_server_InputManager_nativeUnregisterInputChannel }
+            (void*) android_server_InputManager_nativeUnregisterInputChannel },
+    { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
+            (void*) android_server_InputManager_nativeInjectKeyEvent },
+    { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
+            (void*) android_server_InputManager_nativeInjectMotionEvent }
 };
 
 #define FIND_CLASS(var, className) \
@@ -334,7 +974,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");
 
@@ -350,6 +990,15 @@
     GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
             "notifyLidSwitchChanged", "(JZ)V");
 
+    GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
+            "notifyInputChannelBroken", "(Landroid/view/InputChannel;)V");
+
+    GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz,
+            "notifyInputChannelANR", "(Landroid/view/InputChannel;)J");
+
+    GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
+            "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
+
     GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
             "virtualKeyFeedback", "(JIIIIIIJ)V");
 
@@ -379,10 +1028,12 @@
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
     GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
-            "getKeyEventTargets", "(Landroid/view/KeyEvent;II)[Landroid/view/InputTarget;");
+            "getKeyEventTargets",
+            "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I");
 
     GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
-            "getMotionEventTargets", "(Landroid/view/MotionEvent;II)[Landroid/view/InputTarget;");
+            "getMotionEventTargets",
+            "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I");
 
     // VirtualKeyDefinition
 
@@ -404,343 +1055,17 @@
     GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
             "height", "I");
 
+    // InputTargetList
+
+    FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
+
+    GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
+            "<init>", "()V");
+
+    GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
+            "mArray", "[Landroid/view/InputTarget;");
+
     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/test-runner/src/android/test/ServiceTestCase.java b/test-runner/src/android/test/ServiceTestCase.java
index fcb9d55..d9262f6 100644
--- a/test-runner/src/android/test/ServiceTestCase.java
+++ b/test-runner/src/android/test/ServiceTestCase.java
@@ -147,9 +147,6 @@
      * @param intent The Intent as if supplied to {@link android.content.Context#startService}.
      */
     protected void startService(Intent intent) {
-        assertFalse(mServiceStarted);
-        assertFalse(mServiceBound);
-        
         if (!mServiceAttached) {
             setupService();
         }
@@ -159,7 +156,7 @@
             mService.onCreate();
             mServiceCreated = true;
         }
-        mService.onStart(intent, mServiceId);
+        mService.onStartCommand(intent, 0, mServiceId);
         
         mServiceStarted = true;
     }
@@ -183,9 +180,6 @@
      * @return Return an IBinder for making further calls into the Service.
      */
     protected IBinder bindService(Intent intent) {
-        assertFalse(mServiceStarted);
-        assertFalse(mServiceBound);
-        
         if (!mServiceAttached) {
             setupService();
         }
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);