Merge "Change dropbox tag for native crashes." into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 7fe166e..1e1f01c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18504,10 +18504,6 @@
   protected static abstract interface ContactsContract.DataUsageStatColumns {
     field public static final java.lang.String LAST_TIME_USED = "last_time_used";
     field public static final java.lang.String TIMES_USED = "times_used";
-    field public static final java.lang.String USAGE_TYPE = "usage_type";
-    field public static final int USAGE_TYPE_CALL = 0; // 0x0
-    field public static final int USAGE_TYPE_LONG_TEXT = 1; // 0x1
-    field public static final int USAGE_TYPE_SHORT_TEXT = 2; // 0x2
   }
 
   public static final class ContactsContract.Directory implements android.provider.BaseColumns {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 1c02960..ccb9e1f 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -39,6 +39,7 @@
 import android.os.UserHandle;
 import android.util.AndroidException;
 import android.view.IWindowManager;
+import com.android.internal.os.BaseCommand;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -50,12 +51,9 @@
 import java.util.HashSet;
 import java.util.List;
 
-public class Am {
+public class Am extends BaseCommand {
 
     private IActivityManager mAm;
-    private String[] mArgs;
-    private int mNextArg;
-    private String mCurArgData;
 
     private int mStartFlags = 0;
     private boolean mWaitOption = false;
@@ -67,33 +65,155 @@
 
     private String mProfileFile;
 
-    // These are magic strings understood by the Eclipse plugin.
-    private static final String FATAL_ERROR_CODE = "Error type 1";
-    private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
-    private static final String NO_CLASS_ERROR_CODE = "Error type 3";
-
     /**
      * Command-line entry point.
      *
      * @param args The command-line arguments
      */
     public static void main(String[] args) {
-        try {
-            (new Am()).run(args);
-        } catch (IllegalArgumentException e) {
-            showUsage();
-            System.err.println("Error: " + e.getMessage());
-        } catch (Exception e) {
-            e.printStackTrace(System.err);
-            System.exit(1);
-        }
+        (new Am()).run(args);
     }
 
-    private void run(String[] args) throws Exception {
-        if (args.length < 1) {
-            showUsage();
-            return;
-        }
+    public void onShowUsage(PrintStream out) {
+        out.println(
+                "usage: am [subcommand] [options]\n" +
+                "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
+                "               [--R COUNT] [-S] [--opengl-trace]\n" +
+                "               [--user <USER_ID> | current] <INTENT>\n" +
+                "       am startservice [--user <USER_ID> | current] <INTENT>\n" +
+                "       am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
+                "       am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
+                "       am kill-all\n" +
+                "       am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
+                "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
+                "               [--user <USER_ID> | current]\n" +
+                "               [--no-window-animation] <COMPONENT>\n" +
+                "       am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
+                "       am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
+                "       am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
+                "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
+                "       am clear-debug-app\n" +
+                "       am monitor [--gdb <port>]\n" +
+                "       am screen-compat [on|off] <PACKAGE>\n" +
+                "       am to-uri [INTENT]\n" +
+                "       am to-intent-uri [INTENT]\n" +
+                "       am switch-user <USER_ID>\n" +
+                "       am stop-user <USER_ID>\n" +
+                "\n" +
+                "am start: start an Activity.  Options are:\n" +
+                "    -D: enable debugging\n" +
+                "    -W: wait for launch to complete\n" +
+                "    --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
+                "    -P <FILE>: like above, but profiling stops when app goes idle\n" +
+                "    -R: repeat the activity launch <COUNT> times.  Prior to each repeat,\n" +
+                "        the top activity will be finished.\n" +
+                "    -S: force stop the target app before starting the activity\n" +
+                "    --opengl-trace: enable tracing of OpenGL functions\n" +
+                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
+                "        specified then run as the current user.\n" +
+                "\n" +
+                "am startservice: start a Service.  Options are:\n" +
+                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
+                "        specified then run as the current user.\n" +
+                "\n" +
+                "am force-stop: force stop everything associated with <PACKAGE>.\n" +
+                "    --user <USER_ID> | all | current: Specify user to force stop;\n" +
+                "        all users if not specified.\n" +
+                "\n" +
+                "am kill: Kill all processes associated with <PACKAGE>.  Only kills.\n" +
+                "  processes that are safe to kill -- that is, will not impact the user\n" +
+                "  experience.\n" +
+                "    --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
+                "        all users if not specified.\n" +
+                "\n" +
+                "am kill-all: Kill all background processes.\n" +
+                "\n" +
+                "am broadcast: send a broadcast Intent.  Options are:\n" +
+                "    --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
+                "        specified then send to all users.\n" +
+                "    --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
+                "\n" +
+                "am instrument: start an Instrumentation.  Typically this target <COMPONENT>\n" +
+                "  is the form <TEST_PACKAGE>/<RUNNER_CLASS>.  Options are:\n" +
+                "    -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT).  Use with\n" +
+                "        [-e perf true] to generate raw output for performance measurements.\n" +
+                "    -e <NAME> <VALUE>: set argument <NAME> to <VALUE>.  For test runners a\n" +
+                "        common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
+                "    -p <FILE>: write profiling data to <FILE>\n" +
+                "    -w: wait for instrumentation to finish before returning.  Required for\n" +
+                "        test runners.\n" +
+                "    --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
+                "        current user if not specified.\n" +
+                "    --no-window-animation: turn off window animations will running.\n" +
+                "\n" +
+                "am profile: start and stop profiler on a process.  The given <PROCESS> argument\n" +
+                "  may be either a process name or pid.  Options are:\n" +
+                "    --user <USER_ID> | current: When supplying a process name,\n" +
+                "        specify user of process to profile; uses current user if not specified.\n" +
+                "\n" +
+                "am dumpheap: dump the heap of a process.  The given <PROCESS> argument may\n" +
+                "  be either a process name or pid.  Options are:\n" +
+                "    -n: dump native heap instead of managed heap\n" +
+                "    --user <USER_ID> | current: When supplying a process name,\n" +
+                "        specify user of process to dump; uses current user if not specified.\n" +
+                "\n" +
+                "am set-debug-app: set application <PACKAGE> to debug.  Options are:\n" +
+                "    -w: wait for debugger when application starts\n" +
+                "    --persistent: retain this value\n" +
+                "\n" +
+                "am clear-debug-app: clear the previously set-debug-app.\n" +
+                "\n" +
+                "am bug-report: request bug report generation; will launch UI\n" +
+                "    when done to select where it should be delivered.\n" +
+                "\n" +
+                "am monitor: start monitoring for crashes or ANRs.\n" +
+                "    --gdb: start gdbserv on the given port at crash/ANR\n" +
+                "\n" +
+                "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
+                "\n" +
+                "am to-uri: print the given Intent specification as a URI.\n" +
+                "\n" +
+                "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
+                "\n" +
+                "am switch-user: switch to put USER_ID in the foreground, starting\n" +
+                "  execution of that user if it is currently stopped.\n" +
+                "\n" +
+                "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
+                "  code until a later explicit switch to it.\n" +
+                "\n" +
+                "<INTENT> specifications include these flags and arguments:\n" +
+                "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
+                "    [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
+                "    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
+                "    [--esn <EXTRA_KEY> ...]\n" +
+                "    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
+                "    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+                "    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+                "    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
+                "    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
+                "    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
+                "    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
+                "    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
+                "    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
+                "    [-n <COMPONENT>] [-f <FLAGS>]\n" +
+                "    [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
+                "    [--debug-log-resolution] [--exclude-stopped-packages]\n" +
+                "    [--include-stopped-packages]\n" +
+                "    [--activity-brought-to-front] [--activity-clear-top]\n" +
+                "    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
+                "    [--activity-launched-from-history] [--activity-multiple-task]\n" +
+                "    [--activity-no-animation] [--activity-no-history]\n" +
+                "    [--activity-no-user-action] [--activity-previous-is-top]\n" +
+                "    [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
+                "    [--activity-single-top] [--activity-clear-task]\n" +
+                "    [--activity-task-on-home]\n" +
+                "    [--receiver-registered-only] [--receiver-replace-pending]\n" +
+                "    [--selector]\n" +
+                "    [<URI> | <PACKAGE> | <COMPONENT>]\n"
+                );
+    }
+
+    public void onRun() throws Exception {
 
         mAm = ActivityManagerNative.getDefault();
         if (mAm == null) {
@@ -101,9 +221,7 @@
             throw new AndroidException("Can't connect to activity manager; is the system running?");
         }
 
-        mArgs = args;
-        String op = args[0];
-        mNextArg = 1;
+        String op = nextArgRequired();
 
         if (op.equals("start")) {
             runStart();
@@ -142,7 +260,7 @@
         } else if (op.equals("stop-user")) {
             runStopUser();
         } else {
-            throw new IllegalArgumentException("Unknown command: " + op);
+            showError("Error: unknown command '" + op + "'");
         }
     }
 
@@ -1303,193 +1421,4 @@
             return true;
         }
     }
-
-    private String nextOption() {
-        if (mCurArgData != null) {
-            String prev = mArgs[mNextArg - 1];
-            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
-        }
-        if (mNextArg >= mArgs.length) {
-            return null;
-        }
-        String arg = mArgs[mNextArg];
-        if (!arg.startsWith("-")) {
-            return null;
-        }
-        mNextArg++;
-        if (arg.equals("--")) {
-            return null;
-        }
-        if (arg.length() > 1 && arg.charAt(1) != '-') {
-            if (arg.length() > 2) {
-                mCurArgData = arg.substring(2);
-                return arg.substring(0, 2);
-            } else {
-                mCurArgData = null;
-                return arg;
-            }
-        }
-        mCurArgData = null;
-        return arg;
-    }
-
-    private String nextArg() {
-        if (mCurArgData != null) {
-            String arg = mCurArgData;
-            mCurArgData = null;
-            return arg;
-        } else if (mNextArg < mArgs.length) {
-            return mArgs[mNextArg++];
-        } else {
-            return null;
-        }
-    }
-
-    private String nextArgRequired() {
-        String arg = nextArg();
-        if (arg == null) {
-            String prev = mArgs[mNextArg - 1];
-            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
-        }
-        return arg;
-    }
-
-    private static void showUsage() {
-        System.err.println(
-                "usage: am [subcommand] [options]\n" +
-                "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
-                "               [--R COUNT] [-S] [--opengl-trace]\n" +
-                "               [--user <USER_ID> | current] <INTENT>\n" +
-                "       am startservice [--user <USER_ID> | current] <INTENT>\n" +
-                "       am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
-                "       am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
-                "       am kill-all\n" +
-                "       am broadcast [--user <USER_ID> | all | current] <INTENT>\n" +
-                "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
-                "               [--user <USER_ID> | current]\n" +
-                "               [--no-window-animation] <COMPONENT>\n" +
-                "       am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
-                "       am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
-                "       am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
-                "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
-                "       am clear-debug-app\n" +
-                "       am monitor [--gdb <port>]\n" +
-                "       am screen-compat [on|off] <PACKAGE>\n" +
-                "       am to-uri [INTENT]\n" +
-                "       am to-intent-uri [INTENT]\n" +
-                "       am switch-user <USER_ID>\n" +
-                "       am stop-user <USER_ID>\n" +
-                "\n" +
-                "am start: start an Activity.  Options are:\n" +
-                "    -D: enable debugging\n" +
-                "    -W: wait for launch to complete\n" +
-                "    --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
-                "    -P <FILE>: like above, but profiling stops when app goes idle\n" +
-                "    -R: repeat the activity launch <COUNT> times.  Prior to each repeat,\n" +
-                "        the top activity will be finished.\n" +
-                "    -S: force stop the target app before starting the activity\n" +
-                "    --opengl-trace: enable tracing of OpenGL functions\n" +
-                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
-                "        specified then run as the current user.\n" +
-                "\n" +
-                "am startservice: start a Service.  Options are:\n" +
-                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
-                "        specified then run as the current user.\n" +
-                "\n" +
-                "am force-stop: force stop everything associated with <PACKAGE>.\n" +
-                "    --user <USER_ID> | all | current: Specify user to force stop;\n" +
-                "        all users if not specified.\n" +
-                "\n" +
-                "am kill: Kill all processes associated with <PACKAGE>.  Only kills.\n" +
-                "  processes that are safe to kill -- that is, will not impact the user\n" +
-                "  experience.\n" +
-                "    --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
-                "        all users if not specified.\n" +
-                "\n" +
-                "am kill-all: Kill all background processes.\n" +
-                "\n" +
-                "am broadcast: send a broadcast Intent.  Options are:\n" +
-                "    --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
-                "        specified then send to all users.\n" +
-                "    --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
-                "\n" +
-                "am instrument: start an Instrumentation.  Typically this target <COMPONENT>\n" +
-                "  is the form <TEST_PACKAGE>/<RUNNER_CLASS>.  Options are:\n" +
-                "    -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT).  Use with\n" +
-                "        [-e perf true] to generate raw output for performance measurements.\n" +
-                "    -e <NAME> <VALUE>: set argument <NAME> to <VALUE>.  For test runners a\n" +
-                "        common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
-                "    -p <FILE>: write profiling data to <FILE>\n" +
-                "    -w: wait for instrumentation to finish before returning.  Required for\n" +
-                "        test runners.\n" +
-                "    --user <USER_ID> | current: Specify user instrumentation runs in;\n" +
-                "        current user if not specified.\n" +
-                "    --no-window-animation: turn off window animations will running.\n" +
-                "\n" +
-                "am profile: start and stop profiler on a process.  The given <PROCESS> argument\n" +
-                "  may be either a process name or pid.  Options are:\n" +
-                "    --user <USER_ID> | current: When supplying a process name,\n" +
-                "        specify user of process to profile; uses current user if not specified.\n" +
-                "\n" +
-                "am dumpheap: dump the heap of a process.  The given <PROCESS> argument may\n" +
-                "  be either a process name or pid.  Options are:\n" +
-                "    -n: dump native heap instead of managed heap\n" +
-                "    --user <USER_ID> | current: When supplying a process name,\n" +
-                "        specify user of process to dump; uses current user if not specified.\n" +
-                "\n" +
-                "am set-debug-app: set application <PACKAGE> to debug.  Options are:\n" +
-                "    -w: wait for debugger when application starts\n" +
-                "    --persistent: retain this value\n" +
-                "\n" +
-                "am clear-debug-app: clear the previously set-debug-app.\n" +
-                "\n" +
-                "am bug-report: request bug report generation; will launch UI\n" +
-                "    when done to select where it should be delivered.\n" +
-                "\n" +
-                "am monitor: start monitoring for crashes or ANRs.\n" +
-                "    --gdb: start gdbserv on the given port at crash/ANR\n" +
-                "\n" +
-                "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
-                "\n" +
-                "am to-uri: print the given Intent specification as a URI.\n" +
-                "\n" +
-                "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
-                "\n" +
-                "am switch-user: switch to put USER_ID in the foreground, starting\n" +
-                "  execution of that user if it is currently stopped.\n" +
-                "\n" +
-                "am stop-user: stop execution of USER_ID, not allowing it to run any\n" +
-                "  code until a later explicit switch to it.\n" +
-                "\n" +
-                "<INTENT> specifications include these flags and arguments:\n" +
-                "    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
-                "    [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
-                "    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
-                "    [--esn <EXTRA_KEY> ...]\n" +
-                "    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
-                "    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
-                "    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
-                "    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]\n" +
-                "    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
-                "    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]\n" +
-                "    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
-                "    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
-                "    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" +
-                "    [-n <COMPONENT>] [-f <FLAGS>]\n" +
-                "    [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
-                "    [--debug-log-resolution] [--exclude-stopped-packages]\n" +
-                "    [--include-stopped-packages]\n" +
-                "    [--activity-brought-to-front] [--activity-clear-top]\n" +
-                "    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]\n" +
-                "    [--activity-launched-from-history] [--activity-multiple-task]\n" +
-                "    [--activity-no-animation] [--activity-no-history]\n" +
-                "    [--activity-no-user-action] [--activity-previous-is-top]\n" +
-                "    [--activity-reorder-to-front] [--activity-reset-task-if-needed]\n" +
-                "    [--activity-single-top] [--activity-clear-task]\n" +
-                "    [--activity-task-on-home]\n" +
-                "    [--receiver-registered-only] [--receiver-replace-pending]\n" +
-                "    [--selector]\n" +
-                "    [<URI> | <PACKAGE> | <COMPONENT>]\n"
-                );
-    }
 }
diff --git a/cmds/media/Android.mk b/cmds/media/Android.mk
new file mode 100644
index 0000000..b9451c5
--- /dev/null
+++ b/cmds/media/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2013 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := media_cmd
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := media
+LOCAL_SRC_FILES := media
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/media/MODULE_LICENSE_APACHE2 b/cmds/media/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/media/MODULE_LICENSE_APACHE2
diff --git a/cmds/media/NOTICE b/cmds/media/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/media/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/cmds/media/media b/cmds/media/media
new file mode 100755
index 0000000..1194442
--- /dev/null
+++ b/cmds/media/media
@@ -0,0 +1,6 @@
+# Script to start "media_cmd" on the device, which has a very rudimentary
+# shell.
+#
+base=/system
+export CLASSPATH=$base/framework/media_cmd.jar
+exec app_process $base/bin com.android.commands.media.Media "$@"
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
new file mode 100644
index 0000000..56af7d6
--- /dev/null
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -0,0 +1,220 @@
+/*
+**
+** Copyright 2013, 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.commands.media;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.IAudioService;
+import android.media.IRemoteControlDisplay;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.AndroidException;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import com.android.internal.os.BaseCommand;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+
+public class Media extends BaseCommand {
+
+    private IAudioService mAudioService;
+
+    /**
+     * Command-line entry point.
+     *
+     * @param args The command-line arguments
+     */
+    public static void main(String[] args) {
+        (new Media()).run(args);
+    }
+
+    public void onShowUsage(PrintStream out) {
+        out.println(
+                "usage: media [subcommand] [options]\n" +
+                "       media dispatch KEY\n" +
+                "       media remote-display\n" +
+                "\n" +
+                "media dispatch: dispatch a media key to the current media client.\n" +
+                "                KEY may be: play, pause, play-pause, mute, headsethook,\n" +
+                "                stop, next, previous, rewind, recordm fast-forword.\n" +
+                "media remote-display: monitor remote display updates.\n"
+        );
+    }
+
+    public void onRun() throws Exception {
+        mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
+                Context.AUDIO_SERVICE));
+        if (mAudioService == null) {
+            System.err.println(NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException("Can't connect to audio service; is the system running?");
+        }
+
+        String op = nextArgRequired();
+
+        if (op.equals("dispatch")) {
+            runDispatch();
+        } else if (op.equals("remote-display")) {
+            runRemoteDisplay();
+        } else {
+            showError("Error: unknown command '" + op + "'");
+            return;
+        }
+    }
+
+    private void sendMediaKey(KeyEvent event) {
+        try {
+            mAudioService.dispatchMediaKeyEvent(event);
+        } catch (RemoteException e) {
+        }
+    }
+
+    private void runDispatch() throws Exception {
+        String cmd = nextArgRequired();
+        int keycode;
+        if ("play".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_PLAY;
+        } else if ("pause".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_PAUSE;
+        } else if ("play-pause".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
+        } else if ("mute".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MUTE;
+        } else if ("headsethook".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_HEADSETHOOK;
+        } else if ("stop".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_STOP;
+        } else if ("next".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_NEXT;
+        } else if ("previous".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+        } else if ("rewind".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_REWIND;
+        } else if ("record".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_RECORD;
+        } else if ("fast-forward".equals(cmd)) {
+            keycode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
+        } else {
+            showError("Error: unknown dispatch code '" + cmd + "'");
+            return;
+        }
+
+        final long now = SystemClock.uptimeMillis();
+        sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+        sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0, 0,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+    }
+
+    class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub {
+        RemoteDisplayMonitor() {
+        }
+
+
+        @Override
+        public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent,
+                boolean clearing) {
+            System.out.println("New client: id=" + clientGeneration
+                    + " intent=" + clientMediaIntent + " clearing=" + clearing);
+        }
+
+        @Override
+        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+                long currentPosMs, float speed) {
+            System.out.println("New state: id=" + generationId + " state=" + state
+                    + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed);
+        }
+
+        @Override
+        public void setTransportControlInfo(int generationId, int transportControlFlags,
+                int posCapabilities) {
+            System.out.println("New control info: id=" + generationId
+                    + " flags=0x" + Integer.toHexString(transportControlFlags)
+                    + " cap=0x" + Integer.toHexString(posCapabilities));
+        }
+
+        @Override
+        public void setMetadata(int generationId, Bundle metadata) {
+            System.out.println("New metadata: id=" + generationId
+                    + " data=" + metadata);
+        }
+
+        @Override
+        public void setArtwork(int generationId, Bitmap artwork) {
+            System.out.println("New artwork: id=" + generationId
+                    + " art=" + artwork);
+        }
+
+        @Override
+        public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) {
+            System.out.println("New metadata+artwork: id=" + generationId
+                    + " data=" + metadata + " art=" + artwork);
+        }
+
+        void printUsageMessage() {
+            System.out.println("Monitoring remote control displays...  available commands:");
+            System.out.println("(q)uit: finish monitoring");
+        }
+
+        void run() throws RemoteException {
+            printUsageMessage();
+
+            mAudioService.registerRemoteControlDisplay(this, 0, 0);
+
+            try {
+                InputStreamReader converter = new InputStreamReader(System.in);
+                BufferedReader in = new BufferedReader(converter);
+                String line;
+
+                while ((line = in.readLine()) != null) {
+                    boolean addNewline = true;
+                    if (line.length() <= 0) {
+                        addNewline = false;
+                    } else if ("q".equals(line) || "quit".equals(line)) {
+                        break;
+                    } else {
+                        System.out.println("Invalid command: " + line);
+                    }
+
+                    synchronized (this) {
+                        if (addNewline) {
+                            System.out.println("");
+                        }
+                        printUsageMessage();
+                    }
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                mAudioService.unregisterRemoteControlDisplay(this);
+            }
+        }
+    }
+
+    private void runRemoteDisplay() throws Exception {
+        RemoteDisplayMonitor monitor = new RemoteDisplayMonitor();
+        monitor.run();
+    }
+}
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index 31eba96..815a0ac 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -26,21 +26,15 @@
 import android.util.AndroidException;
 import android.view.Display;
 import android.view.IWindowManager;
+import com.android.internal.os.BaseCommand;
 
+import java.io.PrintStream;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class Wm {
+public class Wm extends BaseCommand {
 
     private IWindowManager mWm;
-    private String[] mArgs;
-    private int mNextArg;
-    private String mCurArgData;
-
-    // These are magic strings understood by the Eclipse plugin.
-    private static final String FATAL_ERROR_CODE = "Error type 1";
-    private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
-    private static final String NO_CLASS_ERROR_CODE = "Error type 3";
 
     /**
      * Command-line entry point.
@@ -48,23 +42,25 @@
      * @param args The command-line arguments
      */
     public static void main(String[] args) {
-        try {
-            (new Wm()).run(args);
-        } catch (IllegalArgumentException e) {
-            showUsage();
-            System.err.println("Error: " + e.getMessage());
-        } catch (Exception e) {
-            e.printStackTrace(System.err);
-            System.exit(1);
-        }
+        (new Wm()).run(args);
     }
 
-    private void run(String[] args) throws Exception {
-        if (args.length < 1) {
-            showUsage();
-            return;
-        }
+    public void onShowUsage(PrintStream out) {
+        out.println(
+                "usage: wm [subcommand] [options]\n" +
+                "       wm size [reset|WxH]\n" +
+                "       wm density [reset|DENSITY]\n" +
+                "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
+                "\n" +
+                "wm size: return or override display size.\n" +
+                "\n" +
+                "wm density: override display density.\n" +
+                "\n" +
+                "wm overscan: set overscan area for display.\n"
+                );
+    }
 
+    public void onRun() throws Exception {
         mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
                         Context.WINDOW_SERVICE));
         if (mWm == null) {
@@ -72,9 +68,7 @@
             throw new AndroidException("Can't connect to window manager; is the system running?");
         }
 
-        mArgs = args;
-        String op = args[0];
-        mNextArg = 1;
+        String op = nextArgRequired();
 
         if (op.equals("size")) {
             runDisplaySize();
@@ -83,7 +77,8 @@
         } else if (op.equals("overscan")) {
             runDisplayOverscan();
         } else {
-            throw new IllegalArgumentException("Unknown command: " + op);
+            showError("Error: unknown command '" + op + "'");
+            return;
         }
     }
 
@@ -198,69 +193,4 @@
         } catch (RemoteException e) {
         }
     }
-
-    private String nextOption() {
-        if (mCurArgData != null) {
-            String prev = mArgs[mNextArg - 1];
-            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
-        }
-        if (mNextArg >= mArgs.length) {
-            return null;
-        }
-        String arg = mArgs[mNextArg];
-        if (!arg.startsWith("-")) {
-            return null;
-        }
-        mNextArg++;
-        if (arg.equals("--")) {
-            return null;
-        }
-        if (arg.length() > 1 && arg.charAt(1) != '-') {
-            if (arg.length() > 2) {
-                mCurArgData = arg.substring(2);
-                return arg.substring(0, 2);
-            } else {
-                mCurArgData = null;
-                return arg;
-            }
-        }
-        mCurArgData = null;
-        return arg;
-    }
-
-    private String nextArg() {
-        if (mCurArgData != null) {
-            String arg = mCurArgData;
-            mCurArgData = null;
-            return arg;
-        } else if (mNextArg < mArgs.length) {
-            return mArgs[mNextArg++];
-        } else {
-            return null;
-        }
-    }
-
-    private String nextArgRequired() {
-        String arg = nextArg();
-        if (arg == null) {
-            String prev = mArgs[mNextArg - 1];
-            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
-        }
-        return arg;
-    }
-
-    private static void showUsage() {
-        System.err.println(
-                "usage: wm [subcommand] [options]\n" +
-                "       wm size [reset|WxH]\n" +
-                "       wm density [reset|DENSITY]\n" +
-                "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
-                "\n" +
-                "wm size: return or override display size.\n" +
-                "\n" +
-                "wm density: override display density.\n" +
-                "\n" +
-                "wm overscan: set overscan area for display.\n"
-                );
-    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9bf8830..19d495b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -371,9 +371,9 @@
                     return new DisplayManager(ctx.getOuterContext());
                 }});
 
-        registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() {
-                public Object createService(ContextImpl ctx) {
-                    return InputMethodManager.getInstance(ctx);
+        registerService(INPUT_METHOD_SERVICE, new StaticServiceFetcher() {
+                public Object createStaticService() {
+                    return InputMethodManager.getInstance();
                 }});
 
         registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 51867bc..7d8a36e 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -1,7 +1,20 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package android.app;
 
-import com.android.internal.view.IInputMethodSession;
-
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
@@ -25,7 +38,6 @@
 import android.view.inputmethod.InputMethodManager;
 
 import java.io.File;
-import java.lang.ref.WeakReference;
 
 /**
  * Convenience for implementing an activity that will be implemented
@@ -65,7 +77,6 @@
 
     private NativeContentView mNativeContentView;
     private InputMethodManager mIMM;
-    private InputMethodCallback mInputMethodCallback;
 
     private int mNativeHandle;
     
@@ -117,22 +128,6 @@
             super(context, attrs);
         }
     }
-    
-    static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
-        WeakReference<NativeActivity> mNa;
-
-        InputMethodCallback(NativeActivity na) {
-            mNa = new WeakReference<NativeActivity>(na);
-        }
-
-        @Override
-        public void finishedEvent(int seq, boolean handled) {
-            NativeActivity na = mNa.get();
-            if (na != null) {
-                na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled);
-            }
-        }
-    }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -141,7 +136,6 @@
         ActivityInfo ai;
         
         mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
-        mInputMethodCallback = new InputMethodCallback(this);
 
         getWindow().takeSurface(this);
         getWindow().takeInputQueue(this);
@@ -353,7 +347,8 @@
     }
     
     void preDispatchKeyEvent(KeyEvent event, int seq) {
-        mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback);
+        // FIXME: Input dispatch should be redirected back through ViewRootImpl again.
+        finishPreDispatchKeyEventNative(mNativeHandle, seq, false);
     }
 
     void setWindowFlags(int flags, int mask) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 9c0064e..3342068 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -709,7 +709,7 @@
     public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
         try {
             //Log.v(TAG, "Sending new wallpaper offsets from app...");
-            WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+            WindowManagerGlobal.getWindowSession().setWallpaperPosition(
                     windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
             //Log.v(TAG, "...app returning after sending offsets!");
         } catch (RemoteException e) {
@@ -747,7 +747,7 @@
             int x, int y, int z, Bundle extras) {
         try {
             //Log.v(TAG, "Sending new wallpaper offsets from app...");
-            WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
+            WindowManagerGlobal.getWindowSession().sendWallpaperCommand(
                     windowToken, action, x, y, z, extras, false);
             //Log.v(TAG, "...app returning after sending offsets!");
         } catch (RemoteException e) {
@@ -767,7 +767,7 @@
      */
     public void clearWallpaperOffsets(IBinder windowToken) {
         try {
-            WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+            WindowManagerGlobal.getWindowSession().setWallpaperPosition(
                     windowToken, -1, -1, -1, -1);
         } catch (RemoteException e) {
             // Ignore.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ef9b0bf..03e241a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -256,6 +256,12 @@
      * Return the Looper for the main thread of the current process.  This is
      * the thread used to dispatch calls to application components (activities,
      * services, etc).
+     * <p>
+     * By definition, this method returns the same result as would be obtained
+     * by calling {@link Looper#getMainLooper() Looper.getMainLooper()}.
+     * </p>
+     *
+     * @return The main looper.
      */
     public abstract Looper getMainLooper();
 
diff --git a/core/java/android/os/LatencyTimer.java b/core/java/android/os/LatencyTimer.java
deleted file mode 100644
index ed2f0f9..0000000
--- a/core/java/android/os/LatencyTimer.java
+++ /dev/null
@@ -1,94 +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 android.os;
-
-import android.util.Log;
-
-import java.util.HashMap;
-
-/**
- * A class to help with measuring latency in your code.
- * 
- * Suggested usage:
- * 1) Instanciate a LatencyTimer as a class field.
- *      private [static] LatencyTimer mLt = new LatencyTimer(100, 1000);
- * 2) At various points in the code call sample with a string and the time delta to some fixed time.
- *    The string should be unique at each point of the code you are measuring.
- *      mLt.sample("before processing event", System.nanoTime() - event.getEventTimeNano());
- *      processEvent(event);
- *      mLt.sample("after processing event ", System.nanoTime() - event.getEventTimeNano());
- *
- * @hide
- */
-public final class LatencyTimer
-{
-    final String TAG = "LatencyTimer";
-    final int mSampleSize;
-    final int mScaleFactor;
-    volatile HashMap<String, long[]> store = new HashMap<String, long[]>();
-
-    /**
-    * Creates a LatencyTimer object
-    * @param sampleSize number of samples to collect before printing out the average
-    * @param scaleFactor divisor used to make each sample smaller to prevent overflow when
-    *        (sampleSize * average sample value)/scaleFactor > Long.MAX_VALUE
-    */
-    public LatencyTimer(int sampleSize, int scaleFactor) {
-        if (scaleFactor == 0) {
-            scaleFactor = 1;
-        }
-        mScaleFactor = scaleFactor;
-        mSampleSize = sampleSize;
-    }
-
-    /**
-     * Add a sample delay for averaging.
-     * @param tag string used for printing out the result. This should be unique at each point of
-     *  this called.
-     * @param delta time difference from an unique point of reference for a particular iteration
-     */
-    public void sample(String tag, long delta) {
-        long[] array = getArray(tag);
-
-        // array[mSampleSize] holds the number of used entries
-        final int index = (int) array[mSampleSize]++;
-        array[index] = delta;
-        if (array[mSampleSize] == mSampleSize) {
-            long totalDelta = 0;
-            for (long d : array) {
-                totalDelta += d/mScaleFactor;
-            }
-            array[mSampleSize] = 0;
-            Log.i(TAG, tag + " average = " + totalDelta / mSampleSize);
-        }
-    }
-
-    private long[] getArray(String tag) {
-        long[] data = store.get(tag);
-        if (data == null) {
-            synchronized(store) {
-                data = store.get(tag);
-                if (data == null) {
-                    data = new long[mSampleSize + 1];
-                    store.put(tag, data);
-                    data[mSampleSize] = 0;
-                }
-            }
-        }
-        return data;
-    }
-}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 38f4d5e..363a1bf 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -192,6 +192,14 @@
     }
 
     /**
+     * Returns true if the current thread is this looper's thread.
+     * @hide
+     */
+    public boolean isCurrentThread() {
+        return Thread.currentThread() == mThread;
+    }
+
+    /**
      * Quits the looper.
      *
      * Causes the {@link #loop} method to terminate as soon as possible.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 66083c8..367d576 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3796,50 +3796,11 @@
      * Columns in the Data_Usage_Stat table
      */
     protected interface DataUsageStatColumns {
-        /** What the referenced {@link Data} was used for.
-         * @see DataUsageStatColumns#USAGE_TYPE_CALL
-         * @see DataUsageStatColumns#USAGE_TYPE_LONG_TEXT
-         * @see DataUsageStatColumns#USAGE_TYPE_SHORT_TEXT
-         */
-        public static final String USAGE_TYPE = "usage_type";
-
         /** The last time (in milliseconds) this {@link Data} was used. */
         public static final String LAST_TIME_USED = "last_time_used";
 
-        /** The number of times the referenced {@link Data} has been used for the purpose described
-         * in {@link DataUsageStatColumns#USAGE_TYPE}.
-         */
+        /** The number of times the referenced {@link Data} has been used. */
         public static final String TIMES_USED = "times_used";
-
-        /**
-         * Integer value for USAGE_TYPE.
-         * This type of usage refers to voice interaction, which includes phone calls, voice chat,
-         * and video chat.
-         *
-         * @see DataUsageFeedback#USAGE_TYPE
-         * @see DataUsageStatColumns#USAGE_TYPE
-         */
-        public static final int USAGE_TYPE_CALL = 0;
-
-        /**
-         * Integer value for USAGE_TYPE.
-         * This type of usage refers to text interaction involving longer messages, which includes
-         * email.
-         *
-         * @see DataUsageFeedback#USAGE_TYPE
-         * @see DataUsageStatColumns#USAGE_TYPE
-         */
-        public static final int USAGE_TYPE_LONG_TEXT = 1;
-
-        /**
-         * Integer value for USAGE_TYPE.
-         * This type of usage for text interaction involving shorter messages, which includes SMS
-         * and text chat with email addresses.
-         *
-         * @see DataUsageFeedback#USAGE_TYPE
-         * @see DataUsageStatColumns#USAGE_TYPE
-         */
-        public static final int USAGE_TYPE_SHORT_TEXT = 2;
     }
 
     /**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 71d8fb6..5db8168 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -762,7 +762,7 @@
             mWindowToken = wrapper.mWindowToken;
             mSurfaceHolder.setSizeFromLayout();
             mInitializing = true;
-            mSession = WindowManagerGlobal.getWindowSession(getMainLooper());
+            mSession = WindowManagerGlobal.getWindowSession();
             
             mWindow.setSession(mSession);
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a520e17..7c82f7e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14060,6 +14060,10 @@
      * managed: remove them when they should not be displayed anymore. The
      * overlay will always have the same size as its host view.
      *
+     * <p>Note: Overlays do not currently work correctly with {@link
+     * SurfaceView} or {@link TextureView}; contents in overlays for these
+     * types of views may not display correctly.</p>
+     *
      * @return The ViewOverlay object for this view.
      * @see ViewOverlay
      */
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c07191a..311d1d0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2975,6 +2975,10 @@
      * representation of a view in a parent container, such as might be used
      * by an animation effect.
      *
+     * <p>Note: Overlays do not currently work correctly with {@link
+     * SurfaceView} or {@link TextureView}; contents in overlays for these
+     * types of views may not display correctly.</p>
+     *
      * @return The ViewGroupOverlay object for this view.
      * @see ViewGroupOverlay
      */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index efa8a9e..22586f6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -42,7 +42,6 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
-import android.os.LatencyTimer;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -116,9 +115,6 @@
      * at 60 Hz. This can be used to measure the potential framerate.
      */
     private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
-    
-    private static final boolean MEASURE_LATENCY = false;
-    private static LatencyTimer lt;
 
     /**
      * Maximum time we allow the user to roll the trackball enough to generate
@@ -138,26 +134,15 @@
     private static boolean sRenderThreadQueried = false;
     private static final Object[] sRenderThreadQueryLock = new Object[0];
 
+    final Context mContext;
     final IWindowSession mWindowSession;
     final Display mDisplay;
     final String mBasePackageName;
 
-    long mLastTrackballTime = 0;
-    final TrackballAxis mTrackballAxisX = new TrackballAxis();
-    final TrackballAxis mTrackballAxisY = new TrackballAxis();
-
-    final SimulatedDpad mSimulatedDpad;
-
-    int mLastJoystickXDirection;
-    int mLastJoystickYDirection;
-    int mLastJoystickXKeyCode;
-    int mLastJoystickYKeyCode;
-
     final int[] mTmpLocation = new int[2];
 
     final TypedValue mTmpValue = new TypedValue();
-    
-    final InputMethodCallback mInputMethodCallback;
+
     final Thread mThread;
 
     final WindowLeaked mLocation;
@@ -231,38 +216,23 @@
     int mClientWindowLayoutFlags;
     boolean mLastOverscanRequested;
 
-     /** Event was not handled and is finished.
-      * @hide */
-    public static final int EVENT_NOT_HANDLED = 0;
-     /** Event was handled and is finished.
-      * @hide */
-    public static final int EVENT_HANDLED = 1;
-    /** Event is waiting on the IME.
-     * @hide */
-    public static final int EVENT_PENDING_IME = 2;
-    /** Event requires post-IME dispatch.
-     * @hide */
-    public static final int EVENT_POST_IME = 3;
-
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
     private int mQueuedInputEventPoolSize;
 
     /* Input event queue.
-     * Pending input events are input events waiting to be handled by the application. Current
-     * input events are input events which are being handled but are waiting on some action by the
-     * IME, even if they themselves may not need to be handled by the IME.
+     * Pending input events are input events waiting to be delivered to the input stages
+     * and handled by the application.
      */
     QueuedInputEvent mPendingInputEventHead;
     QueuedInputEvent mPendingInputEventTail;
     int mPendingInputEventCount;
-    QueuedInputEvent mActiveInputEventHead;
-    QueuedInputEvent mActiveInputEventTail;
-    int mActiveInputEventCount;
     boolean mProcessInputEventsScheduled;
     String mPendingInputEventQueueLengthCounterName = "pq";
-    String mActiveInputEventQueueLengthCounterName = "aq";
+
+    InputStage mFirstInputStage;
+    InputStage mFirstPostImeInputStage;
 
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
@@ -361,18 +331,8 @@
     }
     
     public ViewRootImpl(Context context, Display display) {
-        super();
-
-        if (MEASURE_LATENCY) {
-            if (lt == null) {
-                lt = new LatencyTimer(100, 1000);
-            }
-        }
-
-        // Initialize the statics when this class is first instantiated. This is
-        // done here instead of in the static block because Zygote does not
-        // allow the spawning of threads.
-        mWindowSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
+        mContext = context;
+        mWindowSession = WindowManagerGlobal.getWindowSession();
         mDisplay = display;
         mBasePackageName = context.getBasePackageName();
 
@@ -390,7 +350,6 @@
         mWinFrame = new Rect();
         mWindow = new W(this);
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
-        mInputMethodCallback = new InputMethodCallback(this);
         mViewVisibility = View.GONE;
         mTransparentRegion = new Region();
         mPreviousTransparentRegion = new Region();
@@ -411,7 +370,6 @@
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mAttachInfo.mScreenOn = powerManager.isScreenOn();
         loadSystemProperties();
-        mSimulatedDpad = new SimulatedDpad(context);
     }
 
     /**
@@ -659,8 +617,22 @@
                     view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
                 }
 
-                mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
-                mActiveInputEventQueueLengthCounterName = "aq:" + attrs.getTitle();
+                // Set up the input pipeline.
+                CharSequence counterSuffix = attrs.getTitle();
+                InputStage syntheticStage = new SyntheticInputStage();
+                InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);
+                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
+                        "aq:native-post-ime:" + counterSuffix);
+                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
+                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
+                        "aq:ime:" + counterSuffix);
+                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
+                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
+                        "aq:native-pre-ime:" + counterSuffix);
+
+                mFirstInputStage = nativePreImeStage;
+                mFirstPostImeInputStage = earlyPostImeStage;
+                mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
             }
         }
     }
@@ -2859,7 +2831,7 @@
             mWindowSession.remove(mWindow);
         } catch (RemoteException e) {
         }
-        
+
         // Dispose the input channel after removing the window so the Window Manager
         // doesn't interpret the input channel being closed as an abnormal termination.
         if (mInputChannel != null) {
@@ -3362,365 +3334,822 @@
         return false;
     }
 
-    private int deliverInputEvent(QueuedInputEvent q) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
-        try {
+    /**
+     * Base class for implementing a stage in the chain of responsibility
+     * for processing input events.
+     * <p>
+     * Events are delivered to the stage by the {@link #deliver} method.  The stage
+     * then has the choice of finishing the event or forwarding it to the next stage.
+     * </p>
+     */
+    abstract class InputStage {
+        private final InputStage mNext;
+
+        protected static final int FORWARD = 0;
+        protected static final int FINISH_HANDLED = 1;
+        protected static final int FINISH_NOT_HANDLED = 2;
+
+        /**
+         * Creates an input stage.
+         * @param next The next stage to which events should be forwarded.
+         */
+        public InputStage(InputStage next) {
+            mNext = next;
+        }
+
+        /**
+         * Delivers an event to be processed.
+         */
+        public final void deliver(QueuedInputEvent q) {
+            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
+                forward(q);
+            } else if (mView == null || !mAdded) {
+                finish(q, false);
+            } else {
+                apply(q, onProcess(q));
+            }
+        }
+
+        /**
+         * Marks the the input event as finished then forwards it to the next stage.
+         */
+        protected void finish(QueuedInputEvent q, boolean handled) {
+            q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
+            if (handled) {
+                q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
+            }
+            forward(q);
+        }
+
+        /**
+         * Forwards the event to the next stage.
+         */
+        protected void forward(QueuedInputEvent q) {
+            onDeliverToNext(q);
+        }
+
+        /**
+         * Applies a result code from {@link #onProcess} to the specified event.
+         */
+        protected void apply(QueuedInputEvent q, int result) {
+            if (result == FORWARD) {
+                forward(q);
+            } else if (result == FINISH_HANDLED) {
+                finish(q, true);
+            } else if (result == FINISH_NOT_HANDLED) {
+                finish(q, false);
+            } else {
+                throw new IllegalArgumentException("Invalid result: " + result);
+            }
+        }
+
+        /**
+         * Called when an event is ready to be processed.
+         * @return A result code indicating how the event was handled.
+         */
+        protected int onProcess(QueuedInputEvent q) {
+            return FORWARD;
+        }
+
+        /**
+         * Called when an event is being delivered to the next stage.
+         */
+        protected void onDeliverToNext(QueuedInputEvent q) {
+            if (mNext != null) {
+                mNext.deliver(q);
+            } else {
+                finishInputEvent(q);
+            }
+        }
+    }
+
+    /**
+     * Base class for implementing an input pipeline stage that supports
+     * asynchronous and out-of-order processing of input events.
+     * <p>
+     * In addition to what a normal input stage can do, an asynchronous
+     * input stage may also defer an input event that has been delivered to it
+     * and finish or forward it later.
+     * </p>
+     */
+    abstract class AsyncInputStage extends InputStage {
+        private final String mTraceCounter;
+
+        private QueuedInputEvent mQueueHead;
+        private QueuedInputEvent mQueueTail;
+        private int mQueueLength;
+
+        protected static final int DEFER = 3;
+
+        /**
+         * Creates an asynchronous input stage.
+         * @param next The next stage to which events should be forwarded.
+         * @param traceCounter The name of a counter to record the size of
+         * the queue of pending events.
+         */
+        public AsyncInputStage(InputStage next, String traceCounter) {
+            super(next);
+            mTraceCounter = traceCounter;
+        }
+
+        /**
+         * Marks the event as deferred, which is to say that it will be handled
+         * asynchronously.  The caller is responsible for calling {@link #forward}
+         * or {@link #finish} later when it is done handling the event.
+         */
+        protected void defer(QueuedInputEvent q) {
+            q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
+            enqueue(q);
+        }
+
+        @Override
+        protected void forward(QueuedInputEvent q) {
+            // Clear the deferred flag.
+            q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;
+
+            // Fast path if the queue is empty.
+            QueuedInputEvent curr = mQueueHead;
+            if (curr == null) {
+                super.forward(q);
+                return;
+            }
+
+            // Determine whether the event must be serialized behind any others
+            // before it can be delivered to the next stage.  This is done because
+            // deferred events might be handled out of order by the stage.
+            final int deviceId = q.mEvent.getDeviceId();
+            QueuedInputEvent prev = null;
+            boolean blocked = false;
+            while (curr != null && curr != q) {
+                if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
+                    blocked = true;
+                }
+                prev = curr;
+                curr = curr.mNext;
+            }
+
+            // If the event is blocked, then leave it in the queue to be delivered later.
+            // Note that the event might not yet be in the queue if it was not previously
+            // deferred so we will enqueue it if needed.
+            if (blocked) {
+                if (curr == null) {
+                    enqueue(q);
+                }
+                return;
+            }
+
+            // The event is not blocked.  Deliver it immediately.
+            if (curr != null) {
+                curr = curr.mNext;
+                dequeue(q, prev);
+            }
+            super.forward(q);
+
+            // Dequeuing this event may have unblocked successors.  Deliver them.
+            while (curr != null) {
+                if (deviceId == curr.mEvent.getDeviceId()) {
+                    if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
+                        break;
+                    }
+                    QueuedInputEvent next = curr.mNext;
+                    dequeue(curr, prev);
+                    super.forward(curr);
+                    curr = next;
+                } else {
+                    prev = curr;
+                    curr = curr.mNext;
+                }
+            }
+        }
+
+        @Override
+        protected void apply(QueuedInputEvent q, int result) {
+            if (result == DEFER) {
+                defer(q);
+            } else {
+                super.apply(q, result);
+            }
+        }
+
+        private void enqueue(QueuedInputEvent q) {
+            if (mQueueTail == null) {
+                mQueueHead = q;
+                mQueueTail = q;
+            } else {
+                mQueueTail.mNext = q;
+                mQueueTail = q;
+            }
+
+            mQueueLength += 1;
+            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
+        }
+
+        private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
+            if (prev == null) {
+                mQueueHead = q.mNext;
+            } else {
+                prev.mNext = q.mNext;
+            }
+            if (mQueueTail == q) {
+                mQueueTail = prev;
+            }
+            q.mNext = null;
+
+            mQueueLength -= 1;
+            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
+        }
+    }
+
+    /**
+     * Delivers pre-ime input events to a native activity.
+     * Does not support pointer events.
+     */
+    final class NativePreImeInputStage extends AsyncInputStage {
+        public NativePreImeInputStage(InputStage next, String traceCounter) {
+            super(next, traceCounter);
+        }
+
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
+            return FORWARD;
+        }
+    }
+
+    /**
+     * Delivers pre-ime input events to the view hierarchy.
+     * Does not support pointer events.
+     */
+    final class ViewPreImeInputStage extends InputStage {
+        public ViewPreImeInputStage(InputStage next) {
+            super(next);
+        }
+
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
             if (q.mEvent instanceof KeyEvent) {
-                return deliverKeyEvent(q);
+                return processKeyEvent(q);
+            }
+            return FORWARD;
+        }
+
+        private int processKeyEvent(QueuedInputEvent q) {
+            final KeyEvent event = (KeyEvent)q.mEvent;
+            if (mView.dispatchKeyEventPreIme(event)) {
+                return FINISH_HANDLED;
+            }
+            return FORWARD;
+        }
+    }
+
+    /**
+     * Delivers input events to the ime.
+     * Does not support pointer events.
+     */
+    final class ImeInputStage extends AsyncInputStage
+            implements InputMethodManager.FinishedInputEventCallback {
+        public ImeInputStage(InputStage next, String traceCounter) {
+            super(next, traceCounter);
+        }
+
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
+            if (mLastWasImTarget) {
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null) {
+                    final InputEvent event = q.mEvent;
+                    if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
+                    int result = imm.dispatchInputEvent(event, q, this, mHandler);
+                    if (result == InputMethodManager.DISPATCH_HANDLED) {
+                        return FINISH_HANDLED;
+                    } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
+                        return FINISH_NOT_HANDLED;
+                    } else {
+                        return DEFER; // callback will be invoked later
+                    }
+                }
+            }
+            return FORWARD;
+        }
+
+        @Override
+        public void onFinishedInputEvent(Object token, boolean handled) {
+            QueuedInputEvent q = (QueuedInputEvent)token;
+            if (handled) {
+                finish(q, true);
+                return;
+            }
+
+            // If the window doesn't currently have input focus, then drop
+            // this event.  This could be an event that came back from the
+            // IME dispatch but the window has lost focus in the meantime.
+            if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+                Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+                finish(q, false);
+                return;
+            }
+
+            forward(q);
+        }
+    }
+
+    /**
+     * Performs early processing of post-ime input events.
+     */
+    final class EarlyPostImeInputStage extends InputStage {
+        public EarlyPostImeInputStage(InputStage next) {
+            super(next);
+        }
+
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
+            if (q.mEvent instanceof KeyEvent) {
+                return processKeyEvent(q);
             } else {
                 final int source = q.mEvent.getSource();
                 if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                    return deliverPointerEvent(q);
-                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    return deliverTrackballEvent(q);
-                } else {
-                    return deliverGenericMotionEvent(q);
+                    return processPointerEvent(q);
                 }
             }
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            return FORWARD;
+        }
+
+        private int processKeyEvent(QueuedInputEvent q) {
+            final KeyEvent event = (KeyEvent)q.mEvent;
+
+            // If the key's purpose is to exit touch mode then we consume it
+            // and consider it handled.
+            if (checkForLeavingTouchModeAndConsume(event)) {
+                return FINISH_HANDLED;
+            }
+
+            // Make sure the fallback event policy sees all keys that will be
+            // delivered to the view hierarchy.
+            mFallbackEventHandler.preDispatchKeyEvent(event);
+            return FORWARD;
+        }
+
+        private int processPointerEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+
+            // Translate the pointer event for compatibility, if needed.
+            if (mTranslator != null) {
+                mTranslator.translateEventInScreenToAppWindow(event);
+            }
+
+            // Enter touch mode on down or scroll.
+            final int action = event.getAction();
+            if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
+                ensureTouchMode(true);
+            }
+
+            // Offset the scroll position.
+            if (mCurScrollY != 0) {
+                event.offsetLocation(0, mCurScrollY);
+            }
+
+            // Remember the touch position for possible drag-initiation.
+            if (event.isTouchEvent()) {
+                mLastTouchPoint.x = event.getRawX();
+                mLastTouchPoint.y = event.getRawY();
+            }
+            return FORWARD;
         }
     }
 
-    private int deliverInputEventPostIme(QueuedInputEvent q) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
-        try {
+    /**
+     * Delivers post-ime input events to a native activity.
+     */
+    final class NativePostImeInputStage extends AsyncInputStage {
+        public NativePostImeInputStage(InputStage next, String traceCounter) {
+            super(next, traceCounter);
+        }
+
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
+            return FORWARD;
+        }
+    }
+
+    /**
+     * Delivers post-ime input events to the view hierarchy.
+     */
+    final class ViewPostImeInputStage extends InputStage {
+        public ViewPostImeInputStage(InputStage next) {
+            super(next);
+        }
+
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
             if (q.mEvent instanceof KeyEvent) {
-                return deliverKeyEventPostIme(q);
+                return processKeyEvent(q);
             } else {
                 final int source = q.mEvent.getSource();
-                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
-                    return deliverTrackballEventPostIme(q);
+                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+                    return processPointerEvent(q);
+                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    return processTrackballEvent(q);
                 } else {
-                    return deliverGenericMotionEventPostIme(q);
+                    return processGenericMotionEvent(q);
                 }
             }
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
-    }
 
-    private int deliverPointerEvent(QueuedInputEvent q) {
-        final MotionEvent event = (MotionEvent)q.mEvent;
-        final boolean isTouchEvent = event.isTouchEvent();
-        if (mInputEventConsistencyVerifier != null) {
-            if (isTouchEvent) {
-                mInputEventConsistencyVerifier.onTouchEvent(event, 0);
-            } else {
-                mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+        private int processKeyEvent(QueuedInputEvent q) {
+            final KeyEvent event = (KeyEvent)q.mEvent;
+
+            // Deliver the key to the view hierarchy.
+            if (mView.dispatchKeyEvent(event)) {
+                return FINISH_HANDLED;
             }
+
+            // If the Control modifier is held, try to interpret the key as a shortcut.
+            if (event.getAction() == KeyEvent.ACTION_DOWN
+                    && event.isCtrlPressed()
+                    && event.getRepeatCount() == 0
+                    && !KeyEvent.isModifierKey(event.getKeyCode())) {
+                if (mView.dispatchKeyShortcutEvent(event)) {
+                    return FINISH_HANDLED;
+                }
+            }
+
+            // Apply the fallback event policy.
+            if (mFallbackEventHandler.dispatchKeyEvent(event)) {
+                return FINISH_HANDLED;
+            }
+
+            // Handle automatic focus changes.
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                int direction = 0;
+                switch (event.getKeyCode()) {
+                    case KeyEvent.KEYCODE_DPAD_LEFT:
+                        if (event.hasNoModifiers()) {
+                            direction = View.FOCUS_LEFT;
+                        }
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_RIGHT:
+                        if (event.hasNoModifiers()) {
+                            direction = View.FOCUS_RIGHT;
+                        }
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_UP:
+                        if (event.hasNoModifiers()) {
+                            direction = View.FOCUS_UP;
+                        }
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_DOWN:
+                        if (event.hasNoModifiers()) {
+                            direction = View.FOCUS_DOWN;
+                        }
+                        break;
+                    case KeyEvent.KEYCODE_TAB:
+                        if (event.hasNoModifiers()) {
+                            direction = View.FOCUS_FORWARD;
+                        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+                            direction = View.FOCUS_BACKWARD;
+                        }
+                        break;
+                }
+                if (direction != 0) {
+                    View focused = mView.findFocus();
+                    if (focused != null) {
+                        View v = focused.focusSearch(direction);
+                        if (v != null && v != focused) {
+                            // do the math the get the interesting rect
+                            // of previous focused into the coord system of
+                            // newly focused view
+                            focused.getFocusedRect(mTempRect);
+                            if (mView instanceof ViewGroup) {
+                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+                                        focused, mTempRect);
+                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+                                        v, mTempRect);
+                            }
+                            if (v.requestFocus(direction, mTempRect)) {
+                                playSoundEffect(SoundEffectConstants
+                                        .getContantForFocusDirection(direction));
+                                return FINISH_HANDLED;
+                            }
+                        }
+
+                        // Give the focused view a last chance to handle the dpad key.
+                        if (mView.dispatchUnhandledMove(focused, direction)) {
+                            return FINISH_HANDLED;
+                        }
+                    } else {
+                        // find the best view to give focus to in this non-touch-mode with no-focus
+                        View v = focusSearch(null, direction);
+                        if (v != null && v.requestFocus(direction)) {
+                            return FINISH_HANDLED;
+                        }
+                    }
+                }
+            }
+            return FORWARD;
         }
 
-        // If there is no view, then the event will not be handled.
-        if (mView == null || !mAdded) {
-            return EVENT_NOT_HANDLED;
+        private int processPointerEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+
+            if (mView.dispatchPointerEvent(event)) {
+                return FINISH_HANDLED;
+            }
+            return FORWARD;
         }
 
-        // Translate the pointer event for compatibility, if needed.
-        if (mTranslator != null) {
-            mTranslator.translateEventInScreenToAppWindow(event);
+        private int processTrackballEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+
+            if (mView.dispatchTrackballEvent(event)) {
+                return FINISH_HANDLED;
+            }
+            return FORWARD;
         }
 
-        // Enter touch mode on down or scroll.
-        final int action = event.getAction();
-        if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
-            ensureTouchMode(true);
-        }
+        private int processGenericMotionEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
 
-        // Offset the scroll position.
-        if (mCurScrollY != 0) {
-            event.offsetLocation(0, mCurScrollY);
+            // Deliver the event to the view.
+            if (mView.dispatchGenericMotionEvent(event)) {
+                return FINISH_HANDLED;
+            }
+            return FORWARD;
         }
-        if (MEASURE_LATENCY) {
-            lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
-        }
-
-        // Remember the touch position for possible drag-initiation.
-        if (isTouchEvent) {
-            mLastTouchPoint.x = event.getRawX();
-            mLastTouchPoint.y = event.getRawY();
-        }
-
-        // Dispatch touch to view hierarchy.
-        boolean handled = mView.dispatchPointerEvent(event);
-        if (MEASURE_LATENCY) {
-            lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
-        }
-        return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
     }
 
-    private int deliverTrackballEvent(QueuedInputEvent q) {
-        final MotionEvent event = (MotionEvent)q.mEvent;
-        if (mInputEventConsistencyVerifier != null) {
-            mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+    /**
+     * Performs default processing of unhandled input events.
+     */
+    final class SyntheticInputStage extends InputStage {
+        private final TrackballAxis mTrackballAxisX = new TrackballAxis();
+        private final TrackballAxis mTrackballAxisY = new TrackballAxis();
+        private long mLastTrackballTime;
+
+        private int mLastJoystickXDirection;
+        private int mLastJoystickYDirection;
+        private int mLastJoystickXKeyCode;
+        private int mLastJoystickYKeyCode;
+
+        private SimulatedDpad mSimulatedDpad;
+
+        public SyntheticInputStage() {
+            super(null);
+            mSimulatedDpad = new SimulatedDpad(mContext);
         }
 
-        int result = EVENT_POST_IME;
-        if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
-            if (LOCAL_LOGV)
-                Log.v(TAG, "Dispatching trackball " + event + " to " + mView);
-
-            // Dispatch to the IME before propagating down the view hierarchy.
-            result = dispatchImeInputEvent(q);
-        }
-        return result;
-    }
-
-    private int deliverTrackballEventPostIme(QueuedInputEvent q) {
-        final MotionEvent event = (MotionEvent) q.mEvent;
-
-        // If there is no view, then the event will not be handled.
-        if (mView == null || !mAdded) {
-            return EVENT_NOT_HANDLED;
+        @Override
+        protected int onProcess(QueuedInputEvent q) {
+            q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
+            if (q.mEvent instanceof MotionEvent) {
+                final int source = q.mEvent.getSource();
+                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                    return processTrackballEvent(q);
+                } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                    return processJoystickEvent(q);
+                } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
+                        == InputDevice.SOURCE_TOUCH_NAVIGATION) {
+                    return processTouchNavigationEvent(q);
+                }
+            }
+            return FORWARD;
         }
 
-        // Deliver the trackball event to the view.
-        if (mView.dispatchTrackballEvent(event)) {
-            // If we reach this, we delivered a trackball event to mView and
-            // mView consumed it. Because we will not translate the trackball
-            // event into a key event, touch mode will not exit, so we exit
-            // touch mode here.
-            ensureTouchMode(false);
+        @Override
+        protected void onDeliverToNext(QueuedInputEvent q) {
+            if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
+                // Cancel related synthetic events if any prior stage has handled the event.
+                if (q.mEvent instanceof MotionEvent) {
+                    final int source = q.mEvent.getSource();
+                    if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                        cancelTrackballEvent(q);
+                    } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                        cancelJoystickEvent(q);
+                    } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
+                            == InputDevice.SOURCE_TOUCH_NAVIGATION) {
+                        cancelTouchNavigationEvent(q);
+                    }
+                }
+            }
+            super.onDeliverToNext(q);
+        }
+
+        private int processTrackballEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+
+            // Translate the trackball event into DPAD keys and try to deliver those.
+            final TrackballAxis x = mTrackballAxisX;
+            final TrackballAxis y = mTrackballAxisY;
+            long curTime = SystemClock.uptimeMillis();
+            if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
+                // It has been too long since the last movement,
+                // so restart at the beginning.
+                x.reset(0);
+                y.reset(0);
+                mLastTrackballTime = curTime;
+            }
+
+            final int action = event.getAction();
+            final int metaState = event.getMetaState();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                    x.reset(2);
+                    y.reset(2);
+                    enqueueInputEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+                            KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+                            InputDevice.SOURCE_KEYBOARD));
+                    break;
+                case MotionEvent.ACTION_UP:
+                    x.reset(2);
+                    y.reset(2);
+                    enqueueInputEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+                            KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+                            InputDevice.SOURCE_KEYBOARD));
+                    break;
+            }
+
+            if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
+                    + x.step + " dir=" + x.dir + " acc=" + x.acceleration
+                    + " move=" + event.getX()
+                    + " / Y=" + y.position + " step="
+                    + y.step + " dir=" + y.dir + " acc=" + y.acceleration
+                    + " move=" + event.getY());
+            final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
+            final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
+
+            // Generate DPAD events based on the trackball movement.
+            // We pick the axis that has moved the most as the direction of
+            // the DPAD.  When we generate DPAD events for one axis, then the
+            // other axis is reset -- we don't want to perform DPAD jumps due
+            // to slight movements in the trackball when making major movements
+            // along the other axis.
+            int keycode = 0;
+            int movement = 0;
+            float accel = 1;
+            if (xOff > yOff) {
+                movement = x.generate();
+                if (movement != 0) {
+                    keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
+                            : KeyEvent.KEYCODE_DPAD_LEFT;
+                    accel = x.acceleration;
+                    y.reset(2);
+                }
+            } else if (yOff > 0) {
+                movement = y.generate();
+                if (movement != 0) {
+                    keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
+                            : KeyEvent.KEYCODE_DPAD_UP;
+                    accel = y.acceleration;
+                    x.reset(2);
+                }
+            }
+
+            if (keycode != 0) {
+                if (movement < 0) movement = -movement;
+                int accelMovement = (int)(movement * accel);
+                if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+                        + " accelMovement=" + accelMovement
+                        + " accel=" + accel);
+                if (accelMovement > movement) {
+                    if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+                            + keycode);
+                    movement--;
+                    int repeatCount = accelMovement - movement;
+                    enqueueInputEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
+                            KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+                            InputDevice.SOURCE_KEYBOARD));
+                }
+                while (movement > 0) {
+                    if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+                            + keycode);
+                    movement--;
+                    curTime = SystemClock.uptimeMillis();
+                    enqueueInputEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_DOWN, keycode, 0, metaState,
+                            KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+                            InputDevice.SOURCE_KEYBOARD));
+                    enqueueInputEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_UP, keycode, 0, metaState,
+                            KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+                            InputDevice.SOURCE_KEYBOARD));
+                }
+                mLastTrackballTime = curTime;
+            }
+
+            // Unfortunately we can't tell whether the application consumed the keys, so
+            // we always consider the trackball event handled.
+            return FINISH_HANDLED;
+        }
+
+        private void cancelTrackballEvent(QueuedInputEvent q) {
             mLastTrackballTime = Integer.MIN_VALUE;
-            return EVENT_HANDLED;
-        }
 
-        // Translate the trackball event into DPAD keys and try to deliver those.
-        final TrackballAxis x = mTrackballAxisX;
-        final TrackballAxis y = mTrackballAxisY;
-
-        long curTime = SystemClock.uptimeMillis();
-        if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
-            // It has been too long since the last movement,
-            // so restart at the beginning.
-            x.reset(0);
-            y.reset(0);
-            mLastTrackballTime = curTime;
-        }
-
-        final int action = event.getAction();
-        final int metaState = event.getMetaState();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                x.reset(2);
-                y.reset(2);
-                enqueueInputEvent(new KeyEvent(curTime, curTime,
-                        KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
-                        KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
-                        InputDevice.SOURCE_KEYBOARD));
-                break;
-            case MotionEvent.ACTION_UP:
-                x.reset(2);
-                y.reset(2);
-                enqueueInputEvent(new KeyEvent(curTime, curTime,
-                        KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
-                        KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
-                        InputDevice.SOURCE_KEYBOARD));
-                break;
-        }
-
-        if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
-                + x.step + " dir=" + x.dir + " acc=" + x.acceleration
-                + " move=" + event.getX()
-                + " / Y=" + y.position + " step="
-                + y.step + " dir=" + y.dir + " acc=" + y.acceleration
-                + " move=" + event.getY());
-        final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
-        final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
-
-        // Generate DPAD events based on the trackball movement.
-        // We pick the axis that has moved the most as the direction of
-        // the DPAD.  When we generate DPAD events for one axis, then the
-        // other axis is reset -- we don't want to perform DPAD jumps due
-        // to slight movements in the trackball when making major movements
-        // along the other axis.
-        int keycode = 0;
-        int movement = 0;
-        float accel = 1;
-        if (xOff > yOff) {
-            movement = x.generate((2/event.getXPrecision()));
-            if (movement != 0) {
-                keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
-                        : KeyEvent.KEYCODE_DPAD_LEFT;
-                accel = x.acceleration;
-                y.reset(2);
-            }
-        } else if (yOff > 0) {
-            movement = y.generate((2/event.getYPrecision()));
-            if (movement != 0) {
-                keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
-                        : KeyEvent.KEYCODE_DPAD_UP;
-                accel = y.acceleration;
-                x.reset(2);
+            // If we reach this, we consumed a trackball event.
+            // Because we will not translate the trackball event into a key event,
+            // touch mode will not exit, so we exit touch mode here.
+            if (mView != null && mAdded) {
+                ensureTouchMode(false);
             }
         }
 
-        if (keycode != 0) {
-            if (movement < 0) movement = -movement;
-            int accelMovement = (int)(movement * accel);
-            if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
-                    + " accelMovement=" + accelMovement
-                    + " accel=" + accel);
-            if (accelMovement > movement) {
-                if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
-                        + keycode);
-                movement--;
-                int repeatCount = accelMovement - movement;
-                enqueueInputEvent(new KeyEvent(curTime, curTime,
-                        KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
-                        KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
-                        InputDevice.SOURCE_KEYBOARD));
-            }
-            while (movement > 0) {
-                if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
-                        + keycode);
-                movement--;
-                curTime = SystemClock.uptimeMillis();
-                enqueueInputEvent(new KeyEvent(curTime, curTime,
-                        KeyEvent.ACTION_DOWN, keycode, 0, metaState,
-                        KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
-                        InputDevice.SOURCE_KEYBOARD));
-                enqueueInputEvent(new KeyEvent(curTime, curTime,
-                        KeyEvent.ACTION_UP, keycode, 0, metaState,
-                        KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
-                        InputDevice.SOURCE_KEYBOARD));
-            }
-            mLastTrackballTime = curTime;
-        }
-
-        // Unfortunately we can't tell whether the application consumed the keys, so
-        // we always consider the trackball event handled.
-        return EVENT_HANDLED;
-    }
-
-    private int deliverGenericMotionEvent(QueuedInputEvent q) {
-        final MotionEvent event = (MotionEvent)q.mEvent;
-        if (mInputEventConsistencyVerifier != null) {
-            mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
-        }
-
-        int result = EVENT_POST_IME;
-        if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
-            if (LOCAL_LOGV)
-                Log.v(TAG, "Dispatching generic motion " + event + " to " + mView);
-
-            // Dispatch to the IME before propagating down the view hierarchy.
-            result = dispatchImeInputEvent(q);
-        }
-        return result;
-    }
-
-    private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
-        final MotionEvent event = (MotionEvent) q.mEvent;
-        final int source = event.getSource();
-        final boolean isJoystick = event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK);
-        final boolean isTouchNavigation = event.isFromSource(InputDevice.SOURCE_TOUCH_NAVIGATION);
-
-        // If there is no view, then the event will not be handled.
-        if (mView == null || !mAdded) {
-            if (isJoystick) {
-                updateJoystickDirection(event, false);
-            } else if (isTouchNavigation) {
-                mSimulatedDpad.updateTouchNavigation(this, event, false);
-            }
-            return EVENT_NOT_HANDLED;
-        }
-
-        // Deliver the event to the view.
-        if (mView.dispatchGenericMotionEvent(event)) {
-            if (isJoystick) {
-                updateJoystickDirection(event, false);
-            } else if (isTouchNavigation) {
-                mSimulatedDpad.updateTouchNavigation(this, event, false);
-            }
-            return EVENT_HANDLED;
-        }
-
-        if (isJoystick) {
-            // Translate the joystick event into DPAD keys and try to deliver
-            // those.
+        private int processJoystickEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
             updateJoystickDirection(event, true);
-            return EVENT_HANDLED;
-        }
-        if (isTouchNavigation) {
-            mSimulatedDpad.updateTouchNavigation(this, event, true);
-            return EVENT_HANDLED;
-        }
-        return EVENT_NOT_HANDLED;
-    }
-
-    private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
-        final long time = event.getEventTime();
-        final int metaState = event.getMetaState();
-        final int deviceId = event.getDeviceId();
-        final int source = event.getSource();
-
-        int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X));
-        if (xDirection == 0) {
-            xDirection = joystickAxisValueToDirection(event.getX());
+            return FINISH_HANDLED;
         }
 
-        int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
-        if (yDirection == 0) {
-            yDirection = joystickAxisValueToDirection(event.getY());
+        private void cancelJoystickEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+            updateJoystickDirection(event, false);
         }
 
-        if (xDirection != mLastJoystickXDirection) {
-            if (mLastJoystickXKeyCode != 0) {
-                mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
-                enqueueInputEvent(new KeyEvent(time, time,
-                        KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
-                mLastJoystickXKeyCode = 0;
+        private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
+            final long time = event.getEventTime();
+            final int metaState = event.getMetaState();
+            final int deviceId = event.getDeviceId();
+            final int source = event.getSource();
+
+            int xDirection = joystickAxisValueToDirection(
+                    event.getAxisValue(MotionEvent.AXIS_HAT_X));
+            if (xDirection == 0) {
+                xDirection = joystickAxisValueToDirection(event.getX());
             }
 
-            mLastJoystickXDirection = xDirection;
+            int yDirection = joystickAxisValueToDirection(
+                    event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+            if (yDirection == 0) {
+                yDirection = joystickAxisValueToDirection(event.getY());
+            }
 
-            if (xDirection != 0 && synthesizeNewKeys) {
-                mLastJoystickXKeyCode = xDirection > 0
-                        ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
-                final KeyEvent e = new KeyEvent(time, time,
-                        KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
-                enqueueInputEvent(e);
-                Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
-                m.setAsynchronous(true);
-                mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
+            if (xDirection != mLastJoystickXDirection) {
+                if (mLastJoystickXKeyCode != 0) {
+                    mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+                    enqueueInputEvent(new KeyEvent(time, time,
+                            KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
+                            deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+                    mLastJoystickXKeyCode = 0;
+                }
+
+                mLastJoystickXDirection = xDirection;
+
+                if (xDirection != 0 && synthesizeNewKeys) {
+                    mLastJoystickXKeyCode = xDirection > 0
+                            ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+                    final KeyEvent e = new KeyEvent(time, time,
+                            KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
+                            deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+                    enqueueInputEvent(e);
+                    Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+                    m.setAsynchronous(true);
+                    mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+                }
+            }
+
+            if (yDirection != mLastJoystickYDirection) {
+                if (mLastJoystickYKeyCode != 0) {
+                    mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+                    enqueueInputEvent(new KeyEvent(time, time,
+                            KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
+                            deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+                    mLastJoystickYKeyCode = 0;
+                }
+
+                mLastJoystickYDirection = yDirection;
+
+                if (yDirection != 0 && synthesizeNewKeys) {
+                    mLastJoystickYKeyCode = yDirection > 0
+                            ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+                    final KeyEvent e = new KeyEvent(time, time,
+                            KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
+                            deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+                    enqueueInputEvent(e);
+                    Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+                    m.setAsynchronous(true);
+                    mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+                }
             }
         }
 
-        if (yDirection != mLastJoystickYDirection) {
-            if (mLastJoystickYKeyCode != 0) {
-                mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
-                enqueueInputEvent(new KeyEvent(time, time,
-                        KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
-                mLastJoystickYKeyCode = 0;
-            }
-
-            mLastJoystickYDirection = yDirection;
-
-            if (yDirection != 0 && synthesizeNewKeys) {
-                mLastJoystickYKeyCode = yDirection > 0
-                        ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
-                final KeyEvent e = new KeyEvent(time, time,
-                        KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
-                enqueueInputEvent(e);
-                Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
-                m.setAsynchronous(true);
-                mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
+        private int joystickAxisValueToDirection(float value) {
+            if (value >= 0.5f) {
+                return 1;
+            } else if (value <= -0.5f) {
+                return -1;
+            } else {
+                return 0;
             }
         }
-    }
 
-    private static int joystickAxisValueToDirection(float value) {
-        if (value >= 0.5f) {
-            return 1;
-        } else if (value <= -0.5f) {
-            return -1;
-        } else {
-            return 0;
+        private int processTouchNavigationEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+            mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, true);
+            return FINISH_HANDLED;
+        }
+
+        private void cancelTouchNavigationEvent(QueuedInputEvent q) {
+            final MotionEvent event = (MotionEvent)q.mEvent;
+            mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, false);
         }
     }
 
@@ -3800,136 +4229,6 @@
         return false;
     }
 
-    private int deliverKeyEvent(QueuedInputEvent q) {
-        final KeyEvent event = (KeyEvent)q.mEvent;
-        if (mInputEventConsistencyVerifier != null) {
-            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
-        }
-
-        int result = EVENT_POST_IME;
-        if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
-            if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
-
-            // Perform predispatching before the IME.
-            if (mView.dispatchKeyEventPreIme(event)) {
-                return EVENT_HANDLED;
-            }
-
-            // Dispatch to the IME before propagating down the view hierarchy.
-            result = dispatchImeInputEvent(q);
-        }
-        return result;
-    }
-
-    private int deliverKeyEventPostIme(QueuedInputEvent q) {
-        final KeyEvent event = (KeyEvent)q.mEvent;
-
-        // If the view went away, then the event will not be handled.
-        if (mView == null || !mAdded) {
-            return EVENT_NOT_HANDLED;
-        }
-
-        // If the key's purpose is to exit touch mode then we consume it and consider it handled.
-        if (checkForLeavingTouchModeAndConsume(event)) {
-            return EVENT_HANDLED;
-        }
-
-        // Make sure the fallback event policy sees all keys that will be delivered to the
-        // view hierarchy.
-        mFallbackEventHandler.preDispatchKeyEvent(event);
-
-        // Deliver the key to the view hierarchy.
-        if (mView.dispatchKeyEvent(event)) {
-            return EVENT_HANDLED;
-        }
-
-        // If the Control modifier is held, try to interpret the key as a shortcut.
-        if (event.getAction() == KeyEvent.ACTION_DOWN
-                && event.isCtrlPressed()
-                && event.getRepeatCount() == 0
-                && !KeyEvent.isModifierKey(event.getKeyCode())) {
-            if (mView.dispatchKeyShortcutEvent(event)) {
-                return EVENT_HANDLED;
-            }
-        }
-
-        // Apply the fallback event policy.
-        if (mFallbackEventHandler.dispatchKeyEvent(event)) {
-            return EVENT_HANDLED;
-        }
-
-        // Handle automatic focus changes.
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            int direction = 0;
-            switch (event.getKeyCode()) {
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                    if (event.hasNoModifiers()) {
-                        direction = View.FOCUS_LEFT;
-                    }
-                    break;
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    if (event.hasNoModifiers()) {
-                        direction = View.FOCUS_RIGHT;
-                    }
-                    break;
-                case KeyEvent.KEYCODE_DPAD_UP:
-                    if (event.hasNoModifiers()) {
-                        direction = View.FOCUS_UP;
-                    }
-                    break;
-                case KeyEvent.KEYCODE_DPAD_DOWN:
-                    if (event.hasNoModifiers()) {
-                        direction = View.FOCUS_DOWN;
-                    }
-                    break;
-                case KeyEvent.KEYCODE_TAB:
-                    if (event.hasNoModifiers()) {
-                        direction = View.FOCUS_FORWARD;
-                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
-                        direction = View.FOCUS_BACKWARD;
-                    }
-                    break;
-            }
-            if (direction != 0) {
-                View focused = mView.findFocus();
-                if (focused != null) {
-                    View v = focused.focusSearch(direction);
-                    if (v != null && v != focused) {
-                        // do the math the get the interesting rect
-                        // of previous focused into the coord system of
-                        // newly focused view
-                        focused.getFocusedRect(mTempRect);
-                        if (mView instanceof ViewGroup) {
-                            ((ViewGroup) mView).offsetDescendantRectToMyCoords(
-                                    focused, mTempRect);
-                            ((ViewGroup) mView).offsetRectIntoDescendantCoords(
-                                    v, mTempRect);
-                        }
-                        if (v.requestFocus(direction, mTempRect)) {
-                            playSoundEffect(SoundEffectConstants
-                                    .getContantForFocusDirection(direction));
-                            return EVENT_HANDLED;
-                        }
-                    }
-
-                    // Give the focused view a last chance to handle the dpad key.
-                    if (mView.dispatchUnhandledMove(focused, direction)) {
-                        return EVENT_HANDLED;
-                    }
-                } else {
-                    // find the best view to give focus to in this non-touch-mode with no-focus
-                    View v = focusSearch(null, direction);
-                    if (v != null && v.requestFocus(direction)) {
-                        return EVENT_HANDLED;
-                    }
-                }
-            }
-        }
-
-        // Key was unhandled.
-        return EVENT_NOT_HANDLED;
-    }
-
     /* drag/drop */
     void setLocalDragState(Object obj) {
         mLocalDragState = obj;
@@ -4361,13 +4660,25 @@
      * needing a queue on the application's side.
      */
     private static final class QueuedInputEvent {
-        public static final int FLAG_DELIVER_POST_IME = 1;
+        public static final int FLAG_DELIVER_POST_IME = 1 << 0;
+        public static final int FLAG_DEFERRED = 1 << 1;
+        public static final int FLAG_FINISHED = 1 << 2;
+        public static final int FLAG_FINISHED_HANDLED = 1 << 3;
+        public static final int FLAG_RESYNTHESIZED = 1 << 4;
 
         public QueuedInputEvent mNext;
 
         public InputEvent mEvent;
         public InputEventReceiver mReceiver;
         public int mFlags;
+
+        public boolean shouldSkipIme() {
+            if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
+                return true;
+            }
+            return mEvent instanceof MotionEvent
+                    && mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
+        }
     }
 
     private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
@@ -4440,14 +4751,7 @@
     }
 
     void doProcessInputEvents() {
-        // Handle all of the available pending input events. Currently this will immediately
-        // process all of the events it can until it encounters one that must go through the IME.
-        // After that it will continue adding events to the active input queue but will wait for a
-        // response from the IME, regardless of whether that particular event needs it or not, in
-        // order to guarantee ordering consistency. This could be slightly improved by only
-        // queueing events whose source has previously encountered something that needs to be
-        // handled by the IME, and otherwise handling them immediately since we only need to
-        // guarantee ordering within a given source.
+        // Deliver all pending input events in the queue.
         while (mPendingInputEventHead != null) {
             QueuedInputEvent q = mPendingInputEventHead;
             mPendingInputEventHead = q.mNext;
@@ -4460,25 +4764,7 @@
             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                     mPendingInputEventCount);
 
-            int result = deliverInputEvent(q);
-
-            if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) {
-                finishInputEvent(q, result == EVENT_HANDLED);
-            } else if (result == EVENT_PENDING_IME) {
-                enqueueActiveInputEvent(q);
-            } else {
-                q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
-                // If the IME decided not to handle this event, and we have no events already being
-                // handled by the IME, go ahead and handle this one and then continue to the next
-                // input event. Otherwise, queue it up and handle it after whatever in front of it
-                // in the queue has been handled.
-                if (mActiveInputEventHead == null) {
-                    result = deliverInputEventPostIme(q);
-                    finishInputEvent(q, result == EVENT_HANDLED);
-                } else {
-                    enqueueActiveInputEvent(q);
-                }
-            }
+            deliverInputEvent(q);
         }
 
         // We are done processing all input events that we can process right now
@@ -4489,114 +4775,27 @@
         }
     }
 
-    private void enqueueActiveInputEvent(QueuedInputEvent q) {
-        if (mActiveInputEventHead == null) {
-            mActiveInputEventHead = q;
-            mActiveInputEventTail = q;
-        } else {
-            mActiveInputEventTail.mNext = q;
-            mActiveInputEventTail = q;
-        }
-        mActiveInputEventCount += 1;
-        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
-                mActiveInputEventCount);
-    }
+    private void deliverInputEvent(QueuedInputEvent q) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
+        try {
+            if (mInputEventConsistencyVerifier != null) {
+                mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
+            }
 
-    private QueuedInputEvent dequeueActiveInputEvent() {
-        return dequeueActiveInputEvent(mActiveInputEventHead);
-    }
-
-
-    private QueuedInputEvent dequeueActiveInputEvent(QueuedInputEvent q) {
-        QueuedInputEvent curr = mActiveInputEventHead;
-        QueuedInputEvent prev = null;
-        while (curr != null && curr != q) {
-            prev = curr;
-            curr = curr.mNext;
-        }
-        if (curr != null) {
-            if (mActiveInputEventHead == curr) {
-                mActiveInputEventHead = curr.mNext;
+            InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
+            if (stage != null) {
+                stage.deliver(q);
             } else {
-                prev.mNext = curr.mNext;
+                finishInputEvent(q);
             }
-            if (mActiveInputEventTail == curr) {
-                mActiveInputEventTail = prev;
-            }
-            curr.mNext = null;
-
-            mActiveInputEventCount -= 1;
-            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
-                    mActiveInputEventCount);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
         }
-        return curr;
     }
 
-    private QueuedInputEvent findActiveInputEvent(int seq) {
-        QueuedInputEvent q = mActiveInputEventHead;
-        while (q != null && q.mEvent.getSequenceNumber() != seq) {
-            q = q.mNext;
-        }
-        return q;
-    }
-
-    int dispatchImeInputEvent(QueuedInputEvent q) {
-        if (mLastWasImTarget) {
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                final InputEvent event = q.mEvent;
-                final int seq = event.getSequenceNumber();
-                if (DEBUG_IMF)
-                    Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event);
-                return imm.dispatchInputEvent(mView.getContext(), seq, event,
-                        mInputMethodCallback);
-            }
-        }
-        return EVENT_POST_IME;
-    }
-
-    void handleImeFinishedEvent(int seq, boolean handled) {
-        QueuedInputEvent q = findActiveInputEvent(seq);
-        if (q != null) {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + " event=" + q);
-            }
-
-            if (handled) {
-                dequeueActiveInputEvent(q);
-                finishInputEvent(q, true);
-            } else {
-                q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
-            }
-
-
-            // Flush all of the input events that are no longer waiting on the IME
-            while (mActiveInputEventHead != null && (mActiveInputEventHead.mFlags &
-                        QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
-                q = dequeueActiveInputEvent();
-                // If the window doesn't currently have input focus, then drop
-                // this event.  This could be an event that came back from the
-                // IME dispatch but the window has lost focus in the meantime.
-                handled = false;
-                if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
-                    Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
-                } else {
-                    handled = (deliverInputEventPostIme(q) == EVENT_HANDLED);
-                }
-                finishInputEvent(q, handled);
-            }
-        } else {
-            if (DEBUG_IMF) {
-                Log.v(TAG, "IME finished event: seq=" + seq
-                        + " handled=" + handled + ", event not found!");
-            }
-        }
-
-    }
-
-    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
+    private void finishInputEvent(QueuedInputEvent q) {
         if (q.mReceiver != null) {
+            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
             q.mReceiver.finishInputEvent(q.mEvent, handled);
         } else {
             q.mEvent.recycleIfNeededAfterDispatch();
@@ -4605,7 +4804,7 @@
         recycleQueuedInputEvent(q);
     }
 
-    private static boolean isTerminalInputEvent(InputEvent event) {
+    static boolean isTerminalInputEvent(InputEvent event) {
         if (event instanceof KeyEvent) {
             final KeyEvent keyEvent = (KeyEvent)event;
             return keyEvent.getAction() == KeyEvent.ACTION_UP;
@@ -5141,22 +5340,6 @@
             ((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
         }
     }
-    
-    static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
-        private WeakReference<ViewRootImpl> mViewAncestor;
-
-        public InputMethodCallback(ViewRootImpl viewAncestor) {
-            mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
-        }
-
-        @Override
-        public void finishedEvent(int seq, boolean handled) {
-            final ViewRootImpl viewAncestor = mViewAncestor.get();
-            if (viewAncestor != null) {
-                viewAncestor.handleImeFinishedEvent(seq, handled);
-            }
-        }
-    }
 
     static class W extends IWindow.Stub {
         private final WeakReference<ViewRootImpl> mViewAncestor;
@@ -5329,8 +5512,11 @@
          */
         static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
 
+        static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
+        static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
+        static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
+
         float position;
-        float absPosition;
         float acceleration = 1;
         long lastMoveTime = 0;
         int step;
@@ -5407,21 +5593,18 @@
                 }
             }
             position += off;
-            return (absPosition = Math.abs(position));
+            return Math.abs(position);
         }
 
         /**
          * Generate the number of discrete movement events appropriate for
          * the currently collected trackball movement.
          *
-         * @param precision The minimum movement required to generate the
-         * first discrete movement.
-         *
          * @return Returns the number of discrete movements, either positive
          * or negative, or 0 if there is not enough trackball movement yet
          * for a discrete movement.
          */
-        int generate(float precision) {
+        int generate() {
             int movement = 0;
             nonAccelMovement = 0;
             do {
@@ -5431,7 +5614,7 @@
                     // to do this as soon as possible instead of waiting for
                     // a full movement, in order to make things look responsive.
                     case 0:
-                        if (absPosition < precision) {
+                        if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
                             return movement;
                         }
                         movement += dir;
@@ -5442,13 +5625,12 @@
                     // to wait for the second complete trackball motion before
                     // generating the second discrete movement.
                     case 1:
-                        if (absPosition < 2) {
+                        if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
                             return movement;
                         }
                         movement += dir;
                         nonAccelMovement += dir;
-                        position += dir > 0 ? -2 : 2;
-                        absPosition = Math.abs(position);
+                        position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
                         step = 2;
                         break;
                     // After the first two, we generate discrete movements
@@ -5459,12 +5641,11 @@
                     // a longer increasing acceleration to continuous movement
                     // in one direction.
                     default:
-                        if (absPosition < 1) {
+                        if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
                             return movement;
                         }
                         movement += dir;
-                        position += dir >= 0 ? -1 : 1;
-                        absPosition = Math.abs(position);
+                        position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
                         float acc = acceleration;
                         acc *= 1.1f;
                         acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 7eb26fa..0ff46e9 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -136,11 +136,11 @@
         }
     }
 
-    public static IWindowSession getWindowSession(Looper mainLooper) {
+    public static IWindowSession getWindowSession() {
         synchronized (WindowManagerGlobal.class) {
             if (sWindowSession == null) {
                 try {
-                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
+                    InputMethodManager imm = InputMethodManager.getInstance();
                     IWindowManager windowManager = getWindowManagerService();
                     sWindowSession = windowManager.openSession(
                             imm.getClient(), imm.getInputContext());
@@ -351,7 +351,7 @@
         View view = root.getView();
 
         if (view != null) {
-            InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
+            InputMethodManager imm = InputMethodManager.getInstance();
             if (imm != null) {
                 imm.windowDismissed(mViews[index].getWindowToken());
             }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 855b6d4..4df4734 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -34,9 +34,11 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
-import android.os.SystemClock;
+import android.os.Trace;
 import android.text.style.SuggestionSpan;
 import android.util.Log;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.view.InputChannel;
@@ -45,6 +47,7 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
+import android.util.SparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -200,8 +203,9 @@
     static final boolean DEBUG = false;
     static final String TAG = "InputMethodManager";
 
-    static final Object mInstanceSync = new Object();
-    static InputMethodManager mInstance;
+    static final String PENDING_EVENT_COUNTER = "aq:imm";
+
+    static InputMethodManager sInstance;
 
     /**
      * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
@@ -232,7 +236,14 @@
      */
     static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
 
-    private static final int MAX_PENDING_EVENT_POOL_SIZE = 4;
+    /** @hide */
+    public static final int DISPATCH_IN_PROGRESS = -1;
+
+    /** @hide */
+    public static final int DISPATCH_NOT_HANDLED = 0;
+
+    /** @hide */
+    public static final int DISPATCH_HANDLED = 1;
 
     final IInputMethodManager mService;
     final Looper mMainLooper;
@@ -323,10 +334,8 @@
     InputChannel mCurChannel;
     ImeInputEventSender mCurSender;
 
-    PendingEvent mPendingEventPool;
-    int mPendingEventPoolSize;
-    PendingEvent mPendingEventHead;
-    PendingEvent mPendingEventTail;
+    final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
+    final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
 
     // -----------------------------------------------------------
     
@@ -334,8 +343,10 @@
     static final int MSG_BIND = 2;
     static final int MSG_UNBIND = 3;
     static final int MSG_SET_ACTIVE = 4;
-    static final int MSG_EVENT_TIMEOUT = 5;
-    
+    static final int MSG_SEND_INPUT_EVENT = 5;
+    static final int MSG_TIMEOUT_INPUT_EVENT = 6;
+    static final int MSG_FLUSH_INPUT_EVENT = 7;
+
     class H extends Handler {
         H(Looper looper) {
             super(looper, null, true);
@@ -453,15 +464,16 @@
                     }
                     return;
                 }
-                case MSG_EVENT_TIMEOUT: {
-                    // Even though the message contains both the sequence number
-                    // and the PendingEvent object itself, we only pass the
-                    // sequence number to the timeoutEvent function because it's
-                    // possible for the PendingEvent object to be dequeued and
-                    // recycled concurrently.  To avoid a possible race, we make
-                    // a point of always looking up the PendingEvent within the
-                    // queue given only the sequence number of the event.
-                    timeoutEvent(msg.arg1);
+                case MSG_SEND_INPUT_EVENT: {
+                    sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
+                    return;
+                }
+                case MSG_TIMEOUT_INPUT_EVENT: {
+                    finishedInputEvent(msg.arg1, false, true);
+                    return;
+                }
+                case MSG_FLUSH_INPUT_EVENT: {
+                    finishedInputEvent(msg.arg1, false, false);
                     return;
                 }
             }
@@ -538,10 +550,6 @@
         mH = new H(looper);
         mIInputContext = new ControlledInputConnectionWrapper(looper,
                 mDummyInputConnection, this);
-        
-        if (mInstance == null) {
-            mInstance = this;
-        }
     }
 
     /**
@@ -549,25 +557,15 @@
      * doesn't already exist.
      * @hide
      */
-    static public InputMethodManager getInstance(Context context) {
-        return getInstance(context.getMainLooper());
-    }
-    
-    /**
-     * Internally, the input method manager can't be context-dependent, so
-     * we have this here for the places that need it.
-     * @hide
-     */
-    static public InputMethodManager getInstance(Looper mainLooper) {
-        synchronized (mInstanceSync) {
-            if (mInstance != null) {
-                return mInstance;
+    public static InputMethodManager getInstance() {
+        synchronized (InputMethodManager.class) {
+            if (sInstance == null) {
+                IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
+                IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
+                sInstance = new InputMethodManager(service, Looper.getMainLooper());
             }
-            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
-            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
-            mInstance = new InputMethodManager(service, mainLooper);
+            return sInstance;
         }
-        return mInstance;
     }
     
     /**
@@ -575,8 +573,8 @@
      * if it exists.
      * @hide
      */
-    static public InputMethodManager peekInstance() {
-        return mInstance;
+    public static InputMethodManager peekInstance() {
+        return sInstance;
     }
     
     /** @hide */
@@ -1585,13 +1583,18 @@
     }
 
     /**
+     * Dispatches an input event to the IME.
+     *
+     * Returns {@link #DISPATCH_HANDLED} if the event was handled.
+     * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
+     * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
+     * callback will be invoked later.
+     *
      * @hide
      */
-    public int dispatchInputEvent(Context context, int seq, InputEvent event,
-            FinishedEventCallback callback) {
+    public int dispatchInputEvent(InputEvent event, Object token,
+            FinishedInputEventCallback callback, Handler handler) {
         synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchInputEvent");
-
             if (mCurMethod != null) {
                 if (event instanceof KeyEvent) {
                     KeyEvent keyEvent = (KeyEvent)event;
@@ -1599,144 +1602,140 @@
                             && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
                             && keyEvent.getRepeatCount() == 0) {
                         showInputMethodPickerLocked();
-                        return ViewRootImpl.EVENT_HANDLED;
+                        return DISPATCH_HANDLED;
                     }
                 }
 
                 if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
-                final long startTime = SystemClock.uptimeMillis();
-                if (mCurChannel != null) {
-                    if (mCurSender == null) {
-                        mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
-                    }
-                    if (mCurSender.sendInputEvent(seq, event)) {
-                        enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                        return ViewRootImpl.EVENT_PENDING_IME;
-                    } else {
-                        Log.w(TAG, "Unable to send input event to IME: "
-                                + mCurId + " dropping: " + event);
-                    }
+
+                PendingEvent p = obtainPendingEventLocked(
+                        event, token, mCurId, callback, handler);
+                if (mMainLooper.isCurrentThread()) {
+                    // Already running on the IMM thread so we can send the event immediately.
+                    return sendInputEventOnMainLooperLocked(p);
                 }
+
+                // Post the event to the IMM thread.
+                Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
+                msg.setAsynchronous(true);
+                mH.sendMessage(msg);
+                return DISPATCH_IN_PROGRESS;
             }
         }
-        return ViewRootImpl.EVENT_POST_IME;
+        return DISPATCH_NOT_HANDLED;
     }
 
-    void finishedEvent(int seq, boolean handled) {
-        final FinishedEventCallback callback;
+    // Must be called on the main looper
+    void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+        final boolean handled;
         synchronized (mH) {
-            PendingEvent p = dequeuePendingEventLocked(seq);
-            if (p == null) {
+            int result = sendInputEventOnMainLooperLocked(p);
+            if (result == DISPATCH_IN_PROGRESS) {
+                return;
+            }
+
+            handled = (result == DISPATCH_HANDLED);
+        }
+
+        invokeFinishedInputEventCallback(p, handled);
+    }
+
+    // Must be called on the main looper
+    int sendInputEventOnMainLooperLocked(PendingEvent p) {
+        if (mCurChannel != null) {
+            if (mCurSender == null) {
+                mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
+            }
+
+            final InputEvent event = p.mEvent;
+            final int seq = event.getSequenceNumber();
+            if (mCurSender.sendInputEvent(seq, event)) {
+                mPendingEvents.put(seq, p);
+                Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
+                        mPendingEvents.size());
+
+                Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
+                msg.setAsynchronous(true);
+                mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
+                return DISPATCH_IN_PROGRESS;
+            }
+
+            Log.w(TAG, "Unable to send input event to IME: "
+                    + mCurId + " dropping: " + event);
+        }
+        return DISPATCH_NOT_HANDLED;
+    }
+
+    void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+        final PendingEvent p;
+        synchronized (mH) {
+            int index = mPendingEvents.indexOfKey(seq);
+            if (index < 0) {
                 return; // spurious, event already finished or timed out
             }
-            mH.removeMessages(MSG_EVENT_TIMEOUT, p);
-            callback = p.mCallback;
-            recyclePendingEventLocked(p);
-        }
-        callback.finishedEvent(seq, handled);
-    }
 
-    void timeoutEvent(int seq) {
-        final FinishedEventCallback callback;
-        synchronized (mH) {
-            PendingEvent p = dequeuePendingEventLocked(seq);
-            if (p == null) {
-                return; // spurious, event already finished or timed out
-            }
-            long delay = SystemClock.uptimeMillis() - p.mStartTime;
-            Log.w(TAG, "Timeout waiting for IME to handle input event after "
-                    + delay + "ms: " + p.mInputMethodId);
-            callback = p.mCallback;
-            recyclePendingEventLocked(p);
-        }
-        callback.finishedEvent(seq, false);
-    }
+            p = mPendingEvents.valueAt(index);
+            mPendingEvents.removeAt(index);
+            Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
 
-    private void enqueuePendingEventLocked(
-            long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
-        PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback);
-        if (mPendingEventTail != null) {
-            mPendingEventTail.mNext = p;
-            mPendingEventTail = p;
-        } else {
-            mPendingEventHead = p;
-            mPendingEventTail = p;
-        }
-
-        Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p);
-        msg.setAsynchronous(true);
-        mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
-    }
-
-    private PendingEvent dequeuePendingEventLocked(int seq) {
-        PendingEvent p = mPendingEventHead;
-        if (p == null) {
-            return null;
-        }
-        if (p.mSeq == seq) {
-            mPendingEventHead = p.mNext;
-            if (mPendingEventHead == null) {
-                mPendingEventTail = null;
-            }
-        } else {
-            PendingEvent prev;
-            do {
-                prev = p;
-                p = p.mNext;
-                if (p == null) {
-                    return null;
-                }
-            } while (p.mSeq != seq);
-            prev.mNext = p.mNext;
-            if (mPendingEventTail == p) {
-                mPendingEventTail = prev;
+            if (timeout) {
+                Log.w(TAG, "Timeout waiting for IME to handle input event after "
+                        + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
+            } else {
+                mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
             }
         }
-        p.mNext = null;
-        return p;
+
+        invokeFinishedInputEventCallback(p, handled);
     }
 
-    private PendingEvent obtainPendingEventLocked(
-            long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
-        PendingEvent p = mPendingEventPool;
-        if (p != null) {
-            mPendingEventPoolSize -= 1;
-            mPendingEventPool = p.mNext;
-            p.mNext = null;
+    // Assumes the event has already been removed from the queue.
+    void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+        p.mHandled = handled;
+        if (p.mHandler.getLooper().isCurrentThread()) {
+            // Already running on the callback handler thread so we can send the
+            // callback immediately.
+            p.run();
         } else {
-            p = new PendingEvent();
-        }
-
-        p.mStartTime = startTime;
-        p.mSeq = seq;
-        p.mInputMethodId = inputMethodId;
-        p.mCallback = callback;
-        return p;
-    }
-
-    private void recyclePendingEventLocked(PendingEvent p) {
-        p.mInputMethodId = null;
-        p.mCallback = null;
-
-        if (mPendingEventPoolSize < MAX_PENDING_EVENT_POOL_SIZE) {
-            mPendingEventPoolSize += 1;
-            p.mNext = mPendingEventPool;
-            mPendingEventPool = p;
+            // Post the event to the callback handler thread.
+            // In this case, the callback will be responsible for recycling the event.
+            Message msg = Message.obtain(p.mHandler, p);
+            msg.setAsynchronous(true);
+            msg.sendToTarget();
         }
     }
 
     private void flushPendingEventsLocked() {
-        mH.removeMessages(MSG_EVENT_TIMEOUT);
+        mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
 
-        PendingEvent p = mPendingEventHead;
-        while (p != null) {
-            Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p);
+        final int count = mPendingEvents.size();
+        for (int i = 0; i < count; i++) {
+            int seq = mPendingEvents.keyAt(i);
+            Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
             msg.setAsynchronous(true);
-            mH.sendMessage(msg);
-            p = p.mNext;
+            msg.sendToTarget();
         }
     }
 
+    private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+            String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
+        PendingEvent p = mPendingEventPool.acquire();
+        if (p == null) {
+            p = new PendingEvent();
+        }
+        p.mEvent = event;
+        p.mToken = token;
+        p.mInputMethodId = inputMethodId;
+        p.mCallback = callback;
+        p.mHandler = handler;
+        return p;
+    }
+
+    private void recyclePendingEventLocked(PendingEvent p) {
+        p.recycle();
+        mPendingEventPool.release(p);
+    }
+
     public void showInputMethodPicker() {
         synchronized (mH) {
             showInputMethodPickerLocked();
@@ -1946,8 +1945,8 @@
      * the IME has been finished.
      * @hide
      */
-    public interface FinishedEventCallback {
-        public void finishedEvent(int seq, boolean handled);
+    public interface FinishedInputEventCallback {
+        public void onFinishedInputEvent(Object token, boolean handled);
     }
 
     private final class ImeInputEventSender extends InputEventSender {
@@ -1957,16 +1956,34 @@
 
         @Override
         public void onInputEventFinished(int seq, boolean handled) {
-            finishedEvent(seq, handled);
+            finishedInputEvent(seq, handled, false);
         }
     }
 
-    private static final class PendingEvent {
-        public PendingEvent mNext;
-
-        public long mStartTime;
-        public int mSeq;
+    private final class PendingEvent implements Runnable {
+        public InputEvent mEvent;
+        public Object mToken;
         public String mInputMethodId;
-        public FinishedEventCallback mCallback;
+        public FinishedInputEventCallback mCallback;
+        public Handler mHandler;
+        public boolean mHandled;
+
+        public void recycle() {
+            mEvent = null;
+            mToken = null;
+            mInputMethodId = null;
+            mCallback = null;
+            mHandler = null;
+            mHandled = false;
+        }
+
+        @Override
+        public void run() {
+            mCallback.onFinishedInputEvent(mToken, mHandled);
+
+            synchronized (mH) {
+                recyclePendingEventLocked(this);
+            }
+        }
     }
 }
diff --git a/core/java/android/webkit/HTML5Audio.java b/core/java/android/webkit/HTML5Audio.java
index 684ec07..17eb2df 100644
--- a/core/java/android/webkit/HTML5Audio.java
+++ b/core/java/android/webkit/HTML5Audio.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -84,6 +85,7 @@
     // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate
     private Timer mTimer;
     private final class TimeupdateTask extends TimerTask {
+        @Override
         public void run() {
             HTML5Audio.this.obtainMessage(TIMEUPDATE).sendToTarget();
         }
@@ -139,11 +141,13 @@
     // (i.e. the webviewcore thread here)
 
     // MediaPlayer.OnBufferingUpdateListener
+    @Override
     public void onBufferingUpdate(MediaPlayer mp, int percent) {
         nativeOnBuffering(percent, mNativePointer);
     }
 
     // MediaPlayer.OnCompletionListener;
+    @Override
     public void onCompletion(MediaPlayer mp) {
         mState = COMPLETE;
         mProcessingOnEnd = true;
@@ -156,6 +160,7 @@
     }
 
     // MediaPlayer.OnErrorListener
+    @Override
     public boolean onError(MediaPlayer mp, int what, int extra) {
         mState = ERROR;
         resetMediaPlayer();
@@ -164,6 +169,7 @@
     }
 
     // MediaPlayer.OnPreparedListener
+    @Override
     public void onPrepared(MediaPlayer mp) {
         mState = PREPARED;
         if (mTimer != null) {
@@ -178,6 +184,7 @@
     }
 
     // MediaPlayer.OnSeekCompleteListener
+    @Override
     public void onSeekComplete(MediaPlayer mp) {
         nativeOnTimeupdate(mp.getCurrentPosition(), mNativePointer);
     }
@@ -231,7 +238,7 @@
                 headers.put(HIDE_URL_LOGS, "true");
             }
 
-            mMediaPlayer.setDataSource(url, headers);
+            mMediaPlayer.setDataSource(mContext, Uri.parse(url), headers);
             mState = INITIALIZED;
             mMediaPlayer.prepareAsync();
         } catch (IOException e) {
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 589a358..34cfea5 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -244,11 +244,11 @@
                 public void onClick(DialogInterface dialog, int which) {
                     PackageManager pm = getContext().getPackageManager();
                     pm.revokePermission(mPackageName, mPerm.name);
-                    PermissionItemView.this.setVisibility(View.INVISIBLE);
+                    PermissionItemView.this.setVisibility(View.GONE);
                 }
             };
-            builder.setNegativeButton(R.string.cancel, null);
-            builder.setPositiveButton(R.string.revoke, ocl);
+            builder.setNegativeButton(R.string.revoke, ocl);
+            builder.setPositiveButton(R.string.ok, null);
         }
     }
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 4b62c2d..c7914f3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2433,7 +2433,7 @@
                     mFirstPosition;
         } else {
             final int lastPos = mFirstPosition + getChildCount() - 1;
-            nextSelected = selectedPos != INVALID_POSITION && selectedPos < lastPos?
+            nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ?
                     selectedPos - 1 :
                     lastPos;
         }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 529de2e..3df7258 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -220,28 +220,29 @@
     // with MeasureSpec value overflow and RelativeLayout was one source of them.
     // Some apps came to rely on them. :(
     private boolean mAllowBrokenMeasureSpecs = false;
+    // Compatibility hack. Old versions of the platform would not take
+    // margins and padding into account when generating the height measure spec
+    // for children during the horizontal measure pass.
+    private boolean mMeasureVerticalWithPaddingMargin = false;
 
     // A default width used for RTL measure pass
-    private static int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
+    private static final int DEFAULT_WIDTH = Integer.MAX_VALUE / 2;
 
     public RelativeLayout(Context context) {
         super(context);
-        mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
-                Build.VERSION_CODES.JELLY_BEAN_MR1;
+        queryCompatibilityModes(context);
     }
 
     public RelativeLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         initFromAttributes(context, attrs);
-        mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
-                Build.VERSION_CODES.JELLY_BEAN_MR1;
+        queryCompatibilityModes(context);
     }
 
     public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         initFromAttributes(context, attrs);
-        mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
-                Build.VERSION_CODES.JELLY_BEAN_MR1;
+        queryCompatibilityModes(context);
     }
 
     private void initFromAttributes(Context context, AttributeSet attrs) {
@@ -251,6 +252,12 @@
         a.recycle();
     }
 
+    private void queryCompatibilityModes(Context context) {
+        int version = context.getApplicationInfo().targetSdkVersion;
+        mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
+        mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
+    }
+
     @Override
     public boolean shouldDelayChildPressedState() {
         return false;
@@ -692,6 +699,11 @@
                 params.leftMargin, params.rightMargin,
                 mPaddingLeft, mPaddingRight,
                 myWidth);
+        int maxHeight = myHeight;
+        if (mMeasureVerticalWithPaddingMargin) {
+            maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom -
+                    params.topMargin - params.bottomMargin);
+        }
         int childHeightMeasureSpec;
         if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
             if (params.height >= 0) {
@@ -704,9 +716,9 @@
                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
             }
         } else if (params.width == LayoutParams.MATCH_PARENT) {
-            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
         } else {
-            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
         }
         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
new file mode 100644
index 0000000..e26b27d
--- /dev/null
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -0,0 +1,146 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.internal.os;
+
+import java.io.PrintStream;
+
+public abstract class BaseCommand {
+
+    protected String[] mArgs;
+    private int mNextArg;
+    private String mCurArgData;
+
+    // These are magic strings understood by the Eclipse plugin.
+    public static final String FATAL_ERROR_CODE = "Error type 1";
+    public static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
+    public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
+    /**
+     * Call to run the command.
+     */
+    public void run(String[] args) {
+        if (args.length < 1) {
+            onShowUsage(System.out);
+            return;
+        }
+
+        mArgs = args;
+        mNextArg = 0;
+        mCurArgData = null;
+
+        try {
+            onRun();
+        } catch (IllegalArgumentException e) {
+            onShowUsage(System.err);
+            System.err.println();
+            System.err.println("Error: " + e.getMessage());
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            System.exit(1);
+        }
+    }
+
+    /**
+     * Convenience to show usage information to error output.
+     */
+    public void showUsage() {
+        onShowUsage(System.err);
+    }
+
+    /**
+     * Convenience to show usage information to error output along
+     * with an error message.
+     */
+    public void showError(String message) {
+        onShowUsage(System.err);
+        System.err.println();
+        System.err.println(message);
+    }
+
+    /**
+     * Implement the command.
+     */
+    public abstract void onRun() throws Exception;
+
+    /**
+     * Print help text for the command.
+     */
+    public abstract void onShowUsage(PrintStream out);
+
+    /**
+     * Return the next option on the command line -- that is an argument that
+     * starts with '-'.  If the next argument is not an option, null is returned.
+     */
+    public String nextOption() {
+        if (mCurArgData != null) {
+            String prev = mArgs[mNextArg - 1];
+            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
+        }
+        if (mNextArg >= mArgs.length) {
+            return null;
+        }
+        String arg = mArgs[mNextArg];
+        if (!arg.startsWith("-")) {
+            return null;
+        }
+        mNextArg++;
+        if (arg.equals("--")) {
+            return null;
+        }
+        if (arg.length() > 1 && arg.charAt(1) != '-') {
+            if (arg.length() > 2) {
+                mCurArgData = arg.substring(2);
+                return arg.substring(0, 2);
+            } else {
+                mCurArgData = null;
+                return arg;
+            }
+        }
+        mCurArgData = null;
+        return arg;
+    }
+
+    /**
+     * Return the next argument on the command line, whatever it is; if there are
+     * no arguments left, return null.
+     */
+    public String nextArg() {
+        if (mCurArgData != null) {
+            String arg = mCurArgData;
+            mCurArgData = null;
+            return arg;
+        } else if (mNextArg < mArgs.length) {
+            return mArgs[mNextArg++];
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Return the next argument on the command line, whatever it is; if there are
+     * no arguments left, throws an IllegalArgumentException to report this to the user.
+     */
+    public String nextArgRequired() {
+        String arg = nextArg();
+        if (arg == null) {
+            String prev = mArgs[mNextArg - 1];
+            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
+        }
+        return arg;
+    }
+}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1995670..a0e1603 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2997,11 +2997,11 @@
     <!-- [CHAR LIMIT=NONE] Stub notification title for an app running a service that has provided
          a bad bad notification for itself. -->
     <string name="app_running_notification_title"><xliff:g id="app_name">%1$s</xliff:g>
-        running</string>
+        is running</string>
     <!-- [CHAR LIMIT=NONE] Stub notification text for an app running a service that has provided
          a bad bad notification for itself. -->
-    <string name="app_running_notification_text"><xliff:g id="app_name">%1$s</xliff:g>
-        is currently running</string>
+    <string name="app_running_notification_text">Touch for more information
+        or to stop the app.</string>
 
     <!-- Preference framework strings. -->
     <string name="ok">OK</string>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b80a166..917a47d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -50,6 +50,7 @@
     private long mVolumeKeyUpTime;
     private final boolean mUseMasterVolume;
     private final boolean mUseVolumeKeySounds;
+    private final Binder mToken = new Binder();
     private static String TAG = "AudioManager";
 
     /**
@@ -2075,7 +2076,8 @@
         IAudioService service = getService();
         try {
             // pi != null
-            service.registerMediaButtonIntent(pi, eventReceiver);
+            service.registerMediaButtonIntent(pi, eventReceiver,
+                    eventReceiver == null ? mToken : null);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index b22aa1d..773d7b6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -530,6 +530,9 @@
         // Register for package removal intent broadcasts for media button receiver persistence
         IntentFilter pkgFilter = new IntentFilter();
         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         pkgFilter.addDataScheme("package");
         context.registerReceiver(mReceiver, pkgFilter);
 
@@ -4033,14 +4036,21 @@
                         0,
                         null,
                         SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
-            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+                    || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                     // a package is being removed, not replaced
                     String packageName = intent.getData().getSchemeSpecificPart();
                     if (packageName != null) {
-                        removeMediaButtonReceiverForPackage(packageName);
+                        cleanupMediaButtonReceiverForPackage(packageName, true);
                     }
                 }
+            } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
+                    || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
+                String packageName = intent.getData().getSchemeSpecificPart();
+                if (packageName != null) {
+                    cleanupMediaButtonReceiverForPackage(packageName, false);
+                }
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 AudioSystem.setParameters("screen_state=on");
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
@@ -4847,8 +4857,9 @@
         }
     }
 
-    private static class RemoteControlStackEntry {
+    private static class RemoteControlStackEntry implements DeathRecipient {
         public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+        final public AudioService mService;
         /**
          * The target for the ACTION_MEDIA_BUTTON events.
          * Always non null.
@@ -4859,6 +4870,7 @@
          * Always non null.
          */
         final public ComponentName mReceiverComponent;
+        public IBinder mToken;
         public String mCallingPackageName;
         public int mCallingUid;
         /**
@@ -4889,9 +4901,12 @@
         }
 
         /** precondition: mediaIntent != null */
-        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
+        public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
+                ComponentName eventReceiver, IBinder token) {
+            mService = service;
             mMediaIntent = mediaIntent;
             mReceiverComponent = eventReceiver;
+            mToken = token;
             mCallingUid = -1;
             mRcClient = null;
             mRccId = ++sLastRccId;
@@ -4901,6 +4916,17 @@
                     RemoteControlClient.PLAYBACK_SPEED_1X);
 
             resetPlaybackInfo();
+            if (mToken != null) {
+                try {
+                    mToken.linkToDeath(this, 0);
+                } catch (RemoteException e) {
+                    mService.mAudioHandler.post(new Runnable() {
+                        @Override public void run() {
+                            mService.unregisterMediaButtonIntent(mMediaIntent);
+                        }
+                    });
+                }
+            }
         }
 
         public void unlinkToRcClientDeath() {
@@ -4916,9 +4942,22 @@
             }
         }
 
+        public void destroy() {
+            unlinkToRcClientDeath();
+            if (mToken != null) {
+                mToken.unlinkToDeath(this, 0);
+                mToken = null;
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mService.unregisterMediaButtonIntent(mMediaIntent);
+        }
+
         @Override
         protected void finalize() throws Throwable {
-            unlinkToRcClientDeath();// unlink exception handled inside method
+            destroy(); // unlink exception handled inside method
             super.finalize();
         }
     }
@@ -5017,11 +5056,12 @@
      * Remove any entry in the remote control stack that has the same package name as packageName
      * Pre-condition: packageName != null
      */
-    private void removeMediaButtonReceiverForPackage(String packageName) {
+    private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
         synchronized(mRCStack) {
             if (mRCStack.empty()) {
                 return;
             } else {
+                final PackageManager pm = mContext.getPackageManager();
                 RemoteControlStackEntry oldTop = mRCStack.peek();
                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
                 // iterate over the stack entries
@@ -5029,10 +5069,19 @@
                 //  evaluated it, traversal order doesn't matter here)
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-                    if (packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
+                    if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
                         // a stack entry is from the package being removed, remove it from the stack
                         stackIterator.remove();
-                        rcse.unlinkToRcClientDeath();
+                        rcse.destroy();
+                    } else if (rcse.mReceiverComponent != null) {
+                        try {
+                            // Check to see if this receiver still exists.
+                            pm.getReceiverInfo(rcse.mReceiverComponent, 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            // Not found -- remove it!
+                            stackIterator.remove();
+                            rcse.destroy();
+                        }
                     }
                 }
                 if (mRCStack.empty()) {
@@ -5075,7 +5124,7 @@
             mediaButtonIntent.setComponent(eventReceiver);
             PendingIntent pi = PendingIntent.getBroadcast(mContext,
                     0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
-            registerMediaButtonIntent(pi, eventReceiver);
+            registerMediaButtonIntent(pi, eventReceiver, null);
         }
     }
 
@@ -5085,7 +5134,8 @@
      * Called synchronized on mAudioFocusLock, then mRCStack
      * precondition: mediaIntent != null
      */
-    private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target) {
+    private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
+            IBinder token) {
         // already at top of stack?
         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
             return;
@@ -5107,7 +5157,7 @@
             Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
         }
         if (!wasInsideStack) {
-            rcse = new RemoteControlStackEntry(mediaIntent, target);
+            rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
         }
         mRCStack.push(rcse); // rcse is never null
 
@@ -5129,7 +5179,7 @@
             for (int index = mRCStack.size()-1; index >= 0; index--) {
                 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
                 if (rcse.mMediaIntent.equals(pi)) {
-                    rcse.unlinkToRcClientDeath();
+                    rcse.destroy();
                     // ok to remove element while traversing the stack since we're leaving the loop
                     mRCStack.removeElementAt(index);
                     break;
@@ -5417,12 +5467,13 @@
      * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
      * precondition: mediaIntent != null
      */
-    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
+    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
+            IBinder token) {
         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
 
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
-                pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver);
+                pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
                 // new RC client, assume every type of information shall be queried
                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 25aae8f..13f6c02 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -120,7 +120,7 @@
     oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
     void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
 
-           void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+           void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
     oneway void unregisterMediaButtonIntent(in PendingIntent pi);
 
     oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b6b49a2..45a8b99 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -22,6 +22,7 @@
 import android.media.MediaFormat;
 import android.view.Surface;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import java.util.Map;
 
 /**
@@ -395,6 +396,27 @@
          * see {@link #CRYPTO_MODE_UNENCRYPTED} and {@link #CRYPTO_MODE_AES_CTR}.
          */
         public int mode;
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append(numSubSamples + " subsamples, key [");
+            String hexdigits = "0123456789abcdef";
+            for (int i = 0; i < key.length; i++) {
+                builder.append(hexdigits.charAt((key[i] & 0xf0) >> 4));
+                builder.append(hexdigits.charAt(key[i] & 0x0f));
+            }
+            builder.append("], iv [");
+            for (int i = 0; i < key.length; i++) {
+                builder.append(hexdigits.charAt((iv[i] & 0xf0) >> 4));
+                builder.append(hexdigits.charAt(iv[i] & 0x0f));
+            }
+            builder.append("], clear ");
+            builder.append(Arrays.toString(numBytesOfClearData));
+            builder.append(", encrypted ");
+            builder.append(Arrays.toString(numBytesOfEncryptedData));
+            return builder.toString();
+        }
     };
 
     /**
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 749ef12..286e203 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -26,7 +26,10 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.UUID;
 
 /**
  * MediaExtractor facilitates extraction of demuxed, typically encoded,  media data
@@ -195,6 +198,38 @@
     public native final int getTrackCount();
 
     /**
+     * Get the PSSH info if present. This returns a map of uuid-to-bytes, with the uuid specifying
+     * the crypto scheme, and the bytes being the data specific to that scheme.
+     * {@hide}
+     */
+    public Map<UUID, byte[]> getPsshInfo() {
+        Map<UUID, byte[]> psshMap = null;
+        Map<String, Object> formatMap = getFileFormatNative();
+        if (formatMap != null && formatMap.containsKey("pssh")) {
+            ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh");
+            rawpssh.order(ByteOrder.nativeOrder());
+            rawpssh.rewind();
+            formatMap.remove("pssh");
+            // parse the flat pssh bytebuffer into something more manageable
+            psshMap = new HashMap<UUID, byte[]>();
+            while (rawpssh.remaining() > 0) {
+                rawpssh.order(ByteOrder.BIG_ENDIAN);
+                long msb = rawpssh.getLong();
+                long lsb = rawpssh.getLong();
+                UUID uuid = new UUID(msb, lsb);
+                rawpssh.order(ByteOrder.nativeOrder());
+                int datalen = rawpssh.getInt();
+                byte [] psshdata = new byte[datalen];
+                rawpssh.get(psshdata);
+                psshMap.put(uuid, psshdata);
+            }
+        }
+        return psshMap;
+    }
+
+    private native Map<String, Object> getFileFormatNative();
+
+    /**
      * Get the track format at the specified index.
      * More detail on the representation can be found at {@link android.media.MediaCodec}
      */
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 3a42db2..cd1d9ce 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -372,7 +372,7 @@
 
         default:
         {
-            jniThrowException(env, "java/lang/IllegalStateException", NULL);
+            jniThrowException(env, "java/lang/IllegalStateException", msg);
             break;
         }
     }
@@ -455,13 +455,13 @@
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
     if (codec == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
         return;
     }
 
     status_t err = codec->start();
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(env, err, "start failed");
 }
 
 static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 23949fa..1704d5c 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -162,6 +162,18 @@
     return ConvertMessageToMap(env, msg, format);
 }
 
+status_t JMediaExtractor::getFileFormat(jobject *format) const {
+    sp<AMessage> msg;
+    status_t err;
+    if ((err = mImpl->getFileFormat(&msg)) != OK) {
+        return err;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    return ConvertMessageToMap(env, msg, format);
+}
+
 status_t JMediaExtractor::selectTrack(size_t index) {
     return mImpl->selectTrack(index);
 }
@@ -339,6 +351,26 @@
     return format;
 }
 
+static jobject android_media_MediaExtractor_getFileFormatNative(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    jobject format;
+    status_t err = extractor->getFileFormat(&format);
+
+    if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return NULL;
+    }
+
+    return format;
+}
+
 static void android_media_MediaExtractor_selectTrack(
         JNIEnv *env, jobject thiz, jint index) {
     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -768,6 +800,9 @@
 
     { "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
 
+    { "getFileFormatNative", "()Ljava/util/Map;",
+        (void *)android_media_MediaExtractor_getFileFormatNative },
+
     { "getTrackFormatNative", "(I)Ljava/util/Map;",
         (void *)android_media_MediaExtractor_getTrackFormatNative },
 
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 03900db..ccbad8c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -45,6 +45,8 @@
     size_t countTracks() const;
     status_t getTrackFormat(size_t index, jobject *format) const;
 
+    status_t getFileFormat(jobject *format) const;
+
     status_t selectTrack(size_t index);
     status_t unselectTrack(size_t index);
 
diff --git a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
index 7628754..f859880 100644
--- a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
+++ b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
@@ -97,7 +97,7 @@
     public UniverseBackground(Context context) {
         super(context);
         setBackgroundColor(0xff000000);
-        mSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
+        mSession = WindowManagerGlobal.getWindowSession();
         mContent = View.inflate(context, R.layout.universe, null);
         addView(mContent);
         mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 52f552b..c2dc159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -708,6 +708,7 @@
 
     private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
         public void onClick(View v) {
+            awakenDreams();
             toggleRecentApps();
         }
     };
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 49460de..5f9e921 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -59,6 +59,8 @@
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -1801,7 +1803,23 @@
                 ? com.android.internal.R.anim.lock_screen_wallpaper_behind_enter
                 : com.android.internal.R.anim.lock_screen_behind_enter);
     }
-    
+
+    private static void awakenDreams() {
+        IDreamManager dreamManager = getDreamManager();
+        if (dreamManager != null) {
+            try {
+                dreamManager.awaken();
+            } catch (RemoteException e) {
+                // fine, stay asleep then
+            }
+        }
+    }
+
+    static IDreamManager getDreamManager() {
+        return IDreamManager.Stub.asInterface(
+                ServiceManager.checkService(DreamService.DREAM_SERVICE));
+    }
+
     static ITelephony getTelephonyService() {
         return ITelephony.Stub.asInterface(
                 ServiceManager.checkService(Context.TELEPHONY_SERVICE));
@@ -4550,6 +4568,7 @@
     }
 
     void startDockOrHome() {
+        awakenDreams();
         // We don't have dock home anymore. Home is home. If you lived here, you'd be home by now.
         mContext.startActivityAsUser(mHomeIntent, UserHandle.CURRENT);
     }
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 8bc2da2..59577ad 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -157,3 +157,8 @@
 # ConfigUpdateInstallReceiver.java
 # ---------------------------
 51300 config_install_failed (dir|3)
+
+# ---------------------------
+# IntentFirewall.java
+# ---------------------------
+51400 ifw_intent_matched (Intent Type|1|5),(Component Name|3),(Caller Uid|1|5),(Caller Pkg Count|1|1),(Caller Pkgs|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 3d7da7b..3d2e912 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2594,8 +2594,7 @@
         }
 
         boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
-                callerApp==null?null:callerApp.info, callingPackage, callingUid, callingPid,
-                resolvedType, aInfo);
+                callerApp==null?null:callerApp.info, callingUid, callingPid, resolvedType, aInfo);
 
         if (mMainStack) {
             if (mService.mController != null) {
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 8ff1c7d..fccaab5 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -376,37 +376,37 @@
                             // icon, but this used to be able to slip through, so for
                             // those dirty apps give it the app's icon.
                             foregroundNoti.icon = appInfo.icon;
-                            if (foregroundNoti.contentView == null) {
-                                // In this case the app may not have specified a
-                                // content view...  so we'll give them something to show.
-                                CharSequence appName = appInfo.loadLabel(
-                                        ams.mContext.getPackageManager());
-                                if (appName == null) {
-                                    appName = appInfo.packageName;
-                                }
-                                Context ctx = null;
-                                try {
-                                    ctx = ams.mContext.createPackageContext(
-                                            appInfo.packageName, 0);
-                                    Intent runningIntent = new Intent(
-                                            Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
-                                    runningIntent.setData(Uri.fromParts("package",
-                                            appInfo.packageName, null));
-                                    PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
-                                            runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-                                    foregroundNoti.setLatestEventInfo(ctx,
-                                            ams.mContext.getString(
-                                                    com.android.internal.R.string
-                                                            .app_running_notification_title,
-                                                    appName),
-                                            ams.mContext.getString(
-                                                    com.android.internal.R.string
-                                                            .app_running_notification_text,
-                                                    appName),
-                                            pi);
-                                } catch (PackageManager.NameNotFoundException e) {
-                                    foregroundNoti.icon = 0;
-                                }
+
+                            // Do not allow apps to present a sneaky invisible content view either.
+                            foregroundNoti.contentView = null;
+                            foregroundNoti.bigContentView = null;
+                            CharSequence appName = appInfo.loadLabel(
+                                    ams.mContext.getPackageManager());
+                            if (appName == null) {
+                                appName = appInfo.packageName;
+                            }
+                            Context ctx = null;
+                            try {
+                                ctx = ams.mContext.createPackageContext(
+                                        appInfo.packageName, 0);
+                                Intent runningIntent = new Intent(
+                                        Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                                runningIntent.setData(Uri.fromParts("package",
+                                        appInfo.packageName, null));
+                                PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
+                                        runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+                                foregroundNoti.setLatestEventInfo(ctx,
+                                        ams.mContext.getString(
+                                                com.android.internal.R.string
+                                                        .app_running_notification_title,
+                                                appName),
+                                        ams.mContext.getString(
+                                                com.android.internal.R.string
+                                                        .app_running_notification_text,
+                                                appName),
+                                        pi);
+                            } catch (PackageManager.NameNotFoundException e) {
+                                foregroundNoti.icon = 0;
                             }
                         }
                         if (foregroundNoti.icon == 0) {
diff --git a/services/java/com/android/server/firewall/AndFilter.java b/services/java/com/android/server/firewall/AndFilter.java
index cabf00b..e4276d0 100644
--- a/services/java/com/android/server/firewall/AndFilter.java
+++ b/services/java/com/android/server/firewall/AndFilter.java
@@ -26,11 +26,10 @@
 class AndFilter extends FilterList {
     @Override
     public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ApplicationInfo resolvedApp) {
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
         for (int i=0; i<children.size(); i++) {
-            if (!children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid,
-                    callerPid, resolvedType, resolvedApp)) {
+            if (!children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+                    resolvedApp)) {
                 return false;
             }
         }
diff --git a/services/java/com/android/server/firewall/CategoryFilter.java b/services/java/com/android/server/firewall/CategoryFilter.java
index d5e9fe8..4938cb8 100644
--- a/services/java/com/android/server/firewall/CategoryFilter.java
+++ b/services/java/com/android/server/firewall/CategoryFilter.java
@@ -34,7 +34,7 @@
     }
 
     @Override
-    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
             int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
         Set<String> categories = intent.getCategories();
         if (categories == null) {
diff --git a/services/java/com/android/server/firewall/Filter.java b/services/java/com/android/server/firewall/Filter.java
index 7639466..0e783e8 100644
--- a/services/java/com/android/server/firewall/Filter.java
+++ b/services/java/com/android/server/firewall/Filter.java
@@ -26,17 +26,14 @@
      * @param ifw The IntentFirewall instance
      * @param intent The intent being started/bound/broadcast
      * @param callerApp An ApplicationInfo of an application in the caller's process. This may not
- *                  be the specific app that is actually sending the intent. This also may be
- *                  null, if the caller is the system process, or an unrecognized process (e.g.
- *                  am start)
-     * @param callerPackage The package name of the component sending the intent. This value is
-*                      provided by the caller and might be forged/faked.
+     *                  be the specific app that is actually sending the intent. This also may be
+     *                  null, if the caller is the system process, or an unrecognized process (e.g.
+     *                  am start)
      * @param callerUid
      * @param callerPid
      * @param resolvedType The resolved mime type of the intent
      * @param resolvedApp The application that contains the resolved component that the intent is
      */
     boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ApplicationInfo resolvedApp);
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp);
 }
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
index 062183b..08e6b45 100644
--- a/services/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -16,18 +16,21 @@
 
 package com.android.server.firewall;
 
+import android.app.AppGlobals;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.os.Environment;
-import android.os.ServiceManager;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.util.Xml;
 import com.android.internal.util.XmlUtils;
+import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
-import com.android.server.pm.PackageManagerService;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -46,11 +49,18 @@
     private static final File RULES_FILE =
             new File(Environment.getSystemSecureDirectory(), "ifw/ifw.xml");
 
+    private static final int LOG_PACKAGES_MAX_LENGTH = 150;
+    private static final int LOG_PACKAGES_SUFFICIENT_LENGTH = 125;
+
     private static final String TAG_RULES = "rules";
     private static final String TAG_ACTIVITY = "activity";
     private static final String TAG_SERVICE = "service";
     private static final String TAG_BROADCAST = "broadcast";
 
+    private static final int TYPE_ACTIVITY = 0;
+    private static final int TYPE_SERVICE = 1;
+    private static final int TYPE_BROADCAST = 2;
+
     private static final HashMap<String, FilterFactory> factoryMap;
 
     private final AMSInterface mAms;
@@ -76,7 +86,6 @@
                 StringFilter.HOST,
                 StringFilter.MIME_TYPE,
                 StringFilter.PATH,
-                StringFilter.SENDER_PACKAGE,
                 StringFilter.SSP,
 
                 CategoryFilter.FACTORY,
@@ -98,17 +107,16 @@
         readRules(getRulesFile());
     }
 
-    public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ActivityInfo resolvedActivity) {
+    public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp, int callerUid,
+            int callerPid, String resolvedType, ActivityInfo resolvedActivity) {
         List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0);
         boolean log = false;
         boolean block = false;
 
         for (int i=0; i< matchingRules.size(); i++) {
             Rule rule = matchingRules.get(i);
-            if (rule.matches(this, intent, callerApp, callerPackage, callerUid, callerPid,
-                    resolvedType, resolvedActivity.applicationInfo)) {
+            if (rule.matches(this, intent, callerApp, callerUid, callerPid, resolvedType,
+                    resolvedActivity.applicationInfo)) {
                 block |= rule.getBlock();
                 log |= rule.getLog();
 
@@ -121,12 +129,81 @@
         }
 
         if (log) {
-            // TODO: log info about intent to event log
+            logIntent(TYPE_ACTIVITY, intent, callerUid, resolvedType);
         }
 
         return !block;
     }
 
+    private static void logIntent(int intentType, Intent intent, int callerUid,
+            String resolvedType) {
+        // The component shouldn't be null, but let's double check just to be safe
+        ComponentName cn = intent.getComponent();
+        String shortComponent = null;
+        if (cn != null) {
+            shortComponent = cn.flattenToShortString();
+        }
+
+        String callerPackages = null;
+        int callerPackageCount = 0;
+        IPackageManager pm = AppGlobals.getPackageManager();
+        if (pm != null) {
+            try {
+                String[] callerPackagesArray = pm.getPackagesForUid(callerUid);
+                if (callerPackagesArray != null) {
+                    callerPackageCount = callerPackagesArray.length;
+                    callerPackages = joinPackages(callerPackagesArray);
+                }
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Remote exception while retrieving packages", ex);
+            }
+        }
+
+        EventLogTags.writeIfwIntentMatched(intentType, shortComponent, callerUid,
+                callerPackageCount, callerPackages, intent.getAction(), resolvedType,
+                intent.getDataString(), intent.getFlags());
+    }
+
+    /**
+     * Joins a list of package names such that the resulting string is no more than
+     * LOG_PACKAGES_MAX_LENGTH.
+     *
+     * Only full package names will be added to the result, unless every package is longer than the
+     * limit, in which case one of the packages will be truncated and added. In this case, an
+     * additional '-' character will be added to the end of the string, to denote the truncation.
+     *
+     * If it encounters a package that won't fit in the remaining space, it will continue on to the
+     * next package, unless the total length of the built string so far is greater than
+     * LOG_PACKAGES_SUFFICIENT_LENGTH, in which case it will stop and return what it has.
+     */
+    private static String joinPackages(String[] packages) {
+        boolean first = true;
+        StringBuilder sb = new StringBuilder();
+        for (int i=0; i<packages.length; i++) {
+            String pkg = packages[i];
+
+            // + 1 length for the comma. This logic technically isn't correct for the first entry,
+            // but it's not critical.
+            if (sb.length() + pkg.length() + 1 < LOG_PACKAGES_MAX_LENGTH) {
+                if (!first) {
+                    sb.append(',');
+                } else {
+                    first = false;
+                }
+                sb.append(pkg);
+            } else if (sb.length() >= LOG_PACKAGES_SUFFICIENT_LENGTH) {
+                return sb.toString();
+            }
+        }
+        if (sb.length() == 0 && packages.length > 0) {
+            String pkg = packages[0];
+            // truncating from the end - the last part of the package name is more likely to be
+            // interesting/unique
+            return pkg.substring(pkg.length() - LOG_PACKAGES_MAX_LENGTH + 1) + '-';
+        }
+        return null;
+    }
+
     public static File getRulesFile() {
         return RULES_FILE;
     }
@@ -313,7 +390,12 @@
     }
 
     boolean signaturesMatch(int uid1, int uid2) {
-        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
-        return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+        try {
+            IPackageManager pm = AppGlobals.getPackageManager();
+            return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "Remote exception while checking signatures", ex);
+            return false;
+        }
     }
 }
diff --git a/services/java/com/android/server/firewall/NotFilter.java b/services/java/com/android/server/firewall/NotFilter.java
index 2ff108a1..f0fc337 100644
--- a/services/java/com/android/server/firewall/NotFilter.java
+++ b/services/java/com/android/server/firewall/NotFilter.java
@@ -33,10 +33,9 @@
 
     @Override
     public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ApplicationInfo resolvedApp) {
-        return !mChild.matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
-                resolvedType, resolvedApp);
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+        return !mChild.matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+                resolvedApp);
     }
 
     public static final FilterFactory FACTORY = new FilterFactory("not") {
diff --git a/services/java/com/android/server/firewall/OrFilter.java b/services/java/com/android/server/firewall/OrFilter.java
index 1ed1c85..72db31e 100644
--- a/services/java/com/android/server/firewall/OrFilter.java
+++ b/services/java/com/android/server/firewall/OrFilter.java
@@ -26,11 +26,10 @@
 class OrFilter extends FilterList {
     @Override
     public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ApplicationInfo resolvedApp) {
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
         for (int i=0; i<children.size(); i++) {
-            if (children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
-                    resolvedType, resolvedApp)) {
+            if (children.get(i).matches(ifw, intent, callerApp, callerUid, callerPid, resolvedType,
+                    resolvedApp)) {
                 return true;
             }
         }
diff --git a/services/java/com/android/server/firewall/PortFilter.java b/services/java/com/android/server/firewall/PortFilter.java
index 2b2a198..fe7e085 100644
--- a/services/java/com/android/server/firewall/PortFilter.java
+++ b/services/java/com/android/server/firewall/PortFilter.java
@@ -42,8 +42,7 @@
 
     @Override
     public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ApplicationInfo resolvedApp) {
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
         int port = -1;
         Uri uri = intent.getData();
         if (uri != null) {
diff --git a/services/java/com/android/server/firewall/SenderFilter.java b/services/java/com/android/server/firewall/SenderFilter.java
index 0b790bd..58bdd73 100644
--- a/services/java/com/android/server/firewall/SenderFilter.java
+++ b/services/java/com/android/server/firewall/SenderFilter.java
@@ -68,8 +68,7 @@
     private static final Filter SIGNATURE = new Filter() {
         @Override
         public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-                String callerPackage, int callerUid, int callerPid, String resolvedType,
-                ApplicationInfo resolvedApp) {
+                int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
             if (callerApp == null) {
                 return false;
             }
@@ -80,8 +79,7 @@
     private static final Filter SYSTEM = new Filter() {
         @Override
         public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-                String callerPackage, int callerUid, int callerPid, String resolvedType,
-                ApplicationInfo resolvedApp) {
+                int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
             if (callerApp == null) {
                 // if callerApp is null, the caller is the system process
                 return false;
@@ -93,8 +91,7 @@
     private static final Filter SYSTEM_OR_SIGNATURE = new Filter() {
         @Override
         public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-                String callerPackage, int callerUid, int callerPid, String resolvedType,
-                ApplicationInfo resolvedApp) {
+                int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
             return isSystemApp(callerApp, callerUid, callerPid) ||
                     ifw.signaturesMatch(callerUid, resolvedApp.uid);
         }
@@ -103,8 +100,7 @@
     private static final Filter USER_ID = new Filter() {
         @Override
         public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-                String callerPackage, int callerUid, int callerPid, String resolvedType,
-                ApplicationInfo resolvedApp) {
+                int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
             // This checks whether the caller is either the system process, or has the same user id
             // I.e. the same app, or an app that uses the same shared user id.
             // This is the same set of applications that would be able to access the component if
diff --git a/services/java/com/android/server/firewall/SenderPermissionFilter.java b/services/java/com/android/server/firewall/SenderPermissionFilter.java
index 02d8b15..310da20 100644
--- a/services/java/com/android/server/firewall/SenderPermissionFilter.java
+++ b/services/java/com/android/server/firewall/SenderPermissionFilter.java
@@ -34,8 +34,7 @@
 
     @Override
     public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
-            String callerPackage, int callerUid, int callerPid, String resolvedType,
-            ApplicationInfo resolvedApp) {
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
         // We assume the component is exported here. If the component is not exported, then
         // ActivityManager would only resolve to this component for callers from the same uid.
         // In this case, it doesn't matter whether the component is exported or not.
diff --git a/services/java/com/android/server/firewall/StringFilter.java b/services/java/com/android/server/firewall/StringFilter.java
index de5a69f..ed5d3f3 100644
--- a/services/java/com/android/server/firewall/StringFilter.java
+++ b/services/java/com/android/server/firewall/StringFilter.java
@@ -119,10 +119,9 @@
     protected abstract boolean matchesValue(String value);
 
     @Override
-    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
             int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
-        String value = mValueProvider.getValue(intent, callerApp, callerPackage, resolvedType,
-                resolvedApp);
+        String value = mValueProvider.getValue(intent, callerApp, resolvedType, resolvedApp);
         return matchesValue(value);
     }
 
@@ -137,7 +136,7 @@
         }
 
         public abstract String getValue(Intent intent, ApplicationInfo callerApp,
-                String callerPackage, String resolvedType, ApplicationInfo resolvedApp);
+                String resolvedType, ApplicationInfo resolvedApp);
     }
 
     private static class EqualsFilter extends StringFilter {
@@ -231,8 +230,8 @@
 
     public static final ValueProvider COMPONENT = new ValueProvider("component") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             ComponentName cn = intent.getComponent();
             if (cn != null) {
                 return cn.flattenToString();
@@ -243,8 +242,8 @@
 
     public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             ComponentName cn = intent.getComponent();
             if (cn != null) {
                 return cn.getClassName();
@@ -255,8 +254,8 @@
 
     public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             ComponentName cn = intent.getComponent();
             if (cn != null) {
                 return cn.getPackageName();
@@ -265,28 +264,18 @@
         }
     };
 
-    public static final ValueProvider SENDER_PACKAGE = new ValueProvider("sender-package") {
-        @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
-            // TODO: We can't trust this value, so maybe should check all packages in the caller process?
-            return callerPackage;
-        }
-    };
-
-
     public static final FilterFactory ACTION = new ValueProvider("action") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             return intent.getAction();
         }
     };
 
     public static final ValueProvider DATA = new ValueProvider("data") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             Uri data = intent.getData();
             if (data != null) {
                 return data.toString();
@@ -297,16 +286,16 @@
 
     public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             return resolvedType;
         }
     };
 
     public static final ValueProvider SCHEME = new ValueProvider("scheme") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             Uri data = intent.getData();
             if (data != null) {
                 return data.getScheme();
@@ -317,8 +306,8 @@
 
     public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             Uri data = intent.getData();
             if (data != null) {
                 return data.getSchemeSpecificPart();
@@ -329,8 +318,8 @@
 
     public static final ValueProvider HOST = new ValueProvider("host") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             Uri data = intent.getData();
             if (data != null) {
                 return data.getHost();
@@ -341,8 +330,8 @@
 
     public static final ValueProvider PATH = new ValueProvider("path") {
         @Override
-        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
-                String resolvedType, ApplicationInfo resolvedApp) {
+        public String getValue(Intent intent, ApplicationInfo callerApp, String resolvedType,
+                ApplicationInfo resolvedApp) {
             Uri data = intent.getData();
             if (data != null) {
                 return data.getPath();
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index ca7bba2..3d7dd63 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -8647,14 +8647,16 @@
                 mSettings.writeLPr();
             }
         }
-        // A user ID was deleted here. Go through all users and remove it from
-        // KeyStore.
-        final int appId = outInfo.removedAppId;
-        if (appId != -1) {
-            final KeyStore keyStore = KeyStore.getInstance();
-            if (keyStore != null) {
-                for (final int userId : sUserManager.getUserIds()) {
-                    keyStore.clearUid(UserHandle.getUid(userId, appId));
+        if (outInfo != null) {
+            // A user ID was deleted here. Go through all users and remove it
+            // from KeyStore.
+            final int appId = outInfo.removedAppId;
+            if (appId != -1) {
+                final KeyStore keyStore = KeyStore.getInstance();
+                if (keyStore != null) {
+                    for (final int userId : sUserManager.getUserIds()) {
+                        keyStore.clearUid(UserHandle.getUid(userId, appId));
+                    }
                 }
             }
         }
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 3ed9cef..674955c 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -274,6 +274,33 @@
     }
 
     /**
+     * Make a SignalStrength object from the given parcel as passed up by
+     * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
+     * so the default is a don't care.
+     *
+     * @hide
+     */
+    public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
+        if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
+
+        SignalStrength ss = new SignalStrength();
+        ss.mGsmSignalStrength = in.readInt();
+        ss.mGsmBitErrorRate = in.readInt();
+        ss.mCdmaDbm = in.readInt();
+        ss.mCdmaEcio = in.readInt();
+        ss.mEvdoDbm = in.readInt();
+        ss.mEvdoEcio = in.readInt();
+        ss.mEvdoSnr = in.readInt();
+        ss.mLteSignalStrength = in.readInt();
+        ss.mLteRsrp = in.readInt();
+        ss.mLteRsrq = in.readInt();
+        ss.mLteRssnr = in.readInt();
+        ss.mLteCqi = in.readInt();
+
+        return ss;
+    }
+
+    /**
      * {@link Parcelable#writeToParcel}
      */
     public void writeToParcel(Parcel out, int flags) {
diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
index 7a6e52e..dc4f9c8 100644
--- a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
@@ -22,6 +22,6 @@
 public class InputMethodManager_Accessor {
 
     public static void resetInstance() {
-        InputMethodManager.mInstance = null;
+        InputMethodManager.sInstance = null;
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
index f056040..7c98847 100644
--- a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
@@ -35,28 +35,15 @@
     // ---- Overridden methods ----
 
     @LayoutlibDelegate
-    /*package*/ static InputMethodManager getInstance(Looper mainLooper) {
-        synchronized (InputMethodManager.mInstanceSync) {
-            if (InputMethodManager.mInstance != null) {
-                return InputMethodManager.mInstance;
+    /*package*/ static InputMethodManager getInstance() {
+        synchronized (InputMethodManager.class) {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm == null) {
+                imm = new InputMethodManager(
+                        new BridgeIInputMethodManager(), Looper.getMainLooper());
+                InputMethodManager.sInstance = imm;
             }
-
-            InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
-                    mainLooper);
+            return imm;
         }
-        return InputMethodManager.mInstance;
-    }
-
-    @LayoutlibDelegate
-    /*package*/ static InputMethodManager getInstance(Context context) {
-        synchronized (InputMethodManager.mInstanceSync) {
-            if (InputMethodManager.mInstance != null) {
-                return InputMethodManager.mInstance;
-            }
-
-            InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
-                    Looper.myLooper());
-        }
-        return InputMethodManager.mInstance;
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index f109e39..cbefd3d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -232,7 +232,7 @@
         sCurrentContext = mContext;
 
         // create an InputMethodManager
-        InputMethodManager.getInstance(Looper.myLooper());
+        InputMethodManager.getInstance();
 
         LayoutLog currentLog = mParams.getLog();
         Bridge.setLog(currentLog);