Merge "Misc layoutlib fix in preparation of the access change in framework code."
diff --git a/Android.mk b/Android.mk
index 5cb4d30..b61936f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -378,6 +378,7 @@
     -since ./frameworks/base/api/11.xml 11 \
     -since ./frameworks/base/api/12.xml 12 \
     -since ./frameworks/base/api/13.xml 13 \
+    -since ./frameworks/base/api/current.txt ICS \
 		-werror -hide 113 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
diff --git a/api/current.txt b/api/current.txt
index 91cf3fa..2f78f63 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -90,10 +90,8 @@
     field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
-    field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
-    field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -117,7 +115,6 @@
     field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
-    field public static final java.lang.String VPN = "android.permission.VPN";
     field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
     field public static final java.lang.String WRITE_APN_SETTINGS = "android.permission.WRITE_APN_SETTINGS";
     field public static final java.lang.String WRITE_CALENDAR = "android.permission.WRITE_CALENDAR";
@@ -2331,7 +2328,7 @@
     method public abstract void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction);
   }
 
-  public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+  public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
     ctor public Activity();
     method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void closeContextMenu();
@@ -2436,6 +2433,7 @@
     method protected void onTitleChanged(java.lang.CharSequence, int);
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
+    method public void onTrimMemory(int);
     method public void onUserInteraction();
     method protected void onUserLeaveHint();
     method public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -2731,12 +2729,25 @@
     ctor public AliasActivity();
   }
 
-  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks {
+  public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
     ctor public Application();
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public void onCreate();
     method public void onLowMemory();
     method public void onTerminate();
+    method public void onTrimMemory(int);
+    method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
+    method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
+  }
+
+  public static abstract interface Application.ActivityLifecycleCallbacks {
+    method public abstract void onActivityCreated(android.app.Activity, android.os.Bundle);
+    method public abstract void onActivityDestroyed(android.app.Activity);
+    method public abstract void onActivityPaused(android.app.Activity);
+    method public abstract void onActivityResumed(android.app.Activity);
+    method public abstract void onActivitySaveInstanceState(android.app.Activity, android.os.Bundle);
+    method public abstract void onActivityStarted(android.app.Activity);
+    method public abstract void onActivityStopped(android.app.Activity);
   }
 
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
@@ -2955,7 +2966,7 @@
     method public void setSelectedGroup(int);
   }
 
-  public class Fragment implements android.content.ComponentCallbacks android.view.View.OnCreateContextMenuListener {
+  public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
     ctor public Fragment();
     method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final boolean equals(java.lang.Object);
@@ -3009,6 +3020,7 @@
     method public void onSaveInstanceState(android.os.Bundle);
     method public void onStart();
     method public void onStop();
+    method public void onTrimMemory(int);
     method public void onViewCreated(android.view.View, android.os.Bundle);
     method public void registerForContextMenu(android.view.View);
     method public void setArguments(android.os.Bundle);
@@ -3060,6 +3072,7 @@
     method public abstract android.app.FragmentManager.BackStackEntry getBackStackEntryAt(int);
     method public abstract int getBackStackEntryCount();
     method public abstract android.app.Fragment getFragment(android.os.Bundle, java.lang.String);
+    method public void invalidateOptionsMenu();
     method public abstract void popBackStack();
     method public abstract void popBackStack(java.lang.String, int);
     method public abstract void popBackStack(int, int);
@@ -3545,7 +3558,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class Service extends android.content.ContextWrapper implements android.content.ComponentCallbacks {
+  public abstract class Service extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
     ctor public Service();
     method protected void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final android.app.Application getApplication();
@@ -3558,6 +3571,7 @@
     method public deprecated void onStart(android.content.Intent, int);
     method public int onStartCommand(android.content.Intent, int, int);
     method public void onTaskRemoved(android.content.Intent);
+    method public void onTrimMemory(int);
     method public boolean onUnbind(android.content.Intent);
     method public final void startForeground(int, android.app.Notification);
     method public final void stopForeground(boolean);
@@ -3631,6 +3645,7 @@
   public class WallpaperManager {
     method public void clear() throws java.io.IOException;
     method public void clearWallpaperOffsets(android.os.IBinder);
+    method public void forgetLoadedWallpaper();
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
     method public android.graphics.drawable.Drawable getDrawable();
@@ -4442,6 +4457,14 @@
     method public abstract void onLowMemory();
   }
 
+  public abstract interface ComponentCallbacks2 implements android.content.ComponentCallbacks {
+    method public abstract void onTrimMemory(int);
+    field public static final int TRIM_MEMORY_BACKGROUND = 40; // 0x28
+    field public static final int TRIM_MEMORY_COMPLETE = 80; // 0x50
+    field public static final int TRIM_MEMORY_MODERATE = 60; // 0x3c
+    field public static final int TRIM_MEMORY_UI_HIDDEN = 20; // 0x14
+  }
+
   public final class ComponentName implements java.lang.Cloneable java.lang.Comparable android.os.Parcelable {
     ctor public ComponentName(java.lang.String, java.lang.String);
     ctor public ComponentName(android.content.Context, java.lang.String);
@@ -4463,7 +4486,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class ContentProvider implements android.content.ComponentCallbacks {
+  public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
     ctor public ContentProvider();
     method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>) throws android.content.OperationApplicationException;
     method public void attachInfo(android.content.Context, android.content.pm.ProviderInfo);
@@ -4481,6 +4504,7 @@
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public abstract boolean onCreate();
     method public void onLowMemory();
+    method public void onTrimMemory(int);
     method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method protected final android.os.ParcelFileDescriptor openFileHelper(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -4734,6 +4758,7 @@
     method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
     method public abstract deprecated android.graphics.drawable.Drawable peekWallpaper();
+    method public void registerComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
     method public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
     method public abstract void removeStickyBroadcast(android.content.Intent);
@@ -4754,15 +4779,21 @@
     method public abstract android.content.ComponentName startService(android.content.Intent);
     method public abstract boolean stopService(android.content.Intent);
     method public abstract void unbindService(android.content.ServiceConnection);
+    method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
     method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
     field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
     field public static final java.lang.String ACCOUNT_SERVICE = "account";
     field public static final java.lang.String ACTIVITY_SERVICE = "activity";
     field public static final java.lang.String ALARM_SERVICE = "alarm";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
+    field public static final int BIND_ABOVE_CLIENT = 8; // 0x8
+    field public static final int BIND_ADJUST_WITH_ACTIVITY = 64; // 0x40
+    field public static final int BIND_ALLOW_OOM_MANAGEMENT = 16; // 0x10
     field public static final int BIND_AUTO_CREATE = 1; // 0x1
     field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
+    field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
+    field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
@@ -11307,6 +11338,7 @@
     method public static long getUidUdpRxPackets(int);
     method public static long getUidUdpTxBytes(int);
     method public static long getUidUdpTxPackets(int);
+    method public static void incrementOperationCount(int, int);
     method public static void setThreadStatsTag(int);
     method public static deprecated void setThreadStatsTag(java.lang.String);
     method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
@@ -11448,26 +11480,6 @@
     method public abstract java.lang.String sanitize(java.lang.String);
   }
 
-  public class VpnBuilder {
-    ctor public VpnBuilder();
-    method public android.net.VpnBuilder addAddress(java.lang.String, int);
-    method public android.net.VpnBuilder addAddress(java.net.InetAddress, int);
-    method public android.net.VpnBuilder addDnsServer(java.lang.String);
-    method public android.net.VpnBuilder addDnsServer(java.net.InetAddress);
-    method public android.net.VpnBuilder addRoute(java.lang.String, int);
-    method public android.net.VpnBuilder addRoute(java.net.InetAddress, int);
-    method public android.net.VpnBuilder addSearchDomain(java.lang.String);
-    method public android.os.ParcelFileDescriptor establish();
-    method public static android.content.Intent prepare(android.content.Context);
-    method public static boolean protect(int);
-    method public static boolean protect(java.net.Socket);
-    method public static boolean protect(java.net.DatagramSocket);
-    method public android.net.VpnBuilder setConfigureIntent(android.app.PendingIntent);
-    method public android.net.VpnBuilder setMtu(int);
-    method public android.net.VpnBuilder setSession(java.lang.String);
-    field public static final java.lang.String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED";
-  }
-
 }
 
 package android.net.http {
@@ -18000,18 +18012,6 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.textservice.SpellCheckerService";
   }
 
-  public class SpellCheckerSession {
-    method public void close();
-    method public android.view.textservice.SpellCheckerInfo getSpellChecker();
-    method public void getSuggestions(android.view.textservice.TextInfo, int);
-    method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
-    method public boolean isSessionDisconnected();
-  }
-
-  public static abstract interface SpellCheckerSession.SpellCheckerSessionListener {
-    method public abstract void onGetSuggestions(android.view.textservice.SuggestionsInfo[]);
-  }
-
 }
 
 package android.service.wallpaper {
@@ -22666,8 +22666,11 @@
     field protected static final int[] SELECTED_STATE_SET;
     field protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET;
     field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
-    field public static final int STATUS_BAR_HIDDEN = 1; // 0x1
-    field public static final int STATUS_BAR_VISIBLE = 0; // 0x0
+    field public static final deprecated int STATUS_BAR_HIDDEN = 1; // 0x1
+    field public static final deprecated int STATUS_BAR_VISIBLE = 0; // 0x0
+    field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
+    field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
+    field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
     field public static android.util.Property TRANSLATION_X;
     field public static android.util.Property TRANSLATION_Y;
     field protected static final java.lang.String VIEW_LOG_TAG = "View";
@@ -24108,6 +24111,19 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
+  public class SpellCheckerSession {
+    method public void close();
+    method public android.view.textservice.SpellCheckerInfo getSpellChecker();
+    method public void getSuggestions(android.view.textservice.TextInfo, int);
+    method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
+    method public boolean isSessionDisconnected();
+    field public static final java.lang.String SERVICE_META_DATA = "android.view.textservice.scs";
+  }
+
+  public static abstract interface SpellCheckerSession.SpellCheckerSessionListener {
+    method public abstract void onGetSuggestions(android.view.textservice.SuggestionsInfo[]);
+  }
+
   public final class SuggestionsInfo implements android.os.Parcelable {
     ctor public SuggestionsInfo(int, java.lang.String[]);
     ctor public SuggestionsInfo(int, java.lang.String[], int, int);
@@ -24138,7 +24154,7 @@
   }
 
   public final class TextServicesManager {
-    method public android.service.textservice.SpellCheckerSession newSpellCheckerSession(java.util.Locale, android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
+    method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
   }
 
 }
@@ -27522,6 +27538,7 @@
 
   public class DataInputStream extends java.io.FilterInputStream implements java.io.DataInput {
     ctor public DataInputStream(java.io.InputStream);
+    method public final int read(byte[]) throws java.io.IOException;
     method public final int read(byte[], int, int) throws java.io.IOException;
     method public final boolean readBoolean() throws java.io.IOException;
     method public final byte readByte() throws java.io.IOException;
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index e6c2a0f..dd9fece 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -445,7 +446,10 @@
             // First, clear out the old listeners
             ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
             if (oldListeners != null && oldListeners.size() > 0) {
-                for (AnimatorListener listener : oldListeners) {
+                final ArrayList<AnimatorListener> clonedListeners = new
+                        ArrayList<AnimatorListener>(oldListeners);
+
+                for (AnimatorListener listener : clonedListeners) {
                     if (listener instanceof DependencyListener ||
                             listener instanceof AnimatorSetListener) {
                         node.animation.removeListener(listener);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fb1570e..d5b669e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -19,7 +19,7 @@
 import com.android.internal.app.ActionBarImpl;
 import com.android.internal.policy.PolicyManager;
 
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -626,7 +626,7 @@
 public class Activity extends ContextThemeWrapper
         implements LayoutInflater.Factory2,
         Window.Callback, KeyEvent.Callback,
-        OnCreateContextMenuListener, ComponentCallbacks {
+        OnCreateContextMenuListener, ComponentCallbacks2 {
     private static final String TAG = "Activity";
 
     /** Standard activity result: operation canceled. */
@@ -859,6 +859,7 @@
                     ? mLastNonConfigurationInstances.fragments : null);
         }
         mFragments.dispatchCreate();
+        getApplication().dispatchActivityCreated(this, savedInstanceState);
         mCalled = true;
     }
 
@@ -1001,6 +1002,8 @@
             }
             mCheckedForLoaderManager = true;
         }
+
+        getApplication().dispatchActivityStarted(this);
     }
 
     /**
@@ -1048,6 +1051,7 @@
      * @see #onPause
      */
     protected void onResume() {
+        getApplication().dispatchActivityResumed(this);
         mCalled = true;
     }
 
@@ -1158,6 +1162,7 @@
         if (p != null) {
             outState.putParcelable(FRAGMENTS_TAG, p);
         }
+        getApplication().dispatchActivitySaveInstanceState(this, outState);
     }
 
     /**
@@ -1234,6 +1239,7 @@
      * @see #onStop
      */
     protected void onPause() {
+        getApplication().dispatchActivityPaused(this);
         mCalled = true;
     }
 
@@ -1320,6 +1326,7 @@
      */
     protected void onStop() {
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
+        getApplication().dispatchActivityStopped(this);
         mCalled = true;
     }
 
@@ -1382,6 +1389,8 @@
         if (mSearchManager != null) {
             mSearchManager.stopSearch();
         }
+
+        getApplication().dispatchActivityDestroyed(this);
     }
 
     /**
@@ -1580,12 +1589,17 @@
         nci.loaders = mAllLoaderManagers;
         return nci;
     }
-    
+
     public void onLowMemory() {
         mCalled = true;
         mFragments.dispatchLowMemory();
     }
-    
+
+    public void onTrimMemory(int level) {
+        mCalled = true;
+        mFragments.dispatchTrimMemory(level);
+    }
+
     /**
      * Return the FragmentManager for interacting with fragments associated
      * with this activity.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e93f88..8931675 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -18,7 +18,7 @@
 
 import android.app.backup.BackupAgent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.Context;
@@ -3258,10 +3258,10 @@
         }
     }
 
-    ArrayList<ComponentCallbacks> collectComponentCallbacksLocked(
+    ArrayList<ComponentCallbacks2> collectComponentCallbacksLocked(
             boolean allActivities, Configuration newConfig) {
-        ArrayList<ComponentCallbacks> callbacks
-                = new ArrayList<ComponentCallbacks>();
+        ArrayList<ComponentCallbacks2> callbacks
+                = new ArrayList<ComponentCallbacks2>();
 
         if (mActivities.size() > 0) {
             Iterator<ActivityClientRecord> it = mActivities.values().iterator();
@@ -3311,10 +3311,10 @@
         return callbacks;
     }
 
-    private void performConfigurationChanged(
-            ComponentCallbacks cb, Configuration config) {
+    private final void performConfigurationChanged(
+            ComponentCallbacks2 cb, Configuration config) {
         // Only for Activity objects, check that they actually call up to their
-        // superclass implementation.  ComponentCallbacks is an interface, so
+        // superclass implementation.  ComponentCallbacks2 is an interface, so
         // we check the runtime type and act accordingly.
         Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
         if (activity != null) {
@@ -3418,7 +3418,7 @@
     
     final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
 
-        ArrayList<ComponentCallbacks> callbacks = null;
+        ArrayList<ComponentCallbacks2> callbacks = null;
 
         synchronized (mPackages) {
             if (mPendingConfiguration != null) {
@@ -3558,7 +3558,7 @@
     }
         
     final void handleLowMemory() {
-        ArrayList<ComponentCallbacks> callbacks;
+        ArrayList<ComponentCallbacks2> callbacks;
 
         synchronized (mPackages) {
             callbacks = collectComponentCallbacksLocked(true, null);
@@ -3583,6 +3583,16 @@
 
     final void handleTrimMemory(int level) {
         WindowManagerImpl.getDefault().trimMemory(level);
+        ArrayList<ComponentCallbacks2> callbacks;
+
+        synchronized (mPackages) {
+            callbacks = collectComponentCallbacksLocked(true, null);
+        }
+
+        final int N = callbacks.size();
+        for (int i=0; i<N; i++) {
+            callbacks.get(i).onTrimMemory(level);
+        }
     }
 
     private void handleBindApplication(AppBindData data) {
@@ -4128,7 +4138,7 @@
             }
         }
         
-        ViewRootImpl.addConfigCallback(new ComponentCallbacks() {
+        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
             public void onConfigurationChanged(Configuration newConfig) {
                 synchronized (mPackages) {
                     // We need to apply this change to the resources
@@ -4148,6 +4158,8 @@
             }
             public void onLowMemory() {
             }
+            public void onTrimMemory(int level) {
+            }
         });
     }
 
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 10cc9f8..dd9ea26 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -16,10 +16,14 @@
 
 package android.app;
 
+import java.util.ArrayList;
+
 import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Configuration;
+import android.os.Bundle;
 
 /**
  * Base class for those who need to maintain global application state. You can
@@ -36,10 +40,25 @@
  * {@link android.content.Context#getApplicationContext() Context.getApplicationContext()}
  * when first constructing the singleton.</p>
  */
-public class Application extends ContextWrapper implements ComponentCallbacks {
+public class Application extends ContextWrapper implements ComponentCallbacks2 {
+    private ArrayList<ComponentCallbacks> mComponentCallbacks =
+            new ArrayList<ComponentCallbacks>();
+    private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+            new ArrayList<ActivityLifecycleCallbacks>();
+
     /** @hide */
     public LoadedApk mLoadedApk;
-    
+
+    public interface ActivityLifecycleCallbacks {
+        void onActivityCreated(Activity activity, Bundle savedInstanceState);
+        void onActivityStarted(Activity activity);
+        void onActivityResumed(Activity activity);
+        void onActivityPaused(Activity activity);
+        void onActivityStopped(Activity activity);
+        void onActivitySaveInstanceState(Activity activity, Bundle outState);
+        void onActivityDestroyed(Activity activity);
+    }
+
     public Application() {
         super(null);
     }
@@ -63,11 +82,59 @@
      */
     public void onTerminate() {
     }
-    
+
     public void onConfigurationChanged(Configuration newConfig) {
+        Object[] callbacks = collectComponentCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ComponentCallbacks)callbacks[i]).onConfigurationChanged(newConfig);
+            }
+        }
     }
-    
+
     public void onLowMemory() {
+        Object[] callbacks = collectComponentCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ComponentCallbacks)callbacks[i]).onLowMemory();
+            }
+        }
+    }
+
+    public void onTrimMemory(int level) {
+        Object[] callbacks = collectComponentCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                Object c = callbacks[i];
+                if (c instanceof ComponentCallbacks2) {
+                    ((ComponentCallbacks2)c).onTrimMemory(level);
+                }
+            }
+        }
+    }
+
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        synchronized (mComponentCallbacks) {
+            mComponentCallbacks.add(callback);
+        }
+    }
+
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        synchronized (mComponentCallbacks) {
+            mComponentCallbacks.remove(callback);
+        }
+    }
+
+    public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.add(callback);
+        }
+    }
+
+    public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.remove(callback);
+        }
     }
     
     // ------------------ Internal API ------------------
@@ -79,4 +146,89 @@
         attachBaseContext(context);
         mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
     }
+
+    /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityCreated(activity,
+                        savedInstanceState);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityStarted(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityStarted(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityResumed(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityResumed(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityPaused(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityPaused(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityStopped(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityStopped(activity);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivitySaveInstanceState(Activity activity, Bundle outState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivitySaveInstanceState(activity,
+                        outState);
+            }
+        }
+    }
+
+    /* package */ void dispatchActivityDestroyed(Activity activity) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i=0; i<callbacks.length; i++) {
+                ((ActivityLifecycleCallbacks)callbacks[i]).onActivityDestroyed(activity);
+            }
+        }
+    }
+
+    private Object[] collectComponentCallbacks() {
+        Object[] callbacks = null;
+        synchronized (mComponentCallbacks) {
+            if (mComponentCallbacks.size() > 0) {
+                callbacks = mComponentCallbacks.toArray();
+            }
+        }
+        return callbacks;
+    }
+
+    private Object[] collectActivityLifecycleCallbacks() {
+        Object[] callbacks = null;
+        synchronized (mActivityLifecycleCallbacks) {
+            if (mActivityLifecycleCallbacks.size() > 0) {
+                callbacks = mActivityLifecycleCallbacks.toArray();
+            }
+        }
+        return callbacks;
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a99cec2..b4bdb2f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1109,6 +1109,12 @@
             throw new RuntimeException("Not supported in system context");
         }
         try {
+            IBinder token = getActivityToken();
+            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
+                    && mPackageInfo.getApplicationInfo().targetSdkVersion
+                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                flags |= BIND_WAIVE_PRIORITY;
+            }
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(),
                 service, service.resolveTypeIfNeeded(getContentResolver()),
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index e2746d4..a8621f8 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,7 +17,7 @@
 package android.app;
 
 import android.animation.Animator;
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -30,14 +30,14 @@
 import android.util.DebugUtils;
 import android.util.SparseArray;
 import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.View.OnCreateContextMenuListener;
+import android.view.ViewGroup;
 import android.widget.AdapterView;
 
 import java.io.FileDescriptor;
@@ -332,7 +332,7 @@
  * pressing back will pop it to return the user to whatever previous state
  * the activity UI was in.
  */
-public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
+public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
     private static final HashMap<String, Class<?>> sClassMap =
             new HashMap<String, Class<?>>();
     
@@ -883,8 +883,8 @@
     public void setHasOptionsMenu(boolean hasMenu) {
         if (mHasMenu != hasMenu) {
             mHasMenu = hasMenu;
-            if (isAdded() && !isHidden() && isResumed()) {
-                mActivity.invalidateOptionsMenu();
+            if (isAdded() && !isHidden()) {
+                mFragmentManager.invalidateOptionsMenu();
             }
         }
     }
@@ -1182,6 +1182,10 @@
         mCalled = true;
     }
     
+    public void onTrimMemory(int level) {
+        mCalled = true;
+    }
+
     /**
      * Called when the view previously created by {@link #onCreateView} has
      * been detached from the fragment.  The next time the fragment needs
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 24550c5..712b55f 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -315,6 +315,12 @@
     public static void enableDebugLogging(boolean enabled) {
         FragmentManagerImpl.DEBUG = enabled;
     }
+
+    /**
+     * Invalidate the attached activity's options menu as necessary.
+     * This may end up being deferred until we move to the resumed state.
+     */
+    public void invalidateOptionsMenu() { }
 }
 
 final class FragmentManagerState implements Parcelable {
@@ -1722,6 +1728,17 @@
         }
     }
 
+    public void dispatchTrimMemory(int level) {
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null) {
+                    f.onTrimMemory(level);
+                }
+            }
+        }
+    }
+
     public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         boolean show = false;
         ArrayList<Fragment> newMenus = null;
@@ -1805,7 +1822,16 @@
             }
         }
     }
-    
+
+    @Override
+    public void invalidateOptionsMenu() {
+        if (mActivity != null && mCurState == Fragment.RESUMED) {
+            mActivity.invalidateOptionsMenu();
+        } else {
+            mNeedMenuInvalidate = true;
+        }
+    }
+
     public static int reverseTransit(int transit) {
         int rev = 0;
         switch (transit) {
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 8e2d360..8fa95b4 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -172,6 +172,7 @@
         mSearchView.setOnCloseListener(mOnCloseListener);
         mSearchView.setOnQueryTextListener(mOnQueryChangeListener);
         mSearchView.setOnSuggestionListener(mOnSuggestionSelectionListener);
+        mSearchView.onActionViewExpanded();
 
         mCloseSearch = findViewById(com.android.internal.R.id.closeButton);
         mCloseSearch.setOnClickListener(new View.OnClickListener() {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 4c21d04..ebde6e0 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -16,7 +16,7 @@
 
 package android.app;
 
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.ContextWrapper;
@@ -274,7 +274,7 @@
  * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.java
  *      bind}
  */
-public abstract class Service extends ContextWrapper implements ComponentCallbacks {
+public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
     private static final String TAG = "Service";
 
     public Service() {
@@ -451,7 +451,10 @@
     
     public void onLowMemory() {
     }
-    
+
+    public void onTrimMemory(int level) {
+    }
+
     /**
      * Return the communication channel to the service.  May return null if 
      * clients can not bind to the service.  The returned
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8472b31..ff04757 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -234,14 +234,25 @@
                 } catch (OutOfMemoryError e) {
                     Log.w(TAG, "No memory load current wallpaper", e);
                 }
-                if (mWallpaper == null && returnDefault) {
-                    mDefaultWallpaper = getDefaultWallpaperLocked(context);
-                    return mDefaultWallpaper;
+                if (returnDefault) {
+                    if (mWallpaper == null) {
+                        mDefaultWallpaper = getDefaultWallpaperLocked(context);
+                        return mDefaultWallpaper;
+                    } else {
+                        mDefaultWallpaper = null;
+                    }
                 }
                 return mWallpaper;
             }
         }
-        
+
+        public void forgetLoadedWallpaper() {
+            synchronized (this) {
+                mWallpaper = null;
+                mDefaultWallpaper = null;
+            }
+        }
+
         private Bitmap getCurrentWallpaperLocked(Context context) {
             try {
                 Bundle params = new Bundle();
@@ -402,6 +413,16 @@
     }
 
     /**
+     * Remove all internal references to the last loaded wallpaper.  Useful
+     * for apps that want to reduce memory usage when they only temporarily
+     * need to have the wallpaper.  After calling, the next request for the
+     * wallpaper will require reloading it again from disk.
+     */
+    public void forgetLoadedWallpaper() {
+        sGlobals.forgetLoadedWallpaper();
+    }
+
+    /**
      * If the current wallpaper is a live wallpaper component, return the
      * information about that wallpaper.  Otherwise, if it is a static image,
      * simply return null.
@@ -716,6 +737,7 @@
             c.drawBitmap(bm, null, targetRect, paint);
 
             bm.recycle();
+            c.setBitmap(null);
             return newbm;
         } catch (OutOfMemoryError e) {
             Log.w(TAG, "Can't generate default bitmap", e);
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index d645ac3..c154296 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -185,7 +185,8 @@
      *
      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
      */
-    void acknowledgeFullBackupOrRestore(int token, boolean allow, in String password,
+    void acknowledgeFullBackupOrRestore(int token, boolean allow,
+            in String curPassword, in String encryptionPassword,
             IFullBackupRestoreObserver observer);
 
     /**
diff --git a/core/java/android/content/ComponentCallbacks.java b/core/java/android/content/ComponentCallbacks.java
index 37cc141..dad60b0 100644
--- a/core/java/android/content/ComponentCallbacks.java
+++ b/core/java/android/content/ComponentCallbacks.java
@@ -51,13 +51,4 @@
      * The system will perform a gc for you after returning from this method.
      */
     void onLowMemory();
-
-    /** @hide */
-    static final int TRIM_MEMORY_COMPLETE = 80;
-
-    /** @hide */
-    static final int TRIM_MEMORY_MODERATE = 50;
-
-    /** @hide */
-    static final int TRIM_MEMORY_BACKGROUND = 20;
 }
diff --git a/core/java/android/content/ComponentCallbacks2.java b/core/java/android/content/ComponentCallbacks2.java
new file mode 100644
index 0000000..8b9f97c
--- /dev/null
+++ b/core/java/android/content/ComponentCallbacks2.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Extended {@link ComponentCallbacks} interface with a new callback for
+ * finer-grained memory management.
+ */
+public interface ComponentCallbacks2 extends ComponentCallbacks {
+
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is nearing the end
+     * of the background LRU list, and if more memory isn't found soon it will
+     * be killed.
+     */
+    static final int TRIM_MEMORY_COMPLETE = 80;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process is around the middle
+     * of the background LRU list; freeing memory can help the system keep
+     * other processes running later in the list for better overall performance.
+     */
+    static final int TRIM_MEMORY_MODERATE = 60;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process has gone on to the
+     * LRU list.  This is a good opportunity to clean up resources that can
+     * efficiently and quickly be re-built if the user returns to the app.
+     */
+    static final int TRIM_MEMORY_BACKGROUND = 40;
+    
+    /**
+     * Level for {@link #onTrimMemory(int)}: the process had been showing
+     * a user interface, and is no longer doing so.  Large allocations with
+     * the UI should be released at this point to allow memory to be better
+     * managed.
+     */
+    static final int TRIM_MEMORY_UI_HIDDEN = 20;
+
+    /**
+     * Called when the operating system has determined that it is a good
+     * time for a process to trim unneeded memory from its process.  This will
+     * happen for example when it goes in the background and there is not enough
+     * memory to keep as many background processes running as desired.
+     * 
+     * @param level The context of the trim, giving a hint of the amount of
+     * trimming the application may like to perform.  May be
+     * {@link #TRIM_MEMORY_COMPLETE}, {@link #TRIM_MEMORY_MODERATE},
+     * {@link #TRIM_MEMORY_BACKGROUND}, or {@link #TRIM_MEMORY_UI_HIDDEN}.
+     */
+    void onTrimMemory(int level);
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 1a5c675..8057d4b 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -78,7 +78,7 @@
  * ContentProvider instance, so subclasses don't have to worry about the details of
  * cross-process calls.</p>
  */
-public abstract class ContentProvider implements ComponentCallbacks {
+public abstract class ContentProvider implements ComponentCallbacks2 {
     private static final String TAG = "ContentProvider";
 
     /*
@@ -491,6 +491,9 @@
     public void onLowMemory() {
     }
 
+    public void onTrimMemory(int level) {
+    }
+
     /**
      * Implement this to handle query requests from clients.
      * This method can be called from multiple threads, as described in
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 364821e..1e72092 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -532,7 +532,7 @@
                 try {
                     AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
                     if(fd == null) {
-                        releaseProvider(provider);
+                        // The provider will be released by the finally{} clause
                         return null;
                     }
                     ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
@@ -596,7 +596,7 @@
         try {
             AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
             if (fd == null) {
-                releaseProvider(provider);
+                // The provider will be released by the finally{} clause
                 return null;
             }
             ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2a02446..46712a9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -107,12 +107,17 @@
      * this still provides you with access to the service object while the
      * service is created.
      *
-     * <p>Specifying this flag also tells the system to treat the service
-     * as being as important as your own process -- that is, when deciding
-     * which process should be killed to free memory, the service will only
-     * be considered a candidate as long as the processes of any such bindings
-     * is also a candidate to be killed.  This is to avoid situations where
-     * the service is being continually created and killed due to low memory.
+     * <p>Note that prior to {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH},
+     * not supplying this flag would also impact how important the system
+     * consider's the target service's process to be.  When set, the only way
+     * for it to be raised was by binding from a service in which case it will
+     * only be important when that activity is in the foreground.  Now to
+     * achieve this behavior you must explicitly supply the new flag
+     * {@link #BIND_ADJUST_WITH_ACTIVITY}.  For compatibility, old applications
+     * that don't specify {@link #BIND_AUTO_CREATE} will automatically have
+     * the flags {@link #BIND_WAIVE_PRIORITY} and
+     * {@link #BIND_ADJUST_WITH_ACTIVITY} set for them in order to achieve
+     * the same result.
      */
     public static final int BIND_AUTO_CREATE = 0x0001;
 
@@ -139,14 +144,48 @@
     public static final int BIND_NOT_FOREGROUND = 0x0004;
 
     /**
+     * Flag for {@link #bindService}: indicates that the client application
+     * binding to this service considers the service to be more important than
+     * the app itself.  When set, the platform will try to have the out of
+     * memory kill the app before it kills the service it is bound to, though
+     * this is not guaranteed to be the case.
+     */
+    public static final int BIND_ABOVE_CLIENT = 0x0008;
+
+    /**
      * Flag for {@link #bindService}: allow the process hosting the bound
      * service to go through its normal memory management.  It will be
      * treated more like a running service, allowing the system to
      * (temporarily) expunge the process if low on memory or for some other
-     * whim it may have.
-     * @hide
+     * whim it may have, and being more aggressive about making it a candidate
+     * to be killed (and restarted) if running for a long time.
      */
-    public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0008;
+    public static final int BIND_ALLOW_OOM_MANAGEMENT = 0x0010;
+
+    /**
+     * Flag for {@link #bindService}: don't impact the scheduling or
+     * memory management priority of the target service's hosting process.
+     * Allows the service's process to be managed on the background LRU list
+     * just like a regular application process in the background.
+     */
+    public static final int BIND_WAIVE_PRIORITY = 0x0020;
+
+    /**
+     * Flag for {@link #bindService}: this service is very important to
+     * the client, so should be brought to the foreground process level
+     * when the client is.  Normally a process can only be raised to the
+     * visibility level by a client, even if that client is in the foreground.
+     */
+    public static final int BIND_IMPORTANT = 0x0040;
+
+    /**
+     * Flag for {@link #bindService}: If binding from an activity, allow the
+     * target service's process importance to be raised based on whether the
+     * activity is visible to the user, regardless whether another flag is
+     * used to reduce the amount that the client process's overall importance
+     * is used to impact it.
+     */
+    public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0040;
 
     /** Return an AssetManager instance for your application's package. */
     public abstract AssetManager getAssets();
@@ -195,6 +234,25 @@
     public abstract Context getApplicationContext();
 
     /**
+     * Add a new {@link ComponentCallbacks} to the base application of the
+     * Context, which will be called at the same times as the ComponentCallbacks
+     * methods of activities and other components are called.  Note that you
+     * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
+     * appropriate in the future; this will not be removed for you.
+     */
+    public void registerComponentCallbacks(ComponentCallbacks callback) {
+        getApplicationContext().registerComponentCallbacks(callback);
+    }
+
+    /**
+     * Remove a {@link ComponentCallbacks} objec that was previously registered
+     * with {@link #registerComponentCallbacks(ComponentCallbacks)}.
+     */
+    public void unregisterComponentCallbacks(ComponentCallbacks callback) {
+        getApplicationContext().unregisterComponentCallbacks(callback);
+    }
+
+    /**
      * Return a localized, styled CharSequence from the application's package's
      * default string table.
      *
@@ -1219,8 +1277,10 @@
      *      {@link IntentFilter} published by a service.
      * @param conn Receives information as the service is started and stopped.
      * @param flags Operation options for the binding.  May be 0,
-     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, or
-     *          {@link #BIND_NOT_FOREGROUND}.
+     *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
+     *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
+     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, or
+     *          {@link #BIND_WAIVE_PRIORITY}.
      * @return If you have successfully bound to the service, true is returned;
      *         false is returned if the connection is not made so you will not
      *         receive the service object.
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index b487764..6767747 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -131,7 +131,7 @@
     private static final int CAMERA_MSG_RAW_IMAGE        = 0x080;
     private static final int CAMERA_MSG_COMPRESSED_IMAGE = 0x100;
     private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200;
-    private static final int CAMERA_MSG_METADATA_FACE    = 0x400;
+    private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400;
     private static final int CAMERA_MSG_ALL_MSGS         = 0x4FF;
 
     private int mNativeContext; // accessed by native methods
@@ -721,7 +721,7 @@
                 }
                 return;
 
-            case CAMERA_MSG_METADATA_FACE:
+            case CAMERA_MSG_PREVIEW_METADATA:
                 if (mFaceListener != null) {
                     mFaceListener.onFaceDetection((Face[])msg.obj, mCamera);
                 }
@@ -1156,6 +1156,9 @@
      * @hide
      */
     public static class Face {
+        public Face() {
+        }
+
         /**
          * Bounds of the face. (-1000, -1000) represents the top-left of the
          * camera field of view, and (1000, 1000) represents the bottom-right of
@@ -1168,7 +1171,7 @@
          *
          * @see #startFaceDetection(int)
          */
-        Rect rect;
+        public Rect rect;
 
         /**
          * The confidence level of the face. The range is 1 to 100. 100 is the
@@ -1177,32 +1180,32 @@
          *
          * @see #startFaceDetection(int)
          */
-        int score;
+        public int score;
 
         /**
          * An unique id per face while the face is visible to the tracker. If
          * the face leaves the field-of-view and comes back, it will get a new
          * id. If the value is 0, id is not supported.
          */
-        int id;
+        public int id;
 
         /**
          * The coordinates of the center of the left eye. The range is -1000 to
          * 1000. null if this is not supported.
          */
-        Point leftEye;
+        public Point leftEye;
 
         /**
          * The coordinates of the center of the right eye. The range is -1000 to
          * 1000. null if this is not supported.
          */
-        Point rightEye;
+        public Point rightEye;
 
         /**
          * The coordinates of the center of the mouth. The range is -1000 to
          * 1000. null if this is not supported.
          */
-        Point mouth;
+        public Point mouth;
     }
 
     // Error codes match the enum in include/ui/Camera.h
@@ -1471,6 +1474,7 @@
                                             "preferred-preview-size-for-video";
         private static final String KEY_MAX_NUM_DETECTED_FACES_HW = "max-num-detected-faces-hw";
         private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw";
+        private static final String KEY_RECORDING_HINT = "recording-hint";
 
         // Parameter key suffix for supported values.
         private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -3172,6 +3176,37 @@
             throw new IllegalArgumentException("Invalid face detection type " + type);
         }
 
+        /**
+         * Sets the hint of the recording mode. If this is true, {@link
+         * android.media.MediaRecorder#start()} may be faster or has less
+         * glitches. This should be called before starting the preview for the
+         * best result. But it is allowed to change the hint while the preview
+         * is active. The default value is false.
+         *
+         * The apps can still call {@link #takePicture(Camera.ShutterCallback,
+         * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}
+         * when the hint is true. The apps can call MediaRecorder.start() when
+         * the hint is false. But the performance may be worse.
+         *
+         * @param hint true if the apps intend to record a video using
+         *             {@link android.media.MediaRecorder}.
+         * @hide
+         */
+        public void setRecordingHint(boolean hint) {
+            set(KEY_RECORDING_HINT, hint ? TRUE : FALSE);
+        }
+
+        /**
+         * Gets the current recording hint.
+         *
+         * @return the current recording hint state.
+         * @see #setRecordingHint(boolean)
+         * @hide
+         */
+        public boolean getRecordingHint() {
+            return TRUE.equals(get(KEY_RECORDING_HINT));
+        }
+
         // Splits a comma delimited string to an ArrayList of String.
         // Return null if the passing string is null or the size is 0.
         private ArrayList<String> split(String str) {
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index b548623..551926c 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -408,6 +408,23 @@
     }
 
     /**
+     * Returns the current default USB function.
+     *
+     * @return name of the default function.
+     *
+     * {@hide}
+     */
+    public String getDefaultFunction() {
+        String functions = SystemProperties.get("persist.sys.usb.config", "");
+        int commaIndex = functions.indexOf(',');
+        if (commaIndex > 0) {
+            return functions.substring(0, commaIndex);
+        } else {
+            return functions;
+        }
+    }
+
+    /**
      * Sets the current USB function.
      * If function is null, then the current function is set to the default function.
      *
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 0548250..c41d182 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,16 +23,21 @@
 /** {@hide} */
 interface INetworkStatsService {
 
-    /** Return historical stats for traffic that matches template. */
+    /** Return historical network layer stats for traffic that matches template. */
     NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template);
-    /** Return historical stats for specific UID traffic that matches template. */
+    /** Return historical network layer stats for specific UID traffic that matches template. */
     NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag);
 
-    /** Return usage summary for traffic that matches template. */
+    /** Return network layer usage summary for traffic that matches template. */
     NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
-    /** Return usage summary per UID for traffic that matches template. */
+    /** Return network layer usage summary per UID for traffic that matches template. */
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
 
+    /** Return data layer snapshot of UID network usage. */
+    NetworkStats getDataLayerSnapshotForUid(int uid);
+    /** Increment data layer count of operations performed for UID and tag. */
+    void incrementOperationCount(int uid, int tag, int operationCount);
+
     /** Force update of statistics. */
     void forceUpdate();
 
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 132f3ba..75646fd 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -80,9 +80,9 @@
     public LinkProperties(LinkProperties source) {
         if (source != null) {
             mIfaceName = source.getInterfaceName();
-            mLinkAddresses = source.getLinkAddresses();
-            mDnses = source.getDnses();
-            mRoutes = source.getRoutes();
+            for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
+            for (InetAddress i : source.getDnses()) mDnses.add(i);
+            for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
             mHttpProxy = (source.getHttpProxy() == null)  ?
                 null : new ProxyProperties(source.getHttpProxy());
         }
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index fbff7d8..0e8e7fc 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,6 +21,8 @@
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.util.Objects;
+
 import java.io.CharArrayWriter;
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -56,6 +58,7 @@
     private long[] rxPackets;
     private long[] txBytes;
     private long[] txPackets;
+    private int[] operations;
 
     public static class Entry {
         public String iface;
@@ -65,12 +68,13 @@
         public long rxPackets;
         public long txBytes;
         public long txPackets;
+        public int operations;
 
         public Entry() {
         }
 
         public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes,
-                long txPackets) {
+                long txPackets, int operations) {
             this.iface = iface;
             this.uid = uid;
             this.tag = tag;
@@ -78,6 +82,7 @@
             this.rxPackets = rxPackets;
             this.txBytes = txBytes;
             this.txPackets = txPackets;
+            this.operations = operations;
         }
     }
 
@@ -91,6 +96,7 @@
         this.rxPackets = new long[initialSize];
         this.txBytes = new long[initialSize];
         this.txPackets = new long[initialSize];
+        this.operations = new int[initialSize];
     }
 
     public NetworkStats(Parcel parcel) {
@@ -103,11 +109,32 @@
         rxPackets = parcel.createLongArray();
         txBytes = parcel.createLongArray();
         txPackets = parcel.createLongArray();
+        operations = parcel.createIntArray();
+    }
+
+    /** {@inheritDoc} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(elapsedRealtime);
+        dest.writeInt(size);
+        dest.writeStringArray(iface);
+        dest.writeIntArray(uid);
+        dest.writeIntArray(tag);
+        dest.writeLongArray(rxBytes);
+        dest.writeLongArray(rxPackets);
+        dest.writeLongArray(txBytes);
+        dest.writeLongArray(txPackets);
+        dest.writeIntArray(operations);
     }
 
     public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
             long txBytes, long txPackets) {
-        return addValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets));
+        return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0);
+    }
+
+    public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
+        return addValues(
+                new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /**
@@ -124,6 +151,7 @@
             rxPackets = Arrays.copyOf(rxPackets, newLength);
             txBytes = Arrays.copyOf(txBytes, newLength);
             txPackets = Arrays.copyOf(txPackets, newLength);
+            operations = Arrays.copyOf(operations, newLength);
         }
 
         iface[size] = entry.iface;
@@ -133,6 +161,7 @@
         rxPackets[size] = entry.rxPackets;
         txBytes[size] = entry.txBytes;
         txPackets[size] = entry.txPackets;
+        operations[size] = entry.operations;
         size++;
 
         return this;
@@ -150,6 +179,7 @@
         entry.rxPackets = rxPackets[i];
         entry.txBytes = txBytes[i];
         entry.txPackets = txPackets[i];
+        entry.operations = operations[i];
         return entry;
     }
 
@@ -167,8 +197,9 @@
     }
 
     public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
-            long txBytes, long txPackets) {
-        return combineValues(new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets));
+            long txBytes, long txPackets, int operations) {
+        return combineValues(
+                new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /**
@@ -186,6 +217,7 @@
             rxPackets[i] += entry.rxPackets;
             txBytes[i] += entry.txBytes;
             txPackets[i] += entry.txPackets;
+            operations[i] += entry.operations;
         }
         return this;
     }
@@ -195,7 +227,7 @@
      */
     public int findIndex(String iface, int uid, int tag) {
         for (int i = 0; i < size; i++) {
-            if (equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
+            if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
                 return i;
             }
         }
@@ -203,6 +235,22 @@
     }
 
     /**
+     * Splice in {@link #operations} from the given {@link NetworkStats} based
+     * on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
+     * since operation counts are at data layer.
+     */
+    public void spliceOperationsFrom(NetworkStats stats) {
+        for (int i = 0; i < size; i++) {
+            final int j = stats.findIndex(IFACE_ALL, uid[i], tag[i]);
+            if (j == -1) {
+                operations[i] = 0;
+            } else {
+                operations[i] = stats.operations[j];
+            }
+        }
+    }
+
+    /**
      * Return list of unique interfaces known by this data structure.
      */
     public String[] getUniqueIfaces() {
@@ -289,15 +337,17 @@
                 entry.rxPackets = rxPackets[i];
                 entry.txBytes = txBytes[i];
                 entry.txPackets = txPackets[i];
+                entry.operations = operations[i];
             } else {
                 // existing row, subtract remote value
                 entry.rxBytes = rxBytes[i] - value.rxBytes[j];
                 entry.rxPackets = rxPackets[i] - value.rxPackets[j];
                 entry.txBytes = txBytes[i] - value.txBytes[j];
                 entry.txPackets = txPackets[i] - value.txPackets[j];
+                entry.operations = operations[i] - value.operations[j];
                 if (enforceMonotonic
                         && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
-                                || entry.txPackets < 0)) {
+                                || entry.txPackets < 0 || entry.operations < 0)) {
                     throw new IllegalArgumentException("found non-monotonic values");
                 }
                 if (clampNegative) {
@@ -305,6 +355,7 @@
                     entry.rxPackets = Math.max(0, entry.rxPackets);
                     entry.txBytes = Math.max(0, entry.txBytes);
                     entry.txPackets = Math.max(0, entry.txPackets);
+                    entry.operations = Math.max(0, entry.operations);
                 }
             }
 
@@ -314,10 +365,6 @@
         return result;
     }
 
-    private static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
@@ -325,11 +372,12 @@
             pw.print(prefix);
             pw.print("  iface="); pw.print(iface[i]);
             pw.print(" uid="); pw.print(uid[i]);
-            pw.print(" tag="); pw.print(tag[i]);
+            pw.print(" tag=0x"); pw.print(Integer.toHexString(tag[i]));
             pw.print(" rxBytes="); pw.print(rxBytes[i]);
             pw.print(" rxPackets="); pw.print(rxPackets[i]);
             pw.print(" txBytes="); pw.print(txBytes[i]);
-            pw.print(" txPackets="); pw.println(txPackets[i]);
+            pw.print(" txPackets="); pw.print(txPackets[i]);
+            pw.print(" operations="); pw.println(operations[i]);
         }
     }
 
@@ -345,19 +393,6 @@
         return 0;
     }
 
-    /** {@inheritDoc} */
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(elapsedRealtime);
-        dest.writeInt(size);
-        dest.writeStringArray(iface);
-        dest.writeIntArray(uid);
-        dest.writeIntArray(tag);
-        dest.writeLongArray(rxBytes);
-        dest.writeLongArray(rxPackets);
-        dest.writeLongArray(txBytes);
-        dest.writeLongArray(txPackets);
-    }
-
     public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
         public NetworkStats createFromParcel(Parcel in) {
             return new NetworkStats(in);
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 8bd1738..4ffabb1 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -16,6 +16,16 @@
 
 package android.net;
 
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStatsHistory.DataStreamUtils.readLongArray;
+import static android.net.NetworkStatsHistory.DataStreamUtils.writeLongArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.readIntArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.writeIntArray;
+import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -43,19 +53,26 @@
     private static final int VERSION_INIT = 1;
 
     // TODO: teach about varint encoding to use less disk space
-    // TODO: extend to record rxPackets/txPackets
+    // TODO: teach about omitting entire fields to reduce parcel pressure
+    // TODO: persist/restore packet and operation counts
 
     private final long bucketDuration;
     private int bucketCount;
     private long[] bucketStart;
     private long[] rxBytes;
+    private long[] rxPackets;
     private long[] txBytes;
+    private long[] txPackets;
+    private int[] operations;
 
     public static class Entry {
         public long bucketStart;
         public long bucketDuration;
         public long rxBytes;
+        public long rxPackets;
         public long txBytes;
+        public long txPackets;
+        public int operations;
     }
 
     public NetworkStatsHistory(long bucketDuration) {
@@ -66,15 +83,21 @@
         this.bucketDuration = bucketDuration;
         bucketStart = new long[initialSize];
         rxBytes = new long[initialSize];
+        rxPackets = new long[initialSize];
         txBytes = new long[initialSize];
+        txPackets = new long[initialSize];
+        operations = new int[initialSize];
         bucketCount = 0;
     }
 
     public NetworkStatsHistory(Parcel in) {
         bucketDuration = in.readLong();
         bucketStart = readLongArray(in);
-        rxBytes = in.createLongArray();
-        txBytes = in.createLongArray();
+        rxBytes = readLongArray(in);
+        rxPackets = readLongArray(in);
+        txBytes = readLongArray(in);
+        txPackets = readLongArray(in);
+        operations = readIntArray(in);
         bucketCount = bucketStart.length;
     }
 
@@ -83,17 +106,24 @@
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, rxBytes, bucketCount);
+        writeLongArray(out, rxPackets, bucketCount);
         writeLongArray(out, txBytes, bucketCount);
+        writeLongArray(out, txPackets, bucketCount);
+        writeIntArray(out, operations, bucketCount);
     }
 
     public NetworkStatsHistory(DataInputStream in) throws IOException {
+        // TODO: read packet and operation counts
         final int version = in.readInt();
         switch (version) {
             case VERSION_INIT: {
                 bucketDuration = in.readLong();
                 bucketStart = readLongArray(in);
                 rxBytes = readLongArray(in);
+                rxPackets = new long[bucketStart.length];
                 txBytes = readLongArray(in);
+                txPackets = new long[bucketStart.length];
+                operations = new int[bucketStart.length];
                 bucketCount = bucketStart.length;
                 break;
             }
@@ -104,6 +134,7 @@
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
+        // TODO: write packet and operation counts
         out.writeInt(VERSION_INIT);
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
@@ -148,7 +179,10 @@
         entry.bucketStart = bucketStart[i];
         entry.bucketDuration = bucketDuration;
         entry.rxBytes = rxBytes[i];
+        entry.rxPackets = rxPackets[i];
         entry.txBytes = txBytes[i];
+        entry.txPackets = txPackets[i];
+        entry.operations = operations[i];
         return entry;
     }
 
@@ -156,17 +190,27 @@
      * Record that data traffic occurred in the given time range. Will
      * distribute across internal buckets, creating new buckets as needed.
      */
-    public void recordData(long start, long end, long rx, long tx) {
-        if (rx < 0 || tx < 0) {
-            throw new IllegalArgumentException(
-                    "tried recording negative data: rx=" + rx + ", tx=" + tx);
+    @Deprecated
+    public void recordData(long start, long end, long rxBytes, long txBytes) {
+        recordData(start, end,
+                new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0));
+    }
+
+    /**
+     * Record that data traffic occurred in the given time range. Will
+     * distribute across internal buckets, creating new buckets as needed.
+     */
+    public void recordData(long start, long end, NetworkStats.Entry entry) {
+        if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0
+                || entry.operations < 0) {
+            throw new IllegalArgumentException("tried recording negative data");
         }
 
         // create any buckets needed by this range
         ensureBuckets(start, end);
 
         // distribute data usage into buckets
-        final long duration = end - start;
+        long duration = end - start;
         for (int i = bucketCount - 1; i >= 0; i--) {
             final long curStart = bucketStart[i];
             final long curEnd = curStart + bucketDuration;
@@ -177,10 +221,22 @@
             if (curStart > end) continue;
 
             final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
-            if (overlap > 0) {
-                this.rxBytes[i] += rx * overlap / duration;
-                this.txBytes[i] += tx * overlap / duration;
-            }
+            if (overlap <= 0) continue;
+
+            // integer math each time is faster than floating point
+            final long fracRxBytes = entry.rxBytes * overlap / duration;
+            final long fracRxPackets = entry.rxPackets * overlap / duration;
+            final long fracTxBytes = entry.txBytes * overlap / duration;
+            final long fracTxPackets = entry.txPackets * overlap / duration;
+            final int fracOperations = (int) (entry.operations * overlap / duration);
+
+            rxBytes[i] += fracRxBytes; entry.rxBytes -= fracRxBytes;
+            rxPackets[i] += fracRxPackets; entry.rxPackets -= fracRxPackets;
+            txBytes[i] += fracTxBytes; entry.txBytes -= fracTxBytes;
+            txPackets[i] += fracTxPackets; entry.txPackets -= fracTxPackets;
+            operations[i] += fracOperations; entry.operations -= fracOperations;
+
+            duration -= overlap;
         }
     }
 
@@ -189,10 +245,19 @@
      * for combining together stats for external reporting.
      */
     public void recordEntireHistory(NetworkStatsHistory input) {
+        final NetworkStats.Entry entry = new NetworkStats.Entry(
+                IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
         for (int i = 0; i < input.bucketCount; i++) {
             final long start = input.bucketStart[i];
             final long end = start + input.bucketDuration;
-            recordData(start, end, input.rxBytes[i], input.txBytes[i]);
+
+            entry.rxBytes = input.rxBytes[i];
+            entry.rxPackets = input.rxPackets[i];
+            entry.txBytes = input.txBytes[i];
+            entry.txPackets = input.txPackets[i];
+            entry.operations = input.operations[i];
+
+            recordData(start, end, entry);
         }
     }
 
@@ -223,7 +288,10 @@
             final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
             bucketStart = Arrays.copyOf(bucketStart, newLength);
             rxBytes = Arrays.copyOf(rxBytes, newLength);
+            rxPackets = Arrays.copyOf(rxPackets, newLength);
             txBytes = Arrays.copyOf(txBytes, newLength);
+            txPackets = Arrays.copyOf(txPackets, newLength);
+            operations = Arrays.copyOf(operations, newLength);
         }
 
         // create gap when inserting bucket in middle
@@ -233,12 +301,18 @@
 
             System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
             System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
+            System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
             System.arraycopy(txBytes, index, txBytes, dstPos, length);
+            System.arraycopy(txPackets, index, txPackets, dstPos, length);
+            System.arraycopy(operations, index, operations, dstPos, length);
         }
 
         bucketStart[index] = start;
         rxBytes[index] = 0;
+        rxPackets[index] = 0;
         txBytes[index] = 0;
+        txPackets[index] = 0;
+        operations[index] = 0;
         bucketCount++;
     }
 
@@ -260,7 +334,10 @@
             final int length = bucketStart.length;
             bucketStart = Arrays.copyOfRange(bucketStart, i, length);
             rxBytes = Arrays.copyOfRange(rxBytes, i, length);
+            rxPackets = Arrays.copyOfRange(rxPackets, i, length);
             txBytes = Arrays.copyOfRange(txBytes, i, length);
+            txPackets = Arrays.copyOfRange(txPackets, i, length);
+            operations = Arrays.copyOfRange(operations, i, length);
             bucketCount -= i;
         }
     }
@@ -282,7 +359,10 @@
         entry.bucketStart = start;
         entry.bucketDuration = end - start;
         entry.rxBytes = 0;
+        entry.rxPackets = 0;
         entry.txBytes = 0;
+        entry.txPackets = 0;
+        entry.operations = 0;
 
         for (int i = bucketCount - 1; i >= 0; i--) {
             final long curStart = bucketStart[i];
@@ -295,14 +375,16 @@
 
             // include full value for active buckets, otherwise only fractional
             final boolean activeBucket = curStart < now && curEnd > now;
-            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
-            if (activeBucket || overlap == bucketDuration) {
-                entry.rxBytes += rxBytes[i];
-                entry.txBytes += txBytes[i];
-            } else if (overlap > 0) {
-                entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
-                entry.txBytes += txBytes[i] * overlap / bucketDuration;
-            }
+            final long overlap = activeBucket ? bucketDuration
+                    : Math.min(curEnd, end) - Math.max(curStart, start);
+            if (overlap <= 0) continue;
+
+            // integer math each time is faster than floating point
+            entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
+            entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
+            entry.txBytes += txBytes[i] * overlap / bucketDuration;
+            entry.txPackets += txPackets[i] * overlap / bucketDuration;
+            entry.operations += operations[i] * overlap / bucketDuration;
         }
 
         return entry;
@@ -315,17 +397,19 @@
     public void generateRandom(long start, long end, long rx, long tx) {
         ensureBuckets(start, end);
 
+        final NetworkStats.Entry entry = new NetworkStats.Entry(
+                IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
         final Random r = new Random();
         while (rx > 1024 && tx > 1024) {
             final long curStart = randomLong(r, start, end);
             final long curEnd = randomLong(r, curStart, end);
-            final long curRx = randomLong(r, 0, rx);
-            final long curTx = randomLong(r, 0, tx);
+            entry.rxBytes = randomLong(r, 0, rx);
+            entry.txBytes = randomLong(r, 0, tx);
 
-            recordData(curStart, curEnd, curRx, curTx);
+            recordData(curStart, curEnd, entry);
 
-            rx -= curRx;
-            tx -= curTx;
+            rx -= entry.rxBytes;
+            tx -= entry.txBytes;
         }
     }
 
@@ -347,7 +431,10 @@
             pw.print(prefix);
             pw.print("  bucketStart="); pw.print(bucketStart[i]);
             pw.print(" rxBytes="); pw.print(rxBytes[i]);
-            pw.print(" txBytes="); pw.println(txBytes[i]);
+            pw.print(" rxPackets="); pw.print(rxPackets[i]);
+            pw.print(" txBytes="); pw.print(txBytes[i]);
+            pw.print(" txPackets="); pw.print(txPackets[i]);
+            pw.print(" operations="); pw.println(operations[i]);
         }
     }
 
@@ -368,41 +455,73 @@
         }
     };
 
-    private static long[] readLongArray(DataInputStream in) throws IOException {
-        final int size = in.readInt();
-        final long[] values = new long[size];
-        for (int i = 0; i < values.length; i++) {
-            values[i] = in.readLong();
+    /**
+     * Utility methods for interacting with {@link DataInputStream} and
+     * {@link DataOutputStream}, mostly dealing with writing partial arrays.
+     */
+    public static class DataStreamUtils {
+        public static long[] readLongArray(DataInputStream in) throws IOException {
+            final int size = in.readInt();
+            final long[] values = new long[size];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = in.readLong();
+            }
+            return values;
         }
-        return values;
-    }
 
-    private static void writeLongArray(DataOutputStream out, long[] values, int size) throws IOException {
-        if (size > values.length) {
-            throw new IllegalArgumentException("size larger than length");
-        }
-        out.writeInt(size);
-        for (int i = 0; i < size; i++) {
-            out.writeLong(values[i]);
+        public static void writeLongArray(DataOutputStream out, long[] values, int size)
+                throws IOException {
+            if (size > values.length) {
+                throw new IllegalArgumentException("size larger than length");
+            }
+            out.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                out.writeLong(values[i]);
+            }
         }
     }
 
-    private static long[] readLongArray(Parcel in) {
-        final int size = in.readInt();
-        final long[] values = new long[size];
-        for (int i = 0; i < values.length; i++) {
-            values[i] = in.readLong();
+    /**
+     * Utility methods for interacting with {@link Parcel} structures, mostly
+     * dealing with writing partial arrays.
+     */
+    public static class ParcelUtils {
+        public static long[] readLongArray(Parcel in) {
+            final int size = in.readInt();
+            final long[] values = new long[size];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = in.readLong();
+            }
+            return values;
         }
-        return values;
-    }
 
-    private static void writeLongArray(Parcel out, long[] values, int size) {
-        if (size > values.length) {
-            throw new IllegalArgumentException("size larger than length");
+        public static void writeLongArray(Parcel out, long[] values, int size) {
+            if (size > values.length) {
+                throw new IllegalArgumentException("size larger than length");
+            }
+            out.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                out.writeLong(values[i]);
+            }
         }
-        out.writeInt(size);
-        for (int i = 0; i < size; i++) {
-            out.writeLong(values[i]);
+
+        public static int[] readIntArray(Parcel in) {
+            final int size = in.readInt();
+            final int[] values = new int[size];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = in.readInt();
+            }
+            return values;
+        }
+
+        public static void writeIntArray(Parcel out, int[] values, int size) {
+            if (size > values.length) {
+                throw new IllegalArgumentException("size larger than length");
+            }
+            out.writeInt(size);
+            for (int i = 0; i < size; i++) {
+                out.writeInt(values[i]);
+            }
         }
     }
 
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index e054930..f138e49 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -20,14 +20,13 @@
 import android.app.backup.BackupManager;
 import android.content.Context;
 import android.media.MediaPlayer;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
 import com.android.server.NetworkManagementSocketTagger;
 
 import dalvik.system.SocketTagger;
+
 import java.net.Socket;
 import java.net.SocketException;
 
@@ -172,7 +171,7 @@
             }
 
             // take snapshot in time; we calculate delta later
-            sActiveProfilingStart = getNetworkStatsForUid(context);
+            sActiveProfilingStart = getDataLayerSnapshotForUid(context);
         }
     }
 
@@ -190,7 +189,7 @@
             }
 
             // subtract starting values and return delta
-            final NetworkStats profilingStop = getNetworkStatsForUid(context);
+            final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
             final NetworkStats profilingDelta = profilingStop.subtractClamped(
                     sActiveProfilingStart);
             sActiveProfilingStart = null;
@@ -199,6 +198,28 @@
     }
 
     /**
+     * Increment count of network operations performed under the given
+     * accounting tag. This can be used to derive bytes-per-operation.
+     *
+     * @param tag Accounting tag used in {@link #setThreadStatsTag(int)}.
+     * @param operationCount Number of operations to increment count by.
+     */
+    public static void incrementOperationCount(int tag, int operationCount) {
+        if (operationCount < 0) {
+            throw new IllegalArgumentException("operation count can only be incremented");
+        }
+
+        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        final int uid = android.os.Process.myUid();
+        try {
+            statsService.incrementOperationCount(uid, tag, operationCount);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Get the total number of packets transmitted through the mobile interface.
      *
      * @return number of packets.  If the statistics are not supported by this device,
@@ -461,14 +482,12 @@
      * Return detailed {@link NetworkStats} for the current UID. Requires no
      * special permission.
      */
-    private static NetworkStats getNetworkStatsForUid(Context context) {
-        final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
-                binder);
-
+    private static NetworkStats getDataLayerSnapshotForUid(Context context) {
+        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         final int uid = android.os.Process.myUid();
         try {
-            return service.getNetworkStatsUidDetail(uid);
+            return statsService.getDataLayerSnapshotForUid(uid);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/core/java/android/net/VpnBuilder.java b/core/java/android/net/VpnBuilder.java
index 25cedb6..4582523 100644
--- a/core/java/android/net/VpnBuilder.java
+++ b/core/java/android/net/VpnBuilder.java
@@ -91,6 +91,7 @@
  *
  * <p class="note">Using this class requires
  * {@link android.Manifest.permission#VPN} permission.
+ * @hide
  */
 public class VpnBuilder {
 
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index 166b21b..1536760 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -61,8 +61,7 @@
         View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);
         if (checkboxView != null && checkboxView instanceof Checkable) {
             ((Checkable) checkboxView).setChecked(mChecked);
-            // Post this so this view is bound and attached when firing the event.
-            postSendAccessibilityEventForView(checkboxView);
+            sendAccessibilityEvent(checkboxView);
         }
 
         syncSummaryView(view);
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 3dbd522..17f0c1b 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -102,8 +102,8 @@
         View checkableView = view.findViewById(com.android.internal.R.id.switchWidget);
         if (checkableView != null && checkableView instanceof Checkable) {
             ((Checkable) checkableView).setChecked(mChecked);
-            // Post this so this view is bound and attached when firing the event.
-            postSendAccessibilityEventForView(checkableView);
+
+            sendAccessibilityEvent(checkableView);
 
             if (checkableView instanceof Switch) {
                 final Switch switchView = (Switch) checkableView;
diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java
index b6f00ad..d186b20 100644
--- a/core/java/android/preference/TwoStatePreference.java
+++ b/core/java/android/preference/TwoStatePreference.java
@@ -37,10 +37,9 @@
     private CharSequence mSummaryOn;
     private CharSequence mSummaryOff;
     boolean mChecked;
-    private boolean mSendAccessibilityEventViewClickedType;
+    private boolean mSendClickAccessibilityEvent;
     private boolean mDisableDependentsState;
 
-    private SendAccessibilityEventTypeViewClicked mSendAccessibilityEventTypeViewClicked;
 
     public TwoStatePreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -60,9 +59,7 @@
 
         boolean newValue = !isChecked();
 
-        // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change
-        // not sending
-        mSendAccessibilityEventViewClickedType = true;
+        mSendClickAccessibilityEvent = true;
 
         if (!callChangeListener(newValue)) {
             return;
@@ -188,26 +185,19 @@
                 : (Boolean) defaultValue);
     }
 
-    /**
-     * Post send an accessibility event for the given view if appropriate.
-     *
-     * @param view View that should send the event
-     */
-    void postSendAccessibilityEventForView(View view) {
-        // send an event to announce the value change of the state. It is done here
-        // because clicking a preference does not immediately change the checked state
-        // for example when enabling the WiFi
-        if (mSendAccessibilityEventViewClickedType
-                && AccessibilityManager.getInstance(getContext()).isEnabled()
-                && view.isEnabled()) {
-            mSendAccessibilityEventViewClickedType = false;
-            if (mSendAccessibilityEventTypeViewClicked == null) {
-                mSendAccessibilityEventTypeViewClicked =
-                    new SendAccessibilityEventTypeViewClicked();
-            }
-            mSendAccessibilityEventTypeViewClicked.mView = view;
-            view.post(mSendAccessibilityEventTypeViewClicked);
+    void sendAccessibilityEvent(View view) {
+        // Since the view is still not attached we create, populate,
+        // and send the event directly since we do not know when it
+        // will be attached and posting commands is not as clean.
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext());
+        if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) {
+            AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
+            view.onInitializeAccessibilityEvent(event);
+            view.dispatchPopulateAccessibilityEvent(event);
+            accessibilityManager.sendAccessibilityEvent(event);
         }
+        mSendClickAccessibilityEvent = false;
     }
 
     /**
@@ -301,13 +291,4 @@
             }
         };
     }
-
-    private final class SendAccessibilityEventTypeViewClicked implements Runnable {
-        private View mView;
-
-        @Override
-        public void run() {
-            mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
-        }
-    }
 }
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f799af3..5da3114 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1148,6 +1148,14 @@
              * <P>Type: INTEGER (boolean)</P>
              */
             public static final String IS_NOTIFICATION = "is_notification";
+
+            /**
+             * The genre of the audio file, if any
+             * <P>Type: TEXT</P>
+             * Does not exist in the database - only used by the media scanner for inserts.
+             * @hide
+             */
+            public static final String GENRE = "genre";
         }
 
         /**
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index cb96969..2c78679 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -274,8 +274,11 @@
             sStaticLayout = null;
         }
 
-        if (reflowed == null)
+        if (reflowed == null) {
             reflowed = new StaticLayout(true);
+        } else {
+            reflowed.prepare();
+        }
 
         reflowed.generate(text, where, where + after,
                 getPaint(), getWidth(), getAlignment(), getTextDirectionHeuristic(),
@@ -356,6 +359,7 @@
 
         synchronized (sLock) {
             sStaticLayout = reflowed;
+            reflowed.finish();
         }
     }
 
@@ -485,7 +489,7 @@
     private int mTopPadding, mBottomPadding;
 
     private static StaticLayout sStaticLayout = new StaticLayout(true);
-    private static Object sLock = new Object();
+    private static final Object[] sLock = new Object[0];
 
     private static final int START = 0;
     private static final int DIR = START;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 2920ac5..c184c11 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -29,14 +29,15 @@
  */
 class MeasuredText {
     private static final boolean localLOGV = false;
-    /* package */ CharSequence mText;
-    /* package */ int mTextStart;
-    /* package */ float[] mWidths;
-    /* package */ char[] mChars;
-    /* package */ byte[] mLevels;
-    /* package */ int mDir;
-    /* package */ boolean mEasy;
-    /* package */ int mLen;
+    CharSequence mText;
+    int mTextStart;
+    float[] mWidths;
+    char[] mChars;
+    byte[] mLevels;
+    int mDir;
+    boolean mEasy;
+    int mLen;
+
     private int mPos;
     private TextPaint mWorkPaint;
 
@@ -44,16 +45,16 @@
         mWorkPaint = new TextPaint();
     }
 
-    private static MeasuredText[] cached = new MeasuredText[3];
+    private static final Object[] sLock = new Object[0];
+    private static MeasuredText[] sCached = new MeasuredText[3];
 
-    /* package */
     static MeasuredText obtain() {
         MeasuredText mt;
-        synchronized (cached) {
-            for (int i = cached.length; --i >= 0;) {
-                if (cached[i] != null) {
-                    mt = cached[i];
-                    cached[i] = null;
+        synchronized (sLock) {
+            for (int i = sCached.length; --i >= 0;) {
+                if (sCached[i] != null) {
+                    mt = sCached[i];
+                    sCached[i] = null;
                     return mt;
                 }
             }
@@ -65,14 +66,14 @@
         return mt;
     }
 
-    /* package */
     static MeasuredText recycle(MeasuredText mt) {
         mt.mText = null;
         if (mt.mLen < 1000) {
-            synchronized(cached) {
-                for (int i = 0; i < cached.length; ++i) {
-                    if (cached[i] == null) {
-                        cached[i] = mt;
+            synchronized(sLock) {
+                for (int i = 0; i < sCached.length; ++i) {
+                    if (sCached[i] == null) {
+                        sCached[i] = mt;
+                        mt.mText = null;
                         break;
                     }
                 }
@@ -84,7 +85,6 @@
     /**
      * Analyzes text for bidirectional runs.  Allocates working buffers.
      */
-    /* package */
     void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
         mText = text;
         mTextStart = start;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index f7b9502..14c71b2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -923,6 +923,14 @@
     public void setMaximumVisibleLineCount(int lineCount) {
         mMaximumVisibleLineCount = lineCount;
     }
+    
+    void prepare() {
+        mMeasured = MeasuredText.obtain();
+    }
+    
+    void finish() {
+        mMeasured = MeasuredText.recycle(mMeasured);
+    }
 
     private int mLineCount;
     private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java
index a491a0b..fd3b9e5 100644
--- a/core/java/android/view/GLES20Layer.java
+++ b/core/java/android/view/GLES20Layer.java
@@ -54,7 +54,10 @@
 
     @Override
     void destroy() {
-        if (mFinalizer != null) mFinalizer.destroy();
+        if (mFinalizer != null) {
+            mFinalizer.destroy();
+            mFinalizer = null;
+        }
         mLayer = 0;
     }
 
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 4e4923b..346adc8 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,7 +17,7 @@
 
 package android.view;
 
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
@@ -975,10 +975,12 @@
             }
 
             switch (level) {
-                case ComponentCallbacks.TRIM_MEMORY_MODERATE:
+                case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
+                case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
+                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                     GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
                     break;
-                case ComponentCallbacks.TRIM_MEMORY_COMPLETE:
+                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                     GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
                     break;
             }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 296e6be..935281a5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1784,18 +1784,51 @@
     public static final int OVER_SCROLL_NEVER = 2;
 
     /**
-     * View has requested the status bar to be visible (the default).
+     * View has requested the system UI (status bar) to be visible (the default).
      *
      * @see #setSystemUiVisibility(int)
      */
-    public static final int STATUS_BAR_VISIBLE = 0;
+    public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
 
     /**
-     * View has requested the status bar to be hidden.
+     * View has requested the system UI to enter an unobtrusive "low profile" mode.
+     *
+     * This is for use in games, book readers, video players, or any other "immersive" application
+     * where the usual system chrome is deemed too distracting. 
+     *
+     * In low profile mode, the status bar and/or navigation icons may dim.
      *
      * @see #setSystemUiVisibility(int)
      */
-    public static final int STATUS_BAR_HIDDEN = 0x00000001;
+    public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
+
+    /**
+     * View has requested that the system navigation be temporarily hidden.
+     *
+     * This is an even less obtrusive state than that called for by
+     * {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls
+     * (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause
+     * those to disappear. This is useful (in conjunction with the
+     * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN FLAG_FULLSCREEN} and 
+     * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN}
+     * window flags) for displaying content using every last pixel on the display.
+     *
+     * There is a limitation: because navigation controls are so important, the least user
+     * interaction will cause them to reappear immediately.
+     *
+     * @see #setSystemUiVisibility(int)
+     */
+    public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
+
+    /**
+     * @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead.
+     */
+    public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;
+
+    /**
+     * @deprecated Use {@link #SYSTEM_UI_FLAG_VISIBLE} instead.
+     */
+    public static final int STATUS_BAR_VISIBLE = SYSTEM_UI_FLAG_VISIBLE;
 
     /**
      * @hide
@@ -1889,7 +1922,7 @@
     /**
      * @hide
      */
-    public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = STATUS_BAR_HIDDEN;
+    public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF;
 
     /**
      * Controls the over-scroll mode for this view.
@@ -1934,7 +1967,17 @@
      * This view's request for the visibility of the status bar.
      * @hide
      */
-    @ViewDebug.ExportedProperty()
+    @ViewDebug.ExportedProperty(flagMapping = {
+        @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_LOW_PROFILE,
+                                equals = SYSTEM_UI_FLAG_LOW_PROFILE,
+                                name = "SYSTEM_UI_FLAG_LOW_PROFILE", outputIf = true),
+        @ViewDebug.FlagToString(mask = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
+                                equals = SYSTEM_UI_FLAG_HIDE_NAVIGATION,
+                                name = "SYSTEM_UI_FLAG_HIDE_NAVIGATION", outputIf = true),
+        @ViewDebug.FlagToString(mask = PUBLIC_STATUS_BAR_VISIBILITY_MASK,
+                                equals = SYSTEM_UI_FLAG_VISIBLE,
+                                name = "SYSTEM_UI_FLAG_VISIBLE", outputIf = true)
+    })
     int mSystemUiVisibility;
 
     /**
@@ -5304,12 +5347,6 @@
                     || action == MotionEvent.ACTION_HOVER_MOVE
                     || action == MotionEvent.ACTION_HOVER_EXIT) {
                 if (dispatchHoverEvent(event)) {
-                    // For compatibility with existing applications that handled HOVER_MOVE
-                    // events in onGenericMotionEvent, dispatch the event there.  The
-                    // onHoverEvent method did not exist at the time.
-                    if (action == MotionEvent.ACTION_HOVER_MOVE) {
-                        dispatchGenericMotionEventInternal(event);
-                    }
                     return true;
                 }
             } else if (dispatchGenericPointerEvent(event)) {
@@ -5357,21 +5394,6 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     protected boolean dispatchHoverEvent(MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_HOVER_ENTER:
-                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
-                    mSendingHoverAccessibilityEvents = true;
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
-                }
-                break;
-            case MotionEvent.ACTION_HOVER_EXIT:
-                if (mSendingHoverAccessibilityEvents) {
-                    mSendingHoverAccessibilityEvents = false;
-                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-                }
-                break;
-        }
-
         //noinspection SimplifiableIfStatement
         if (mOnHoverListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnHoverListener.onHover(this, event)) {
@@ -6012,7 +6034,8 @@
      * </p><p>
      * The default implementation calls {@link #setHovered} to update the hovered state
      * of the view when a hover enter or hover exit event is received, if the view
-     * is enabled and is clickable.
+     * is enabled and is clickable.  The default implementation also sends hover
+     * accessibility events.
      * </p>
      *
      * @param event The motion event that describes the hover.
@@ -6023,6 +6046,21 @@
      * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_ENTER:
+                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = true;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = false;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                }
+                break;
+        }
+
         if (isHoverable()) {
             switch (event.getAction()) {
                 case MotionEvent.ACTION_HOVER_ENTER:
@@ -6032,6 +6070,15 @@
                     setHovered(false);
                     break;
             }
+
+            // Dispatch the event to onGenericMotionEvent before returning true.
+            // This is to provide compatibility with existing applications that
+            // handled HOVER_MOVE events in onGenericMotionEvent and that would
+            // break because of the new default handling for hoverable views
+            // in onHoverEvent.
+            // Note that onGenericMotionEvent will be called by default when
+            // onHoverEvent returns false (refer to dispatchGenericMotionEvent).
+            dispatchGenericMotionEventInternal(event);
             return true;
         }
         return false;
@@ -9676,7 +9723,8 @@
      * @return A HardwareLayer ready to render, or null if an error occurred.
      */
     HardwareLayer getHardwareLayer() {
-        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
+        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null ||
+                !mAttachInfo.mHardwareRenderer.isEnabled()) {
             return null;
         }
 
@@ -10243,6 +10291,7 @@
         canvas.restoreToCount(restoreCount);
 
         if (attachInfo != null) {
+            canvas.setBitmap(null);
             // Restore the cached Canvas for our siblings
             attachInfo.mCanvas = canvas;
         }
@@ -12537,7 +12586,8 @@
 
     /**
      * Request that the visibility of the status bar be changed.
-     * @param visibility  Either {@link #STATUS_BAR_VISIBLE} or {@link #STATUS_BAR_HIDDEN}.
+     * @param visibility  Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or
+     * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}.
      */
     public void setSystemUiVisibility(int visibility) {
         if (visibility != mSystemUiVisibility) {
@@ -12550,7 +12600,8 @@
 
     /**
      * Returns the status bar visibility that this view has requested.
-     * @return Either {@link #STATUS_BAR_VISIBLE} or {@link #STATUS_BAR_HIDDEN}.
+     * @return  Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or
+     * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}.
      */
     public int getSystemUiVisibility() {
         return mSystemUiVisibility;
@@ -13653,7 +13704,8 @@
          * Called when the status bar changes visibility because of a call to
          * {@link View#setSystemUiVisibility(int)}.
          *
-         * @param visibility {@link #STATUS_BAR_VISIBLE} or {@link #STATUS_BAR_HIDDEN}.
+         * @param visibility  Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or
+         * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}.
          */
         public void onSystemUiVisibilityChange(int visibility);
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c89e039..7a1acfd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -36,7 +36,6 @@
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LayoutAnimationController;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9460fe5..b22ab7e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,6 +21,7 @@
 import android.app.ActivityManagerNative;
 import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -554,7 +555,7 @@
         if (mThread != Thread.currentThread()) {
             if (mAttachInfo.mHardwareRenderer != null &&
                     mAttachInfo.mHardwareRenderer.isEnabled()) {
-                HardwareRenderer.trimMemory(ComponentCallbacks.TRIM_MEMORY_MODERATE);
+                HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
             }
         } else {
             if (mAttachInfo.mHardwareRenderer != null &&
@@ -1537,7 +1538,8 @@
             }
         }
 
-        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
+        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
+                viewVisibility != View.VISIBLE;
 
         if (!cancelDraw && !newSurface) {
             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -1591,8 +1593,11 @@
             if (fullRedrawNeeded) {
                 mFullRedrawNeeded = true;
             }
-            // Try again
-            scheduleTraversals();
+            
+            if (viewVisibility == View.VISIBLE) {
+                // Try again
+                scheduleTraversals();
+            }
         }
     }
 
diff --git a/core/java/android/service/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
similarity index 97%
rename from core/java/android/service/textservice/SpellCheckerSession.java
rename to core/java/android/view/textservice/SpellCheckerSession.java
index 0dff3a6..bf07e71 100644
--- a/core/java/android/service/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.service.textservice;
+package android.view.textservice;
 
 import com.android.internal.textservice.ISpellCheckerSession;
 import com.android.internal.textservice.ISpellCheckerSessionListener;
@@ -38,6 +38,12 @@
 public class SpellCheckerSession {
     private static final String TAG = SpellCheckerSession.class.getSimpleName();
     private static final boolean DBG = false;
+    /**
+     * Name under which a SpellChecker service component publishes information about itself.
+     * This meta-data must reference an XML resource.
+     **/
+    public static final String SERVICE_META_DATA = "android.view.textservice.scs";
+
 
     private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
 
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index ae253cf..a60eb24 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -22,9 +22,9 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.service.textservice.SpellCheckerSession;
-import android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 import android.util.Log;
+import android.view.textservice.SpellCheckerSession;
+import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 
 import java.util.Locale;
 
@@ -81,18 +81,18 @@
         }
         // TODO: set a proper locale instead of the dummy locale
         final String localeString = locale == null ? "en" : locale.toString();
-        final SpellCheckerInfo info;
+        final SpellCheckerInfo sci;
         try {
-            info = sService.getCurrentSpellChecker(localeString);
+            sci = sService.getCurrentSpellChecker(localeString);
         } catch (RemoteException e) {
             return null;
         }
-        if (info == null) {
+        if (sci == null) {
             return null;
         }
-        final SpellCheckerSession session = new SpellCheckerSession(info, sService, listener);
+        final SpellCheckerSession session = new SpellCheckerSession(sci, sService, listener);
         try {
-            sService.getSpellCheckerService(info, localeString,
+            sService.getSpellCheckerService(sci.getId(), localeString,
                     session.getTextServicesSessionListener(),
                     session.getSpellCheckerSessionListener());
         } catch (RemoteException e) {
@@ -128,4 +128,18 @@
             return null;
         }
     }
+
+    /**
+     * @hide
+     */
+    public void setCurrentSpellChecker(SpellCheckerInfo sci) {
+        try {
+            if (sci == null) {
+                throw new NullPointerException("SpellCheckerInfo is null");
+            }
+            sService.setCurrentSpellChecker(sci.getId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setCurrentSpellChecker: " + e);
+        }
+    }
 }
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 738bcb9..9fbc4a7 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1163,7 +1163,12 @@
         final SslError ssl_error;
         try {
             X509Certificate cert = new X509CertImpl(cert_der);
-            ssl_error = new SslError(cert_error, cert, url);
+            SslCertificate sslCert = new SslCertificate(cert);
+            if (JniUtil.useChromiumHttpStack()) {
+                ssl_error = SslError.SslErrorFromChromiumErrorCode(cert_error, sslCert, url);
+            } else {
+                ssl_error = new SslError(cert_error, cert, url);
+            }
         } catch (IOException e) {
             // Can't get the certificate, not much to do.
             Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d584acd..642aa22 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -179,6 +179,7 @@
     private boolean         mBlockNetworkImage = false;
     private boolean         mBlockNetworkLoads;
     private boolean         mJavaScriptEnabled = false;
+    private boolean         mHardwareAccelSkia = false;
     private boolean         mShowVisualIndicator = false;
     private PluginState     mPluginState = PluginState.OFF;
     private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
@@ -1243,6 +1244,26 @@
     }
 
     /**
+     * Tell the WebView to use Skia's hardware accelerated rendering path
+     * @param flag True if the WebView should use Skia's hw-accel path
+     * @hide
+     */
+    public synchronized void setHardwareAccelSkiaEnabled(boolean flag) {
+        if (mHardwareAccelSkia != flag) {
+            mHardwareAccelSkia = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @return True if the WebView is using hardware accelerated skia
+     * @hide
+     */
+    public synchronized boolean getHardwareAccelSkiaEnabled() {
+        return mHardwareAccelSkia;
+    }
+
+    /**
      * Tell the WebView to show the visual indicator
      * @param flag True if the WebView should show the visual indicator
      * @hide
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5946ae4..f4fd551 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -625,6 +625,9 @@
     // If we are using a set picture, don't send view updates to webkit
     private boolean mBlockWebkitViewMessages = false;
 
+    // cached value used to determine if we need to switch drawing models
+    private boolean mHardwareAccelSkia = false;
+
     /*
      * Private message ids
      */
@@ -4394,6 +4397,12 @@
             int functor = nativeGetDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
                     mGLViewportEmpty ? null : mViewRectViewport, getScale(), extras);
             ((HardwareCanvas) canvas).callDrawGLFunction(functor);
+
+            if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
+                mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
+                nativeUseHardwareAccelSkia(mHardwareAccelSkia);
+            }
+
         } else {
             DrawFilter df = null;
             if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
@@ -9252,7 +9261,8 @@
     static final int NO_LEFTEDGE = -1;
     native int nativeGetBlockLeftEdge(int x, int y, float scale);
 
-    private native void nativeSetExpandedTileBounds(boolean enabled);
+    private native void     nativeSetExpandedTileBounds(boolean enabled);
+    private native void     nativeUseHardwareAccelSkia(boolean enabled);
 
     // Returns a pointer to the scrollable LayerAndroid at the given point.
     private native int      nativeScrollableLayer(int x, int y, Rect scrollRect,
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index b89c2a9..2788846 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.R;
 
 
 /**
@@ -65,4 +68,14 @@
     public CheckBox(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        if (isChecked()) {
+            event.getText().add(mContext.getString(R.string.checkbox_checked));
+        } else {
+            event.getText().add(mContext.getString(R.string.checkbox_not_checked));
+        }
+    }
 }
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index f3a6da7..7598e54 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -221,4 +221,14 @@
         super.onInitializeAccessibilityEvent(event);
         event.setChecked(mChecked);
     }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        if (isChecked()) {
+            event.getText().add(mContext.getString(R.string.radiobutton_selected));
+        } else {
+            event.getText().add(mContext.getString(R.string.radiobutton_not_selected));
+        }
+    }
 }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 1f29b16..946f009 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2428,7 +2428,7 @@
         if (mItemsCanFocus && (focusResult == null)
                 && selectedView != null && selectedView.hasFocus()) {
             final View focused = selectedView.findFocus();
-            if (distanceToView(focused) > 0) {
+            if (!isViewAncestorOf(focused, this) || distanceToView(focused) > 0) {
                 focused.clearFocus();
             }
         }
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index ebbe1cd..9fa649f 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.R;
 
 
 /**
@@ -72,4 +75,14 @@
             super.toggle();
         }
     }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        if (isChecked()) {
+            event.getText().add(mContext.getString(R.string.radiobutton_selected));
+        } else {
+            event.getText().add(mContext.getString(R.string.radiobutton_not_selected));
+        }
+    }
 }
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 8b4d3a9..3a22bfb 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -977,7 +977,7 @@
 
     private void onSubmitQuery() {
         CharSequence query = mQueryTextView.getText();
-        if (!TextUtils.isEmpty(query)) {
+        if (query != null && TextUtils.getTrimmedLength(query) > 0) {
             if (mOnQueryChangeListener == null
                     || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
                 if (mSearchable != null) {
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 0c80a11..57f73ac 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -364,8 +364,19 @@
     @Override
     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
         super.onPopulateAccessibilityEvent(event);
-        Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
-        event.getText().add(switchText.getText());
+        if (isChecked()) {
+            CharSequence text = mOnLayout.getText();
+            if (TextUtils.isEmpty(text)) {
+                text = mContext.getString(R.string.switch_on);
+            }
+            event.getText().add(text);
+        } else {
+            CharSequence text = mOffLayout.getText();
+            if (TextUtils.isEmpty(text)) {
+                text = mContext.getString(R.string.switch_off);
+            }
+            event.getText().add(text);
+        }
     }
 
     private Layout makeLayout(CharSequence text) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 127b6a3..ab66676 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -143,6 +143,9 @@
 import java.lang.ref.WeakReference;
 import java.text.BreakIterator;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
 
 /**
  * Displays text to the user and optionally allows them to edit it.  A TextView
@@ -326,8 +329,6 @@
     private int mTextSelectHandleLeftRes;
     private int mTextSelectHandleRightRes;
     private int mTextSelectHandleRes;
-    private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout;
-    private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout;
 
     private int mTextEditSuggestionItemLayout;
     private SuggestionsPopupWindow mSuggestionsPopupWindow;
@@ -813,22 +814,6 @@
                 mTextSelectHandleRes = a.getResourceId(attr, 0);
                 break;
 
-            case com.android.internal.R.styleable.TextView_textEditPasteWindowLayout:
-                mTextEditPasteWindowLayout = a.getResourceId(attr, 0);
-                break;
-
-            case com.android.internal.R.styleable.TextView_textEditNoPasteWindowLayout:
-                mTextEditNoPasteWindowLayout = a.getResourceId(attr, 0);
-                break;
-
-            case com.android.internal.R.styleable.TextView_textEditSidePasteWindowLayout:
-                mTextEditSidePasteWindowLayout = a.getResourceId(attr, 0);
-                break;
-
-            case com.android.internal.R.styleable.TextView_textEditSideNoPasteWindowLayout:
-                mTextEditSideNoPasteWindowLayout = a.getResourceId(attr, 0);
-                break;
-
             case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout:
                 mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
                 break;
@@ -8425,6 +8410,9 @@
             if (TextUtils.isEmpty(text)) {
                 text = getHint();
             }
+            if (TextUtils.isEmpty(text)) {
+                text = getContentDescription();
+            }
             if (!TextUtils.isEmpty(text)) {
                 event.getText().add(text);
             }
@@ -8714,7 +8702,7 @@
             final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY);
             stopSelectionActionMode();
             Selection.setSelection((Spannable) mText, offset);
-            getInsertionController().showWithPaste();
+            getInsertionController().showImmediately();
             handled = true;
         }
 
@@ -8786,6 +8774,7 @@
             mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
             mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
             mPopupWindow.setOutsideTouchable(true);
+            mPopupWindow.setClippingEnabled(true);
 
             mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
             mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
@@ -8826,12 +8815,42 @@
             int suggestionIndex; // the index of the suggestion inside suggestionSpan
         }
 
+        /**
+         * Returns the suggestion spans that cover the current cursor position. The suggestion
+         * spans are sorted according to the length of text that they are attached to.
+         */
+        private SuggestionSpan[] getSuggestionSpans() {
+            int pos = TextView.this.getSelectionStart();
+            Spannable spannable = (Spannable) TextView.this.mText;
+            SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
+
+            // Cache the span length for performance reason.
+            final HashMap<SuggestionSpan, Integer> spanLengthMap =
+                new HashMap<SuggestionSpan, Integer>();
+
+            for (SuggestionSpan suggestionSpan : suggestionSpans) {
+                int start = spannable.getSpanStart(suggestionSpan);
+                int end = spannable.getSpanEnd(suggestionSpan);
+                spanLengthMap.put(suggestionSpan, end - start);
+            }
+
+            // The suggestions are sorted according to the lenght of the text that they cover
+            // (shorter first)
+            Arrays.sort(suggestionSpans, new Comparator<SuggestionSpan>() {
+                public int compare(SuggestionSpan span1, SuggestionSpan span2) {
+                    return spanLengthMap.get(span1) - spanLengthMap.get(span2);
+                }
+            });
+
+            return suggestionSpans;
+        }
+
         public void show() {
             if (!(mText instanceof Editable)) return;
 
-            final int pos = TextView.this.getSelectionStart();
-            Spannable spannable = (Spannable)TextView.this.mText;
-            SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
+            Spannable spannable = (Spannable) TextView.this.mText;
+            SuggestionSpan[] suggestionSpans = getSuggestionSpans();
+
             final int nbSpans = suggestionSpans.length;
 
             int totalNbSuggestions = 0;
@@ -9431,83 +9450,84 @@
         }
     }
 
-    private class PastePopupWindow implements OnClickListener {
-        private final PopupWindow mContainer;
-        private final View[] mPasteViews = new View[4];
-        private final int[] mPasteViewLayouts = new int[] { 
-                mTextEditPasteWindowLayout,  mTextEditNoPasteWindowLayout, 
-                mTextEditSidePasteWindowLayout, mTextEditSideNoPasteWindowLayout };
-        
-        public PastePopupWindow() {
-            mContainer = new PopupWindow(TextView.this.mContext, null,
+    private class ActionPopupWindow implements OnClickListener {
+        private static final int TEXT_EDIT_ACTION_POPUP_TEXT =
+                com.android.internal.R.layout.text_edit_action_popup_text;
+        private final PopupWindow mPopupWindow;
+        private TextView mPasteTextView;
+        private TextView mReplaceTextView;
+        private LinearLayout mContentView;
+        // Whether or not the Paste action should be available when the action popup is displayed
+        private boolean mWithPaste;
+
+        public ActionPopupWindow() {
+            mPopupWindow = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
-            mContainer.setSplitTouchEnabled(true);
-            mContainer.setClippingEnabled(false);
-            mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+            mPopupWindow.setClippingEnabled(true);
+            mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
 
-            mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
-            mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
-        }
+            mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
+            mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
 
-        private int viewIndex(boolean onTop) {
-            return (onTop ? 0 : 1<<1) + (canPaste() ? 0 : 1<<0);
-        }
+            mContentView = new LinearLayout(TextView.this.getContext());
+            LayoutParams wrapContent = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT);
+            mContentView.setLayoutParams(wrapContent);
+            mContentView.setOrientation(LinearLayout.HORIZONTAL);
+            mContentView.setBackgroundResource(
+                    com.android.internal.R.drawable.text_edit_side_paste_window);
 
-        private void updateContent(boolean onTop) {
-            final int viewIndex = viewIndex(onTop);
-            View view = mPasteViews[viewIndex];
-
-            if (view == null) {
-                final int layout = mPasteViewLayouts[viewIndex];
-                LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
+            LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-                if (inflater != null) {
-                    view = inflater.inflate(layout, null);
-                }
 
-                if (view == null) {
-                    throw new IllegalArgumentException("Unable to inflate TextEdit paste window");
-                }
+            mPasteTextView = (TextView) inflater.inflate(TEXT_EDIT_ACTION_POPUP_TEXT, null);
+            mPasteTextView.setLayoutParams(wrapContent);
+            mContentView.addView(mPasteTextView);
+            mPasteTextView.setText(com.android.internal.R.string.paste);
+            mPasteTextView.setOnClickListener(this);
 
-                final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
-                view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
-                        ViewGroup.LayoutParams.WRAP_CONTENT));
-                view.measure(size, size);
+            mReplaceTextView = (TextView) inflater.inflate(TEXT_EDIT_ACTION_POPUP_TEXT, null);
+            mReplaceTextView.setLayoutParams(wrapContent);
+            mContentView.addView(mReplaceTextView);
+            mReplaceTextView.setText(com.android.internal.R.string.replace);
+            mReplaceTextView.setOnClickListener(this);
 
-                view.setOnClickListener(this);
-                
-                mPasteViews[viewIndex] = view;
-            }
-
-            mContainer.setContentView(view);
+            mPopupWindow.setContentView(mContentView);
         }
 
         public void show() {
-            updateContent(true);
+            mPasteTextView.setVisibility(mWithPaste && canPaste() ? View.VISIBLE : View.GONE);
+
+            final int size = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            mContentView.measure(size, size);
+
             positionAtCursor();
         }
 
         public void hide() {
-            mContainer.dismiss();
+            mPopupWindow.dismiss();
         }
 
         public boolean isShowing() {
-            return mContainer.isShowing();
+            return mPopupWindow.isShowing();
         }
 
         @Override
-        public void onClick(View v) {
-            if (canPaste()) {
+        public void onClick(View view) {
+            if (view == mPasteTextView && canPaste()) {
                 onTextContextMenuItem(ID_PASTE);
+                hide();
+            } else if (view == mReplaceTextView) {
+                showSuggestions();
             }
-            hide();
         }
 
         void positionAtCursor() {
-            View contentView = mContainer.getContentView();
-            int width = contentView.getMeasuredWidth();
-            int height = contentView.getMeasuredHeight();
-            final int offset = TextView.this.getSelectionStart();
+            int width = mContentView.getMeasuredWidth();
+            int height = mContentView.getMeasuredHeight();
+            final int selectionStart = TextView.this.getSelectionStart();
+            final int selectionEnd = TextView.this.getSelectionEnd();
+            final int offset = (selectionStart + selectionEnd) / 2;
             final int line = mLayout.getLineForOffset(offset);
             final int lineTop = mLayout.getLineTop(line);
             float primaryHorizontal = mLayout.getPrimaryHorizontal(offset);
@@ -9526,37 +9546,28 @@
             coords[0] += bounds.left;
             coords[1] += bounds.top;
 
-            final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+            // Vertical clipping, move under edited line and to the side of insertion cursor
             if (coords[1] < 0) {
-                updateContent(false);
-                // Update dimensions from new view
-                contentView = mContainer.getContentView();
-                width = contentView.getMeasuredWidth();
-                height = contentView.getMeasuredHeight();
-
-                // Vertical clipping, move under edited line and to the side of insertion cursor 
-                // TODO bottom clipping in case there is no system bar
                 coords[1] += height;
                 final int lineBottom = mLayout.getLineBottom(line);
                 final int lineHeight = lineBottom - lineTop;
                 coords[1] += lineHeight;
 
-                // Move to right hand side of insertion cursor by default. TODO RTL text.
+                // Assumes insertion and selection handles share the same height
                 final Drawable handle = mContext.getResources().getDrawable(mTextSelectHandleRes);
-                final int handleHalfWidth = handle.getIntrinsicWidth() / 2;
-
-                if (primaryHorizontal + handleHalfWidth + width < screenWidth) {
-                    coords[0] += handleHalfWidth + width / 2;
-                } else {
-                    coords[0] -= handleHalfWidth + width / 2;                    
-                }
-            } else {
-                // Horizontal clipping
-                coords[0] = Math.max(0, coords[0]);
-                coords[0] = Math.min(screenWidth - width, coords[0]);
+                coords[1] += handle.getIntrinsicHeight();
             }
 
-            mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
+            // Horizontal clipping
+            coords[0] = Math.max(0, coords[0]);
+            final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+            coords[0] = Math.min(screenWidth - width, coords[0]);
+
+            mPopupWindow.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
+        }
+
+        public void setShowWithPaste(boolean withPaste) {
+            mWithPaste = withPaste;
         }
     }
 
@@ -9581,6 +9592,10 @@
         private boolean mIsActive = false;
         // Used to detect that setFrame was called
         private boolean mNeedsUpdate = true;
+        // Transient action popup window for Paste and Replace actions
+        protected ActionPopupWindow mActionPopupWindow;
+        // Used to delay the appearance of the action popup window
+        private Runnable mActionPopupShower;
 
         public HandleView() {
             super(TextView.this.mContext);
@@ -9659,31 +9674,58 @@
 
         public void show() {
             if (isShowing()) {
-                mContainer.update(mContainerPositionX, mContainerPositionY,
-                        mRight - mLeft, mBottom - mTop);
+                mContainer.update(mContainerPositionX, mContainerPositionY, -1, -1);
             } else {
                 mContainer.showAtLocation(TextView.this, 0,
                         mContainerPositionX, mContainerPositionY);
 
-                mIsActive = true;
-
-                ViewTreeObserver vto = TextView.this.getViewTreeObserver();
-                vto.addOnPreDrawListener(this);
+                if (!mIsActive) {
+                    ViewTreeObserver vto = TextView.this.getViewTreeObserver();
+                    vto.addOnPreDrawListener(this);
+                    mIsActive = true;
+                }
             }
+            hideActionPopupWindow();
         }
 
         protected void dismiss() {
             mIsDragging = false;
             mContainer.dismiss();
+            onDetached();
         }
 
         public void hide() {
             dismiss();
 
-            mIsActive = false;
-
             ViewTreeObserver vto = TextView.this.getViewTreeObserver();
             vto.removeOnPreDrawListener(this);
+            mIsActive = false;
+        }
+
+        void showActionPopupWindow(int delay, boolean withPaste) {
+            if (mActionPopupWindow == null) {
+                mActionPopupWindow = new ActionPopupWindow();
+            }
+            if (mActionPopupShower == null) {
+                mActionPopupShower = new Runnable() {
+                    public void run() {
+                        mActionPopupWindow.show();
+                    }
+                };
+            } else {
+                TextView.this.removeCallbacks(mActionPopupShower);
+            }
+            mActionPopupWindow.setShowWithPaste(withPaste);
+            TextView.this.postDelayed(mActionPopupShower, delay);
+        }
+
+        protected void hideActionPopupWindow() {
+            if (mActionPopupShower != null) {
+                TextView.this.removeCallbacks(mActionPopupShower);
+            }
+            if (mActionPopupWindow != null) {
+                mActionPopupWindow.hide();
+            }
         }
 
         public boolean isShowing() {
@@ -9784,8 +9826,7 @@
                 }
 
                 if (isPositionVisible()) {
-                    mContainer.update(mContainerPositionX, mContainerPositionY,
-                            mRight - mLeft, mBottom - mTop);
+                    mContainer.update(mContainerPositionX, mContainerPositionY, -1, -1);
 
                     if (mIsActive && !isShowing()) {
                         show();
@@ -9863,57 +9904,39 @@
         }
 
         void onHandleMoved() {
-            // Does nothing by default
+            hideActionPopupWindow();
         }
 
         public void onDetached() {
-            // Should be overriden to clean possible Runnable
+            hideActionPopupWindow();
         }
     }
 
     private class InsertionHandleView extends HandleView {
-        private static final int DELAY_BEFORE_FADE_OUT = 4000;
+        private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
         private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
 
-        // Used to detect taps on the insertion handle, which will affect the PastePopupWindow
+        // Used to detect taps on the insertion handle, which will affect the ActionPopupWindow
         private float mDownPositionX, mDownPositionY;
-        private PastePopupWindow mPastePopupWindow;
         private Runnable mHider;
-        private Runnable mPastePopupShower;
 
         @Override
         public void show() {
             super.show();
-            hideDelayed();
-            hidePastePopupWindow();
+            hideAfterDelay();
         }
 
-        public void show(int delayBeforePaste) {
+        public void show(int delayBeforeShowActionPopup) {
             show();
 
             final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
             if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) {
-                delayBeforePaste = 0;
+                delayBeforeShowActionPopup = 0;
             }
-            if (delayBeforePaste == 0 || canPaste()) {
-                if (mPastePopupShower == null) {
-                    mPastePopupShower = new Runnable() {
-                        public void run() {
-                            showPastePopupWindow();
-                        }
-                    };
-                }
-                TextView.this.postDelayed(mPastePopupShower, delayBeforePaste);
-            }
+            showActionPopupWindow(delayBeforeShowActionPopup, true);
         }
 
-        @Override
-        protected void dismiss() {
-            super.dismiss();
-            onDetached();
-        }
-
-        private void hideDelayed() {
+        private void hideAfterDelay() {
             removeHiderCallback();
             if (mHider == null) {
                 mHider = new Runnable() {
@@ -9922,7 +9945,7 @@
                     }
                 };
             }
-            TextView.this.postDelayed(mHider, DELAY_BEFORE_FADE_OUT);
+            TextView.this.postDelayed(mHider, DELAY_BEFORE_HANDLE_FADES_OUT);
         }
 
         private void removeHiderCallback() {
@@ -9956,18 +9979,18 @@
                     final float deltaY = mDownPositionY - ev.getRawY();
                     final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
                     if (distanceSquared < mSquaredTouchSlopDistance) {
-                        if (mPastePopupWindow != null && mPastePopupWindow.isShowing()) {
-                            // Tapping on the handle dismisses the displayed paste view,
-                            mPastePopupWindow.hide();
+                        if (mActionPopupWindow != null && mActionPopupWindow.isShowing()) {
+                            // Tapping on the handle dismisses the displayed action popup
+                            mActionPopupWindow.hide();
                         } else {
                             show(0);
                         }
                     }
-                    hideDelayed();
+                    hideAfterDelay();
                     break;
 
                 case MotionEvent.ACTION_CANCEL:
-                    hideDelayed();
+                    hideAfterDelay();
                     break;
 
                 default:
@@ -9992,32 +10015,16 @@
             updateOffset(getOffsetForPosition(x, y));
         }
 
-        void showPastePopupWindow() {
-            if (mPastePopupWindow == null) {
-                mPastePopupWindow = new PastePopupWindow();
-            }
-            mPastePopupWindow.show();
-        }
-
         @Override
         void onHandleMoved() {
+            super.onHandleMoved();
             removeHiderCallback();
-            hidePastePopupWindow();
-        }
-
-        void hidePastePopupWindow() {
-            if (mPastePopupShower != null) {
-                TextView.this.removeCallbacks(mPastePopupShower);
-            }
-            if (mPastePopupWindow != null) {
-                mPastePopupWindow.hide();
-            }
         }
 
         @Override
         public void onDetached() {
+            super.onDetached();
             removeHiderCallback();
-            hidePastePopupWindow();
         }
     }
 
@@ -10056,6 +10063,10 @@
 
             Selection.setSelection((Spannable) mText, offset, selectionEnd);
         }
+
+        public ActionPopupWindow getActionPopupWindow() {
+            return mActionPopupWindow;
+        }
     }
 
     private class SelectionEndHandleView extends HandleView {
@@ -10093,6 +10104,10 @@
 
             Selection.setSelection((Spannable) mText, selectionStart, offset);
         }
+
+        public void setActionPopupWindow(ActionPopupWindow actionPopupWindow) {
+            mActionPopupWindow = actionPopupWindow;
+        }
     }
 
     /**
@@ -10122,16 +10137,16 @@
     }
 
     private class InsertionPointCursorController implements CursorController {
-        private static final int DELAY_BEFORE_PASTE = 2000;
+        private static final int DELAY_BEFORE_PASTE_ACTION = 1600;
 
         private InsertionHandleView mHandle;
 
         public void show() {
-            ((InsertionHandleView) getHandle()).show(DELAY_BEFORE_PASTE);
+            getHandle().show(DELAY_BEFORE_PASTE_ACTION);
         }
 
-        public void showWithPaste() {
-            ((InsertionHandleView) getHandle()).show(0);
+        public void showImmediately() {
+            getHandle().show(0);
         }
 
         public void hide() {
@@ -10146,7 +10161,7 @@
             }
         }
 
-        private HandleView getHandle() {
+        private InsertionHandleView getHandle() {
             if (mHandle == null) {
                 mHandle = new InsertionHandleView();
             }
@@ -10163,6 +10178,7 @@
     }
 
     private class SelectionModifierCursorController implements CursorController {
+        private static final int DELAY_BEFORE_REPLACE_ACTION = 1200;
         // The cursor controller handles, lazily created when shown.
         private SelectionStartHandleView mStartHandle;
         private SelectionEndHandleView mEndHandle;
@@ -10189,6 +10205,11 @@
             mStartHandle.show();
             mEndHandle.show();
 
+            // Make sure both left and right handles share the same ActionPopupWindow (so that
+            // moving any of the handles hides the action popup).
+            mStartHandle.showActionPopupWindow(DELAY_BEFORE_REPLACE_ACTION, false);
+            mEndHandle.setActionPopupWindow(mStartHandle.getActionPopupWindow());
+
             hideInsertionPointCursorController();
             hideSuggestions();
         }
@@ -10217,7 +10238,7 @@
                         final float deltaY = y - mPreviousTapPositionY;
                         final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
                         if (distanceSquared < mSquaredTouchSlopDistance) {
-                            showSuggestions();
+                            startSelectionActionMode();
                             mDiscardNextActionUp = true;
                         }
                     }
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index 3b680e8..ba9de24 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -22,6 +22,9 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.internal.R;
 
 /**
  * Displays checked/unchecked states as a button
@@ -146,5 +149,14 @@
             mIndicatorDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
         }
     }
-    
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        super.onPopulateAccessibilityEvent(event);
+        if (isChecked()) {
+            event.getText().add(mContext.getString(R.string.togglebutton_pressed));
+        } else {
+            event.getText().add(mContext.getString(R.string.togglebutton_not_pressed));
+        }
+    }
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c11fc10..3916e86 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -30,7 +30,7 @@
     void disable(int state);
     void animateExpand();
     void animateCollapse();
-    void setLightsOn(boolean on);
+    void setSystemUiVisibility(int vis);
     void topAppWindowChanged(boolean menuVisible);
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
     void setHardKeyboardStatus(boolean available, boolean enabled);
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index 2a045e3..4d7dfbb 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -28,9 +28,10 @@
  */
 interface ITextServicesManager {
     SpellCheckerInfo getCurrentSpellChecker(String locale);
-    oneway void getSpellCheckerService(in SpellCheckerInfo info, in String locale,
+    oneway void getSpellCheckerService(String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
             in ISpellCheckerSessionListener scListener);
     oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
+    oneway void setCurrentSpellChecker(String sciId);
     SpellCheckerInfo[] getEnabledSpellCheckers();
 }
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 59bef92..4667e5f 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -69,8 +69,8 @@
     public void tag(FileDescriptor fd) throws SocketException {
         final SocketTags options = threadSocketTags.get();
         if (LOGD) {
-            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=" + options.statsTag
-                    + ", statsUid=" + options.statsUid);
+            Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
+                    + Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
         }
         try {
             // TODO: skip tagging when options would be no-op
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 682c3c4..f8be136 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -88,9 +88,12 @@
         return canvas->getDevice()->accessBitmap(false).height();
     }
 
-    static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas,
-                          SkBitmap* bitmap) {
-        canvas->setBitmapDevice(*bitmap);
+    static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas, SkBitmap* bitmap) {
+        if (bitmap) {
+            canvas->setBitmapDevice(*bitmap);
+        } else {
+            canvas->setDevice(NULL);
+        }
     }
  
     static int saveAll(JNIEnv* env, jobject jcanvas) {
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
index 4c84324..488b5c24 100644
--- a/core/jni/android_bluetooth_BluetoothSocket.cpp
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -57,6 +57,9 @@
 
 static const int RFCOMM_SO_SNDBUF = 70 * 1024;  // 70 KB send buffer
 
+static void abortNative(JNIEnv *env, jobject obj);
+static void destroyNative(JNIEnv *env, jobject obj);
+
 static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
     struct asocket *s =
             (struct asocket *) env->GetIntField(obj, field_mSocketData);
@@ -172,6 +175,7 @@
     socklen_t addr_sz;
     struct sockaddr *addr;
     struct asocket *s = get_socketData(env, obj);
+    int retry = 0;
 
     if (!s)
         return;
@@ -226,10 +230,30 @@
         return;
     }
 
+connect:
     ret = asocket_connect(s, addr, addr_sz, -1);
     LOGV("...connect(%d, %s) = %d (errno %d)",
             s->fd, TYPE_AS_STR(type), ret, errno);
 
+    if (ret && errno == EALREADY && retry < 2) {
+        /* workaround for bug 5082381 (EALREADY on ACL collision):
+         * retry the connect. Unfortunately we have to create a new fd.
+         * It's not ideal to switch the fd underneath the object, but
+         * is currently safe */
+        LOGD("Hit bug 5082381 (EALREADY on ACL collision), trying workaround");
+        usleep(100000);
+        retry++;
+        abortNative(env, obj);
+        destroyNative(env, obj);
+        initSocketNative(env, obj);
+        if (env->ExceptionOccurred()) {
+            return;
+        }
+        goto connect;
+    }
+    if (!ret && retry > 0)
+        LOGD("...workaround ok");
+
     if (ret)
         jniThrowIOException(env, errno);
 
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 3dcaa37..884fa78 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -45,6 +45,8 @@
     jfieldID    rect_right;
     jfieldID    rect_bottom;
     jmethodID   post_event;
+    jmethodID   rect_constructor;
+    jmethodID   face_constructor;
 };
 
 static fields_t fields;
@@ -57,8 +59,10 @@
     JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera);
     ~JNICameraContext() { release(); }
     virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
-    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr);
+    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
+                          camera_frame_metadata_t *metadata);
     virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
+    void postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata);
     void addCallbackBuffer(JNIEnv *env, jbyteArray cbb, int msgType);
     void setCallbackMode(JNIEnv *env, bool installed, bool manualMode);
     sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; }
@@ -74,6 +78,8 @@
     jobject     mCameraJObjectWeak;     // weak reference to java object
     jclass      mCameraJClass;          // strong reference to java class
     sp<Camera>  mCamera;                // strong reference to native object
+    jclass      mFaceClass;  // strong reference to Face class
+    jclass      mRectClass;  // strong reference to Rect class
     Mutex       mLock;
 
     /*
@@ -124,6 +130,12 @@
     mCameraJClass = (jclass)env->NewGlobalRef(clazz);
     mCamera = camera;
 
+    jclass faceClazz = env->FindClass("android/hardware/Camera$Face");
+    mFaceClass = (jclass) env->NewGlobalRef(faceClazz);
+
+    jclass rectClazz = env->FindClass("android/graphics/Rect");
+    mRectClass = (jclass) env->NewGlobalRef(rectClazz);
+
     mManualBufferMode = false;
     mManualCameraCallbackSet = false;
 }
@@ -142,6 +154,14 @@
         env->DeleteGlobalRef(mCameraJClass);
         mCameraJClass = NULL;
     }
+    if (mFaceClass != NULL) {
+        env->DeleteGlobalRef(mFaceClass);
+        mFaceClass = NULL;
+    }
+    if (mRectClass != NULL) {
+        env->DeleteGlobalRef(mRectClass);
+        mRectClass = NULL;
+    }
     clearCallbackBuffers_l(env);
     mCamera.clear();
 }
@@ -263,7 +283,8 @@
     }
 }
 
-void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
+void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr,
+                                camera_frame_metadata_t *metadata)
 {
     // VM pointer will be NULL if object is released
     Mutex::Autolock _l(mLock);
@@ -273,8 +294,10 @@
         return;
     }
 
+    int32_t dataMsgType = msgType & ~CAMERA_MSG_PREVIEW_METADATA;
+
     // return data based on callback type
-    switch (msgType) {
+    switch (dataMsgType) {
         case CAMERA_MSG_VIDEO_FRAME:
             // should never happen
             break;
@@ -285,23 +308,63 @@
             LOGV("rawCallback");
             if (mRawImageCallbackBuffers.isEmpty()) {
                 env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
-                        mCameraJObjectWeak, msgType, 0, 0, NULL);
+                        mCameraJObjectWeak, dataMsgType, 0, 0, NULL);
             } else {
-                copyAndPost(env, dataPtr, msgType);
+                copyAndPost(env, dataPtr, dataMsgType);
             }
             break;
 
-        default:
-            LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
-            copyAndPost(env, dataPtr, msgType);
+        // There is no data.
+        case 0:
             break;
+
+        default:
+            LOGV("dataCallback(%d, %p)", dataMsgType, dataPtr.get());
+            copyAndPost(env, dataPtr, dataMsgType);
+            break;
+    }
+
+    // post frame metadata to Java
+    if (metadata && (msgType & CAMERA_MSG_PREVIEW_METADATA)) {
+        postMetadata(env, CAMERA_MSG_PREVIEW_METADATA, metadata);
     }
 }
 
 void JNICameraContext::postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
 {
     // TODO: plumb up to Java. For now, just drop the timestamp
-    postData(msgType, dataPtr);
+    postData(msgType, dataPtr, NULL);
+}
+
+void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata)
+{
+    jobjectArray obj = NULL;
+    obj = (jobjectArray) env->NewObjectArray(metadata->number_of_faces,
+                                             mFaceClass, NULL);
+    if (obj == NULL) {
+        LOGE("Couldn't allocate face metadata array");
+        return;
+    }
+
+    for (int i = 0; i < metadata->number_of_faces; i++) {
+        jobject face = env->NewObject(mFaceClass, fields.face_constructor);
+        env->SetObjectArrayElement(obj, i, face);
+
+        jobject rect = env->NewObject(mRectClass, fields.rect_constructor);
+        env->SetIntField(rect, fields.rect_left, metadata->faces[i].rect[0]);
+        env->SetIntField(rect, fields.rect_top, metadata->faces[i].rect[1]);
+        env->SetIntField(rect, fields.rect_right, metadata->faces[i].rect[2]);
+        env->SetIntField(rect, fields.rect_bottom, metadata->faces[i].rect[3]);
+
+        env->SetObjectField(face, fields.face_rect, rect);
+        env->SetIntField(face, fields.face_score, metadata->faces[i].score);
+
+        env->DeleteLocalRef(face);
+        env->DeleteLocalRef(rect);
+    }
+    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
+            mCameraJObjectWeak, msgType, 0, 0, obj);
+    env->DeleteLocalRef(obj);
 }
 
 void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualMode)
@@ -715,7 +778,7 @@
 }
 
 static void android_hardware_Camera_startFaceDetection(JNIEnv *env, jobject thiz,
-        jint type, jobjectArray face)
+        jint type)
 {
     LOGV("startFaceDetection");
     JNICameraContext* context;
@@ -878,6 +941,19 @@
         return -1;
     }
 
+    clazz = env->FindClass("android/graphics/Rect");
+    fields.rect_constructor = env->GetMethodID(clazz, "<init>", "()V");
+    if (fields.rect_constructor == NULL) {
+        LOGE("Can't find android/graphics/Rect.Rect()");
+        return -1;
+    }
+
+    clazz = env->FindClass("android/hardware/Camera$Face");
+    fields.face_constructor = env->GetMethodID(clazz, "<init>", "()V");
+    if (fields.face_constructor == NULL) {
+        LOGE("Can't find android/hardware/Camera$Face.Face()");
+        return -1;
+    }
 
     // Register native functions
     return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 41056fd..819449a 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -846,14 +846,13 @@
     LOGV("%s", __FUNCTION__);
     native_data_t *nat = get_native_data(env, object);
     if (nat) {
-        DBusMessage *reply, *msg;
+        DBusMessage *msg;
         DBusMessageIter iter;
-        DBusError err;
+        dbus_bool_t reply = JNI_FALSE;
 
         const char *c_key = env->GetStringUTFChars(key, NULL);
         const char *c_path = env->GetStringUTFChars(path, NULL);
 
-        dbus_error_init(&err);
         msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
                                           c_path, DBUS_DEVICE_IFACE, "SetProperty");
         if (!msg) {
@@ -867,19 +866,14 @@
         dbus_message_iter_init_append(msg, &iter);
         append_variant(&iter, type, value);
 
-        reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+        // Asynchronous call - the callbacks come via Device propertyChange
+        reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);
         dbus_message_unref(msg);
 
-        env->ReleaseStringUTFChars(key, c_key);
         env->ReleaseStringUTFChars(path, c_path);
-        if (!reply) {
-            if (dbus_error_is_set(&err)) {
-                LOG_AND_FREE_DBUS_ERROR(&err);
-            } else
-            LOGE("DBus reply is NULL in function %s", __FUNCTION__);
-            return JNI_FALSE;
-        }
-        return JNI_TRUE;
+        env->ReleaseStringUTFChars(key, c_key);
+
+        return reply ? JNI_TRUE : JNI_FALSE;
     }
 #endif
     return JNI_FALSE;
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index daa0adc..01a4c09 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -69,8 +69,7 @@
     mCalculatedIdBits = idBits;
 
     for (uint32_t index = 0; !idBits.isEmpty(); index++) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
+        uint32_t id = idBits.clearFirstMarkedBit();
 
         float vx, vy;
         mVelocityTracker.getVelocity(id, &vx, &vy);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 92ba1d0..f99a94c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -393,7 +393,8 @@
         android:description="@string/permdesc_nfc"
         android:label="@string/permlab_nfc" />
 
-    <!-- Allows applications to provide VPN functionality -->
+    <!-- Allows applications to provide VPN functionality.
+         @hide Pending API council approval -->
     <permission android:name="android.permission.VPN"
         android:permissionGroup="android.permission-group.NETWORK"
         android:protectionLevel="dangerous"
@@ -663,7 +664,7 @@
         android:label="@string/permlab_reorderTasks"
         android:description="@string/permdesc_reorderTasks" />
 
-    <!-- Allows an application to change to remove/kill tasks -->
+    <!-- @hide Allows an application to change to remove/kill tasks -->
     <permission android:name="android.permission.REMOVE_TASKS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signature"
@@ -711,7 +712,7 @@
         android:label="@string/permlab_dump"
         android:description="@string/permdesc_dump" />
 
-    <!-- Allows an application to retrieve the content of the active window
+    <!-- @hide Allows an application to retrieve the content of the active window
          An active window is the window that has fired an accessibility event. -->
     <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
@@ -1503,10 +1504,6 @@
                 android:excludeFromRecents="true">
         </activity>
 
-        <service android:name="com.android.internal.service.wallpaper.ImageWallpaper"
-                android:permission="android.permission.BIND_WALLPAPER">
-        </service>
-
         <receiver android:name="com.android.server.BootReceiver" >
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
index 1b34672..3239dd2 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
index 1b34672..3239dd2 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
index 71ae113..6840962 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
index 739dff3..45c957b 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
index 26976c5..45c957b 100644
--- a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
index 9d787f1..6549253 100644
--- a/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
index 7699c47..ef3ec7a 100644
--- a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
index 7699c47..ef3ec7a 100644
--- a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
index 1e9c9d2..f4f657b 100644
--- a/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
index 27e7965..ef12e72 100644
--- a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
index 1dbabd3..ef12e72 100644
--- a/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
index 12eec10..ec7fa78 100644
--- a/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
index 5a94b8d..93a30e3 100644
--- a/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
index 5a94b8d..93a30e3 100644
--- a/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_small_disabled_focused_holo_dark.9.png
deleted file mode 100644
index 6781d79..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_disabled_focused_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_small_disabled_focused_holo_light.9.png
deleted file mode 100644
index 6781d79..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_disabled_focused_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_small_disabled_holo_dark.9.png
deleted file mode 100644
index c8460eb..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_disabled_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_small_disabled_holo_light.9.png
deleted file mode 100644
index c8460eb..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_disabled_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_small_focused_holo_dark.9.png
deleted file mode 100644
index a116400..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_focused_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_small_focused_holo_light.9.png
deleted file mode 100644
index 0b5c05bd..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_focused_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal_holo_dark.9.png
deleted file mode 100644
index 6a55b98..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_normal_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal_holo_light.9.png
deleted file mode 100644
index 0371310..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_normal_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_small_pressed_holo_dark.9.png
deleted file mode 100644
index 4d05332..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_pressed_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_small_pressed_holo_light.9.png
deleted file mode 100644
index 4d05332..0000000
--- a/core/res/res/drawable-hdpi/btn_default_small_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
old mode 100755
new mode 100644
index 63a9219..3ecf008
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
old mode 100755
new mode 100644
index d977914..6e1f0dd
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
old mode 100755
new mode 100644
index c04393c..90b35b8
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
old mode 100755
new mode 100644
index 96bd351..6b4b388
--- a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
old mode 100755
new mode 100644
index c30b993..c0ed2c6
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
old mode 100755
new mode 100644
index 730c113..fa386b8
--- a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
old mode 100755
new mode 100644
index 17de0eb..9fbd1e99
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
old mode 100755
new mode 100644
index 7e62cf9..1800eb4
--- a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
old mode 100755
new mode 100644
index a06f1fc..45d99ee
--- a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
old mode 100755
new mode 100644
index 21ad0d8..8929825
--- a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
old mode 100755
new mode 100644
index c0f6f74..5fc3fbd
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
old mode 100755
new mode 100644
index c0f6f74..5fc3fbd
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
old mode 100755
new mode 100644
index 44a2f8bb..b0cfa4b
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
old mode 100755
new mode 100644
index 44a2f8bb..b0cfa4b
--- a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
old mode 100755
new mode 100644
index 53eb636f..054c18b
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
old mode 100755
new mode 100644
index 53eb636f..054c18b
--- a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
old mode 100755
new mode 100644
index baab86f..a858836
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
old mode 100755
new mode 100644
index baab86f..a858836
--- a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
old mode 100755
new mode 100644
index 6a954a6..b5aa5c1
--- a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
old mode 100755
new mode 100644
index 6a954a6..b5aa5c1
--- a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_label_left_holo_dark.9.png b/core/res/res/drawable-hdpi/fastscroll_label_left_holo_dark.9.png
index 3eb81a1..0a1bca8 100644
--- a/core/res/res/drawable-hdpi/fastscroll_label_left_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_label_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_label_left_holo_light.9.png b/core/res/res/drawable-hdpi/fastscroll_label_left_holo_light.9.png
index cc14a63..fd5b18d 100644
--- a/core/res/res/drawable-hdpi/fastscroll_label_left_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_label_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_label_right_holo_dark.9.png b/core/res/res/drawable-hdpi/fastscroll_label_right_holo_dark.9.png
index c59f135..97d61f4 100644
--- a/core/res/res/drawable-hdpi/fastscroll_label_right_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_label_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_label_right_holo_light.9.png b/core/res/res/drawable-hdpi/fastscroll_label_right_holo_light.9.png
index 054707d..9afd6ab 100644
--- a/core/res/res/drawable-hdpi/fastscroll_label_right_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_label_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_thumb_default_holo.png b/core/res/res/drawable-hdpi/fastscroll_thumb_default_holo.png
index bc960ab..9233636 100644
--- a/core/res/res/drawable-hdpi/fastscroll_thumb_default_holo.png
+++ b/core/res/res/drawable-hdpi/fastscroll_thumb_default_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_thumb_pressed_holo.png b/core/res/res/drawable-hdpi/fastscroll_thumb_pressed_holo.png
index f850e9e..396a8d6 100644
--- a/core/res/res/drawable-hdpi/fastscroll_thumb_pressed_holo.png
+++ b/core/res/res/drawable-hdpi/fastscroll_thumb_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_track_default_holo_dark.9.png b/core/res/res/drawable-hdpi/fastscroll_track_default_holo_dark.9.png
index 9e10a29..44502db 100644
--- a/core/res/res/drawable-hdpi/fastscroll_track_default_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_track_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_track_default_holo_light.9.png b/core/res/res/drawable-hdpi/fastscroll_track_default_holo_light.9.png
index 9e10a29..44502db 100644
--- a/core/res/res/drawable-hdpi/fastscroll_track_default_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_track_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_dark.9.png
index be260dd..c5637727 100644
--- a/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_light.9.png
index be260dd..88f60b3 100644
--- a/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/fastscroll_track_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_paste_bubble_disabled_holo.png b/core/res/res/drawable-hdpi/ic_paste_bubble_disabled_holo.png
deleted file mode 100644
index 42ac16b..0000000
--- a/core/res/res/drawable-hdpi/ic_paste_bubble_disabled_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_paste_bubble_holo.png b/core/res/res/drawable-hdpi/ic_paste_bubble_holo.png
deleted file mode 100644
index 15bd8b2..0000000
--- a/core/res/res/drawable-hdpi/ic_paste_bubble_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_activated_holo.9.png b/core/res/res/drawable-hdpi/list_activated_holo.9.png
index 12a379b..4ea7afa 100644
--- a/core/res/res/drawable-hdpi/list_activated_holo.9.png
+++ b/core/res/res/drawable-hdpi/list_activated_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_focused_holo.9.png b/core/res/res/drawable-hdpi/list_focused_holo.9.png
index e354629..516f5c7 100644
--- a/core/res/res/drawable-hdpi/list_focused_holo.9.png
+++ b/core/res/res/drawable-hdpi/list_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_longpressed_holo.9.png b/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
index 367c7d9..d06549c 100644
--- a/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
+++ b/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png
index 9eae8f4..82f6734 100644
--- a/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png
index a9131fc..82f6734 100644
--- a/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
index 21e2fb9..5522f5c 100644
--- a/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
index 8e9c02c..a9dbfb9 100644
--- a/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
index 780e86d..5f5b23f 100644
--- a/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selected_holo_light.9.png b/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
index 999d05b..5f5b23f 100644
--- a/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png b/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png
index f134a59..e2c63b0 100644
--- a/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/progress_primary_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png b/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png
index f134a59..e2c63b0 100644
--- a/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/progress_primary_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png b/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png
index 22d608a..4b204e7 100644
--- a/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/progress_secondary_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png b/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png
index 22d608a..4b204e7 100644
--- a/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/progress_secondary_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_holo_dark.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_holo_dark.9.png
index 575edee..7c82955 100644
--- a/core/res/res/drawable-hdpi/scrollbar_handle_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/scrollbar_handle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_holo_light.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_holo_light.9.png
index 575edee..7c82955 100644
--- a/core/res/res/drawable-hdpi/scrollbar_handle_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/scrollbar_handle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png b/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png
index d428e5a..822da81 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_holo.png b/core/res/res/drawable-hdpi/scrubber_control_holo.png
index a5fb73c..9957851 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_holo.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_primary_holo.9.png b/core/res/res/drawable-hdpi/scrubber_primary_holo.9.png
index 3bc78a8..0fdd530 100644
--- a/core/res/res/drawable-hdpi/scrubber_primary_holo.9.png
+++ b/core/res/res/drawable-hdpi/scrubber_primary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_secondary_holo.9.png b/core/res/res/drawable-hdpi/scrubber_secondary_holo.9.png
index 8eadf00..a7eaf66 100644
--- a/core/res/res/drawable-hdpi/scrubber_secondary_holo.9.png
+++ b/core/res/res/drawable-hdpi/scrubber_secondary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
index a2a6678..8c93779 100644
--- a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_16_outer_holo.png b/core/res/res/drawable-hdpi/spinner_16_outer_holo.png
index 733e852..d272f93 100644
--- a/core/res/res/drawable-hdpi/spinner_16_outer_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_16_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
index e81e002..3c371b2d 100644
--- a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_20_outer_holo.png b/core/res/res/drawable-hdpi/spinner_20_outer_holo.png
index 1da2f90..2820b5f 100644
--- a/core/res/res/drawable-hdpi/spinner_20_outer_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_20_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
index 8bbe6a0..a992251 100644
--- a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_48_outer_holo.png b/core/res/res/drawable-hdpi/spinner_48_outer_holo.png
index d023d10..27452b1 100644
--- a/core/res/res/drawable-hdpi/spinner_48_outer_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_48_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
index fa6cb6f..3d426e0 100644
--- a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_76_outer_holo.png b/core/res/res/drawable-hdpi/spinner_76_outer_holo.png
index 00d577f..92f77a3 100644
--- a/core/res/res/drawable-hdpi/spinner_76_outer_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_76_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_active_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_active_holo_dark.9.png
deleted file mode 100644
index a38c03a..0000000
--- a/core/res/res/drawable-hdpi/textfield_active_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_active_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_active_holo_light.9.png
deleted file mode 100644
index 6a88a69..0000000
--- a/core/res/res/drawable-hdpi/textfield_active_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_active_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_multiline_active_holo_dark.9.png
deleted file mode 100644
index 7528479..0000000
--- a/core/res/res/drawable-hdpi/textfield_multiline_active_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_multiline_active_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_multiline_active_holo_light.9.png
deleted file mode 100644
index 4c7d9e7..0000000
--- a/core/res/res/drawable-hdpi/textfield_multiline_active_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
index 918f972..e5197e6 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
index 918f972..e5197e6 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
index 1ae6cf2..9a24b9c 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
index 0514a7c..c832855 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
index 8cc4daf..c832855 100644
--- a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
index 6449650..8838414 100644
--- a/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
index 7f44eae2..e0a1e0d 100644
--- a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
index 7f44eae2..e0a1e0d 100644
--- a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
index 3e9e6c3..e4864c9 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
index 9df36a2..3d9310a 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
index 7abdfde..3d9310a 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
index df4927f..18ec722 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
index cb70e35..1e3314e 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
index cb70e35..1e3314e 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_small_disabled_focused_holo_dark.9.png
deleted file mode 100644
index be14b35..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_disabled_focused_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_small_disabled_focused_holo_light.9.png
deleted file mode 100644
index be14b35..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_disabled_focused_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_small_disabled_holo_dark.9.png
deleted file mode 100644
index dd12fcb..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_disabled_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_small_disabled_holo_light.9.png
deleted file mode 100644
index dd12fcb..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_disabled_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_small_focused_holo_dark.9.png
deleted file mode 100644
index bd9975c..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_focused_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_small_focused_holo_light.9.png
deleted file mode 100644
index 703e394..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_focused_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal_holo_dark.9.png
deleted file mode 100644
index b77faad..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_normal_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal_holo_light.9.png
deleted file mode 100644
index ead3f5e..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_normal_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_small_pressed_holo_dark.9.png
deleted file mode 100644
index d460d0a..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_pressed_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_small_pressed_holo_light.9.png
deleted file mode 100644
index d460d0a..0000000
--- a/core/res/res/drawable-mdpi/btn_default_small_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off.9.png b/core/res/res/drawable-mdpi/btn_toggle_off.9.png
index 26ee1c2..38e810c 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
old mode 100755
new mode 100644
index 1964085..5f2017d
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
old mode 100755
new mode 100644
index d86a9e6..eab31e8
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
old mode 100755
new mode 100644
index f3f1ab2..29f9e23
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
old mode 100755
new mode 100644
index d157fed..2d3574d
--- a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
old mode 100755
new mode 100644
index f99d946..deea02d
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
old mode 100755
new mode 100644
index a313744..d480b2e
--- a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
old mode 100755
new mode 100644
index 00a589f..7f9d813
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
old mode 100755
new mode 100644
index e16e470..848621a
--- a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
old mode 100755
new mode 100644
index 6f7dd45..2a94003
--- a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
old mode 100755
new mode 100644
index 490c83d..75983d8
--- a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on.9.png b/core/res/res/drawable-mdpi/btn_toggle_on.9.png
index 53e95af..ef39dec 100644
--- a/core/res/res/drawable-mdpi/btn_toggle_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
old mode 100755
new mode 100644
index 45dc08f..909586a
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
old mode 100755
new mode 100644
index 45dc08f..909586a
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
old mode 100755
new mode 100644
index 11dc01d..d64e60a
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
old mode 100755
new mode 100644
index 11dc01d..d64e60a
--- a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
old mode 100755
new mode 100644
index 2c95ebd..3b64aa1
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
old mode 100755
new mode 100644
index 2c95ebd..3b64aa1
--- a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
old mode 100755
new mode 100644
index 7c410c0..6039850
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
old mode 100755
new mode 100644
index 7c410c0..6039850
--- a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
old mode 100755
new mode 100644
index afb31e1..21b655b
--- a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
old mode 100755
new mode 100644
index afb31e1..21b655b
--- a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_label_left_holo_dark.9.png b/core/res/res/drawable-mdpi/fastscroll_label_left_holo_dark.9.png
index 0dce616..b3fd908 100644
--- a/core/res/res/drawable-mdpi/fastscroll_label_left_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_label_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_label_left_holo_light.9.png b/core/res/res/drawable-mdpi/fastscroll_label_left_holo_light.9.png
index e1028cc..a64d4d1 100644
--- a/core/res/res/drawable-mdpi/fastscroll_label_left_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_label_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_label_right_holo_dark.9.png b/core/res/res/drawable-mdpi/fastscroll_label_right_holo_dark.9.png
index b6b68ea..6469fca 100644
--- a/core/res/res/drawable-mdpi/fastscroll_label_right_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_label_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_label_right_holo_light.9.png b/core/res/res/drawable-mdpi/fastscroll_label_right_holo_light.9.png
index 5087013..fca2e25 100644
--- a/core/res/res/drawable-mdpi/fastscroll_label_right_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_label_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_thumb_default_holo.png b/core/res/res/drawable-mdpi/fastscroll_thumb_default_holo.png
index bfb55bd..fa23ea4 100644
--- a/core/res/res/drawable-mdpi/fastscroll_thumb_default_holo.png
+++ b/core/res/res/drawable-mdpi/fastscroll_thumb_default_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_thumb_pressed_holo.png b/core/res/res/drawable-mdpi/fastscroll_thumb_pressed_holo.png
index 89d9b01..d8233d9 100644
--- a/core/res/res/drawable-mdpi/fastscroll_thumb_pressed_holo.png
+++ b/core/res/res/drawable-mdpi/fastscroll_thumb_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_track_default_holo_dark.9.png b/core/res/res/drawable-mdpi/fastscroll_track_default_holo_dark.9.png
index cf8c4bb..8796e21 100644
--- a/core/res/res/drawable-mdpi/fastscroll_track_default_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_track_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_track_default_holo_light.9.png b/core/res/res/drawable-mdpi/fastscroll_track_default_holo_light.9.png
index cf8c4bb..8796e21 100644
--- a/core/res/res/drawable-mdpi/fastscroll_track_default_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_track_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_dark.9.png
index fef2168..e74acea 100644
--- a/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_light.9.png
index fef2168..62d80a0 100644
--- a/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/fastscroll_track_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_paste_bubble_disabled_holo.png b/core/res/res/drawable-mdpi/ic_paste_bubble_disabled_holo.png
deleted file mode 100644
index ce6bd86..0000000
--- a/core/res/res/drawable-mdpi/ic_paste_bubble_disabled_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_paste_bubble_holo.png b/core/res/res/drawable-mdpi/ic_paste_bubble_holo.png
deleted file mode 100644
index e483e84..0000000
--- a/core/res/res/drawable-mdpi/ic_paste_bubble_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_activated_holo.9.png b/core/res/res/drawable-mdpi/list_activated_holo.9.png
index f162c9a..3bf8e03 100644
--- a/core/res/res/drawable-mdpi/list_activated_holo.9.png
+++ b/core/res/res/drawable-mdpi/list_activated_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_focused_holo.9.png b/core/res/res/drawable-mdpi/list_focused_holo.9.png
index e2449dd..7c0599e 100644
--- a/core/res/res/drawable-mdpi/list_focused_holo.9.png
+++ b/core/res/res/drawable-mdpi/list_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_longpressed_holo.9.png b/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
index 3483891..2b8a0b3 100644
--- a/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
+++ b/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png
index e3344b6..b60aaa5 100644
--- a/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png
index 2365978..b60aaa5 100644
--- a/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
index b888135..f707453 100644
--- a/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
index 1cc1f7f..441ccc0 100644
--- a/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
index a7f6277..faa0672 100644
--- a/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selected_holo_light.9.png b/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
index b6029ec..faa0672 100644
--- a/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png b/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png
index bac0a23..4bf3cb9 100644
--- a/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/progress_primary_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png b/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png
index bac0a23..4bf3cb9 100644
--- a/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/progress_primary_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png b/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png
index 8be8656..b13c878 100644
--- a/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/progress_secondary_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png b/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png
index 8be8656..b13c878 100644
--- a/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/progress_secondary_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrollbar_handle_holo_dark.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_holo_dark.9.png
index e039c4b..ca0ec0a 100644
--- a/core/res/res/drawable-mdpi/scrollbar_handle_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/scrollbar_handle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrollbar_handle_holo_light.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_holo_light.9.png
index e039c4b..ca0ec0a 100644
--- a/core/res/res/drawable-mdpi/scrollbar_handle_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/scrollbar_handle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
index ac63e7d..c994519 100644
--- a/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
+++ b/core/res/res/drawable-mdpi/scrubber_primary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
index 9590bdc..5e21da4 100644
--- a/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
+++ b/core/res/res/drawable-mdpi/scrubber_secondary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
index 537ab8c..392e1f8 100644
--- a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_16_outer_holo.png b/core/res/res/drawable-mdpi/spinner_16_outer_holo.png
index cee54b7..d862a25 100644
--- a/core/res/res/drawable-mdpi/spinner_16_outer_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_16_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
index 67fb438..f5e7f73 100644
--- a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_20_outer_holo.png b/core/res/res/drawable-mdpi/spinner_20_outer_holo.png
index 25beedd..b7ecfff 100644
--- a/core/res/res/drawable-mdpi/spinner_20_outer_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_20_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
index ed446d6..5231a5b 100644
--- a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_48_outer_holo.png b/core/res/res/drawable-mdpi/spinner_48_outer_holo.png
index 08b2dcd..e1e5b52 100644
--- a/core/res/res/drawable-mdpi/spinner_48_outer_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_48_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
index 2d5e5a1..982f037 100644
--- a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_76_outer_holo.png b/core/res/res/drawable-mdpi/spinner_76_outer_holo.png
index 2edad73..01b6ab38 100644
--- a/core/res/res/drawable-mdpi/spinner_76_outer_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_76_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_active_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_active_holo_dark.9.png
deleted file mode 100644
index d37c8b2..0000000
--- a/core/res/res/drawable-mdpi/textfield_active_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_active_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_active_holo_light.9.png
deleted file mode 100644
index 16f2197..0000000
--- a/core/res/res/drawable-mdpi/textfield_active_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png
deleted file mode 100644
index 44a5d82..0000000
--- a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png
deleted file mode 100644
index 6613683..0000000
--- a/core/res/res/drawable-mdpi/textfield_multiline_active_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/background_holo_dark.png b/core/res/res/drawable-nodpi/background_holo_dark.png
index ea85a3f..85bd6f7 100644
--- a/core/res/res/drawable-nodpi/background_holo_dark.png
+++ b/core/res/res/drawable-nodpi/background_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/background_holo_light.png b/core/res/res/drawable-nodpi/background_holo_light.png
index 04f52ae..5fb4a9d 100644
--- a/core/res/res/drawable-nodpi/background_holo_light.png
+++ b/core/res/res/drawable-nodpi/background_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/list_divider_holo_dark.9.png b/core/res/res/drawable-nodpi/list_divider_holo_dark.9.png
new file mode 100644
index 0000000..2e7951f
--- /dev/null
+++ b/core/res/res/drawable-nodpi/list_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/list_divider_holo_light.9.png b/core/res/res/drawable-nodpi/list_divider_holo_light.9.png
new file mode 100644
index 0000000..17d8a54
--- /dev/null
+++ b/core/res/res/drawable-nodpi/list_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
index eb7ce5e..8a30fab 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
index eb7ce5e..8a30fab 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png
index 4e4d3d4..bb4e7f6 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
index ee291b6..842ea9c 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
index 83e5011..842ea9c 100644
--- a/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png
index 67f007a..5aa02c8 100644
--- a/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
index 4764d1b..025fc00 100644
--- a/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
index 4764d1b..025fc00 100644
--- a/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png
index b949121..02360bd 100644
--- a/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
index 6a35514..5c4a2d1 100644
--- a/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
index 9068d75..5c4a2d1 100644
--- a/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png
index daac2e1..1833ffe 100644
--- a/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
index 94f1a5a..7fc5980 100644
--- a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
index 94f1a5a..7fc5980 100644
--- a/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off.9.png
new file mode 100644
index 0000000..1406188
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..9d9c6f2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..7d9bfd1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
new file mode 100644
index 0000000..0cddd2d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
new file mode 100644
index 0000000..1109fe1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
new file mode 100644
index 0000000..ec33f17
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
new file mode 100644
index 0000000..0b562cc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
new file mode 100644
index 0000000..93f565f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
new file mode 100644
index 0000000..aee803d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png
new file mode 100644
index 0000000..2f56666
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png
new file mode 100644
index 0000000..d636569
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on.9.png
new file mode 100644
index 0000000..90f1e7b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..9ec3fe0c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..9ec3fe0c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
new file mode 100644
index 0000000..5b8bf7b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
new file mode 100644
index 0000000..5b8bf7b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
new file mode 100644
index 0000000..5c3318b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
new file mode 100644
index 0000000..5c3318b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
new file mode 100644
index 0000000..ef7310a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
new file mode 100644
index 0000000..ef7310a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png
new file mode 100644
index 0000000..c55389e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png
new file mode 100644
index 0000000..c55389e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_label_left_holo_dark.9.png b/core/res/res/drawable-xhdpi/fastscroll_label_left_holo_dark.9.png
new file mode 100644
index 0000000..dfc5e6b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_label_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_label_left_holo_light.9.png b/core/res/res/drawable-xhdpi/fastscroll_label_left_holo_light.9.png
new file mode 100644
index 0000000..eab0cb9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_label_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_label_right_holo_dark.9.png b/core/res/res/drawable-xhdpi/fastscroll_label_right_holo_dark.9.png
new file mode 100644
index 0000000..e3e0cde
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_label_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_label_right_holo_light.9.png b/core/res/res/drawable-xhdpi/fastscroll_label_right_holo_light.9.png
new file mode 100644
index 0000000..b2bd5ca
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_label_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_thumb_default_holo.png b/core/res/res/drawable-xhdpi/fastscroll_thumb_default_holo.png
new file mode 100644
index 0000000..cc3fe43
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_thumb_default_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_thumb_pressed_holo.png b/core/res/res/drawable-xhdpi/fastscroll_thumb_pressed_holo.png
new file mode 100644
index 0000000..ba3f566
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_thumb_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_track_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/fastscroll_track_default_holo_dark.9.png
new file mode 100644
index 0000000..431336a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_track_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_track_default_holo_light.9.png b/core/res/res/drawable-xhdpi/fastscroll_track_default_holo_light.9.png
new file mode 100644
index 0000000..431336a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_track_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_track_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/fastscroll_track_pressed_holo_dark.9.png
new file mode 100644
index 0000000..9301f5e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_track_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_track_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/fastscroll_track_pressed_holo_light.9.png
new file mode 100644
index 0000000..d22724a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/fastscroll_track_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_activated_holo.9.png b/core/res/res/drawable-xhdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..eda10e6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_activated_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_focused_holo.9.png b/core/res/res/drawable-xhdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..690cb1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png b/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png
new file mode 100644
index 0000000..e303022
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/list_pressed_holo_dark.9.png
new file mode 100644
index 0000000..80c93da
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..80c93da
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-xhdpi/list_section_divider_holo_dark.9.png
new file mode 100644
index 0000000..76fd13c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-xhdpi/list_section_divider_holo_light.9.png
new file mode 100644
index 0000000..d8fd9e3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png
new file mode 100644
index 0000000..3e8dac8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png b/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png
new file mode 100644
index 0000000..3e8dac8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/progress_primary_holo_dark.9.png b/core/res/res/drawable-xhdpi/progress_primary_holo_dark.9.png
new file mode 100644
index 0000000..dc8711f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/progress_primary_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/progress_primary_holo_light.9.png b/core/res/res/drawable-xhdpi/progress_primary_holo_light.9.png
new file mode 100644
index 0000000..dc8711f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/progress_primary_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/progress_secondary_holo_dark.9.png b/core/res/res/drawable-xhdpi/progress_secondary_holo_dark.9.png
new file mode 100644
index 0000000..39a168f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/progress_secondary_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/progress_secondary_holo_light.9.png b/core/res/res/drawable-xhdpi/progress_secondary_holo_light.9.png
new file mode 100644
index 0000000..39a168f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/progress_secondary_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrollbar_handle_holo_dark.9.png b/core/res/res/drawable-xhdpi/scrollbar_handle_holo_dark.9.png
new file mode 100644
index 0000000..7a31d9d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrollbar_handle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrollbar_handle_holo_light.9.png b/core/res/res/drawable-xhdpi/scrollbar_handle_holo_light.9.png
new file mode 100644
index 0000000..7a31d9d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrollbar_handle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png
new file mode 100644
index 0000000..c3b9bb4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_disabled_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_holo.png b/core/res/res/drawable-xhdpi/scrubber_control_holo.png
new file mode 100644
index 0000000..f72e48c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_control_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_primary_holo.9.png b/core/res/res/drawable-xhdpi/scrubber_primary_holo.9.png
new file mode 100644
index 0000000..328bd1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_primary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_secondary_holo.9.png b/core/res/res/drawable-xhdpi/scrubber_secondary_holo.9.png
new file mode 100644
index 0000000..dcc4221
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_secondary_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_track_holo_dark.9.png b/core/res/res/drawable-xhdpi/scrubber_track_holo_dark.9.png
new file mode 100644
index 0000000..80e4400
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_track_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_track_holo_light.9.png b/core/res/res/drawable-xhdpi/scrubber_track_holo_light.9.png
new file mode 100644
index 0000000..af96c43
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/scrubber_track_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
index f868ff1..f5e9164 100644
--- a/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_16_outer_holo.png b/core/res/res/drawable-xhdpi/spinner_16_outer_holo.png
index abd2d57..6f977a2 100644
--- a/core/res/res/drawable-xhdpi/spinner_16_outer_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_16_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
index d2691d3..16c8430 100644
--- a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png b/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png
index a5c87d1..9593616 100644
--- a/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
index 24176bf..cebf1d8 100644
--- a/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_48_outer_holo.png b/core/res/res/drawable-xhdpi/spinner_48_outer_holo.png
index 53b34ef..5a9e001 100644
--- a/core/res/res/drawable-xhdpi/spinner_48_outer_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_48_outer_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
index fa267b4..c68cc37 100644
--- a/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_76_outer_holo.png b/core/res/res/drawable-xhdpi/spinner_76_outer_holo.png
index 2c1f879..611dc5a 100644
--- a/core/res/res/drawable-xhdpi/spinner_76_outer_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_76_outer_holo.png
Binary files differ
diff --git a/core/res/res/layout/text_edit_action_popup_text.xml b/core/res/res/layout/text_edit_action_popup_text.xml
new file mode 100644
index 0000000..aa6a5e1
--- /dev/null
+++ b/core/res/res/layout/text_edit_action_popup_text.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingLeft="16dip"
+    android:paddingRight="16dip"
+    android:paddingTop="8dip"
+    android:paddingBottom="8dip"
+    android:gravity="center"
+    android:textAppearance="?android:attr/textAppearanceSmallInverse"
+    android:textColor="@android:color/black"
+    android:textAllCaps="true"
+/>
diff --git a/core/res/res/layout/text_edit_no_paste_window.xml b/core/res/res/layout/text_edit_no_paste_window.xml
index 98b16e2..5e9acc2 100644
--- a/core/res/res/layout/text_edit_no_paste_window.xml
+++ b/core/res/res/layout/text_edit_no_paste_window.xml
@@ -25,7 +25,6 @@
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="8dip"
-        android:drawableLeft="@android:drawable/ic_paste_bubble_disabled_holo"
         android:drawablePadding="8dip"
         android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
diff --git a/core/res/res/layout/text_edit_paste_window.xml b/core/res/res/layout/text_edit_paste_window.xml
index 01e5530..adfe56b 100644
--- a/core/res/res/layout/text_edit_paste_window.xml
+++ b/core/res/res/layout/text_edit_paste_window.xml
@@ -25,7 +25,6 @@
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="12dip"
-        android:drawableLeft="@android:drawable/ic_paste_bubble_holo"
         android:drawablePadding="8dip"
         android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
diff --git a/core/res/res/layout/text_edit_side_no_paste_window.xml b/core/res/res/layout/text_edit_side_no_paste_window.xml
index 3eb41fb..dc411a1 100644
--- a/core/res/res/layout/text_edit_side_no_paste_window.xml
+++ b/core/res/res/layout/text_edit_side_no_paste_window.xml
@@ -25,7 +25,6 @@
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="8dip"
-        android:drawableLeft="@android:drawable/ic_paste_bubble_disabled_holo"
         android:drawablePadding="8dip"
         android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
diff --git a/core/res/res/layout/text_edit_side_paste_window.xml b/core/res/res/layout/text_edit_side_paste_window.xml
index 6651786..37b774d 100644
--- a/core/res/res/layout/text_edit_side_paste_window.xml
+++ b/core/res/res/layout/text_edit_side_paste_window.xml
@@ -25,7 +25,6 @@
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="8dip"
-        android:drawableLeft="@android:drawable/ic_paste_bubble_holo"
         android:drawablePadding="8dip"
         android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 553632b..24d5d8d 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -21,7 +21,7 @@
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">48dip</dimen>
     <!-- Width and height of a single notification icon in the status bar -->
-    <dimen name="status_bar_icon_size">32dip</dimen>
+    <dimen name="status_bar_icon_size">24dip</dimen>
     <!-- Size of the giant number (unread count) in the notifications -->
     <dimen name="status_bar_content_number_size">48sp</dimen>
 
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index bcf1991..73103a6 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -89,20 +89,10 @@
        <item>@drawable/btn_radio_off</item>
        <item>@drawable/btn_default_transparent_normal</item>
        <item>@drawable/btn_default_small_selected</item>
-       <item>@drawable/btn_default_small_pressed_holo_light</item>
-       <item>@drawable/btn_default_small_pressed_holo_dark</item>
        <item>@drawable/btn_default_small_pressed</item>
-       <item>@drawable/btn_default_small_normal_holo_light</item>
-       <item>@drawable/btn_default_small_normal_holo_dark</item>
        <item>@drawable/btn_default_small_normal_disable_focused</item>
        <item>@drawable/btn_default_small_normal_disable</item>
        <item>@drawable/btn_default_small_normal</item>
-       <item>@drawable/btn_default_small_focused_holo_light</item>
-       <item>@drawable/btn_default_small_focused_holo_dark</item>
-       <item>@drawable/btn_default_small_disabled_holo_light</item>
-       <item>@drawable/btn_default_small_disabled_holo_dark</item>
-       <item>@drawable/btn_default_small_disabled_focused_holo_light</item>
-       <item>@drawable/btn_default_small_disabled_focused_holo_dark</item>
        <item>@drawable/btn_default_selected</item>
        <item>@drawable/btn_default_pressed_holo_light</item>
        <item>@drawable/btn_default_pressed_holo_dark</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d698341..01cf1f0 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2408,6 +2408,9 @@
     <!-- Text displayed in a popup dialog in TextEdit when the clipboard is empty. 'paste' is used otherwise. [CHAR LIMIT=20] -->
     <string name="pasteDisabled">Nothing to paste</string>
 
+    <!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker -->
+    <string name="replace">Replace</string>
+
     <!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
     <string name="copyUrl">Copy URL</string>
 
@@ -3001,6 +3004,30 @@
     <!-- Description of the button to decrement the NumberPicker value. [CHAR LIMIT=NONE] -->
     <string name="number_picker_decrement_button">Decrement</string>
 
+    <!-- CheckBox - accessibility support -->
+    <!-- Description of the checked state of a CheckBox. [CHAR LIMIT=NONE] -->
+    <string name="checkbox_checked">checked</string>
+    <!-- Description of the not checked state of a CheckBox. [CHAR LIMIT=NONE] -->
+    <string name="checkbox_not_checked">not checked</string>
+
+    <!-- RadioButton/CheckedTextView - accessibility support -->
+    <!-- Description of the selected state of a RadioButton. [CHAR LIMIT=NONE] -->
+    <string name="radiobutton_selected">selected</string>
+    <!-- Description of the not selected state of a RadioButton. [CHAR LIMIT=NONE] -->
+    <string name="radiobutton_not_selected">not selected</string>
+
+    <!-- Switch - accessibility support -->
+    <!-- Description of the on state of a Switch. [CHAR LIMIT=NONE] -->
+    <string name="switch_on">on</string>
+    <!-- Description of the off state of a Switch. [CHAR LIMIT=NONE] -->
+    <string name="switch_off">off</string>
+
+    <!-- ToggleButton - accessibility support -->
+    <!-- Description of the pressed state of a ToggleButton. [CHAR LIMIT=NONE] -->
+    <string name="togglebutton_pressed">pressed</string>
+    <!-- Description of the not pressed state of a ToggleButton. [CHAR LIMIT=NONE] -->
+    <string name="togglebutton_not_pressed">not pressed</string>
+
     <!-- Content description for the action bar "home" affordance. [CHAR LIMIT=NONE] -->
     <string name="action_bar_home_description">Navigate home</string>
     <!-- Content description for the action bar "up" affordance. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/res/raw/history_v1 b/core/tests/coretests/res/raw/history_v1
new file mode 100644
index 0000000..de79491
--- /dev/null
+++ b/core/tests/coretests/res/raw/history_v1
Binary files differ
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 9403d95..242057c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -23,16 +23,18 @@
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
 
+import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
-import junit.framework.TestCase;
+import com.android.frameworks.coretests.R;
 
+import java.io.DataInputStream;
 import java.util.Random;
 
 @SmallTest
-public class NetworkStatsHistoryTest extends TestCase {
+public class NetworkStatsHistoryTest extends AndroidTestCase {
     private static final String TAG = "NetworkStatsHistoryTest";
 
     private static final long TEST_START = 1194220800000L;
@@ -47,6 +49,32 @@
         }
     }
 
+    public void testReadOriginalVersion() throws Exception {
+        final DataInputStream in = new DataInputStream(
+                getContext().getResources().openRawResource(R.raw.history_v1));
+
+        NetworkStatsHistory.Entry entry = null;
+        try {
+            final NetworkStatsHistory history = new NetworkStatsHistory(in);
+            assertEquals(15 * SECOND_IN_MILLIS, history.getBucketDuration());
+
+            entry = history.getValues(0, entry);
+            assertEquals(29143L, entry.rxBytes);
+            assertEquals(6223L, entry.txBytes);
+
+            entry = history.getValues(history.size() - 1, entry);
+            assertEquals(1476L, entry.rxBytes);
+            assertEquals(838L, entry.txBytes);
+
+            entry = history.getValues(Long.MIN_VALUE, Long.MAX_VALUE, entry);
+            assertEquals(332401L, entry.rxBytes);
+            assertEquals(64314L, entry.txBytes);
+
+        } finally {
+            in.close();
+        }
+    }
+
     public void testRecordSingleBucket() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
         stats = new NetworkStatsHistory(BUCKET_SIZE);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 2434e9f..69ad0f4 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -31,9 +31,9 @@
 
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L)
-                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 11)
+                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L, 12);
 
         assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE));
         assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE));
@@ -47,95 +47,95 @@
         assertEquals(0, stats.size());
         assertEquals(2, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L);
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L, 3);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L, 4);
 
         assertEquals(2, stats.size());
         assertEquals(2, stats.internalSize());
 
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L);
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L);
-        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L, 7);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L, 8);
+        stats.addValues(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L, 10);
 
         assertEquals(5, stats.size());
         assertTrue(stats.internalSize() >= 5);
 
-        assertEntry(stats, 0, TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L);
-        assertEntry(stats, 1, TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L);
-        assertEntry(stats, 2, TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L);
-        assertEntry(stats, 3, TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L);
-        assertEntry(stats, 4, TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L);
+        assertValues(stats, 0, TEST_IFACE, TEST_UID, TAG_NONE, 1L, 1L, 2L, 2L, 3);
+        assertValues(stats, 1, TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L, 2L, 2L, 4);
+        assertValues(stats, 2, TEST_IFACE, TEST_UID, TAG_NONE, 3L, 30L, 4L, 40L, 7);
+        assertValues(stats, 3, TEST_IFACE, TEST_UID, TAG_NONE, 4L, 40L, 4L, 40L, 8);
+        assertValues(stats, 4, TEST_IFACE, TEST_UID, TAG_NONE, 5L, 50L, 5L, 50L, 10);
     }
 
     public void testCombineExisting() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 10);
 
-        stats.addValues(TEST_IFACE, 1001, TAG_NONE, 512L, 4L, 256L, 2L);
-        stats.addValues(TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L);
-        stats.combineValues(TEST_IFACE, 1001, TAG_NONE, -128L, -1L, -128L, -1L);
+        stats.addValues(TEST_IFACE, 1001, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+        stats.addValues(TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L, 2);
+        stats.combineValues(TEST_IFACE, 1001, TAG_NONE, -128L, -1L, -128L, -1L, -1);
 
-        assertEntry(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 3L, 128L, 1L);
-        assertEntry(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L);
+        assertValues(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 3L, 128L, 1L, 9);
+        assertValues(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 1L, 128L, 1L, 2);
 
         // now try combining that should create row
-        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L);
-        assertEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L);
-        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L);
-        assertEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 2L, 256L, 2L);
+        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L, 3);
+        assertValues(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L, 3);
+        stats.combineValues(TEST_IFACE, 5005, TAG_NONE, 128L, 1L, 128L, 1L, 3);
+        assertValues(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 2L, 256L, 2L, 6);
     }
 
     public void testSubtractIdenticalData() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats result = after.subtract(before);
 
         // identical data should result in zero delta
-        assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L);
-        assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L);
+        assertValues(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L, 0);
     }
 
     public void testSubtractIdenticalRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1025L, 9L, 2L, 1L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 3L, 1L, 1028L, 9L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
 
         final NetworkStats result = after.subtract(before);
 
         // expect delta between measurements
-        assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 1L, 1L, 2L, 1L);
-        assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 3L, 1L, 4L, 1L);
+        assertValues(result, 0, TEST_IFACE, 100, TAG_NONE, 1L, 1L, 2L, 1L, 4);
+        assertValues(result, 1, TEST_IFACE, 101, TAG_NONE, 3L, 1L, 4L, 1L, 8);
     }
 
     public void testSubtractNewRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 3)
-                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L)
-                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L)
-                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L);
+                .addValues(TEST_IFACE, 100, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .addValues(TEST_IFACE, 101, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+                .addValues(TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
 
         final NetworkStats result = after.subtract(before);
 
         // its okay to have new rows
-        assertEntry(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L);
-        assertEntry(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L);
-        assertEntry(result, 2, TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L);
+        assertValues(result, 0, TEST_IFACE, 100, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 1, TEST_IFACE, 101, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+        assertValues(result, 2, TEST_IFACE, 102, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
     }
 
-    private static void assertEntry(NetworkStats stats, int index, String iface, int uid, int tag,
-            long rxBytes, long rxPackets, long txBytes, long txPackets) {
+    private static void assertValues(NetworkStats stats, int index, String iface, int uid, int tag,
+            long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
         assertEquals(iface, entry.iface);
         assertEquals(uid, entry.uid);
@@ -144,6 +144,7 @@
         assertEquals(rxPackets, entry.rxPackets);
         assertEquals(txBytes, entry.txBytes);
         assertEquals(txPackets, entry.txPackets);
+        assertEquals(operations, entry.operations);
     }
 
 }
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index 986f32c..0b76a36 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -808,7 +808,9 @@
         LOGV("BnDrmManagerService::onTransact :INSTALL_DRM_ENGINE");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        status_t status = installDrmEngine(data.readInt32(), data.readString8());
+        const int uniqueId = data.readInt32();
+        const String8 engineFile = data.readString8();
+        status_t status = installDrmEngine(uniqueId, engineFile);
 
         reply->writeInt32(status);
         return DRM_NO_ERROR;
@@ -822,7 +824,8 @@
         const int uniqueId = data.readInt32();
         const String8 path = data.readString8();
 
-        DrmConstraints* drmConstraints = getConstraints(uniqueId, &path, data.readInt32());
+        DrmConstraints* drmConstraints
+            = getConstraints(uniqueId, &path, data.readInt32());
 
         if (NULL != drmConstraints) {
             //Filling DRM Constraints contents
@@ -948,7 +951,9 @@
         const int uniqueId = data.readInt32();
 
         //Filling DRM info Request
-        DrmInfoRequest* drmInfoRequest = new DrmInfoRequest(data.readInt32(), data.readString8());
+        const int infoType = data.readInt32();
+        const String8 mimeType = data.readString8();
+        DrmInfoRequest* drmInfoRequest = new DrmInfoRequest(infoType, mimeType);
 
         const int size = data.readInt32();
         for (int index = 0; index < size; ++index) {
@@ -1021,7 +1026,9 @@
         LOGV("BnDrmManagerService::onTransact :GET_ORIGINAL_MIMETYPE");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        const String8 originalMimeType = getOriginalMimeType(data.readInt32(), data.readString8());
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+        const String8 originalMimeType = getOriginalMimeType(uniqueId, path);
 
         reply->writeString8(originalMimeType);
         return DRM_NO_ERROR;
@@ -1032,8 +1039,10 @@
         LOGV("BnDrmManagerService::onTransact :GET_DRM_OBJECT_TYPE");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        const int drmObjectType
-            = getDrmObjectType(data.readInt32(), data.readString8(), data.readString8());
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+        const String8 mimeType = data.readString8();
+        const int drmObjectType = getDrmObjectType(uniqueId, path, mimeType);
 
         reply->writeInt32(drmObjectType);
         return DRM_NO_ERROR;
@@ -1044,8 +1053,10 @@
         LOGV("BnDrmManagerService::onTransact :CHECK_RIGHTS_STATUS");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        const int result
-            = checkRightsStatus(data.readInt32(), data.readString8(), data.readInt32());
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+        const int action = data.readInt32();
+        const int result = checkRightsStatus(uniqueId, path, action);
 
         reply->writeInt32(result);
         return DRM_NO_ERROR;
@@ -1061,9 +1072,10 @@
         DecryptHandle handle;
         readDecryptHandleFromParcelData(&handle, data);
 
+        const int action = data.readInt32();
+        const bool reserve = static_cast<bool>(data.readInt32());
         const status_t status
-            = consumeRights(uniqueId, &handle, data.readInt32(),
-                static_cast<bool>(data.readInt32()));
+            = consumeRights(uniqueId, &handle, action, reserve);
         reply->writeInt32(status);
 
         clearDecryptHandle(&handle);
@@ -1080,8 +1092,10 @@
         DecryptHandle handle;
         readDecryptHandleFromParcelData(&handle, data);
 
+        const int playbackStatus = data.readInt32();
+        const int64_t position = data.readInt64();
         const status_t status
-            = setPlaybackStatus(uniqueId, &handle, data.readInt32(), data.readInt64());
+            = setPlaybackStatus(uniqueId, &handle, playbackStatus, position);
         reply->writeInt32(status);
 
         clearDecryptHandle(&handle);
@@ -1093,11 +1107,13 @@
         LOGV("BnDrmManagerService::onTransact :VALIDATE_ACTION");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        bool result = validateAction(
-                                data.readInt32(),
-                                data.readString8(),
-                                data.readInt32(),
-                                ActionDescription(data.readInt32(), data.readInt32()));
+        const int uniqueId = data.readInt32();
+        const String8 path = data.readString8();
+        const int action = data.readInt32();
+        const int outputType = data.readInt32();
+        const int configuration = data.readInt32();
+        bool result = validateAction(uniqueId, path, action,
+                ActionDescription(outputType, configuration));
 
         reply->writeInt32(result);
         return DRM_NO_ERROR;
@@ -1108,7 +1124,9 @@
         LOGV("BnDrmManagerService::onTransact :REMOVE_RIGHTS");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        const status_t status = removeRights(data.readInt32(), data.readString8());
+        int uniqueId = data.readInt32();
+        String8 path = data.readString8();
+        const status_t status = removeRights(uniqueId, path);
         reply->writeInt32(status);
 
         return DRM_NO_ERROR;
@@ -1130,7 +1148,9 @@
         LOGV("BnDrmManagerService::onTransact :OPEN_CONVERT_SESSION");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        const int convertId = openConvertSession(data.readInt32(), data.readString8());
+        const int uniqueId = data.readInt32();
+        const String8 mimeType = data.readString8();
+        const int convertId = openConvertSession(uniqueId, mimeType);
 
         reply->writeInt32(convertId);
         return DRM_NO_ERROR;
@@ -1176,8 +1196,10 @@
         LOGV("BnDrmManagerService::onTransact :CLOSE_CONVERT_SESSION");
         CHECK_INTERFACE(IDrmManagerService, data, reply);
 
-        DrmConvertedStatus*    drmConvertedStatus
-            = closeConvertSession(data.readInt32(), data.readInt32());
+        const int uniqueId = data.readInt32();
+        const int convertId = data.readInt32();
+        DrmConvertedStatus* drmConvertedStatus
+            = closeConvertSession(uniqueId, convertId);
 
         if (NULL != drmConvertedStatus) {
             //Filling Drm Converted Ststus
@@ -1241,8 +1263,10 @@
         const int uniqueId = data.readInt32();
         const int fd = data.readFileDescriptor();
 
+        const off64_t offset = data.readInt64();
+        const off64_t length = data.readInt64();
         DecryptHandle* handle
-            = openDecryptSession(uniqueId, fd, data.readInt64(), data.readInt64());
+            = openDecryptSession(uniqueId, fd, offset, length);
 
         if (NULL != handle) {
             writeDecryptHandleToParcelData(handle, reply);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 35ed4d2..6ae8c9b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -178,17 +178,22 @@
      * @see #getDensity()
      */
     public void setBitmap(Bitmap bitmap) {
-        if (!bitmap.isMutable()) {
-            throw new IllegalStateException();
-        }
         if (isHardwareAccelerated()) {
             throw new RuntimeException("Can't set a bitmap device on a GL canvas");
         }
-        throwIfRecycled(bitmap);
 
-        native_setBitmap(mNativeCanvas, bitmap.ni());
+        int pointer = 0;
+        if (bitmap != null) {
+            if (!bitmap.isMutable()) {
+                throw new IllegalStateException();
+            }
+            throwIfRecycled(bitmap);
+            mDensity = bitmap.mDensity;
+            pointer = bitmap.ni();
+        }
+
+        native_setBitmap(mNativeCanvas, pointer);
         mBitmap = bitmap;
-        mDensity = bitmap.mDensity;
     }
     
     /**
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index f701280..234e165 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -59,7 +59,8 @@
 {
 public:
     virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
-    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr) = 0;
+    virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
+                          camera_frame_metadata_t *metadata) = 0;
     virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) = 0;
 };
 
@@ -138,7 +139,8 @@
 
     // ICameraClient interface
     virtual void        notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
-    virtual void        dataCallback(int32_t msgType, const sp<IMemory>& dataPtr);
+    virtual void        dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
+                                     camera_frame_metadata_t *metadata);
     virtual void        dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
 
     sp<ICamera>         remote();
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index d2f398a..48483fd 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -484,6 +484,17 @@
     // Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read only.
     static const char KEY_VIDEO_FRAME_FORMAT[];
 
+    // Sets the hint of the recording mode. If this is true, MediaRecorder.start
+    // may be faster or has less glitches. This should be called before starting
+    // the preview for the best result. But it is allowed to change the hint
+    // while the preview is active. The default value is false.
+    //
+    // The apps can still call Camera.takePicture when the hint is true. The
+    // apps can call MediaRecorder.start when the hint is false. But the
+    // performance may be worse.
+    // Example value: "true" or "false". Read/write.
+    static const char KEY_RECORDING_HINT[];
+
     // Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
     static const char TRUE[];
     static const char FALSE[];
diff --git a/include/camera/ICameraClient.h b/include/camera/ICameraClient.h
index 236d0f6..b30aa7a 100644
--- a/include/camera/ICameraClient.h
+++ b/include/camera/ICameraClient.h
@@ -22,6 +22,7 @@
 #include <binder/Parcel.h>
 #include <binder/IMemory.h>
 #include <utils/Timers.h>
+#include <system/camera.h>
 
 namespace android {
 
@@ -31,7 +32,8 @@
     DECLARE_META_INTERFACE(CameraClient);
 
     virtual void            notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
-    virtual void            dataCallback(int32_t msgType, const sp<IMemory>& data) = 0;
+    virtual void            dataCallback(int32_t msgType, const sp<IMemory>& data,
+                                         camera_frame_metadata_t *metadata) = 0;
     virtual void            dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& data) = 0;
 };
 
diff --git a/include/ui/Input.h b/include/ui/Input.h
index c9f694a..f1385a0 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -206,10 +206,17 @@
 
     float getAxisValue(int32_t axis) const;
     status_t setAxisValue(int32_t axis, float value);
-    float* editAxisValue(int32_t axis);
 
     void scale(float scale);
 
+    inline float getX() const {
+        return getAxisValue(AMOTION_EVENT_AXIS_X);
+    }
+
+    inline float getY() const {
+        return getAxisValue(AMOTION_EVENT_AXIS_Y);
+    }
+
 #ifdef HAVE_ANDROID_OS
     status_t readFromParcel(Parcel* parcel);
     status_t writeToParcel(Parcel* parcel) const;
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index 600017e..9452e86 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -68,6 +68,30 @@
     // Result is undefined if all bits are unmarked.
     inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); }
 
+    // Finds the first marked bit in the set and clears it.  Returns the bit index.
+    // Result is undefined if all bits are unmarked.
+    inline uint32_t clearFirstMarkedBit() {
+        uint32_t n = firstMarkedBit();
+        clearBit(n);
+        return n;
+    }
+
+    // Finds the first unmarked bit in the set and marks it.  Returns the bit index.
+    // Result is undefined if all bits are marked.
+    inline uint32_t markFirstUnmarkedBit() {
+        uint32_t n = firstUnmarkedBit();
+        markBit(n);
+        return n;
+    }
+
+    // Finds the last marked bit in the set and clears it.  Returns the bit index.
+    // Result is undefined if all bits are unmarked.
+    inline uint32_t clearLastMarkedBit() {
+        uint32_t n = lastMarkedBit();
+        clearBit(n);
+        return n;
+    }
+
     // Gets the index of the specified bit in the set, which is the number of
     // marked bits that appear before the specified bit.
     inline uint32_t getIndexOfBit(uint32_t n) const {
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index 3c00db5..7ac3cc1 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -360,7 +360,8 @@
 }
 
 // callback from camera service when frame or image is ready
-void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr)
+void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
+                          camera_frame_metadata_t *metadata)
 {
     sp<CameraListener> listener;
     {
@@ -368,7 +369,7 @@
         listener = mListener;
     }
     if (listener != NULL) {
-        listener->postData(msgType, dataPtr);
+        listener->postData(msgType, dataPtr, metadata);
     }
 }
 
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index d8fef09..251615d 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -86,6 +86,7 @@
 const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
 const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
 const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
+const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
 
 const char CameraParameters::TRUE[] = "true";
 const char CameraParameters::FALSE[] = "false";
diff --git a/libs/camera/ICameraClient.cpp b/libs/camera/ICameraClient.cpp
index cb3bd0c..183429a 100644
--- a/libs/camera/ICameraClient.cpp
+++ b/libs/camera/ICameraClient.cpp
@@ -51,13 +51,18 @@
     }
 
     // generic data callback from camera service to app with image data
-    void dataCallback(int32_t msgType, const sp<IMemory>& imageData)
+    void dataCallback(int32_t msgType, const sp<IMemory>& imageData,
+                      camera_frame_metadata_t *metadata)
     {
         LOGV("dataCallback");
         Parcel data, reply;
         data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor());
         data.writeInt32(msgType);
         data.writeStrongBinder(imageData->asBinder());
+        if (metadata) {
+            data.writeInt32(metadata->number_of_faces);
+            data.write(metadata->faces, sizeof(camera_face_t) * metadata->number_of_faces);
+        }
         remote()->transact(DATA_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
@@ -96,7 +101,15 @@
             CHECK_INTERFACE(ICameraClient, data, reply);
             int32_t msgType = data.readInt32();
             sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
-            dataCallback(msgType, imageData);
+            camera_frame_metadata_t *metadata = NULL;
+            if (data.dataAvail() > 0) {
+                metadata = new camera_frame_metadata_t;
+                metadata->number_of_faces = data.readInt32();
+                metadata->faces = (camera_face_t *) data.readInplace(
+                        sizeof(camera_face_t) * metadata->number_of_faces);
+            }
+            dataCallback(msgType, imageData, metadata);
+            if (metadata) delete metadata;
             return NO_ERROR;
         } break;
         case DATA_CALLBACK_TIMESTAMP: {
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 16755ad..4f51f03 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -168,18 +168,18 @@
         }
     }
 
+    const int minBufferSlots = mSynchronousMode ?
+            MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
     if (bufferCount == 0) {
-        const int minBufferSlots = mSynchronousMode ?
-                MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
         mClientBufferCount = 0;
         bufferCount = (mServerBufferCount >= minBufferSlots) ?
                 mServerBufferCount : minBufferSlots;
         return setBufferCountServerLocked(bufferCount);
     }
 
-    // We don't allow the client to set a buffer-count less than
-    // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
-    if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
+    if (bufferCount < minBufferSlots) {
+        LOGE("setBufferCount: requested buffer count (%d) is less than "
+                "minimum (%d)", bufferCount, minBufferSlots);
         return BAD_VALUE;
     }
 
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index e6837ea..d1037de 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -258,10 +258,10 @@
     int res = NO_ERROR;
     switch (operation) {
     case NATIVE_WINDOW_CONNECT:
-        res = dispatchConnect(args);
+        // deprecated. must return NO_ERROR.
         break;
     case NATIVE_WINDOW_DISCONNECT:
-        res = dispatchDisconnect(args);
+        // deprecated. must return NO_ERROR.
         break;
     case NATIVE_WINDOW_SET_USAGE:
         res = dispatchSetUsage(args);
@@ -296,6 +296,12 @@
     case NATIVE_WINDOW_SET_SCALING_MODE:
         res = dispatchSetScalingMode(args);
         break;
+    case NATIVE_WINDOW_API_CONNECT:
+        res = dispatchConnect(args);
+        break;
+    case NATIVE_WINDOW_API_DISCONNECT:
+        res = dispatchDisconnect(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index c5858e9..24ec4e8 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -142,7 +142,6 @@
 
 void Caches::clearGarbage() {
     textureCache.clearGarbage();
-    gradientCache.clearGarbage();
     pathCache.clearGarbage();
 
     Mutex::Autolock _l(mGarbageLock);
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 5db73db..7cbb39d 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -20,6 +20,9 @@
 // Turn on to check for OpenGL errors on each frame
 #define DEBUG_OPENGL 1
 
+// Turn on to display informations about the GPU
+#define DEBUG_EXTENSIONS 0
+
 // Turn on to enable initialization information
 #define DEBUG_INIT 0
 
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index eceb5c1c..38d1130 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -23,6 +23,8 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include "Debug.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -31,15 +33,20 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // Debug
-#define DEBUG_EXTENSIONS 0
-
-// Debug
 #if DEBUG_EXTENSIONS
     #define EXT_LOGD(...) LOGD(__VA_ARGS__)
 #else
     #define EXT_LOGD(...)
 #endif
 
+// Vendor strings
+
+#define VENDOR_IMG "Imagination Technologies"
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
 class Extensions {
 public:
     Extensions() {
@@ -58,17 +65,21 @@
         } while (head);
 
         mHasNPot = hasExtension("GL_OES_texture_npot");
-        mHasDrawPath = hasExtension("GL_NV_draw_path");
-        mHasCoverageSample = hasExtension("GL_NV_coverage_sample");
         mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
 
+        const char* vendor = (const char*) glGetString(GL_VENDOR);
+        EXT_LOGD("Vendor: %s", vendor);
+        mNeedsHighpTexCoords = strcmp(vendor, VENDOR_IMG) == 0;
+
+        // We don't need to copy the string, the OpenGL ES spec
+        // guarantees the result of glGetString to point to a
+        // static string as long as our OpenGL context is valid
         mExtensions = buffer;
     }
 
     inline bool hasNPot() const { return mHasNPot; }
-    inline bool hasDrawPath() const { return mHasDrawPath; }
-    inline bool hasCoverageSample() const { return mHasCoverageSample; }
     inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
+    inline bool needsHighpTexCoords() const { return mNeedsHighpTexCoords; }
 
     bool hasExtension(const char* extension) const {
         const String8 s(extension);
@@ -85,8 +96,7 @@
     const char* mExtensions;
 
     bool mHasNPot;
-    bool mHasDrawPath;
-    bool mHasCoverageSample;
+    bool mNeedsHighpTexCoords;
     bool mHasFramebufferFetch;
 }; // class Extensions
 
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 996acd5..aacf22a 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -35,7 +35,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GradientCache::GradientCache():
-        mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+        mCache(GenerationCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
@@ -49,7 +49,7 @@
 }
 
 GradientCache::GradientCache(uint32_t maxByteSize):
-        mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+        mCache(GenerationCache<GradientCacheEntry, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     mCache.setOnEntryRemovedListener(this);
 }
@@ -81,9 +81,8 @@
 // Callbacks
 ///////////////////////////////////////////////////////////////////////////////
 
-void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
-    // Already locked here
-    if (shader) {
+void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
+    if (texture) {
         const uint32_t size = texture->width * texture->height * 4;
         mSize -= size;
     }
@@ -98,34 +97,25 @@
 // Caching
 ///////////////////////////////////////////////////////////////////////////////
 
-Texture* GradientCache::get(SkShader* shader) {
-    return mCache.get(shader);
-}
+Texture* GradientCache::get(uint32_t* colors, float* positions,
+        int count, SkShader::TileMode tileMode) {
 
-void GradientCache::remove(SkShader* shader) {
-    mCache.remove(shader);
-}
+    GradientCacheEntry gradient(colors, positions, count, tileMode);
+    Texture* texture = mCache.get(gradient);
 
-void GradientCache::removeDeferred(SkShader* shader) {
-    Mutex::Autolock _l(mLock);
-    mGarbage.push(shader);
-}
-
-void GradientCache::clearGarbage() {
-    Mutex::Autolock _l(mLock);
-    size_t count = mGarbage.size();
-    for (size_t i = 0; i < count; i++) {
-        mCache.remove(mGarbage.itemAt(i));
+    if (!texture) {
+        texture = addLinearGradient(gradient, colors, positions, count, tileMode);
     }
-    mGarbage.clear();
+
+    return texture;
 }
 
 void GradientCache::clear() {
     mCache.clear();
 }
 
-Texture* GradientCache::addLinearGradient(SkShader* shader, uint32_t* colors,
-        float* positions, int count, SkShader::TileMode tileMode) {
+Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
+        uint32_t* colors, float* positions, int count, SkShader::TileMode tileMode) {
     SkBitmap bitmap;
     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1);
     bitmap.allocPixels();
@@ -156,7 +146,7 @@
     generateTexture(&bitmap, texture);
 
     mSize += size;
-    mCache.put(shader, texture);
+    mCache.put(gradient, texture);
 
     return texture;
 }
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 30da462..086921c 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -22,17 +22,74 @@
 #include <utils/Vector.h>
 
 #include "Texture.h"
+#include "utils/Compare.h"
 #include "utils/GenerationCache.h"
 
 namespace android {
 namespace uirenderer {
 
+struct GradientCacheEntry {
+    GradientCacheEntry() {
+        count = 0;
+        colors = NULL;
+        positions = NULL;
+        tileMode = SkShader::kClamp_TileMode;
+    }
+
+    GradientCacheEntry(uint32_t* colors, float* positions, int count,
+            SkShader::TileMode tileMode) {
+        this->count = count;
+        this->colors = new uint32_t[count];
+        this->positions = new float[count];
+        this->tileMode = tileMode;
+
+        memcpy(this->colors, colors, count * sizeof(uint32_t));
+        memcpy(this->positions, positions, count * sizeof(float));
+    }
+
+    GradientCacheEntry(const GradientCacheEntry& entry) {
+        this->count = entry.count;
+        this->colors = new uint32_t[count];
+        this->positions = new float[count];
+        this->tileMode = entry.tileMode;
+
+        memcpy(this->colors, entry.colors, count * sizeof(uint32_t));
+        memcpy(this->positions, entry.positions, count * sizeof(float));
+    }
+
+    ~GradientCacheEntry() {
+        delete[] colors;
+        delete[] positions;
+    }
+
+    bool operator<(const GradientCacheEntry& r) const {
+        const GradientCacheEntry& rhs = (const GradientCacheEntry&) r;
+        LTE_INT(count) {
+            LTE_INT(tileMode) {
+                int result = memcmp(colors, rhs.colors, count * sizeof(uint32_t));
+                if (result< 0) return true;
+                else if (result == 0) {
+                    result = memcmp(positions, rhs.positions, count * sizeof(float));
+                    if (result < 0) return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    uint32_t* colors;
+    float* positions;
+    int count;
+    SkShader::TileMode tileMode;
+
+}; // GradientCacheEntry
+
 /**
  * A simple LRU gradient cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
  * allowed size will also cause the oldest texture to be kicked out.
  */
-class GradientCache: public OnEntryRemoved<SkShader*, Texture*> {
+class GradientCache: public OnEntryRemoved<GradientCacheEntry, Texture*> {
 public:
     GradientCache();
     GradientCache(uint32_t maxByteSize);
@@ -42,32 +99,13 @@
      * Used as a callback when an entry is removed from the cache.
      * Do not invoke directly.
      */
-    void operator()(SkShader*& shader, Texture*& texture);
+    void operator()(GradientCacheEntry& shader, Texture*& texture);
 
     /**
-     * Adds a new linear gradient to the cache. The generated texture is
-     * returned.
-     */
-    Texture* addLinearGradient(SkShader* shader, uint32_t* colors, float* positions,
-            int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
-    /**
      * Returns the texture associated with the specified shader.
      */
-    Texture* get(SkShader* shader);
-    /**
-     * Removes the texture associated with the specified shader.
-     * Upon remove the texture is freed.
-     */
-    void remove(SkShader* shader);
-    /**
-     * Removes the texture associated with the specified shader. This is meant
-     * to be called from threads that are not the EGL context thread.
-     */
-    void removeDeferred(SkShader* shader);
-    /**
-     * Process deferred removals.
-     */
-    void clearGarbage();
+    Texture* get(uint32_t* colors, float* positions,
+            int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
     /**
      * Clears the cache. This causes all textures to be deleted.
      */
@@ -87,9 +125,17 @@
     uint32_t getSize();
 
 private:
+    /**
+     * Adds a new linear gradient to the cache. The generated texture is
+     * returned.
+     */
+    Texture* addLinearGradient(GradientCacheEntry& gradient,
+            uint32_t* colors, float* positions, int count,
+            SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
+
     void generateTexture(SkBitmap* bitmap, Texture* texture);
 
-    GenerationCache<SkShader*, Texture*> mCache;
+    GenerationCache<GradientCacheEntry, Texture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index d419e3e..c2383f4 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -18,6 +18,7 @@
 
 #include <utils/String8.h>
 
+#include "Caches.h"
 #include "ProgramCache.h"
 
 namespace android {
@@ -64,10 +65,18 @@
 const char* gVS_Header_Varyings_IsAA =
         "varying float widthProportion;\n"
         "varying float lengthProportion;\n";
-const char* gVS_Header_Varyings_HasBitmap =
-        "varying vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_PointHasBitmap =
-        "varying vec2 outPointBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_HasBitmap[2] = {
+        // Default precision
+        "varying vec2 outBitmapTexCoords;\n",
+        // High precision
+        "varying highp vec2 outBitmapTexCoords;\n"
+};
+const char* gVS_Header_Varyings_PointHasBitmap[2] = {
+        // Default precision
+        "varying vec2 outPointBitmapTexCoords;\n",
+        // High precision
+        "varying highp vec2 outPointBitmapTexCoords;\n"
+};
 const char* gVS_Header_Varyings_HasGradient[3] = {
         // Linear
         "varying vec2 linear;\n",
@@ -417,9 +426,10 @@
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
     if (description.hasBitmap) {
+        int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
         shader.append(description.isPoint ?
-                gVS_Header_Varyings_PointHasBitmap :
-                gVS_Header_Varyings_HasBitmap);
+                gVS_Header_Varyings_PointHasBitmap[index] :
+                gVS_Header_Varyings_HasBitmap[index]);
     }
 
     // Begin the shader
@@ -455,7 +465,6 @@
 }
 
 String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
-    // Set the default precision
     String8 shader;
 
     const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
@@ -479,9 +488,10 @@
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
     if (description.hasBitmap) {
+        int index = Caches::getInstance().extensions.needsHighpTexCoords() ? 1 : 0;
         shader.append(description.isPoint ?
-                gVS_Header_Varyings_PointHasBitmap :
-                gVS_Header_Varyings_HasBitmap);
+                gVS_Header_Varyings_PointHasBitmap[index] :
+                gVS_Header_Varyings_HasBitmap[index]);
     }
 
     // Uniforms
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index cd2c405..ee73983 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -166,9 +166,6 @@
     ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
     if (ref == NULL) {
         // If we're not tracking this resource, just delete it
-        if (Caches::hasInstance()) {
-            Caches::getInstance().gradientCache.removeDeferred(resource->getSkShader());
-        }
         delete resource;
         return;
     }
@@ -220,9 +217,6 @@
             break;
             case kShader: {
                 SkiaShader* shader = (SkiaShader*) resource;
-                if (Caches::hasInstance()) {
-                    Caches::getInstance().gradientCache.removeDeferred(shader->getSkShader());
-                }
                 delete shader;
             }
             break;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 06382f2..2428295 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -225,10 +225,7 @@
     GLuint textureSlot = (*textureUnit)++;
     glActiveTexture(gTextureUnitsMap[textureSlot]);
 
-    Texture* texture = mGradientCache->get(mKey);
-    if (!texture) {
-        texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount, mTileX);
-    }
+    Texture* texture = mGradientCache->get(mColors, mPositions, mCount, mTileX);
 
     mat4 screenSpace;
     computeScreenSpaceMatrix(screenSpace, modelView);
@@ -340,10 +337,7 @@
     GLuint textureSlot = (*textureUnit)++;
     glActiveTexture(gTextureUnitsMap[textureSlot]);
 
-    Texture* texture = mGradientCache->get(mKey);
-    if (!texture) {
-        texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount);
-    }
+    Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
 
     mat4 screenSpace;
     computeScreenSpaceMatrix(screenSpace, modelView);
diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index 0c0aa10..86bec6e 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -112,7 +112,7 @@
         goto error;
     }
 
-    if (bccPrepareExecutableEx(drv->mBccScript, cacheDir, resName, 0) != 0) {
+    if (bccPrepareExecutable(drv->mBccScript, cacheDir, resName, 0) != 0) {
         LOGE("bcc: FAILS to prepare executable");
         goto error;
     }
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 0e8ae61..8949730 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -317,6 +317,8 @@
         case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
         case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
         case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+        case NATIVE_WINDOW_API_CONNECT:
+        case NATIVE_WINDOW_API_DISCONNECT:
             // TODO: we should implement these
             return NO_ERROR;
 
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 33ef1fc..e75415b 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -61,13 +61,19 @@
     const size_t c = list.size();
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
-        snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x\n",
-            list.keyAt(i), rec.size/1024.0f, 
-            rec.w, rec.s, rec.h, rec.format, rec.usage);
+        if (rec.size) {
+            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x\n",
+                    list.keyAt(i), rec.size/1024.0f,
+                    rec.w, rec.s, rec.h, rec.format, rec.usage);
+        } else {
+            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %8X | 0x%08x\n",
+                    list.keyAt(i),
+                    rec.w, rec.s, rec.h, rec.format, rec.usage);
+        }
         result.append(buffer);
         total += rec.size;
     }
-    snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f);
+    snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0f);
     result.append(buffer);
     if (mAllocDev->common.version >= 1 && mAllocDev->dump) {
         mAllocDev->dump(mAllocDev, buffer, SIZE);
@@ -101,13 +107,19 @@
     if (err == NO_ERROR) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+        int bpp = bytesPerPixel(format);
+        if (bpp < 0) {
+            // probably a HAL custom format. in any case, we don't know
+            // what its pixel size is.
+            bpp = 0;
+        }
         alloc_rec_t rec;
         rec.w = w;
         rec.h = h;
         rec.s = *stride;
         rec.format = format;
         rec.usage = usage;
-        rec.size = h * stride[0] * bytesPerPixel(format);
+        rec.size = h * stride[0] * bpp;
         list.add(*handle, rec);
     }
 
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 0af7f80..688b998 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -280,6 +280,9 @@
     uint64_t axisBit = 1LL << axis;
     uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
     if (!(bits & axisBit)) {
+        if (value == 0) {
+            return OK; // axes with value 0 do not need to be stored
+        }
         uint32_t count = __builtin_popcountll(bits);
         if (count >= MAX_AXES) {
             tooManyAxes(axis);
@@ -294,23 +297,10 @@
     return OK;
 }
 
-float* PointerCoords::editAxisValue(int32_t axis) {
-    if (axis < 0 || axis > 63) {
-        return NULL;
-    }
-
-    uint64_t axisBit = 1LL << axis;
-    if (!(bits & axisBit)) {
-        return NULL;
-    }
-    uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL));
-    return &values[index];
-}
-
 static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
-    float* value = c.editAxisValue(axis);
-    if (value) {
-        *value *= scaleFactor;
+    float value = c.getAxisValue(axis);
+    if (value != 0) {
+        c.setAxisValue(axis, value * scaleFactor);
     }
 }
 
@@ -574,20 +564,14 @@
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
         PointerCoords& c = mSamplePointerCoords.editItemAt(i);
-        float* xPtr = c.editAxisValue(AMOTION_EVENT_AXIS_X);
-        float* yPtr = c.editAxisValue(AMOTION_EVENT_AXIS_Y);
-        if (xPtr && yPtr) {
-            float x = *xPtr + oldXOffset;
-            float y = *yPtr + oldYOffset;
-            matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point);
-            *xPtr = SkScalarToFloat(point.fX) - newXOffset;
-            *yPtr = SkScalarToFloat(point.fY) - newYOffset;
-        }
+        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
+        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+        matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), &point);
+        c.setAxisValue(AMOTION_EVENT_AXIS_X, SkScalarToFloat(point.fX) - newXOffset);
+        c.setAxisValue(AMOTION_EVENT_AXIS_Y, SkScalarToFloat(point.fY) - newYOffset);
 
-        float* orientationPtr = c.editAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
-        if (orientationPtr) {
-            *orientationPtr = transformAngle(matrix, *orientationPtr);
-        }
+        float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(matrix, orientation));
     }
 }
 
@@ -727,7 +711,7 @@
     }
 
     while (idBits.count() > MAX_POINTERS) {
-        idBits.clearBit(idBits.lastMarkedBit());
+        idBits.clearLastMarkedBit();
     }
 
     Movement& movement = mMovements[mIndex];
@@ -776,7 +760,7 @@
         // We do this on down instead of on up because the client may want to query the
         // final velocity for a pointer that just went up.
         BitSet32 downIdBits;
-        downIdBits.markBit(event->getActionIndex());
+        downIdBits.markBit(event->getPointerId(event->getActionIndex()));
         clearPointers(downIdBits);
         break;
     }
diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp
index e48d5b7..e21c464 100644
--- a/libs/ui/tests/InputEvent_test.cpp
+++ b/libs/ui/tests/InputEvent_test.cpp
@@ -52,9 +52,6 @@
     ASSERT_EQ(0, coords.getAxisValue(1))
             << "getAxisValue should return zero because axis is not present";
 
-    ASSERT_EQ(NULL, coords.editAxisValue(0))
-            << "editAxisValue should return null because axis is not present";
-
     // Set first axis.
     ASSERT_EQ(OK, coords.setAxisValue(1, 5));
     ASSERT_EQ(0x00000002ULL, coords.bits);
@@ -96,26 +93,17 @@
     ASSERT_EQ(2, coords.getAxisValue(3))
             << "getAxisValue should return value of axis";
 
-    // Edit an existing axis value in place.
-    valuePtr = coords.editAxisValue(1);
-    ASSERT_EQ(5, *valuePtr)
-            << "editAxisValue should return pointer to axis value";
-
-    *valuePtr = 7;
-    ASSERT_EQ(7, coords.getAxisValue(1))
-            << "getAxisValue should return value of axis";
-
     // Set an axis with an id between the others.  (inserting value in the middle)
     ASSERT_EQ(OK, coords.setAxisValue(2, 1));
     ASSERT_EQ(0x0000000fULL, coords.bits);
     ASSERT_EQ(4, coords.values[0]);
-    ASSERT_EQ(7, coords.values[1]);
+    ASSERT_EQ(5, coords.values[1]);
     ASSERT_EQ(1, coords.values[2]);
     ASSERT_EQ(2, coords.values[3]);
 
     ASSERT_EQ(4, coords.getAxisValue(0))
             << "getAxisValue should return value of axis";
-    ASSERT_EQ(7, coords.getAxisValue(1))
+    ASSERT_EQ(5, coords.getAxisValue(1))
             << "getAxisValue should return value of axis";
     ASSERT_EQ(1, coords.getAxisValue(2))
             << "getAxisValue should return value of axis";
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 58f9432..8339e4b 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -40,7 +40,6 @@
 import android.provider.MediaStore.Files;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
-import android.provider.MediaStore.Audio.Genres;
 import android.provider.MediaStore.Audio.Playlists;
 import android.sax.Element;
 import android.sax.ElementListener;
@@ -138,11 +137,6 @@
     private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
     private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
 
-    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
-            Audio.Genres._ID, // 0
-            Audio.Genres.NAME, // 1
-    };
-
     private static final String RINGTONES_DIR = "/ringtones/";
     private static final String NOTIFICATIONS_DIR = "/notifications/";
     private static final String ALARMS_DIR = "/alarms/";
@@ -311,10 +305,9 @@
     private Uri mVideoUri;
     private Uri mImagesUri;
     private Uri mThumbsUri;
-    private Uri mGenresUri;
     private Uri mPlaylistsUri;
     private Uri mFilesUri;
-    private boolean mProcessPlaylists, mProcessGenres;
+    private boolean mProcessPlaylists;
     private int mMtpObjectHandle;
 
     private final String mExternalStoragePath;
@@ -413,7 +406,6 @@
     private HashMap<String, FileCacheEntry> mFileCache;
 
     private ArrayList<FileCacheEntry> mPlayLists;
-    private HashMap<String, Uri> mGenreCache;
 
     private DrmManagerClient mDrmManagerClient = null;
 
@@ -735,6 +727,7 @@
                     map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
                             mAlbum : MediaStore.UNKNOWN_STRING);
                     map.put(Audio.Media.COMPOSER, mComposer);
+                    map.put(Audio.Media.GENRE, mGenre);
                     if (mYear != 0) {
                         map.put(Audio.Media.YEAR, mYear);
                     }
@@ -894,46 +887,6 @@
                 values.remove(MediaStore.MediaColumns.DATA);
                 mMediaProvider.update(result, values, null, null);
             }
-            if (mProcessGenres && mGenre != null) {
-                String genre = mGenre;
-                Uri uri = mGenreCache.get(genre);
-                if (uri == null) {
-                    Cursor cursor = null;
-                    try {
-                        // see if the genre already exists
-                        cursor = mMediaProvider.query(
-                                mGenresUri,
-                                GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
-                                        new String[] { genre }, null);
-                        if (cursor == null || cursor.getCount() == 0) {
-                            // genre does not exist, so create the genre in the genre table
-                            values = new ContentValues();
-                            values.put(MediaStore.Audio.Genres.NAME, genre);
-                            uri = mMediaProvider.insert(mGenresUri, values);
-                        } else {
-                            // genre already exists, so compute its Uri
-                            cursor.moveToNext();
-                            uri = ContentUris.withAppendedId(mGenresUri, cursor.getLong(0));
-                        }
-                        if (uri != null) {
-                            uri = Uri.withAppendedPath(uri, Genres.Members.CONTENT_DIRECTORY);
-                            mGenreCache.put(genre, uri);
-                        }
-                    } finally {
-                        // release the cursor if it exists
-                        if (cursor != null) {
-                            cursor.close();
-                        }
-                    }
-                }
-
-                if (uri != null) {
-                    // add entry to audio_genre_map
-                    values = new ContentValues();
-                    values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
-                    mMediaProvider.insert(uri, values);
-                }
-            }
 
             if (notifications && !mDefaultNotificationSet) {
                 if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
@@ -1181,7 +1134,6 @@
             pruneDeadThumbnailFiles();
 
         // allow GC to clean up
-        mGenreCache = null;
         mPlayLists = null;
         mFileCache = null;
         mMediaProvider = null;
@@ -1199,9 +1151,6 @@
         if (!volumeName.equals("internal")) {
             // we only support playlists on external media
             mProcessPlaylists = true;
-            mProcessGenres = true;
-            mGenreCache = new HashMap<String, Uri>();
-            mGenresUri = Genres.getContentUri(volumeName);
             mPlaylistsUri = Playlists.getContentUri(volumeName);
 
             mCaseInsensitivePaths = true;
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 76a8a91..52885d2 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -352,7 +352,9 @@
         } break;
         case SET_VOLUME: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
-            reply->writeInt32(setVolume(data.readFloat(), data.readFloat()));
+            float leftVolume = data.readFloat();
+            float rightVolume = data.readFloat();
+            reply->writeInt32(setVolume(leftVolume, rightVolume));
             return NO_ERROR;
         } break;
         case INVOKE: {
@@ -367,7 +369,9 @@
         } break;
         case GET_METADATA: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
-            const status_t retcode = getMetadata(data.readInt32(), data.readInt32(), reply);
+            bool update_only = static_cast<bool>(data.readInt32());
+            bool apply_filter = static_cast<bool>(data.readInt32());
+            const status_t retcode = getMetadata(update_only, apply_filter, reply);
             reply->setDataPosition(0);
             reply->writeInt32(retcode);
             reply->setDataPosition(0);
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index a11fb80..3dd9249 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -201,11 +201,11 @@
 
 void MediaPlayer::disconnectNativeWindow() {
     if (mConnectedWindow != NULL) {
-        status_t err = native_window_disconnect(mConnectedWindow.get(),
+        status_t err = native_window_api_disconnect(mConnectedWindow.get(),
                 NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
-            LOGW("native_window_disconnect returned an error: %s (%d)",
+            LOGW("native_window_api_disconnect returned an error: %s (%d)",
                     strerror(-err), err);
         }
     }
@@ -224,7 +224,7 @@
     }
 
     if (surface != NULL) {
-        status_t err = native_window_connect(surface.get(),
+        status_t err = native_window_api_connect(surface.get(),
                 NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
@@ -274,7 +274,7 @@
     sp<ANativeWindow> anw;
     if (surfaceTexture != NULL) {
         anw = new SurfaceTextureClient(surfaceTexture);
-        status_t err = native_window_connect(anw.get(),
+        status_t err = native_window_api_connect(anw.get(),
                 NATIVE_WINDOW_API_MEDIA);
 
         if (err != OK) {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 3a3c082..3f4dace 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -132,10 +132,9 @@
 
 LOCAL_STATIC_LIBRARIES += \
         libstagefright_chromium_http \
-        libchromium_net         \
         libwebcore              \
 
-LOCAL_SHARED_LIBRARIES += libstlport
+LOCAL_SHARED_LIBRARIES += libstlport libchromium_net
 include external/stlport/libstlport.mk
 
 LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index de66d99..ea8eaa4 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -37,7 +37,8 @@
     CameraSourceListener(const sp<CameraSource> &source);
 
     virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
-    virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr);
+    virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr,
+                          camera_frame_metadata_t *metadata);
 
     virtual void postDataTimestamp(
             nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
@@ -63,7 +64,8 @@
     LOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
 }
 
-void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) {
+void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
+                                    camera_frame_metadata_t *metadata) {
     LOGV("postData(%d, ptr:%p, size:%d)",
          msgType, dataPtr->pointer(), dataPtr->size());
 
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 5bbed5d..92e84c2 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -407,6 +407,8 @@
 
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
+    bool seekCBR = false;
+
     if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
         int64_t actualSeekTimeUs = seekTimeUs;
         if (mSeeker == NULL
@@ -421,6 +423,7 @@
 
             mCurrentTimeUs = seekTimeUs;
             mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000;
+            seekCBR = true;
         } else {
             mCurrentTimeUs = actualSeekTimeUs;
         }
@@ -454,6 +457,13 @@
             && GetMPEGAudioFrameSize(
                 header, &frame_size, &sample_rate, NULL,
                 &bitrate, &num_samples)) {
+
+            // re-calculate mCurrentTimeUs because we might have called Resync()
+            if (seekCBR) {
+                mCurrentTimeUs = (mCurrentPos - mFirstFramePos) * 8000 / bitrate;
+                mBasisTimeUs = mCurrentTimeUs;
+            }
+
             break;
         }
 
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 020e947..8a42e8b 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -188,7 +188,7 @@
     }
 
     size_t endOffset = offset - 2;
-    while (data[endOffset - 1] == 0x00) {
+    while (endOffset > startOffset + 1 && data[endOffset - 1] == 0x00) {
         --endOffset;
     }
 
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 90d64ba..2578d2d 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -544,6 +544,7 @@
         firstSeqNumberInPlaylist = 0;
     }
 
+    bool seekDiscontinuity = false;
     bool explicitDiscontinuity = false;
     bool bandwidthChanged = false;
 
@@ -580,10 +581,10 @@
                     // reseting the data source will have had the
                     // side effect of discarding any previously queued
                     // bandwidth change discontinuity.
-                    // Therefore we'll need to treat these explicit
+                    // Therefore we'll need to treat these seek
                     // discontinuities as involving a bandwidth change
                     // even if they aren't directly.
-                    explicitDiscontinuity = true;
+                    seekDiscontinuity = true;
                     bandwidthChanged = true;
                 }
             }
@@ -597,11 +598,7 @@
     }
 
     if (mSeqNumber < 0) {
-        if (mPlaylist->isComplete()) {
-            mSeqNumber = firstSeqNumberInPlaylist;
-        } else {
-            mSeqNumber = firstSeqNumberInPlaylist + mPlaylist->size() / 2;
-        }
+        mSeqNumber = firstSeqNumberInPlaylist;
     }
 
     int32_t lastSeqNumberInPlaylist =
@@ -704,15 +701,17 @@
         bandwidthChanged = false;
     }
 
-    if (explicitDiscontinuity || bandwidthChanged) {
+    if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) {
         // Signal discontinuity.
 
-        LOGI("queueing discontinuity (explicit=%d, bandwidthChanged=%d)",
-             explicitDiscontinuity, bandwidthChanged);
+        LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)",
+             seekDiscontinuity, explicitDiscontinuity, bandwidthChanged);
 
         sp<ABuffer> tmp = new ABuffer(188);
         memset(tmp->data(), 0, tmp->size());
-        tmp->data()[1] = bandwidthChanged;
+
+        // signal a 'hard' discontinuity for explicit or bandwidthChanged.
+        tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0;
 
         mDataSource->queueBuffer(tmp);
     }
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index dc6f2c9..5b32b68 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -57,6 +57,7 @@
 protected:
 
     virtual void SetUp() {
+        android::ProcessState::self()->startThreadPool();
         mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
         mSMS->setSynchronousMode(true);
         mSTC = new SurfaceTextureClient(mSMS);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 0b0d0ce..369a067 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -16,24 +16,33 @@
 
 package com.android.mediaframeworktest;
 
+import android.media.EncoderCapabilities.AudioEncoderCap;
+import android.media.EncoderCapabilities.VideoEncoderCap;
 import android.media.MediaRecorder;
 import android.os.Bundle;
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
 import com.android.mediaframeworktest.stress.MediaRecorderStressTest;
 
+import java.util.List;
 import junit.framework.TestSuite;
 
 public class MediaRecorderStressTestRunner extends InstrumentationTestRunner {
 
-    // Default recorder settings
+    public static List<VideoEncoderCap> videoEncoders = MediaProfileReader.getVideoEncoders();
+    public static  List<AudioEncoderCap> audioEncoders = MediaProfileReader.getAudioEncoders();
+
+    //Get the first capability as the default
+    public static VideoEncoderCap videoEncoder = videoEncoders.get(0);
+    public static AudioEncoderCap audioEncoder = audioEncoders.get(0);
+
     public static int mIterations = 100;
-    public static int mVideoEncoder = MediaRecorder.VideoEncoder.H263;
-    public static int mAudioEncdoer = MediaRecorder.AudioEncoder.AMR_NB;
-    public static int mFrameRate = 20;
-    public static int mVideoWidth = 352;
-    public static int mVideoHeight = 288;
-    public static int mBitRate = 100;
+    public static int mVideoEncoder = videoEncoder.mCodec;
+    public static int mAudioEncdoer = audioEncoder.mCodec;
+    public static int mFrameRate = videoEncoder.mMaxFrameRate;
+    public static int mVideoWidth = videoEncoder.mMaxFrameWidth;
+    public static int mVideoHeight = videoEncoder.mMaxFrameHeight;
+    public static int mBitRate = audioEncoder.mMaxBitRate;
     public static boolean mRemoveVideo = true;
     public static int mDuration = 10000;
 
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index ba5d29a..10cea22 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -363,6 +363,12 @@
         EGLConfig iConfig = dp->configs[intptr_t(config)].config;
         EGLint format;
 
+        if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) {
+            LOGE("EGLNativeWindowType %p already connected to another API",
+                    window);
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
         // set the native window's buffers format to match this config
         if (cnx->egl.eglGetConfigAttrib(iDpy,
                 iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
@@ -371,6 +377,7 @@
                 if (err != 0) {
                     LOGE("error setting native window pixel format: %s (%d)",
                             strerror(-err), err);
+                    native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
                     return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
                 }
             }
@@ -383,6 +390,10 @@
                     dp->configs[intptr_t(config)].impl, cnx);
             return s;
         }
+
+        // EGLSurface creation failed
+        native_window_set_buffers_format(window, 0);
+        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
     }
     return EGL_NO_SURFACE;
 }
@@ -443,8 +454,12 @@
     EGLBoolean result = s->cnx->egl.eglDestroySurface(
             dp->disp[s->impl].dpy, s->surface);
     if (result == EGL_TRUE) {
-        if (s->win != NULL) {
-            native_window_set_buffers_geometry(s->win.get(), 0, 0, 0);
+        ANativeWindow* const window = s->win.get();
+        if (window != NULL) {
+            native_window_set_buffers_format(window, 0);
+            if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
+                LOGE("EGLNativeWindowType %p disconnected failed", window);
+            }
         }
         _s.terminate();
     }
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
index 08dcfae..3668b8c 100644
--- a/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
@@ -21,7 +21,7 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="fill_parent" 
                 android:layout_height="wrap_content"
-                android:padding="10dp" >
+                android:padding="16dp" >
 
     <TextView android:id="@+id/confirm_text"
               android:layout_width="match_parent" 
@@ -34,12 +34,26 @@
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_marginBottom="10dp"
-              android:text="@string/backup_password_text" />
+              android:text="@string/current_password_text" />
 
     <EditText android:id="@+id/password"
               android:layout_below="@id/password_desc"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
+              android:layout_marginBottom="10dp"
+              android:password="true" />
+
+    <TextView android:id="@+id/enc_password_desc"
+              android:layout_below="@id/password"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_marginBottom="10dp"
+              android:text="@string/backup_enc_password_text" />
+
+    <EditText android:id="@+id/enc_password"
+              android:layout_below="@id/enc_password_desc"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
               android:layout_marginBottom="30dp"
               android:password="true" />
 
@@ -47,7 +61,7 @@
               android:layout_width="match_parent"
               android:layout_height="20dp"
               android:layout_marginLeft="30dp"
-              android:layout_below="@id/password"
+              android:layout_below="@id/enc_password"
               android:layout_marginBottom="30dp" />
 
     <Button android:id="@+id/button_allow"
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
index 8b12ed4..38fcc49 100644
--- a/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
@@ -21,7 +21,7 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="fill_parent" 
                 android:layout_height="wrap_content"
-                android:padding="10dp" >
+                android:padding="16dp" >
 
     <TextView android:id="@+id/confirm_text"
               android:layout_width="match_parent" 
@@ -34,7 +34,7 @@
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_marginBottom="10dp"
-              android:text="@string/restore_password_text" />
+              android:text="@string/current_password_text" />
 
     <EditText android:id="@+id/password"
               android:layout_below="@id/password_desc"
@@ -43,11 +43,25 @@
               android:layout_marginBottom="30dp"
               android:password="true" />
 
+    <TextView android:id="@+id/enc_password_desc"
+              android:layout_below="@id/password"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_marginBottom="10dp"
+              android:text="@string/restore_enc_password_text" />
+
+    <EditText android:id="@+id/enc_password"
+              android:layout_below="@id/enc_password_desc"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:layout_marginBottom="30dp"
+              android:password="true" />
+
     <TextView android:id="@+id/package_name"
               android:layout_width="match_parent"
               android:layout_height="20dp"
               android:layout_marginLeft="30dp"
-              android:layout_below="@id/password"
+              android:layout_below="@id/enc_password"
               android:layout_marginBottom="30dp" />
 
     <Button android:id="@+id/button_allow"
diff --git a/packages/BackupRestoreConfirmation/res/values/strings.xml b/packages/BackupRestoreConfirmation/res/values/strings.xml
index 48a8df6..f022e57 100644
--- a/packages/BackupRestoreConfirmation/res/values/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values/strings.xml
@@ -30,10 +30,13 @@
     <string name="deny_restore_button_label">Do not restore</string>
 
     <!-- Text for message to user that they must enter their predefined backup password in order to perform this operation. -->
-    <string name="backup_password_text">Please enter your predefined backup password below. The full backup will also be encrypted using this password:</string>
+    <string name="current_password_text">Please enter your current backup password below:</string>
+
+    <!-- Text for message to user that they can must enter an encryption password to use for the full backup operation. -->
+    <string name="backup_enc_password_text">Please enter a password to use for encrypting the full backup data. If this is left blank, your current backup password will be used:</string>
     <!-- Text for message to user that they may optionally supply an encryption password to use for a full backup operation. -->
-    <string name="backup_password_optional">If you wish to encrypt the full backup data, enter a password below:</string>
+    <string name="backup_enc_password_optional">If you wish to encrypt the full backup data, enter a password below:</string>
 
     <!-- Text for message to user when performing a full restore operation, explaining that they must enter the password originally used to encrypt the full backup data. -->
-    <string name="restore_password_text">If the backup data is encrypted, please enter the password below:</string>
+    <string name="restore_enc_password_text">If the restore data is encrypted, please enter the password below:</string>
 </resources>
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index fad58b9..f65a62f 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -63,6 +63,8 @@
     boolean mDidAcknowledge;
 
     TextView mStatusView;
+    TextView mCurPassword;
+    TextView mEncPassword;
     Button mAllowButton;
     Button mDenyButton;
 
@@ -156,17 +158,17 @@
         mAllowButton = (Button) findViewById(R.id.button_allow);
         mDenyButton = (Button) findViewById(R.id.button_deny);
 
-        // For full backup, we vary the password prompt text depending on whether one is predefined
-        if (layoutId == R.layout.confirm_backup) {
-            TextView pwDesc = (TextView) findViewById(R.id.password_desc); 
-            try {
-                if (mBackupManager.hasBackupPassword()) {
-                    pwDesc.setText(R.string.backup_password_text);
-                } else {
-                    pwDesc.setText(R.string.backup_password_optional);
-                }
-            } catch (RemoteException e) {
-                // TODO: bail gracefully
+        mCurPassword = (TextView) findViewById(R.id.password);
+        mEncPassword = (TextView) findViewById(R.id.enc_password);
+        TextView curPwDesc = (TextView) findViewById(R.id.password_desc);
+
+        // We vary the password prompt depending on whether one is predefined
+        if (!haveBackupPassword()) {
+            curPwDesc.setVisibility(View.GONE);
+            mCurPassword.setVisibility(View.GONE);
+            if (layoutId == R.layout.confirm_backup) {
+                TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc);
+                encPwDesc.setText(R.string.backup_enc_password_optional);
             }
         }
 
@@ -204,15 +206,25 @@
             mDidAcknowledge = true;
 
             try {
-                TextView pwView = (TextView) findViewById(R.id.password);
-                mBackupManager.acknowledgeFullBackupOrRestore(mToken, allow,
-                        String.valueOf(pwView.getText()), mObserver);
+                mBackupManager.acknowledgeFullBackupOrRestore(mToken,
+                        allow,
+                        String.valueOf(mCurPassword.getText()),
+                        String.valueOf(mEncPassword.getText()),
+                        mObserver);
             } catch (RemoteException e) {
                 // TODO: bail gracefully if we can't contact the backup manager
             }
         }
     }
 
+    boolean haveBackupPassword() {
+        try {
+            return mBackupManager.hasBackupPassword();
+        } catch (RemoteException e) {
+            return true;        // in the failure case, assume we need one
+        }
+    }
+
     /**
      * The observer binder for showing backup/restore progress.  This binder just bounces
      * the notifications onto the main thread.
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2c13069..c23a8a1 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,7 +3,7 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs
 
 LOCAL_JAVA_LIBRARIES := services
@@ -16,3 +16,5 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index cf0bdd3..2080fad 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -34,6 +34,10 @@
         <service android:name=".LoadAverageService"
                 android:exported="true" />
 
+        <service android:name=".ImageWallpaper"
+                android:permission="android.permission.BIND_WALLPAPER"
+                android:exported="true" />
+
         <receiver android:name=".BootReceiver" >
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 5e48461..fe5b983 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -4,3 +4,12 @@
   public void recentButtonClicked(android.view.View);
   public void toggleLightsOut(android.view.View);
 }
+
+-keep class com.android.systemui.statusbar.policy.KeyButtonView {
+  public float getDrawingAlpha();
+  public float getGlowAlpha();
+  public float getGlowScale();
+  public void setDrawingAlpha(float);
+  public void setGlowAlpha(float);
+  public void setGlowScale(float);
+}
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png
new file mode 100644
index 0000000..d17aae6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_pressed.png
new file mode 100644
index 0000000..5a89d76
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
index 7f96b03..ac5a97b 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png
deleted file mode 100644
index ac5a97b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_default_land.png
rename to packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_pressed.png
deleted file mode 100644
index 8436f5a..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png
new file mode 100644
index 0000000..d050b1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_highlight_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
index c43a019..a90dc9b 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png
deleted file mode 100644
index a90dc9b..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_default_land.png
rename to packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_pressed.png
deleted file mode 100644
index 83068a9..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
index 7af5454..d23f9b7 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png
deleted file mode 100644
index d23f9b7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_default_land.png
rename to packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_pressed.png
deleted file mode 100644
index 56a4a1d..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
index 2d80bb9..cb3c433 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png
deleted file mode 100644
index cb3c433..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_default_land.png
rename to packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_pressed.png
deleted file mode 100644
index afc4057..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-hdpi/notify_panel_clock_bg_normal.9.png
deleted file mode 100644
index b7ad39c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/notify_panel_clock_bg_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/notify_panel_clock_bg_pressed.9.png
deleted file mode 100644
index c93bd8c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/notify_panel_clock_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/notify_panel_notify_bg.9.png b/packages/SystemUI/res/drawable-hdpi/notify_panel_notify_bg.9.png
deleted file mode 100644
index 1680887..0000000
--- a/packages/SystemUI/res/drawable-hdpi/notify_panel_notify_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
index 772f77d..3b826a9 100644
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png
deleted file mode 100644
index 90b4baf..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png
deleted file mode 100644
index bc7034a..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-large-hdpi/notify_panel_clock_bg_normal.9.png
new file mode 100644
index 0000000..002a663
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-large-hdpi/notify_panel_clock_bg_pressed.9.png
new file mode 100644
index 0000000..399d62e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/notify_panel_notify_bg.9.png b/packages/SystemUI/res/drawable-large-hdpi/notify_panel_notify_bg.9.png
new file mode 100644
index 0000000..7385ecc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/notify_panel_notify_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-large-mdpi/notify_panel_clock_bg_normal.9.png
new file mode 100644
index 0000000..5a880e75
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-large-mdpi/notify_panel_clock_bg_pressed.9.png
new file mode 100644
index 0000000..489163d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/notify_panel_notify_bg.9.png b/packages/SystemUI/res/drawable-large-mdpi/notify_panel_notify_bg.9.png
new file mode 100644
index 0000000..78900a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/notify_panel_notify_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_clock_bg_normal.9.png
new file mode 100644
index 0000000..37e7791
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_clock_bg_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_clock_bg_pressed.9.png
new file mode 100644
index 0000000..66fa4a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_clock_bg_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_notify_bg.9.png b/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_notify_bg.9.png
new file mode 100644
index 0000000..11e36ab
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-xhdpi/notify_panel_notify_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png
new file mode 100644
index 0000000..9490833
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_pressed.png
new file mode 100644
index 0000000..0ff3efd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_default_land.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
deleted file mode 100644
index 83a8b26..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png
new file mode 100644
index 0000000..6f3f5fa
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_highlight_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_default_land.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
deleted file mode 100644
index b090b95..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_default_land.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
deleted file mode 100644
index 1affd8f..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_default_land.png
rename to packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
deleted file mode 100644
index c9724fc..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/notify_panel_clock_bg_normal.9.png b/packages/SystemUI/res/drawable-mdpi/notify_panel_clock_bg_normal.9.png
deleted file mode 100644
index 2dcb659..0000000
--- a/packages/SystemUI/res/drawable-mdpi/notify_panel_clock_bg_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/notify_panel_clock_bg_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/notify_panel_clock_bg_pressed.9.png
deleted file mode 100644
index e7ed68b..0000000
--- a/packages/SystemUI/res/drawable-mdpi/notify_panel_clock_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/notify_panel_notify_bg.9.png b/packages/SystemUI/res/drawable-mdpi/notify_panel_notify_bg.9.png
deleted file mode 100644
index e346167..0000000
--- a/packages/SystemUI/res/drawable-mdpi/notify_panel_notify_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
index 0eb71d0..9999598 100644
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
deleted file mode 100644
index 90b4baf..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
deleted file mode 100644
index bc7034a..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png
new file mode 100644
index 0000000..6455423
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_pressed.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_pressed.png
new file mode 100644
index 0000000..bfa8bb4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_default.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_default.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_default_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_default_land.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png
new file mode 100644
index 0000000..97a07fa
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_highlight_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_default.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_default.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_default_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_default_land.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_default.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_default.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_default_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_default_land.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_default.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_default.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_default_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
similarity index 100%
rename from packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_default_land.png
rename to packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png
new file mode 100644
index 0000000..6585ad6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_home.xml b/packages/SystemUI/res/drawable/ic_notify_clear.xml
similarity index 75%
rename from packages/SystemUI/res/drawable/ic_sysbar_home.xml
rename to packages/SystemUI/res/drawable/ic_notify_clear.xml
index f4e585e..9c432b2 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_home.xml
+++ b/packages/SystemUI/res/drawable/ic_notify_clear.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_home_pressed" />
-    <item android:drawable="@drawable/ic_sysbar_home_default" />
+    <item android:state_pressed="true"
+        android:drawable="@drawable/ic_notify_clear_pressed" />
+    <item android:drawable="@drawable/ic_notify_clear_normal" />
 </selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_back.xml b/packages/SystemUI/res/drawable/ic_sysbar_back.xml
deleted file mode 100644
index 327ccd8..0000000
--- a/packages/SystemUI/res/drawable/ic_sysbar_back.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_back_pressed" />
-    <item android:drawable="@drawable/ic_sysbar_back_default" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml b/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
deleted file mode 100644
index 7a10607..0000000
--- a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_menu_pressed" />
-    <item android:drawable="@drawable/ic_sysbar_menu_default" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_recent.xml b/packages/SystemUI/res/drawable/ic_sysbar_recent.xml
deleted file mode 100755
index 39a324b..0000000
--- a/packages/SystemUI/res/drawable/ic_sysbar_recent.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_recent_pressed" />
-    <item android:drawable="@drawable/ic_sysbar_recent_default" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/status_bar_veto.xml b/packages/SystemUI/res/drawable/status_bar_veto.xml
deleted file mode 100644
index 6e1b681..0000000
--- a/packages/SystemUI/res/drawable/status_bar_veto.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/status_bar_veto_pressed" />
-    <item android:drawable="@drawable/status_bar_veto_normal" />
-</selector>
-
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index a2a6473..55e57ab 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -32,8 +32,21 @@
             android:id="@+id/bar_contents"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:clipChildren="false"
             >
 
+            <ImageView android:id="@+id/clear_all_button"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_alignParentRight="true"
+                android:layout_marginTop="1dp"
+                android:layout_marginRight="20dp"
+                android:paddingLeft="15dp"
+                android:paddingRight="15dp"
+                android:src="@drawable/ic_notify_clear"
+                android:visibility="invisible"
+                />
+
             <!-- notification icons & panel access -->
             <include layout="@layout/status_bar_notification_area" 
                 android:layout_width="wrap_content"
@@ -50,6 +63,7 @@
                 android:layout_alignParentLeft="true"
                 systemui:keyCode="4"
                 android:contentDescription="@string/accessibility_back"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight"
                 />
             <LinearLayout
                 android:id="@+id/navigationArea"
@@ -57,6 +71,8 @@
                 android:layout_height="match_parent"
                 android:layout_toRightOf="@+id/back"
                 android:orientation="horizontal"
+                android:clipChildren="false"
+                android:clipToPadding="false"
                 >
                 <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                     android:layout_width="80dip"
@@ -64,12 +80,14 @@
                     android:src="@drawable/ic_sysbar_home"
                     systemui:keyCode="3"
                     android:contentDescription="@string/accessibility_home"
+                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
                     />
-                <ImageView android:id="@+id/recent_apps"
+                <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
                     android:layout_width="80dip"
                     android:layout_height="match_parent"
                     android:src="@drawable/ic_sysbar_recent"
                     android:contentDescription="@string/accessibility_menu"
+                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
                     />
                 <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
                     android:layout_width="80dip"
@@ -78,6 +96,7 @@
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_menu"
+                    systemui:glowBackground="@drawable/ic_sysbar_highlight"
                     />
             </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
index 72519fb..9f11e08 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
@@ -22,7 +22,6 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:gravity="right"
-    android:background="@drawable/notify_panel_bg_protect_tiled"
     >
 
     <RelativeLayout
@@ -31,12 +30,12 @@
         android:layout_width="match_parent"
         android:layout_alignParentBottom="true"
         android:layout_alignParentRight="true"
-        android:layout_marginBottom="-27dip"
+        android:layout_marginBottom="8dp"
         >
 
         <include layout="@layout/status_bar_notification_panel_title"
-            android:layout_width="471dp"
-            android:layout_height="465dp"
+            android:layout_width="478dp"
+            android:layout_height="224dp"
             android:layout_alignParentTop="true"
             android:layout_alignParentRight="true"
             />
@@ -45,11 +44,11 @@
             android:id="@+id/content_frame"
             android:background="@drawable/notify_panel_notify_bg"
             android:layout_height="wrap_content"
-            android:layout_width="447dp"
+            android:layout_width="478dp"
             android:orientation="vertical"
             android:layout_alignParentRight="true"
             android:layout_alignParentTop="true"
-            android:layout_marginTop="352dp"
+            android:layout_marginTop="178dp"
             >
             <ScrollView
                 android:id="@+id/notification_scroller"
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 5d7e8de..6732fee 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -35,6 +35,8 @@
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             android:orientation="horizontal"
+            android:clipChildren="false"
+            android:clipToPadding="false"
             >
 
             <!-- navigation controls -->
@@ -42,47 +44,55 @@
                 android:layout_width="40dp"
                 android:layout_height="match_parent"
                 android:layout_weight="0"
+                android:visibility="invisible"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
                 android:layout_width="80dp"
                 android:layout_height="match_parent"
-                android:src="@drawable/ic_sysbar_back_default"
+                android:src="@drawable/ic_sysbar_back"
                 systemui:keyCode="4"
                 android:layout_weight="0"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:contentDescription="@string/accessibility_back"
                 />
             <View 
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_weight="1"
-                android:contentDescription="@string/accessibility_back"
+                android:visibility="invisible"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                 android:layout_width="80dp"
                 android:layout_height="match_parent"
-                android:src="@drawable/ic_sysbar_home_default"
+                android:src="@drawable/ic_sysbar_home"
                 systemui:keyCode="3"
                 android:layout_weight="0"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:contentDescription="@string/accessibility_home"
                 />
             <View 
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_weight="1"
-                android:contentDescription="@string/accessibility_home"
+                android:visibility="invisible"
                 />
-            <ImageView android:id="@+id/recent_apps"
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
                 android:layout_width="80dp"
                 android:layout_height="match_parent"
-                android:src="@drawable/ic_sysbar_recent_default"
+                android:src="@drawable/ic_sysbar_recent"
                 android:layout_weight="0"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight"
+                android:contentDescription="@string/accessibility_recent"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
                 android:layout_width="40dp"
                 android:layout_height="match_parent"
-                android:src="@drawable/ic_sysbar_menu_default"
+                android:src="@drawable/ic_sysbar_menu"
                 systemui:keyCode="82"
                 android:layout_weight="0"
                 android:visibility="invisible"
                 android:contentDescription="@string/accessibility_menu"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight"
                 />
         </LinearLayout>
 
@@ -106,55 +116,66 @@
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             android:orientation="vertical"
+            android:clipChildren="false"
+            android:clipToPadding="false"
             >
             
             <!-- navigation controls -->
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
                 android:layout_height="40dp"
                 android:layout_width="match_parent"
-                android:src="@drawable/ic_sysbar_menu_default_land"
+                android:src="@drawable/ic_sysbar_menu_land"
                 systemui:keyCode="82"
                 android:layout_weight="0"
                 android:visibility="invisible"
                 />
-            <ImageView android:id="@+id/recent_apps"
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/recent_apps"
                 android:layout_height="80dp"
                 android:layout_width="match_parent"
-                android:src="@drawable/ic_sysbar_recent_default_land"
+                android:src="@drawable/ic_sysbar_recent_land"
                 android:layout_weight="0"
+                android:contentDescription="@string/accessibility_recent"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
                 />
             <View 
                 android:layout_height="match_parent"
                 android:layout_width="match_parent"
                 android:layout_weight="1"
-                android:contentDescription="@string/accessibility_menu"
+                android:visibility="invisible"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                 android:layout_height="80dp"
                 android:layout_width="match_parent"
-                android:src="@drawable/ic_sysbar_home_default_land"
+                android:src="@drawable/ic_sysbar_home_land"
                 systemui:keyCode="3"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_home"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
                 />
             <View 
                 android:layout_height="match_parent"
                 android:layout_width="match_parent"
                 android:layout_weight="1"
-                android:contentDescription="@string/accessibility_back"
+                android:visibility="invisible"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
                 android:layout_height="80dp"
                 android:layout_width="match_parent"
-                android:src="@drawable/ic_sysbar_back_default_land"
+                android:src="@drawable/ic_sysbar_back_land"
                 systemui:keyCode="4"
                 android:layout_weight="0"
+                android:contentDescription="@string/accessibility_back"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
                 />
-            <View
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
                 android:layout_height="40dp"
                 android:layout_width="match_parent"
+                android:src="@drawable/ic_sysbar_menu_land"
+                systemui:keyCode="82"
                 android:layout_weight="0"
+                android:visibility="invisible"
                 android:contentDescription="@string/accessibility_menu"
+                systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
                 />
         </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 6670eff..0cfcae1 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -47,19 +47,13 @@
             android:textAppearance="?android:attr/textAppearanceLarge"
             android:textColor="?android:attr/textColorSecondary"
             />
-        <TextView android:id="@+id/clear_all_button"
+        <ImageView android:id="@+id/clear_all_button"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="center_vertical"
-            android:layout_marginTop="4dp"
-            android:layout_marginBottom="1dp"
-            android:textSize="14sp"
-            android:textColor="#ff000000"
-            android:text="@string/status_bar_clear_all_button"
-            style="?android:attr/buttonStyle"
             android:paddingLeft="15dp"
             android:paddingRight="15dp"
-            android:background="@drawable/btn_default_small"
+            android:src="@drawable/ic_notify_clear"
             />
     </LinearLayout>
 
@@ -91,29 +85,6 @@
                     android:text="@string/status_bar_no_notifications_title"
                     />
 
-                <TextView android:id="@+id/ongoingTitle"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:background="@drawable/title_bar_portrait"
-                    android:paddingLeft="5dp"
-                    android:textAppearance="@style/TextAppearance.StatusBar.Title"
-                    android:text="@string/status_bar_ongoing_events_title"
-                    />
-                <com.android.systemui.statusbar.policy.NotificationRowLayout
-                    android:id="@+id/ongoingItems"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    systemui:rowHeight="@dimen/notification_height"
-                    />
-
-                <TextView android:id="@+id/latestTitle"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:background="@drawable/title_bar_portrait"
-                    android:paddingLeft="5dp"
-                    android:textAppearance="@style/TextAppearance.StatusBar.Title"
-                    android:text="@string/status_bar_latest_events_title"
-                    />
                 <com.android.systemui.statusbar.policy.NotificationRowLayout
                     android:id="@+id/latestItems"
                     android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index aff6a6e..ca584ab 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -3,15 +3,13 @@
     android:layout_height="@dimen/notification_height"
     >
 
-    <ImageButton
+    <Button
         android:id="@+id/veto"
         android:layout_width="48dp"
         android:layout_height="match_parent"
         android:layout_centerVertical="true"
         android:layout_alignParentRight="true"
         android:layout_marginRight="-80dp"
-        android:src="@drawable/status_bar_veto"
-        android:scaleType="center"
         android:background="@null"
         android:paddingRight="8dp"
         android:paddingLeft="8dp"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 5291629..b2b6d50 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -17,6 +17,7 @@
 <resources>
     <declare-styleable name="KeyButtonView">
         <attr name="keyCode" format="integer" />
+        <attr name="glowBackground" format="reference" />
     </declare-styleable>
     <declare-styleable name="ToggleSlider">
         <attr name="text" format="string" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b02015d..ba1aea3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -57,4 +57,7 @@
 
     <!-- opacity at which Notification icons will be drawn in the status bar -->
     <item type="dimen" name="status_bar_icon_drawing_alpha">40%</item>
+
+    <!-- gap on either side of status bar notification icons -->
+    <dimen name="status_bar_icon_padding">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5ca77fc..2b3118d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -185,6 +185,8 @@
     <string name="accessibility_home">Home</string>
     <!-- Content description of the menu button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_menu">Menu</string>
+    <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_recent">Recent applications</string>
 
     <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ime_switch_button">Switch input method button.</string>
diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
similarity index 78%
rename from core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
rename to packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 45f3bc1..7cc5ff7 100644
--- a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -14,23 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.internal.service.wallpaper;
+package com.android.systemui;
 
 import java.io.IOException;
 
-import com.android.internal.view.WindowManagerPolicyThread;
-
 import android.app.WallpaperManager;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
+import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.content.Context;
@@ -45,40 +41,27 @@
     private static final String TAG = "ImageWallpaper";
     private static final boolean DEBUG = false;
 
+    static final boolean FIXED_SIZED_SURFACE = true;
+
     WallpaperManager mWallpaperManager;
-    private HandlerThread mThread;
     private Handler mHandler;
 
     @Override
     public void onCreate() {
         super.onCreate();
         mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
-        Looper looper = WindowManagerPolicyThread.getLooper();
-        if (looper == null) {
-            mThread = new HandlerThread("Wallpaper", Process.THREAD_PRIORITY_FOREGROUND);
-            mThread.start();
-            looper = mThread.getLooper();
-        }
-        setCallbackLooper(looper);
-        mHandler = new Handler(looper);
+        mHandler = new Handler();
     }
 
     public Engine onCreateEngine() {
         return new DrawableEngine();
     }
 
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        if (mThread != null) {
-            mThread.quit();
-        }
-    }
-
     class DrawableEngine extends Engine {
         private final Object mLock = new Object();
         private WallpaperObserver mReceiver;
         Drawable mBackground;
+        int mBackgroundWidth = -1, mBackgroundHeight = -1;
         float mXOffset;
         float mYOffset;
 
@@ -95,7 +78,9 @@
                 }
 
                 synchronized (mLock) {
-                    updateWallpaperLocked();
+                    mBackgroundWidth = mBackgroundHeight = -1;
+                    mBackground = null;
+                    mRedrawNeeded = true;
                     drawFrameLocked();
                 }
             }
@@ -113,10 +98,6 @@
             registerReceiver(mReceiver, filter, null, mHandler);
 
             updateSurfaceSize(surfaceHolder);
-
-            synchronized (mLock) {
-                updateWallpaperLocked();
-            }
         }
 
         @Override
@@ -135,11 +116,14 @@
         }
 
         void updateSurfaceSize(SurfaceHolder surfaceHolder) {
-            surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
-            // Used a fixed size surface, because we are special.  We can do
-            // this because we know the current design of window animations doesn't
-            // cause this to break.
-            //surfaceHolder.setSizeFromLayout();
+            if (FIXED_SIZED_SURFACE) {
+                // Used a fixed size surface, because we are special.  We can do
+                // this because we know the current design of window animations doesn't
+                // cause this to break.
+                surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
+            } else {
+                surfaceHolder.setSizeFromLayout();
+            }
         }
 
         @Override
@@ -216,15 +200,18 @@
                 return;
             }
 
+            if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {
+                // If we don't yet know the size of the wallpaper bitmap,
+                // we need to get it now.
+                updateWallpaperLocked();
+            }
+
             SurfaceHolder sh = getSurfaceHolder();
             final Rect frame = sh.getSurfaceFrame();
-            final Drawable background = mBackground;
             final int dw = frame.width();
             final int dh = frame.height();
-            final int bw = background != null ? background.getIntrinsicWidth() : 0;
-            final int bh = background != null ? background.getIntrinsicHeight() : 0;
-            final int availw = dw - bw;
-            final int availh = dh - bh;
+            final int availw = dw - mBackgroundWidth;
+            final int availh = dh - mBackgroundHeight;
             int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
             int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
 
@@ -241,6 +228,14 @@
             mLastXTranslation = xPixels;
             mLastYTranslation = yPixels;
 
+            if (mBackground == null) {
+                // If we somehow got to this point after we have last flushed
+                // the wallpaper, well we really need it to draw again.  So
+                // seems like we need to reload it.  Ouch.
+                updateWallpaperLocked();
+            }
+
+            //Slog.i(TAG, "************** DRAWING WALLAPER ******************");
             Canvas c = sh.lockCanvas();
             if (c != null) {
                 try {
@@ -251,20 +246,30 @@
                     c.translate(xPixels, yPixels);
                     if (availw < 0 || availh < 0) {
                         c.save(Canvas.CLIP_SAVE_FLAG);
-                        c.clipRect(0, 0, bw, bh, Op.DIFFERENCE);
+                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
                         c.drawColor(0xff000000);
                         c.restore();
                     }
-                    if (background != null) {
-                        background.draw(c);
+                    if (mBackground != null) {
+                        mBackground.draw(c);
                     }
                 } finally {
                     sh.unlockCanvasAndPost(c);
                 }
             }
+
+            if (FIXED_SIZED_SURFACE) {
+                // If the surface is fixed-size, we should only need to
+                // draw it once and then we'll let the window manager
+                // position it appropriately.  As such, we no longer needed
+                // the loaded bitmap.  Yay!
+                mBackground = null;
+                mWallpaperManager.forgetLoadedWallpaper();
+            }
         }
 
         void updateWallpaperLocked() {
+            //Slog.i(TAG, "************** LOADING WALLAPER ******************");
             Throwable exception = null;
             try {
                 mBackground = mWallpaperManager.getFastDrawable();
@@ -286,7 +291,8 @@
                     Log.w(TAG, "Unable reset to default wallpaper!", ex);
                 }
             }
-            mRedrawNeeded = true;
+            mBackgroundWidth = mBackground != null ? mBackground.getIntrinsicWidth() : 0;
+            mBackgroundHeight = mBackground != null ? mBackground.getIntrinsicHeight() : 0;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index eaffd1a..6ecfd94 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -131,9 +131,16 @@
         return result;
     }
 
-    void invalidateGlobalRegion(View view) {
-        RectF childBounds = new RectF(view.getLeft(), view.getTop(), view.getRight(), view
-                .getBottom());
+    // invalidate the view's own bounds all the way up the view hierarchy
+    public static void invalidateGlobalRegion(View view) {
+        invalidateGlobalRegion(
+            view,
+            new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
+    }
+
+    // invalidate a rectangle relative to the view's coordinate system all the way up the view
+    // hierarchy
+    public static void invalidateGlobalRegion(View view, RectF childBounds) {
         childBounds.offset(view.getX(), view.getY());
         if (DEBUG_INVALIDATE)
             Log.v(TAG, "-------------");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 62d7500..c91f513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -54,7 +54,7 @@
     private static final int OP_EXPAND      = 1;
     private static final int OP_COLLAPSE    = 2;
 
-    private static final int MSG_SET_LIGHTS_ON          = 7 << MSG_SHIFT;
+    private static final int MSG_SET_SYSTEMUI_VISIBILITY          = 7 << MSG_SHIFT;
 
     private static final int MSG_TOP_APP_WINDOW_CHANGED = 8 << MSG_SHIFT;
     private static final int MSG_SHOW_IME_BUTTON        = 9 << MSG_SHIFT;
@@ -86,7 +86,7 @@
         public void disable(int state);
         public void animateExpand();
         public void animateCollapse();
-        public void setLightsOn(boolean on);
+        public void setSystemUiVisibility(int vis);
         public void topAppWindowChanged(boolean visible);
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
@@ -160,10 +160,10 @@
         }
     }
 
-    public void setLightsOn(boolean on) {
+    public void setSystemUiVisibility(int vis) {
         synchronized (mList) {
-            mHandler.removeMessages(MSG_SET_LIGHTS_ON);
-            mHandler.obtainMessage(MSG_SET_LIGHTS_ON, on ? 1 : 0, 0, null).sendToTarget();
+            mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY);
+            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, 0, null).sendToTarget();
         }
     }
 
@@ -259,8 +259,8 @@
                         mCallbacks.animateCollapse();
                     }
                     break;
-                case MSG_SET_LIGHTS_ON:
-                    mCallbacks.setLightsOn(msg.arg1 != 0);
+                case MSG_SET_SYSTEMUI_VISIBILITY:
+                    mCallbacks.setSystemUiVisibility(msg.arg1);
                     break;
                 case MSG_TOP_APP_WINDOW_CHANGED:
                     mCallbacks.topAppWindowChanged(msg.arg1 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
index ca75138..918d5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
@@ -78,7 +78,7 @@
         }
 
         disable(switches[0]);
-        setLightsOn(switches[1] != 0);
+        setSystemUiVisibility(switches[1]);
         topAppWindowChanged(switches[2] != 0);
         // StatusBarManagerService has a back up of IME token and it's restored here.
         setImeWindowStatus(binders.get(0), switches[3], switches[4]);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
index f6aa159..20215bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java
@@ -44,14 +44,15 @@
 
         mMoreView = new StatusBarIconView(context, "more", null);
         mMoreView.set(mMoreIcon);
-        addView(mMoreView, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+        super.addView(mMoreView, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+    }
+
+    public void addView(StatusBarIconView v, int index, LinearLayout.LayoutParams p) {
+        super.addView(v, index+1, p);
     }
 
     public void addView(StatusBarIconView v, int index) {
-        if (index == 0) {
-            throw new RuntimeException("Attempt to put view before the more view: " + v);
-        }
-        addView(v, index, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+        super.addView(v, index+1, new LinearLayout.LayoutParams(mIconSize, mIconSize));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 550fc57..2740898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,11 +17,14 @@
 package com.android.systemui.statusbar.phone;
 
 import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.ServiceManager;
 import android.util.AttributeSet;
+import android.util.Slog;
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -36,6 +39,9 @@
 import com.android.systemui.R;
 
 public class NavigationBarView extends LinearLayout {
+    final static boolean DEBUG = false;
+    final static String TAG = "NavigationBarView";
+
     final static boolean DEBUG_DEADZONE = false;
 
     final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
@@ -44,7 +50,12 @@
     final Display mDisplay;
     View mCurrentView = null;
     View[] mRotatedViews = new View[4];
-    Animator mLastAnimator = null;
+    AnimatorSet mLastAnimator = null;
+
+    int mBarSize;
+    boolean mVertical;
+
+    boolean mHidden;
 
     public View getRecentsButton() {
         return mCurrentView.findViewById(R.id.recent_apps);
@@ -56,37 +67,53 @@
 
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mHidden = false;
+
         mDisplay = ((WindowManager)context.getSystemService(
                 Context.WINDOW_SERVICE)).getDefaultDisplay();
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
-        //setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
-        setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
-            @Override
-            public void onSystemUiVisibilityChange(int visibility) {
-                boolean on = (visibility == View.STATUS_BAR_VISIBLE);
-                android.util.Log.d("NavigationBarView", "LIGHTS " 
-                    + (on ? "ON" : "OUT"));
-                setLights(on);
-            }
-        });
+        final Resources res = mContext.getResources();
+        mBarSize = res.getDimensionPixelSize(R.dimen.navigation_bar_size);
+        mVertical = false;
     }
 
-    private void setLights(final boolean on) {
+    public void setHidden(final boolean hide) {
+        if (hide == mHidden) return;
+
+        mHidden = hide;
+        Slog.d(TAG,
+            (hide ? "HIDING" : "SHOWING") + " navigation bar");
+
         float oldAlpha = mCurrentView.getAlpha();
-        android.util.Log.d("NavigationBarView", "animating alpha: " + oldAlpha + " -> "
-            + (on ? 1f : 0f));
+        if (DEBUG) {
+            Slog.d(TAG, "animating alpha: " + oldAlpha + " -> "
+                + (!hide ? 1f : 0f));
+        }
 
         if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel();
 
-        mLastAnimator = ObjectAnimator.ofFloat(mCurrentView, "alpha", oldAlpha, on ? 1f : 0f)
-            .setDuration(on ? 250 : 1500);
+        if (!hide) {
+            setVisibility(View.VISIBLE);
+        }
+
+        // play us off, animatorset
+        mLastAnimator = new AnimatorSet();
+        mLastAnimator.playTogether(
+                ObjectAnimator.ofFloat(mCurrentView, "alpha", hide ? 0f : 1f),
+                ObjectAnimator.ofFloat(mCurrentView,
+                                       mVertical ? "translationX" : "translationY",
+                                       hide ? mBarSize : 0)
+        );
+        mLastAnimator.setDuration(!hide ? 250 : 1000);
         mLastAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator _a) {
                 mLastAnimator = null;
+                if (hide) {
+                    setVisibility(View.INVISIBLE);
+                }
             }
         });
         mLastAnimator.start();
@@ -108,7 +135,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         // immediately bring up the lights
-        setLights(true);
+        setHidden(false);
         return false; // pass it on
     }
 
@@ -119,11 +146,14 @@
         }
         mCurrentView = mRotatedViews[rot];
         mCurrentView.setVisibility(View.VISIBLE);
+        mVertical = (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270);
 
         if (DEBUG_DEADZONE) {
             mCurrentView.findViewById(R.id.deadzone).setBackgroundColor(0x808080FF);
         }
 
-        android.util.Log.d("NavigationBarView", "reorient(): rot=" + mDisplay.getRotation());
+        if (DEBUG) {
+            Slog.d(TAG, "reorient(): rot=" + mDisplay.getRotation());
+        }
     }
 }
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 21b774c..18026f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -32,6 +32,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -114,7 +115,9 @@
     LocationController mLocationController;
     NetworkController mNetworkController;
     
-    int mIconSize;
+    int mNaturalBarHeight = -1;
+    int mIconSize = -1;
+    int mIconHPadding = -1;
     Display mDisplay;
 
     IWindowManager mWindowManager;
@@ -138,17 +141,14 @@
     View mExpandedContents;
     // top bar
     TextView mNoNotificationsTitle;
-    TextView mClearButton;
+    View mClearButton;
     // drag bar
     CloseDragHandle mCloseView;
-    // ongoing
-    NotificationData mOngoing = new NotificationData();
-    TextView mOngoingTitle;
-    ViewGroup mOngoingItems;
-    // latest
-    NotificationData mLatest = new NotificationData();
-    TextView mLatestTitle;
-    ViewGroup mLatestItems;
+    
+    // all notifications
+    NotificationData mNotificationData = new NotificationData();
+    ViewGroup mPile;
+
     // position
     int[] mPositionTmp = new int[2];
     boolean mExpanded;
@@ -198,6 +198,9 @@
     // for disabling the status bar
     int mDisabled = 0;
 
+    // tracking calls to View.setSystemUiVisibility()
+    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+
     private class ExpandedDialog extends Dialog {
         ExpandedDialog(Context context) {
             super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
@@ -243,7 +246,7 @@
 
         Resources res = context.getResources();
 
-        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+        loadDimens();
 
         ExpandedView expanded = (ExpandedView)View.inflate(context,
                 R.layout.status_bar_expanded, null);
@@ -253,20 +256,33 @@
         mIntruderAlertView.setVisibility(View.GONE);
         mIntruderAlertView.setClickable(true);
 
+        PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
+                R.layout.status_bar, null);
+        sb.mService = this;
+        mStatusBarView = sb;
+
         try {
             boolean showNav = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
             if (showNav) {
                 mNavigationBarView = 
                     (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
+
+                sb.setOnSystemUiVisibilityChangeListener(
+                    new View.OnSystemUiVisibilityChangeListener() {
+                        @Override
+                        public void onSystemUiVisibilityChange(int visibility) {
+                            if (DEBUG) {
+                                Slog.d(TAG, "systemUi: " + visibility);
+                            }
+                            boolean hide = (0 != (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION));
+                            mNavigationBarView.setHidden(hide);
+                        }
+                    });
             }
         } catch (Resources.NotFoundException ex) {
             // no nav bar for you
         }
 
-        PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
-                R.layout.status_bar, null);
-        sb.mService = this;
-
         // figure out which pixel-format to use for the status bar.
         mPixelFormat = PixelFormat.TRANSLUCENT;
         Drawable bg = sb.getBackground();
@@ -274,7 +290,6 @@
             mPixelFormat = bg.getOpacity();
         }
 
-        mStatusBarView = sb;
         mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
         mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
         mIcons = (LinearLayout)sb.findViewById(R.id.icons);
@@ -284,19 +299,13 @@
         mExpandedDialog = new ExpandedDialog(context);
         mExpandedView = expanded;
         mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
-        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
-        mOngoingItems = (ViewGroup)expanded.findViewById(R.id.ongoingItems);
-        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
-        mLatestItems = (ViewGroup)expanded.findViewById(R.id.latestItems);
+        mPile = (ViewGroup)expanded.findViewById(R.id.latestItems);
         mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
-        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+        mClearButton = expanded.findViewById(R.id.clear_all_button);
         mClearButton.setOnClickListener(mClearButtonListener);
         mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
         mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
 
-        mOngoingTitle.setVisibility(View.GONE);
-        mLatestTitle.setVisibility(View.GONE);
-
         mTicker = new MyTicker(context, sb);
 
         TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
@@ -548,7 +557,7 @@
 
                 View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
                 button.setOnClickListener(
-                    new Launcher(notification.notification.contentIntent,
+                    new NotificationClicker(notification.notification.contentIntent,
                         notification.pkg, notification.tag, notification.id));
 
                 // 2. Animate mIntruderAlertView in
@@ -579,25 +588,20 @@
     }
 
     public void updateNotification(IBinder key, StatusBarNotification notification) {
-        Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
+        if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
 
-        NotificationData oldList;
-        NotificationData.Entry oldEntry = mOngoing.findByKey(key);
-        if (oldEntry != null) {
-            oldList = mOngoing;
-        } else {
-            oldEntry = mLatest.findByKey(key);
-            if (oldEntry == null) {
-                Slog.w(TAG, "updateNotification for unknown key: " + key);
-                return;
-            }
-            oldList = mLatest;
+        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
+        if (oldEntry == null) {
+            Slog.w(TAG, "updateNotification for unknown key: " + key);
+            return;
         }
+
         final StatusBarNotification oldNotification = oldEntry.notification;
         final RemoteViews oldContentView = oldNotification.notification.contentView;
 
         final RemoteViews contentView = notification.notification.contentView;
 
+
         if (DEBUG) {
             Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
                     + " ongoing=" + oldNotification.isOngoing()
@@ -622,8 +626,8 @@
         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
                 && notification.priority == oldNotification.priority;
                 // priority now encompasses isOngoing()
-        boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1;
-        if (contentsUnchanged && (orderUnchanged || isLastAnyway)) {
+        boolean isFirstAnyway = rowParent.indexOfChild(oldEntry.row) == 0;
+        if (contentsUnchanged && (orderUnchanged || isFirstAnyway)) {
             if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
             oldEntry.notification = notification;
             try {
@@ -632,7 +636,7 @@
                 // update the contentIntent
                 final PendingIntent contentIntent = notification.notification.contentIntent;
                 if (contentIntent != null) {
-                    oldEntry.content.setOnClickListener(new Launcher(contentIntent,
+                    oldEntry.content.setOnClickListener(new NotificationClicker(contentIntent,
                                 notification.pkg, notification.tag, notification.id));
                 } else {
                     oldEntry.content.setOnClickListener(null);
@@ -640,7 +644,8 @@
                 // Update the icon.
                 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
                         notification.notification.icon, notification.notification.iconLevel,
-                        notification.notification.number, notification.notification.tickerText);
+                        notification.notification.number,
+                        notification.notification.tickerText);
                 if (!oldEntry.icon.set(ic)) {
                     handleNotificationError(key, notification, "Couldn't update icon: " + ic);
                     return;
@@ -652,7 +657,6 @@
                     oldEntry.largeIcon.getLayoutParams().width = 0;
                     oldEntry.largeIcon.setVisibility(View.INVISIBLE);
                 }
-
             }
             catch (RuntimeException e) {
                 // It failed to add cleanly.  Log, and remove the view from the panel.
@@ -692,15 +696,6 @@
         }
     }
 
-    private int chooseIconIndex(boolean isOngoing, int viewIndex) {
-        final int latestSize = mLatest.size();
-        if (isOngoing) {
-            return latestSize + (mOngoing.size() - viewIndex);
-        } else {
-            return latestSize - viewIndex;
-        }
-    }
-
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         updateRecentsPanel();
@@ -757,7 +752,7 @@
         content.setOnFocusChangeListener(mFocusChangeListener);
         PendingIntent contentIntent = n.contentIntent;
         if (contentIntent != null) {
-            content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
+            content.setOnClickListener(new NotificationClicker(contentIntent, notification.pkg,
                         notification.tag, notification.id));
         } else {
             content.setOnClickListener(null);
@@ -784,82 +779,232 @@
     }
 
     StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
-        NotificationData list;
-        ViewGroup parent;
-        final boolean isOngoing = notification.isOngoing();
-        if (isOngoing) {
-            list = mOngoing;
-            parent = mOngoingItems;
-        } else {
-            list = mLatest;
-            parent = mLatestItems;
+        if (DEBUG) {
+            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
         }
-        // Construct the expanded view.
-        final View[] views = makeNotificationView(notification, parent);
-        if (views == null) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
-                    + notification);
-            return null;
-        }
-        final View row = views[0];
-        final View content = views[1];
-        final View expanded = views[2];
         // Construct the icon.
         final StatusBarIconView iconView = new StatusBarIconView(mContext,
                 notification.pkg + "/0x" + Integer.toHexString(notification.id),
                 notification.notification);
-        final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
-                    notification.notification.iconLevel, notification.notification.number,
+        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+        final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                    notification.notification.icon,
+                    notification.notification.iconLevel,
+                    notification.notification.number,
                     notification.notification.tickerText);
         if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Coulding create icon: " + ic);
+            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
             return null;
         }
-        // Add the expanded view.
-        final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
-        parent.addView(row, viewIndex);
-        // Add the icon.
-        final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
-        mNotificationIcons.addView(iconView, iconIndex);
+        // Construct the expanded view.
+        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
+        if (!inflateViews(entry, mPile)) {
+            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+                    + notification);
+            return null;
+        }
+
+        // Add the expanded view and icon.
+        int pos = mNotificationData.add(entry);
+        if (DEBUG) {
+            Slog.d(TAG, "addNotificationViews: added at " + pos);
+        }
+        updateNotificationIcons();
+
         return iconView;
     }
 
-    StatusBarNotification removeNotificationViews(IBinder key) {
-        NotificationData.Entry entry = mOngoing.remove(key);
-        if (entry == null) {
-            entry = mLatest.remove(key);
-            if (entry == null) {
-                Slog.w(TAG, "removeNotification for unknown key: " + key);
-                return null;
+    private void loadNotificationShade() {
+        int N = mNotificationData.size();
+
+        ArrayList<View> toShow = new ArrayList<View>();
+
+        for (int i=0; i<N; i++) {
+            View row = mNotificationData.get(N-i-1).row;
+            toShow.add(row);
+        }
+
+        ArrayList<View> toRemove = new ArrayList<View>();
+        for (int i=0; i<mPile.getChildCount(); i++) {
+            View child = mPile.getChildAt(i);
+            if (!toShow.contains(child)) {
+                toRemove.add(child);
             }
         }
+
+        for (View remove : toRemove) {
+            mPile.removeView(remove);
+        }
+
+        for (int i=0; i<toShow.size(); i++) {
+            View v = toShow.get(i);
+            if (v.getParent() == null) {
+                mPile.addView(v, 0); // the notification shade has newest at the top
+            }
+        }
+    }
+
+    private void reloadAllNotificationIcons() {
+        if (mNotificationIcons == null) return;
+        mNotificationIcons.removeAllViews();
+        updateNotificationIcons();
+    }
+
+    private void updateNotificationIcons() {
+        loadNotificationShade();
+
+        final LinearLayout.LayoutParams params
+            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
+
+        int N = mNotificationData.size();
+
+        if (DEBUG) {
+            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
+        }
+
+        ArrayList<View> toShow = new ArrayList<View>();
+
+        for (int i=0; i<N; i++) {
+            toShow.add(mNotificationData.get(N-i-1).icon);
+        }
+
+        ArrayList<View> toRemove = new ArrayList<View>();
+        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
+            View child = mNotificationIcons.getChildAt(i);
+            if (!toShow.contains(child)) {
+                toRemove.add(child);
+            }
+        }
+
+        for (View remove : toRemove) {
+            mNotificationIcons.removeView(remove);
+        }
+
+        for (int i=0; i<toShow.size(); i++) {
+            View v = toShow.get(i);
+            if (v.getParent() == null) {
+                mNotificationIcons.addView(v, i, params);
+            }
+        }
+    }
+
+    void workAroundBadLayerDrawableOpacity(View v) {
+        LayerDrawable d = (LayerDrawable)v.getBackground();
+        if (d == null) return;
+        v.setBackgroundDrawable(null);
+        d.setOpacity(PixelFormat.TRANSLUCENT);
+        v.setBackgroundDrawable(d);
+    }
+
+    private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
+        StatusBarNotification sbn = entry.notification;
+        RemoteViews remoteViews = sbn.notification.contentView;
+        if (remoteViews == null) {
+            return false;
+        }
+
+        // create the row view
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
+        workAroundBadLayerDrawableOpacity(row);
+        View vetoButton = row.findViewById(R.id.veto);
+        if (entry.notification.isClearable()) {
+            final String _pkg = sbn.pkg;
+            final String _tag = sbn.tag;
+            final int _id = sbn.id;
+            vetoButton.setOnClickListener(new View.OnClickListener() {
+                    public void onClick(View v) {
+                        try {
+                            mBarService.onNotificationClear(_pkg, _tag, _id);
+                        } catch (RemoteException ex) {
+                            // system process is dead if we're here.
+                        }
+                    }
+                });
+        } else {
+            if ((sbn.notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) {
+                vetoButton.setVisibility(View.INVISIBLE);
+                vetoButton.setContentDescription("VETO");
+            } else {
+                vetoButton.setVisibility(View.GONE);
+            }
+        }
+        vetoButton.setContentDescription(mContext.getString(
+                R.string.accessibility_remove_notification));
+
+        // the large icon
+        ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon);
+        if (sbn.notification.largeIcon != null) {
+            largeIcon.setImageBitmap(sbn.notification.largeIcon);
+            largeIcon.setContentDescription(sbn.notification.tickerText);
+        } else {
+            largeIcon.getLayoutParams().width = 0;
+            largeIcon.setVisibility(View.INVISIBLE);
+        }
+        largeIcon.setContentDescription(sbn.notification.tickerText);
+
+        // bind the click event to the content area
+        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+        // XXX: update to allow controls within notification views
+        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+//        content.setOnFocusChangeListener(mFocusChangeListener);
+        PendingIntent contentIntent = sbn.notification.contentIntent;
+        if (contentIntent != null) {
+            content.setOnClickListener(new NotificationClicker(contentIntent,
+                        sbn.pkg, sbn.tag, sbn.id));
+        } else {
+            content.setOnClickListener(null);
+        }
+
+        View expanded = null;
+        Exception exception = null;
+        try {
+            expanded = remoteViews.apply(mContext, content);
+        }
+        catch (RuntimeException e) {
+            exception = e;
+        }
+        if (expanded == null) {
+            final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+            return false;
+        } else {
+            content.addView(expanded);
+            row.setDrawingCacheEnabled(true);
+        }
+
+        entry.row = row;
+        entry.content = content;
+        entry.expanded = expanded;
+        entry.largeIcon = largeIcon;
+
+        return true;
+    }
+
+    StatusBarNotification removeNotificationViews(IBinder key) {
+        NotificationData.Entry entry = mNotificationData.remove(key);
+        if (entry == null) {
+            Slog.w(TAG, "removeNotification for unknown key: " + key);
+            return null;
+        }
         // Remove the expanded view.
-        ((ViewGroup)entry.row.getParent()).removeView(entry.row);
-        // Remove the icon.
-        ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
+        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
+        if (rowParent != null) rowParent.removeView(entry.row);
+        updateNotificationIcons();
 
         return entry.notification;
     }
 
     private void setAreThereNotifications() {
-        boolean ongoing = mOngoing.hasVisibleItems();
-        boolean latest = mLatest.hasVisibleItems();
+        mClearButton.setVisibility(mNotificationData.hasClearableItems() 
+                ? View.VISIBLE 
+                : View.INVISIBLE);
 
-        // (no ongoing notifications are clearable)
-        if (mLatest.hasClearableItems()) {
-            mClearButton.setVisibility(View.VISIBLE);
-        } else {
-            mClearButton.setVisibility(View.INVISIBLE);
-        }
-
-        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
-        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
-        if (ongoing || latest) {
-            mNoNotificationsTitle.setVisibility(View.GONE);
-        } else {
-            mNoNotificationsTitle.setVisibility(View.VISIBLE);
-        }
+        mNoNotificationsTitle.setVisibility(mNotificationData.size() > 0
+                ? View.GONE
+                : View.VISIBLE);
     }
 
 
@@ -1275,22 +1420,31 @@
         return false;
     }
 
-    public void setLightsOn(boolean on) {
-        Log.v(TAG, "lights " + (on ? "on" : "off"));
-        if (!on) {
-            // All we do for "lights out" mode on a phone is hide the status bar,
-            // which the window manager does.  But we do need to hide the windowshade
-            // on our own.
-            animateCollapse();
+    @Override // CommandQueue
+    public void setSystemUiVisibility(int vis) {
+        if (vis != mSystemUiVisibility) {
+            mSystemUiVisibility = vis;
+
+            if (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
+                animateCollapse();
+            }
+
+            notifyUiVisibilityChanged();
         }
-        notifyLightsChanged(on);
     }
 
-    private void notifyLightsChanged(boolean shown) {
+    public void setLightsOn(boolean on) {
+        Log.v(TAG, "setLightsOn(" + on + ")");
+        if (on) {
+            setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
+        } else {
+            setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE);
+        }
+    }
+
+    private void notifyUiVisibilityChanged() {
         try {
-            Slog.d(TAG, "lights " + (shown?"on":"out"));
-            mWindowManager.statusBarVisibilityChanged(
-                    shown ? View.STATUS_BAR_VISIBLE : View.STATUS_BAR_HIDDEN);
+            mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
         } catch (RemoteException ex) {
         }
     }
@@ -1313,13 +1467,17 @@
     @Override
     public void setHardKeyboardStatus(boolean available, boolean enabled) { }
 
-    private class Launcher implements View.OnClickListener {
+    public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
+        return new NotificationClicker(intent, pkg, tag, id);
+    }
+
+    private class NotificationClicker implements View.OnClickListener {
         private PendingIntent mIntent;
         private String mPkg;
         private String mTag;
         private int mId;
 
-        Launcher(PendingIntent intent, String pkg, String tag, int id) {
+        NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
             mIntent = intent;
             mPkg = pkg;
             mTag = tag;
@@ -1477,10 +1635,7 @@
             pw.println("  mExpandedDialog: " + mExpandedDialog);
             pw.println("  mTrackingParams: " + mTrackingParams);
             pw.println("  mTrackingView: " + viewInfo(mTrackingView));
-            pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
-            pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
-            pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
-            pw.println("  mLatestItems: " + viewInfo(mLatestItems));
+            pw.println("  mPile: " + viewInfo(mPile));
             pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
             pw.println("  mCloseView: " + viewInfo(mCloseView));
             pw.println("  mTickerView: " + viewInfo(mTickerView));
@@ -1715,10 +1870,12 @@
         }
     }
 
+    // The user is not allowed to get stuck without navigation UI. Upon the slightest user
+    // interaction we bring the navigation back.
     public void userActivity() {
-        try {
-            mBarService.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
-        } catch (RemoteException ex) { }
+        if (mNavigationBarView != null) {
+            mNavigationBarView.setHidden(false);
+        }
     }
 
     public void toggleRecentApps() {
@@ -1827,11 +1984,32 @@
         final Context context = mContext;
         final Resources res = context.getResources();
 
-        mClearButton.setText(context.getText(R.string.status_bar_clear_all_button));
-        mOngoingTitle.setText(context.getText(R.string.status_bar_ongoing_events_title));
-        mLatestTitle.setText(context.getText(R.string.status_bar_latest_events_title));
+        if (mClearButton instanceof TextView) {
+            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
+        }
         mNoNotificationsTitle.setText(context.getText(R.string.status_bar_no_notifications_title));
 
+        loadDimens();
+    }
+    
+    protected void loadDimens() {
+        final Resources res = mContext.getResources();
+
+        mNaturalBarHeight = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+
+        int newIconSize = res.getDimensionPixelSize(
+            com.android.internal.R.dimen.status_bar_icon_size);
+        int newIconHPadding = res.getDimensionPixelSize(
+            R.dimen.status_bar_icon_padding);
+
+        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
+//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
+            mIconHPadding = newIconHPadding;
+            mIconSize = newIconSize;
+            //reloadAllNotificationIcons(); // reload the tray
+        }
+
         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
 
         if (false) Slog.v(TAG, "updateResources");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index c82220d..38a1029 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -16,10 +16,15 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.Canvas;
+import android.graphics.RectF;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.ServiceManager;
@@ -33,7 +38,9 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.SoundEffectConstants;
+import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.RemoteViews.RemoteView;
 
@@ -48,18 +55,25 @@
     int mCode;
     int mRepeat;
     int mTouchSlop;
+    Drawable mGlowBG;
+    float mGlowAlpha = 0f, mGlowScale = 1f, mDrawingAlpha = 1f;
 
     Runnable mCheckLongPress = new Runnable() {
         public void run() {
             if (isPressed()) {
-                mRepeat++;
-                sendEvent(KeyEvent.ACTION_DOWN,
-                        KeyEvent.FLAG_FROM_SYSTEM
-                        | KeyEvent.FLAG_VIRTUAL_HARD_KEY
-                        | KeyEvent.FLAG_LONG_PRESS);
 
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-                //playSoundEffect(SoundEffectConstants.CLICK);
+                if (mCode != 0) {
+                    mRepeat++;
+                    sendEvent(KeyEvent.ACTION_DOWN,
+                            KeyEvent.FLAG_FROM_SYSTEM
+                            | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                            | KeyEvent.FLAG_LONG_PRESS);
+
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+                } else {
+                    // Just an old-fashioned ImageView
+                    performLongClick();
+                }
             }
         }
     };
@@ -75,8 +89,10 @@
                 defStyle, 0);
 
         mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
-        if (mCode == 0) {
-            Slog.w(TAG, "KeyButtonView without key code id=0x" + Integer.toHexString(getId()));
+
+        mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
+        if (mGlowBG != null) {
+            mDrawingAlpha = 0.5f;
         }
         
         a.recycle();
@@ -88,6 +104,99 @@
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mGlowBG != null) {
+            canvas.save();
+            final int w = getWidth();
+            final int h = getHeight();
+            canvas.scale(mGlowScale, mGlowScale, w*0.5f, h*0.5f);
+            mGlowBG.setBounds(0, 0, w, h);
+            mGlowBG.setAlpha((int)(mGlowAlpha * 255));
+            mGlowBG.draw(canvas);
+            canvas.restore();
+
+            canvas.saveLayerAlpha(null, (int)(mDrawingAlpha * 255), Canvas.ALL_SAVE_FLAG);
+        }
+        super.onDraw(canvas);
+        if (mGlowBG != null) {
+            canvas.restore();
+        }
+    }
+
+    public float getDrawingAlpha() {
+        if (mGlowBG == null) return 0;
+        return mDrawingAlpha;
+    }
+
+    public void setDrawingAlpha(float x) {
+        if (mGlowBG == null) return;
+        mDrawingAlpha = x;
+        invalidate();
+    }
+
+    public float getGlowAlpha() {
+        if (mGlowBG == null) return 0;
+        return mGlowAlpha;
+    }
+
+    public void setGlowAlpha(float x) {
+        if (mGlowBG == null) return;
+        mGlowAlpha = x;
+        invalidate();
+    }
+
+    public float getGlowScale() {
+        if (mGlowBG == null) return 0;
+        return mGlowScale;
+    }
+
+    public void setGlowScale(float x) {
+        if (mGlowBG == null) return;
+        mGlowScale = x;
+        final float w = getWidth();
+        final float h = getHeight();
+        if (x < 1.0f) {
+            invalidate();
+        } else {
+            final float rx = (w * (x - 1.0f)) / 2.0f;
+            final float ry = (h * (x - 1.0f)) / 2.0f;
+            com.android.systemui.SwipeHelper.invalidateGlobalRegion(
+                    this,
+                    new RectF(getLeft() - rx,
+                              getTop() - ry,
+                              getRight() + rx,
+                              getBottom() + ry));
+        }
+    }
+
+    public void setPressed(boolean pressed) {
+        if (mGlowBG != null) {
+            if (pressed != isPressed()) {
+                AnimatorSet as = new AnimatorSet();
+                if (pressed) {
+                    if (mGlowScale < 1.7f) mGlowScale = 1.7f;
+                    if (mGlowAlpha < 0.5f) mGlowAlpha = 0.5f;
+                    setDrawingAlpha(1f);
+                    as.playTogether(
+                        ObjectAnimator.ofFloat(this, "glowAlpha", 1f),
+                        ObjectAnimator.ofFloat(this, "glowScale", 1.8f)
+                    );
+                    as.setDuration(50);
+                } else {
+                    as.playTogether(
+                        ObjectAnimator.ofFloat(this, "glowAlpha", 0f),
+                        ObjectAnimator.ofFloat(this, "glowScale", 1f),
+                        ObjectAnimator.ofFloat(this, "drawingAlpha", 0.5f)
+                    );
+                    as.setDuration(500);
+                }
+                as.start();
+            }
+        }
+        super.setPressed(pressed);
+    }
+
     public boolean onTouchEvent(MotionEvent ev) {
         final int action = ev.getAction();
         int x, y;
@@ -131,12 +240,18 @@
                     mSending = false;
                     final int flags = KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY;
                     removeCallbacks(mCheckLongPress);
-                    if (doIt) {
-                        sendEvent(KeyEvent.ACTION_UP, flags);
-                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
-                        playSoundEffect(SoundEffectConstants.CLICK);
+
+                    if (mCode != 0) {
+                        if (doIt) {
+                            sendEvent(KeyEvent.ACTION_UP, flags);
+                            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+                            playSoundEffect(SoundEffectConstants.CLICK);
+                        } else {
+                            sendEvent(KeyEvent.ACTION_UP, flags | KeyEvent.FLAG_CANCELED);
+                        }
                     } else {
-                        sendEvent(KeyEvent.ACTION_UP, flags | KeyEvent.FLAG_CANCELED);
+                        // no key code, just a regular ImageView
+                        if (doIt) performClick();
                     }
                 }
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 90234c7..2a0dfb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -324,7 +324,14 @@
             if (child.getVisibility() == GONE) {
                 continue;
             }
-            final int thisRowHeight = (int)(child.getAlpha() * mRowHeight);
+            float alpha = child.getAlpha();
+            if (alpha > 1.0f) {
+                if (DEBUG) {
+                    Slog.w(TAG, "alpha=" + alpha + " > 1!!! " + child);
+                }
+                alpha = 1f;
+            }
+            final int thisRowHeight = (int)(alpha * mRowHeight);
             if (DEBUG) {
                 Slog.d(TAG, String.format(
                             "laying out child #%d: (0, %d, %d, %d) h=%d",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index a316e4b..74dbfef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -49,6 +49,7 @@
     Rect mContentArea = new Rect();
     View mSettingsView;
     ViewGroup mContentParent;
+    TabletStatusBar mBar;
 
     // amount to slide mContentParent down by when mContentFrame is missing
     float mContentFrameMissingTranslation;
@@ -63,6 +64,10 @@
         super(context, attrs, defStyle);
     }
 
+    public void setBar(TabletStatusBar b) {
+        mBar = b;
+    }
+
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
@@ -202,15 +207,16 @@
               ;
 
         set.setDuration(200);
-        if (!showing) {
-            set.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator _a) {
+        set.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator _a) {
+                if (!showing) {
                     mContentFrame.setVisibility(View.GONE);
                     mContentFrame.setAlpha(1f);
                 }
-            });
-        }
+                updateClearButton();
+            }
+        });
         set.start();
     }
 
@@ -247,12 +253,23 @@
                         removeSettingsView();
                     }
                 }
+                updateClearButton();
                 updatePanelModeButtons();
             }
         });
         a.start();
     }
  
+    public void updateClearButton() {
+        if (mBar != null) {
+            final boolean showX 
+                = (isShowing()
+                        && mNotificationScroller.getVisibility() == View.VISIBLE 
+                        && mNotificationCount > 0);
+            mBar.getClearButton().setVisibility(showX ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
     public void updatePanelModeButtons() {
         final boolean settingsVisible = (mSettingsView != null);
         mSettingsButton.setVisibility(!settingsVisible ? View.VISIBLE : View.INVISIBLE);
@@ -294,11 +311,11 @@
         AnimatorSet mContentAnim;
 
         // should group this into a multi-property animation
-        final static int OPEN_DURATION = 136;
-        final static int CLOSE_DURATION = 250;
+        final static int OPEN_DURATION = 300;
+        final static int CLOSE_DURATION = 300;
 
         // the panel will start to appear this many px from the end
-        final int HYPERSPACE_OFFRAMP = 100;
+        final int HYPERSPACE_OFFRAMP = 200;
 
         Choreographer() {
         }
@@ -306,10 +323,6 @@
         void createAnimation(boolean appearing) {
             // mVisible: previous state; appearing: new state
             
-            View root = findViewById(R.id.panel_root);
-            Animator bgAnim = ObjectAnimator.ofInt(root.getBackground(), "alpha",
-                    mVisible ? 255 : 0, appearing ? 255 : 0);
-
             float start, end;
 
             // 0: on-screen
@@ -347,7 +360,6 @@
             mContentAnim = new AnimatorSet();
             mContentAnim
                 .play(fadeAnim)
-                .with(bgAnim)
                 .with(posAnim)
                 ;
             mContentAnim.setDuration((DEBUG?10:1)*(appearing ? OPEN_DURATION : CLOSE_DURATION));
@@ -363,6 +375,9 @@
             mContentAnim.start();
 
             mVisible = appearing;
+
+            // we want to start disappearing promptly
+            if (!mVisible) updateClearButton();
         }
 
         public void onAnimationCancel(Animator animation) {
@@ -376,6 +391,9 @@
             }
             mContentParent.setLayerType(View.LAYER_TYPE_NONE, null);
             mContentAnim = null;
+
+            // we want to show the X lazily
+            if (mVisible) updateClearButton();
         }
 
         public void onAnimationRepeat(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 96f6e2f..dc7e3137 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -122,6 +122,7 @@
     View mNotificationTrigger;
     NotificationIconArea mNotificationIconArea;
     ViewGroup mNavigationArea;
+    View mClearButton;
 
     boolean mNotificationDNDMode;
     NotificationData.Entry mNotificationDNDDummyEntry;
@@ -176,6 +177,8 @@
     private InputMethodsPanel mInputMethodsPanel;
     private CompatModePanel mCompatModePanel;
 
+    int mSystemUiVisibility = 0;
+
     public Context getContext() { return mContext; }
 
     protected void addPanelWindows() {
@@ -185,6 +188,7 @@
         // Notification Panel
         mNotificationPanel = (NotificationPanel)View.inflate(context,
                 R.layout.status_bar_notification_panel, null);
+        mNotificationPanel.setBar(this);
         mNotificationPanel.show(false, false);
         mNotificationPanel.setOnTouchListener(
                 new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel));
@@ -449,6 +453,10 @@
         // the more notifications icon
         mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
 
+        // the "X" that appears in place of the clock when the panel is showing notifications
+        mClearButton = sb.findViewById(R.id.clear_all_button);
+        mClearButton.setOnClickListener(mClearButtonListener);
+
         // where the icons go
         mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
         mIconLayout.setOnTouchListener(new NotificationIconTouchListener());
@@ -579,6 +587,21 @@
         return sb;
     }
 
+    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            try {
+                mBarService.onClearAllNotifications();
+            } catch (RemoteException ex) {
+                // system process is dead if we're here.
+            }
+            animateCollapse();
+        }
+    };
+
+    public View getClearButton() {
+        return mClearButton;
+    }
+
     public int getStatusBarHeight() {
         return mHeightReceiver.getHeight();
     }
@@ -729,14 +752,16 @@
                     if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)");
                     mBarContents.setVisibility(View.VISIBLE);
                     mShadow.setVisibility(View.GONE);
-                    notifyLightsChanged(true);
+                    mSystemUiVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                    notifyUiVisibilityChanged();
                     break;
                 case MSG_HIDE_CHROME:
                     if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
                     animateCollapse();
                     mBarContents.setVisibility(View.GONE);
                     mShadow.setVisibility(View.VISIBLE);
-                    notifyLightsChanged(false);
+                    mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                    notifyUiVisibilityChanged();
                     break;
                 case MSG_STOP_TICKER:
                     mTicker.halt();
@@ -1025,17 +1050,40 @@
         mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
     }
 
-    // called by StatusBar
-    @Override
+    private void notifyUiVisibilityChanged() {
+        try {
+            mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    @Override // CommandQueue
+    public void setSystemUiVisibility(int vis) {
+        if (vis != mSystemUiVisibility) {
+            mSystemUiVisibility = vis;
+
+            mHandler.removeMessages(MSG_HIDE_CHROME);
+            mHandler.removeMessages(MSG_SHOW_CHROME);
+            mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) 
+                    ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
+
+            notifyUiVisibilityChanged();
+        }
+    }
+
     public void setLightsOn(boolean on) {
         // Policy note: if the frontmost activity needs the menu key, we assume it is a legacy app
         // that can't handle lights-out mode.
         if (mMenuButton.getVisibility() == View.VISIBLE) {
             on = true;
         }
-        mHandler.removeMessages(MSG_HIDE_CHROME);
-        mHandler.removeMessages(MSG_SHOW_CHROME);
-        mHandler.sendEmptyMessage(on ? MSG_SHOW_CHROME : MSG_HIDE_CHROME);
+
+        Slog.v(TAG, "setLightsOn(" + on + ")");
+        if (on) {
+            setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
+        } else {
+            setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE);
+        }
     }
 
     public void topAppWindowChanged(boolean showMenu) {
@@ -1156,7 +1204,6 @@
     }
 
     private void setAreThereNotifications() {
-        final boolean hasClearable = mNotificationData.hasClearableItems();
     }
 
     /**
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
new file mode 100644
index 0000000..28e4b86
--- /dev/null
+++ b/packages/SystemUI/tests/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := SystemUITests
+
+# sign this with platform cert, so this test is allowed to inject key events into
+# UI it doesn't own. This is necessary to allow screenshots to be taken
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
new file mode 100644
index 0000000..e52806d
--- /dev/null
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.systemui.tests">
+
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.systemui.tests"
+        android:label="Tests for SystemUI">
+    </instrumentation>
+</manifest>
diff --git a/packages/SystemUI/tests/res/layout/main.xml b/packages/SystemUI/tests/res/layout/main.xml
new file mode 100644
index 0000000..56dffe6
--- /dev/null
+++ b/packages/SystemUI/tests/res/layout/main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="this is a test activity"
+    />
+    <EditText
+        android:layout_height="wrap_content"
+        android:id="@+id/editText1"
+        android:layout_width="match_parent">
+        <requestFocus></requestFocus>
+    </EditText>
+</LinearLayout>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java
new file mode 100644
index 0000000..2935373
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot;
+
+import com.android.systemui.tests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A stub activity used in {@link ScreenshotTest}.
+ */
+public class ScreenshotStubActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java
new file mode 100644
index 0000000..a0bc4d7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Environment;
+import android.os.FileObserver;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.io.File;
+
+/**
+ * Functional tests for the global screenshot feature.
+ */
+@LargeTest
+public class ScreenshotTest extends ActivityInstrumentationTestCase2<ScreenshotStubActivity> {
+
+    private static final String LOG_TAG = "ScreenshotTest";
+    private static final int SCREEN_WAIT_TIME_SEC = 5;
+
+    public ScreenshotTest() {
+        super(ScreenshotStubActivity.class);
+    }
+
+    /**
+     * A simple test for screenshots that launches an Activity, injects the key event combo
+     * to trigger the screenshot, and verifies the screenshot was taken successfully.
+     */
+    public void testScreenshot() throws Exception {
+        Log.d(LOG_TAG, "starting testScreenshot");
+        // launch the activity.
+        ScreenshotStubActivity activity = getActivity();
+        assertNotNull(activity);
+
+        File screenshotDir = getScreenshotDir();
+        NewScreenshotObserver observer = new NewScreenshotObserver(
+                screenshotDir.getAbsolutePath());
+        observer.startWatching();
+        takeScreenshot();
+        // unlikely, but check if a new screenshot file was already created
+        if (observer.getCreatedPath() == null) {
+            // wait for screenshot to be created
+            synchronized(observer) {
+                observer.wait(SCREEN_WAIT_TIME_SEC*1000);
+            }
+        }
+        assertNotNull(String.format("Could not find screenshot after %d seconds",
+                SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());
+
+        File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
+        try {
+            assertTrue(String.format("Detected new screenshot %s but its not a file",
+                    screenshotFile.getName()), screenshotFile.isFile());
+            assertTrue(String.format("Detected new screenshot %s but its not an image",
+                    screenshotFile.getName()), isValidImage(screenshotFile));
+        } finally {
+            // delete the file to prevent external storage from filing up
+            screenshotFile.delete();
+        }
+    }
+
+    private static class NewScreenshotObserver extends FileObserver {
+        private String mAddedPath = null;
+
+        NewScreenshotObserver(String path) {
+            super(path, FileObserver.CREATE);
+        }
+
+        synchronized String getCreatedPath() {
+            return mAddedPath;
+        }
+
+        @Override
+        public void onEvent(int event, String path) {
+            Log.d(LOG_TAG, String.format("Detected new file added %s", path));
+            synchronized (this) {
+                mAddedPath = path;
+                notify();
+            }
+        }
+    }
+
+    /**
+     * Inject the key sequence to take a screenshot.
+     */
+    private void takeScreenshot() {
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_POWER));
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_VOLUME_DOWN));
+        // the volume down key event will cause the 'volume adjustment' UI to appear in the
+        // foreground, and steal UI focus
+        // unfortunately this means the next key event will get directed to the
+        // 'volume adjustment' UI, instead of this test's activity
+        // for this reason this test must be signed with platform certificate, to grant this test
+        // permission to inject key events to another process
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_VOLUME_DOWN));
+        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_POWER));
+    }
+
+    /**
+     * Get the directory where screenshot images are stored.
+     */
+    private File getScreenshotDir() {
+        // TODO: get this dir location from a constant
+        return new File(Environment.getExternalStorageDirectory(), "Pictures" + File.separator +
+                "Screenshots");
+    }
+
+    /**
+     * Return true if file is valid image file
+     */
+    private boolean isValidImage(File screenshotFile) {
+        Bitmap b = BitmapFactory.decodeFile(screenshotFile.getAbsolutePath());
+        // TODO: do more checks on image
+        return b != null;
+    }
+}
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index b03649e..e193be0 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -350,10 +350,9 @@
                             dataCallbackTimestamp,
                             (void *)cameraId);
 
-    // Enable zoom, error, and focus messages by default
-    enableMsgType(CAMERA_MSG_ERROR |
-                  CAMERA_MSG_ZOOM |
-                  CAMERA_MSG_FOCUS);
+    // Enable zoom, error, focus, and metadata messages by default
+    enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS |
+                  CAMERA_MSG_PREVIEW_METADATA);
 
     // Callback is disabled by default
     mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
@@ -460,10 +459,10 @@
 
 static void disconnectWindow(const sp<ANativeWindow>& window) {
     if (window != 0) {
-        status_t result = native_window_disconnect(window.get(),
+        status_t result = native_window_api_disconnect(window.get(),
                 NATIVE_WINDOW_API_CAMERA);
         if (result != NO_ERROR) {
-            LOGW("native_window_disconnect failed: %s (%d)", strerror(-result),
+            LOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result),
                     result);
         }
     }
@@ -526,9 +525,9 @@
     }
 
     if (window != 0) {
-        result = native_window_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
+        result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA);
         if (result != NO_ERROR) {
-            LOGE("native_window_connect failed: %s (%d)", strerror(-result),
+            LOGE("native_window_api_connect failed: %s (%d)", strerror(-result),
                     result);
             return result;
         }
@@ -995,15 +994,15 @@
     if (client == 0) return;
     if (!client->lockIfMessageWanted(msgType)) return;
 
-    if (dataPtr == 0) {
+    if (dataPtr == 0 && metadata == NULL) {
         LOGE("Null data returned in data callback");
         client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
         return;
     }
 
-    switch (msgType) {
+    switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) {
         case CAMERA_MSG_PREVIEW_FRAME:
-            client->handlePreviewData(dataPtr);
+            client->handlePreviewData(msgType, dataPtr, metadata);
             break;
         case CAMERA_MSG_POSTVIEW_FRAME:
             client->handlePostview(dataPtr);
@@ -1015,7 +1014,7 @@
             client->handleCompressedPicture(dataPtr);
             break;
         default:
-            client->handleGenericData(msgType, dataPtr);
+            client->handleGenericData(msgType, dataPtr, metadata);
             break;
     }
 }
@@ -1055,7 +1054,9 @@
 }
 
 // preview callback - frame buffer update
-void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) {
+void CameraService::Client::handlePreviewData(int32_t msgType,
+                                              const sp<IMemory>& mem,
+                                              camera_frame_metadata_t *metadata) {
     ssize_t offset;
     size_t size;
     sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
@@ -1087,11 +1088,11 @@
         // Is the received frame copied out or not?
         if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
             LOG2("frame is copied");
-            copyFrameAndPostCopiedFrame(c, heap, offset, size);
+            copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata);
         } else {
             LOG2("frame is forwarded");
             mLock.unlock();
-            c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
+            c->dataCallback(msgType, mem, metadata);
         }
     } else {
         mLock.unlock();
@@ -1105,7 +1106,7 @@
     sp<ICameraClient> c = mCameraClient;
     mLock.unlock();
     if (c != 0) {
-        c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem);
+        c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL);
     }
 }
 
@@ -1120,7 +1121,7 @@
     sp<ICameraClient> c = mCameraClient;
     mLock.unlock();
     if (c != 0) {
-        c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
+        c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL);
     }
 }
 
@@ -1131,7 +1132,7 @@
     sp<ICameraClient> c = mCameraClient;
     mLock.unlock();
     if (c != 0) {
-        c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
+        c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL);
     }
 }
 
@@ -1146,11 +1147,11 @@
 }
 
 void CameraService::Client::handleGenericData(int32_t msgType,
-    const sp<IMemory>& dataPtr) {
+    const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) {
     sp<ICameraClient> c = mCameraClient;
     mLock.unlock();
     if (c != 0) {
-        c->dataCallback(msgType, dataPtr);
+        c->dataCallback(msgType, dataPtr, metadata);
     }
 }
 
@@ -1164,8 +1165,9 @@
 }
 
 void CameraService::Client::copyFrameAndPostCopiedFrame(
-        const sp<ICameraClient>& client, const sp<IMemoryHeap>& heap,
-        size_t offset, size_t size) {
+        int32_t msgType, const sp<ICameraClient>& client,
+        const sp<IMemoryHeap>& heap, size_t offset, size_t size,
+        camera_frame_metadata_t *metadata) {
     LOG2("copyFrameAndPostCopiedFrame");
     // It is necessary to copy out of pmem before sending this to
     // the callback. For efficiency, reuse the same MemoryHeapBase
@@ -1197,7 +1199,7 @@
     }
 
     mLock.unlock();
-    client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
+    client->dataCallback(msgType, frame, metadata);
 }
 
 int CameraService::Client::getOrientation(int degrees, bool mirror) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index af7f06e..57abf83 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -147,18 +147,22 @@
         static sp<Client>       getClientFromCookie(void* user);
         // handlers for messages
         void                    handleShutter(void);
-        void                    handlePreviewData(const sp<IMemory>& mem);
+        void                    handlePreviewData(int32_t msgType, const sp<IMemory>& mem,
+                                                  camera_frame_metadata_t *metadata);
         void                    handlePostview(const sp<IMemory>& mem);
         void                    handleRawPicture(const sp<IMemory>& mem);
         void                    handleCompressedPicture(const sp<IMemory>& mem);
         void                    handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2);
-        void                    handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr);
+        void                    handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr,
+                                                  camera_frame_metadata_t *metadata);
         void                    handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
 
         void                    copyFrameAndPostCopiedFrame(
+                                    int32_t msgType,
                                     const sp<ICameraClient>& client,
                                     const sp<IMemoryHeap>& heap,
-                                    size_t offset, size_t size);
+                                    size_t offset, size_t size,
+                                    camera_frame_metadata_t *metadata);
 
         int                     getOrientation(int orientation, bool mirror);
 
diff --git a/services/input/Android.mk b/services/input/Android.mk
index e36507a..afbe546 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_SRC_FILES:= \
     EventHub.cpp \
     InputDispatcher.cpp \
+    InputListener.cpp \
     InputManager.cpp \
     InputReader.cpp \
     InputWindow.cpp \
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index af13945..ce9e14f 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -2550,16 +2550,16 @@
     return splitMotionEntry;
 }
 
-void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
+void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
+    LOGD("notifyConfigurationChanged - eventTime=%lld", args->eventTime);
 #endif
 
     bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(eventTime);
+        ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime);
         needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
@@ -2568,19 +2568,21 @@
     }
 }
 
-void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-        uint32_t policyFlags, int32_t action, int32_t flags,
-        int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
             "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
-            eventTime, deviceId, source, policyFlags, action, flags,
-            keyCode, scanCode, metaState, downTime);
+            args->eventTime, args->deviceId, args->source, args->policyFlags,
+            args->action, args->flags, args->keyCode, args->scanCode,
+            args->metaState, args->downTime);
 #endif
-    if (! validateKeyEvent(action)) {
+    if (!validateKeyEvent(args->action)) {
         return;
     }
 
+    uint32_t policyFlags = args->policyFlags;
+    int32_t flags = args->flags;
+    int32_t metaState = args->metaState;
     if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
         policyFlags |= POLICY_FLAG_VIRTUAL;
         flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
@@ -2604,8 +2606,9 @@
     policyFlags |= POLICY_FLAG_TRUSTED;
 
     KeyEvent event;
-    event.initialize(deviceId, source, action, flags, keyCode, scanCode,
-            metaState, 0, downTime, eventTime);
+    event.initialize(args->deviceId, args->source, args->action,
+            flags, args->keyCode, args->scanCode, metaState, 0,
+            args->downTime, args->eventTime);
 
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
 
@@ -2629,9 +2632,10 @@
         }
 
         int32_t repeatCount = 0;
-        KeyEntry* newEntry = new KeyEntry(eventTime,
-                deviceId, source, policyFlags, action, flags, keyCode, scanCode,
-                metaState, repeatCount, downTime);
+        KeyEntry* newEntry = new KeyEntry(args->eventTime,
+                args->deviceId, args->source, policyFlags,
+                args->action, flags, args->keyCode, args->scanCode,
+                metaState, repeatCount, args->downTime);
 
         needWake = enqueueInboundEventLocked(newEntry);
         mLock.unlock();
@@ -2642,43 +2646,39 @@
     }
 }
 
-void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-        uint32_t policyFlags, int32_t action, int32_t flags,
-        int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-        uint32_t pointerCount, const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords,
-        float xPrecision, float yPrecision, nsecs_t downTime) {
+void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, edgeFlags=0x%x, "
             "xPrecision=%f, yPrecision=%f, downTime=%lld",
-            eventTime, deviceId, source, policyFlags, action, flags,
-            metaState, buttonState, edgeFlags,
-            xPrecision, yPrecision, downTime);
-    for (uint32_t i = 0; i < pointerCount; i++) {
+            args->eventTime, args->deviceId, args->source, args->policyFlags,
+            args->action, args->flags, args->metaState, args->buttonState,
+            args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
+    for (uint32_t i = 0; i < args->pointerCount; i++) {
         LOGD("  Pointer %d: id=%d, toolType=%d, "
                 "x=%f, y=%f, pressure=%f, size=%f, "
                 "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
                 "orientation=%f",
-                i, pointerProperties[i].id,
-                pointerProperties[i].toolType,
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-                pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+                i, args->pointerProperties[i].id,
+                args->pointerProperties[i].toolType,
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
-    if (! validateMotionEvent(action, pointerCount, pointerProperties)) {
+    if (!validateMotionEvent(args->action, args->pointerCount, args->pointerProperties)) {
         return;
     }
 
+    uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
-    mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
 
     bool needWake;
     { // acquire lock
@@ -2688,10 +2688,11 @@
             mLock.unlock();
 
             MotionEvent event;
-            event.initialize(deviceId, source, action, flags, edgeFlags, metaState,
-                    buttonState, 0, 0,
-                    xPrecision, yPrecision, downTime, eventTime,
-                    pointerCount, pointerProperties, pointerCoords);
+            event.initialize(args->deviceId, args->source, args->action, args->flags,
+                    args->edgeFlags, args->metaState, args->buttonState, 0, 0,
+                    args->xPrecision, args->yPrecision,
+                    args->downTime, args->eventTime,
+                    args->pointerCount, args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
             if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -2702,8 +2703,8 @@
         }
 
         // Attempt batching and streaming of move events.
-        if (action == AMOTION_EVENT_ACTION_MOVE
-                || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+        if (args->action == AMOTION_EVENT_ACTION_MOVE
+                || args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
             // BATCHING CASE
             //
             // Try to append a move sample to the tail of the inbound queue for this device.
@@ -2716,20 +2717,22 @@
                 }
 
                 MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-                if (motionEntry->deviceId != deviceId
-                        || motionEntry->source != source) {
+                if (motionEntry->deviceId != args->deviceId
+                        || motionEntry->source != args->source) {
                     // Keep looking for this device and source.
                     continue;
                 }
 
-                if (!motionEntry->canAppendSamples(action, pointerCount, pointerProperties)) {
+                if (!motionEntry->canAppendSamples(args->action,
+                        args->pointerCount, args->pointerProperties)) {
                     // Last motion event in the queue for this device and source is
                     // not compatible for appending new samples.  Stop here.
                     goto NoBatchingOrStreaming;
                 }
 
                 // Do the batching magic.
-                batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
+                batchMotionLocked(motionEntry, args->eventTime,
+                        args->metaState, args->pointerCoords,
                         "most recent motion event for this device and source in the inbound queue");
                 mLock.unlock();
                 return; // done!
@@ -2744,15 +2747,18 @@
                     && (!mPendingEvent->dispatchInProgress || !mCurrentInputTargetsValid)
                     && mPendingEvent->type == EventEntry::TYPE_MOTION) {
                 MotionEntry* motionEntry = static_cast<MotionEntry*>(mPendingEvent);
-                if (motionEntry->deviceId == deviceId && motionEntry->source == source) {
-                    if (!motionEntry->canAppendSamples(action, pointerCount, pointerProperties)) {
+                if (motionEntry->deviceId == args->deviceId
+                        && motionEntry->source == args->source) {
+                    if (!motionEntry->canAppendSamples(args->action,
+                            args->pointerCount, args->pointerProperties)) {
                         // Pending motion event is for this device and source but it is
                         // not compatible for appending new samples.  Stop here.
                         goto NoBatchingOrStreaming;
                     }
 
                     // Do the batching magic.
-                    batchMotionLocked(motionEntry, eventTime, metaState, pointerCoords,
+                    batchMotionLocked(motionEntry, args->eventTime,
+                            args->metaState, args->pointerCoords,
                             "pending motion event");
                     mLock.unlock();
                     return; // done!
@@ -2799,16 +2805,16 @@
 
                     MotionEntry* motionEntry = static_cast<MotionEntry*>(
                             dispatchEntry->eventEntry);
-                    if (motionEntry->action != action
-                            || motionEntry->deviceId != deviceId
-                            || motionEntry->source != source
-                            || motionEntry->pointerCount != pointerCount
+                    if (motionEntry->action != args->action
+                            || motionEntry->deviceId != args->deviceId
+                            || motionEntry->source != args->source
+                            || motionEntry->pointerCount != args->pointerCount
                             || motionEntry->isInjected()) {
                         // The motion event is not compatible with this move.
                         continue;
                     }
 
-                    if (action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                    if (args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
                         if (mLastHoverWindowHandle == NULL) {
 #if DEBUG_BATCHING
                             LOGD("Not streaming hover move because there is no "
@@ -2818,8 +2824,8 @@
                         }
 
                         sp<InputWindowHandle> hoverWindowHandle = findTouchedWindowAtLocked(
-                                pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
-                                pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+                                args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X),
+                                args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
                         if (mLastHoverWindowHandle != hoverWindowHandle) {
 #if DEBUG_BATCHING
                             LOGD("Not streaming hover move because the last hovered window "
@@ -2834,7 +2840,7 @@
 
                     // Hurray!  This foreground target is currently dispatching a move event
                     // that we can stream onto.  Append the motion sample and resume dispatch.
-                    motionEntry->appendSample(eventTime, pointerCoords);
+                    motionEntry->appendSample(args->eventTime, args->pointerCoords);
 #if DEBUG_BATCHING
                     LOGD("Appended motion sample onto batch for most recently dispatched "
                             "motion event for this device and source in the outbound queues.  "
@@ -2854,10 +2860,11 @@
         }
 
         // Just enqueue a new motion event.
-        MotionEntry* newEntry = new MotionEntry(eventTime,
-                deviceId, source, policyFlags, action, flags, metaState, buttonState, edgeFlags,
-                xPrecision, yPrecision, downTime,
-                pointerCount, pointerProperties, pointerCoords);
+        MotionEntry* newEntry = new MotionEntry(args->eventTime,
+                args->deviceId, args->source, policyFlags,
+                args->action, args->flags, args->metaState, args->buttonState,
+                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
+                args->pointerCount, args->pointerProperties, args->pointerCoords);
 
         needWake = enqueueInboundEventLocked(newEntry);
         mLock.unlock();
@@ -2898,15 +2905,17 @@
 #endif
 }
 
-void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
-        uint32_t policyFlags) {
+void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    LOGD("notifySwitch - switchCode=%d, switchValue=%d, policyFlags=0x%x",
-            switchCode, switchValue, policyFlags);
+    LOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchCode=%d, switchValue=%d",
+            args->eventTime, args->policyFlags,
+            args->switchCode, args->switchValue);
 #endif
 
+    uint32_t policyFlags = args->policyFlags;
     policyFlags |= POLICY_FLAG_TRUSTED;
-    mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags);
+    mPolicy->notifySwitch(args->eventTime,
+            args->switchCode, args->switchValue, policyFlags);
 }
 
 int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 1d39b2e..01c7b35 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -34,6 +34,7 @@
 
 #include "InputWindow.h"
 #include "InputApplication.h"
+#include "InputListener.h"
 
 
 namespace android {
@@ -270,7 +271,7 @@
 
 /* Notifies the system about input events generated by the input reader.
  * The dispatcher is expected to be mostly asynchronous. */
-class InputDispatcherInterface : public virtual RefBase {
+class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
 protected:
     InputDispatcherInterface() { }
     virtual ~InputDispatcherInterface() { }
@@ -288,23 +289,6 @@
      */
     virtual void dispatchOnce() = 0;
 
-    /* Notifies the dispatcher about new events.
-     *
-     * These methods should only be called on the input reader thread.
-     */
-    virtual void notifyConfigurationChanged(nsecs_t eventTime) = 0;
-    virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-            int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
-    virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            uint32_t policyFlags, int32_t action, int32_t flags,
-            int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-            uint32_t pointerCount, const PointerProperties* pointerProperties,
-            const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime) = 0;
-    virtual void notifySwitch(nsecs_t when,
-            int32_t switchCode, int32_t switchValue, uint32_t policyFlags) = 0;
-
     /* Injects an input event and optionally waits for sync.
      * The synchronization mode determines whether the method blocks while waiting for
      * input injection to proceed.
@@ -389,18 +373,10 @@
 
     virtual void dispatchOnce();
 
-    virtual void notifyConfigurationChanged(nsecs_t eventTime);
-    virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-            int32_t scanCode, int32_t metaState, nsecs_t downTime);
-    virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            uint32_t policyFlags, int32_t action, int32_t flags,
-            int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-            uint32_t pointerCount, const PointerProperties* pointerProperties,
-            const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime);
-    virtual void notifySwitch(nsecs_t when,
-            int32_t switchCode, int32_t switchValue, uint32_t policyFlags) ;
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+    virtual void notifyKey(const NotifyKeyArgs* args);
+    virtual void notifyMotion(const NotifyMotionArgs* args);
+    virtual void notifySwitch(const NotifySwitchArgs* args);
 
     virtual int32_t injectInputEvent(const InputEvent* event,
             int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
diff --git a/services/input/InputListener.cpp b/services/input/InputListener.cpp
new file mode 100644
index 0000000..4f9fe90
--- /dev/null
+++ b/services/input/InputListener.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputListener"
+
+//#define LOG_NDEBUG 0
+
+#include "InputListener.h"
+
+#include <cutils/log.h>
+
+namespace android {
+
+// --- NotifyConfigurationChangedArgs ---
+
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(nsecs_t eventTime) :
+        eventTime(eventTime) {
+}
+
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
+        const NotifyConfigurationChangedArgs& other) :
+        eventTime(other.eventTime) {
+}
+
+void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
+    listener->notifyConfigurationChanged(this);
+}
+
+
+// --- NotifyKeyArgs ---
+
+NotifyKeyArgs::NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
+        uint32_t policyFlags,
+        int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+        int32_t metaState, nsecs_t downTime) :
+        eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
+        action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
+        metaState(metaState), downTime(downTime) {
+}
+
+NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
+        eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
+        policyFlags(other.policyFlags),
+        action(other.action), flags(other.flags),
+        keyCode(other.keyCode), scanCode(other.scanCode),
+        metaState(other.metaState), downTime(other.downTime) {
+}
+
+void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
+    listener->notifyKey(this);
+}
+
+
+// --- NotifyMotionArgs ---
+
+NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
+        uint32_t policyFlags,
+        int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
+        int32_t edgeFlags, uint32_t pointerCount,
+        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
+        float xPrecision, float yPrecision, nsecs_t downTime) :
+        eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
+        action(action), flags(flags), metaState(metaState), buttonState(buttonState),
+        edgeFlags(edgeFlags), pointerCount(pointerCount),
+        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        this->pointerProperties[i].copyFrom(pointerProperties[i]);
+        this->pointerCoords[i].copyFrom(pointerCoords[i]);
+    }
+}
+
+NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
+        eventTime(other.eventTime), deviceId(other.deviceId), source(other.source),
+        policyFlags(other.policyFlags),
+        action(other.action), flags(other.flags),
+        metaState(other.metaState), buttonState(other.buttonState),
+        edgeFlags(other.edgeFlags), pointerCount(other.pointerCount),
+        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(other.pointerProperties[i]);
+        pointerCoords[i].copyFrom(other.pointerCoords[i]);
+    }
+}
+
+void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
+    listener->notifyMotion(this);
+}
+
+
+// --- NotifySwitchArgs ---
+
+NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
+        int32_t switchCode, int32_t switchValue) :
+        eventTime(eventTime), policyFlags(policyFlags),
+        switchCode(switchCode), switchValue(switchValue) {
+}
+
+NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
+        eventTime(other.eventTime), policyFlags(other.policyFlags),
+        switchCode(other.switchCode), switchValue(other.switchValue) {
+}
+
+void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
+    listener->notifySwitch(this);
+}
+
+
+// --- QueuedInputListener ---
+
+QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
+        mInnerListener(innerListener) {
+}
+
+QueuedInputListener::~QueuedInputListener() {
+    size_t count = mArgsQueue.size();
+    for (size_t i = 0; i < count; i++) {
+        delete mArgsQueue[i];
+    }
+}
+
+void QueuedInputListener::notifyConfigurationChanged(
+        const NotifyConfigurationChangedArgs* args) {
+    mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
+}
+
+void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
+    mArgsQueue.push(new NotifyKeyArgs(*args));
+}
+
+void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
+    mArgsQueue.push(new NotifyMotionArgs(*args));
+}
+
+void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
+    mArgsQueue.push(new NotifySwitchArgs(*args));
+}
+
+void QueuedInputListener::flush() {
+    size_t count = mArgsQueue.size();
+    for (size_t i = 0; i < count; i++) {
+        NotifyArgs* args = mArgsQueue[i];
+        args->notify(mInnerListener);
+        delete args;
+    }
+    mArgsQueue.clear();
+}
+
+
+} // namespace android
diff --git a/services/input/InputListener.h b/services/input/InputListener.h
new file mode 100644
index 0000000..3fef132
--- /dev/null
+++ b/services/input/InputListener.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_LISTENER_H
+#define _UI_INPUT_LISTENER_H
+
+#include <ui/Input.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class InputListenerInterface;
+
+
+/* Superclass of all input event argument objects */
+struct NotifyArgs {
+    virtual ~NotifyArgs() { }
+
+    virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
+};
+
+
+/* Describes a configuration change event. */
+struct NotifyConfigurationChangedArgs : public NotifyArgs {
+    nsecs_t eventTime;
+
+    inline NotifyConfigurationChangedArgs() { }
+
+    NotifyConfigurationChangedArgs(nsecs_t eventTime);
+
+    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
+
+    virtual ~NotifyConfigurationChangedArgs() { }
+
+    virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
+
+
+/* Describes a key event. */
+struct NotifyKeyArgs : public NotifyArgs {
+    nsecs_t eventTime;
+    int32_t deviceId;
+    uint32_t source;
+    uint32_t policyFlags;
+    int32_t action;
+    int32_t flags;
+    int32_t keyCode;
+    int32_t scanCode;
+    int32_t metaState;
+    nsecs_t downTime;
+
+    inline NotifyKeyArgs() { }
+
+    NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
+            int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+            int32_t metaState, nsecs_t downTime);
+
+    NotifyKeyArgs(const NotifyKeyArgs& other);
+
+    virtual ~NotifyKeyArgs() { }
+
+    virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
+
+
+/* Describes a motion event. */
+struct NotifyMotionArgs : public NotifyArgs {
+    nsecs_t eventTime;
+    int32_t deviceId;
+    uint32_t source;
+    uint32_t policyFlags;
+    int32_t action;
+    int32_t flags;
+    int32_t metaState;
+    int32_t buttonState;
+    int32_t edgeFlags;
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+    float xPrecision;
+    float yPrecision;
+    nsecs_t downTime;
+
+    inline NotifyMotionArgs() { }
+
+    NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
+            int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
+            int32_t edgeFlags, uint32_t pointerCount,
+            const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
+            float xPrecision, float yPrecision, nsecs_t downTime);
+
+    NotifyMotionArgs(const NotifyMotionArgs& other);
+
+    virtual ~NotifyMotionArgs() { }
+
+    virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
+
+
+/* Describes a switch event. */
+struct NotifySwitchArgs : public NotifyArgs {
+    nsecs_t eventTime;
+    uint32_t policyFlags;
+    int32_t switchCode;
+    int32_t switchValue;
+
+    inline NotifySwitchArgs() { }
+
+    NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags,
+            int32_t switchCode, int32_t switchValue);
+
+    NotifySwitchArgs(const NotifySwitchArgs& other);
+
+    virtual ~NotifySwitchArgs() { }
+
+    virtual void notify(const sp<InputListenerInterface>& listener) const;
+};
+
+
+/*
+ * The interface used by the InputReader to notify the InputListener about input events.
+ */
+class InputListenerInterface : public virtual RefBase {
+protected:
+    InputListenerInterface() { }
+    virtual ~InputListenerInterface() { }
+
+public:
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
+    virtual void notifyKey(const NotifyKeyArgs* args) = 0;
+    virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
+    virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
+};
+
+
+/*
+ * An implementation of the listener interface that queues up and defers dispatch
+ * of decoded events until flushed.
+ */
+class QueuedInputListener : public InputListenerInterface {
+protected:
+    virtual ~QueuedInputListener();
+
+public:
+    QueuedInputListener(const sp<InputListenerInterface>& innerListener);
+
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+    virtual void notifyKey(const NotifyKeyArgs* args);
+    virtual void notifyMotion(const NotifyMotionArgs* args);
+    virtual void notifySwitch(const NotifySwitchArgs* args);
+
+    void flush();
+
+private:
+    sp<InputListenerInterface> mInnerListener;
+    Vector<NotifyArgs*> mArgsQueue;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_LISTENER_H
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index db312ad..8786c24 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -180,8 +180,9 @@
             || (action == AKEY_EVENT_ACTION_UP
                     && (lastButtonState & buttonState)
                     && !(currentButtonState & buttonState))) {
-        context->getDispatcher()->notifyKey(when, deviceId, source, policyFlags,
+        NotifyKeyArgs args(when, deviceId, source, policyFlags,
                 action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+        context->getListener()->notifyKey(&args);
     }
 }
 
@@ -201,13 +202,19 @@
 
 InputReader::InputReader(const sp<EventHubInterface>& eventHub,
         const sp<InputReaderPolicyInterface>& policy,
-        const sp<InputDispatcherInterface>& dispatcher) :
-        mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+        const sp<InputListenerInterface>& listener) :
+        mContext(this), mEventHub(eventHub), mPolicy(policy),
         mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
-    refreshConfiguration(0);
-    updateGlobalMetaState();
-    updateInputConfiguration();
+    mQueuedListener = new QueuedInputListener(listener);
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        refreshConfigurationLocked(0);
+        updateGlobalMetaStateLocked();
+        updateInputConfigurationLocked();
+    } // release lock
 }
 
 InputReader::~InputReader() {
@@ -217,39 +224,52 @@
 }
 
 void InputReader::loopOnce() {
-    uint32_t changes;
+    int32_t timeoutMillis;
     { // acquire lock
-        AutoMutex _l(mStateLock);
+        AutoMutex _l(mLock);
 
-        changes = mConfigurationChangesToRefresh;
-        mConfigurationChangesToRefresh = 0;
+        uint32_t changes = mConfigurationChangesToRefresh;
+        if (changes) {
+            mConfigurationChangesToRefresh = 0;
+            refreshConfigurationLocked(changes);
+        }
+
+        timeoutMillis = -1;
+        if (mNextTimeout != LLONG_MAX) {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
+        }
     } // release lock
 
-    if (changes) {
-        refreshConfiguration(changes);
-    }
-
-    int32_t timeoutMillis = -1;
-    if (mNextTimeout != LLONG_MAX) {
-        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
-    }
-
     size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
-    if (count) {
-        processEvents(mEventBuffer, count);
-    }
-    if (!count || timeoutMillis == 0) {
-        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (count) {
+            processEventsLocked(mEventBuffer, count);
+        }
+        if (!count || timeoutMillis == 0) {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 #if DEBUG_RAW_EVENTS
-        LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+            LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
 #endif
-        mNextTimeout = LLONG_MAX;
-        timeoutExpired(now);
-    }
+            mNextTimeout = LLONG_MAX;
+            timeoutExpiredLocked(now);
+        }
+    } // release lock
+
+    // Flush queued events out to the listener.
+    // This must happen outside of the lock because the listener could potentially call
+    // back into the InputReader's methods, such as getScanCodeState, or become blocked
+    // on another thread similarly waiting to acquire the InputReader lock thereby
+    // resulting in a deadlock.  This situation is actually quite plausible because the
+    // listener is actually the input dispatcher, which calls into the window manager,
+    // which occasionally calls into the input reader.
+    mQueuedListener->flush();
 }
 
-void InputReader::processEvents(const RawEvent* rawEvents, size_t count) {
+void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
     for (const RawEvent* rawEvent = rawEvents; count;) {
         int32_t type = rawEvent->type;
         size_t batchSize = 1;
@@ -265,17 +285,17 @@
 #if DEBUG_RAW_EVENTS
             LOGD("BatchSize: %d Count: %d", batchSize, count);
 #endif
-            processEventsForDevice(deviceId, rawEvent, batchSize);
+            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
         } else {
             switch (rawEvent->type) {
             case EventHubInterface::DEVICE_ADDED:
-                addDevice(rawEvent->deviceId);
+                addDeviceLocked(rawEvent->deviceId);
                 break;
             case EventHubInterface::DEVICE_REMOVED:
-                removeDevice(rawEvent->deviceId);
+                removeDeviceLocked(rawEvent->deviceId);
                 break;
             case EventHubInterface::FINISHED_DEVICE_SCAN:
-                handleConfigurationChanged(rawEvent->when);
+                handleConfigurationChangedLocked(rawEvent->when);
                 break;
             default:
                 LOG_ASSERT(false); // can't happen
@@ -287,11 +307,11 @@
     }
 }
 
-void InputReader::addDevice(int32_t deviceId) {
+void InputReader::addDeviceLocked(int32_t deviceId) {
     String8 name = mEventHub->getDeviceName(deviceId);
     uint32_t classes = mEventHub->getDeviceClasses(deviceId);
 
-    InputDevice* device = createDevice(deviceId, name, classes);
+    InputDevice* device = createDeviceLocked(deviceId, name, classes);
     device->configure(&mConfig, 0);
 
     if (device->isIgnored()) {
@@ -301,39 +321,23 @@
                 device->getSources());
     }
 
-    bool added = false;
-    { // acquire device registry writer lock
-        RWLock::AutoWLock _wl(mDeviceRegistryLock);
-
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex < 0) {
-            mDevices.add(deviceId, device);
-            added = true;
-        }
-    } // release device registry writer lock
-
-    if (! added) {
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        mDevices.add(deviceId, device);
+    } else {
         LOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
         delete device;
         return;
     }
 }
 
-void InputReader::removeDevice(int32_t deviceId) {
-    bool removed = false;
+void InputReader::removeDeviceLocked(int32_t deviceId) {
     InputDevice* device = NULL;
-    { // acquire device registry writer lock
-        RWLock::AutoWLock _wl(mDeviceRegistryLock);
-
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            device = mDevices.valueAt(deviceIndex);
-            mDevices.removeItemsAt(deviceIndex, 1);
-            removed = true;
-        }
-    } // release device registry writer lock
-
-    if (! removed) {
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex >= 0) {
+        device = mDevices.valueAt(deviceIndex);
+        mDevices.removeItemsAt(deviceIndex, 1);
+    } else {
         LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
         return;
     }
@@ -351,8 +355,9 @@
     delete device;
 }
 
-InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
-    InputDevice* device = new InputDevice(this, deviceId, name);
+InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
+        const String8& name, uint32_t classes) {
+    InputDevice* device = new InputDevice(&mContext, deviceId, name);
 
     // External devices.
     if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
@@ -404,52 +409,45 @@
     return device;
 }
 
-void InputReader::processEventsForDevice(int32_t deviceId,
+void InputReader::processEventsForDeviceLocked(int32_t deviceId,
         const RawEvent* rawEvents, size_t count) {
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        LOGW("Discarding event for unknown deviceId %d.", deviceId);
+        return;
+    }
 
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex < 0) {
-            LOGW("Discarding event for unknown deviceId %d.", deviceId);
-            return;
-        }
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    if (device->isIgnored()) {
+        //LOGD("Discarding event for ignored deviceId %d.", deviceId);
+        return;
+    }
 
-        InputDevice* device = mDevices.valueAt(deviceIndex);
-        if (device->isIgnored()) {
-            //LOGD("Discarding event for ignored deviceId %d.", deviceId);
-            return;
-        }
-
-        device->process(rawEvents, count);
-    } // release device registry reader lock
+    device->process(rawEvents, count);
 }
 
-void InputReader::timeoutExpired(nsecs_t when) {
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-        for (size_t i = 0; i < mDevices.size(); i++) {
-            InputDevice* device = mDevices.valueAt(i);
-            if (!device->isIgnored()) {
-                device->timeoutExpired(when);
-            }
+void InputReader::timeoutExpiredLocked(nsecs_t when) {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        if (!device->isIgnored()) {
+            device->timeoutExpired(when);
         }
-    } // release device registry reader lock
+    }
 }
 
-void InputReader::handleConfigurationChanged(nsecs_t when) {
+void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
     // Reset global meta state because it depends on the list of all configured devices.
-    updateGlobalMetaState();
+    updateGlobalMetaStateLocked();
 
     // Update input configuration.
-    updateInputConfiguration();
+    updateInputConfigurationLocked();
 
     // Enqueue configuration changed.
-    mDispatcher->notifyConfigurationChanged(when);
+    NotifyConfigurationChangedArgs args(when);
+    mQueuedListener->notifyConfigurationChanged(&args);
 }
 
-void InputReader::refreshConfiguration(uint32_t changes) {
+void InputReader::refreshConfigurationLocked(uint32_t changes) {
     mPolicy->getReaderConfiguration(&mConfig);
     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
 
@@ -459,84 +457,60 @@
         if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
             mEventHub->requestReopenDevices();
         } else {
-            { // acquire device registry reader lock
-                RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-                for (size_t i = 0; i < mDevices.size(); i++) {
-                    InputDevice* device = mDevices.valueAt(i);
-                    device->configure(&mConfig, changes);
-                }
-            } // release device registry reader lock
+            for (size_t i = 0; i < mDevices.size(); i++) {
+                InputDevice* device = mDevices.valueAt(i);
+                device->configure(&mConfig, changes);
+            }
         }
     }
 }
 
-void InputReader::updateGlobalMetaState() {
-    { // acquire state lock
-        AutoMutex _l(mStateLock);
+void InputReader::updateGlobalMetaStateLocked() {
+    mGlobalMetaState = 0;
 
-        mGlobalMetaState = 0;
-
-        { // acquire device registry reader lock
-            RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                InputDevice* device = mDevices.valueAt(i);
-                mGlobalMetaState |= device->getMetaState();
-            }
-        } // release device registry reader lock
-    } // release state lock
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        mGlobalMetaState |= device->getMetaState();
+    }
 }
 
-int32_t InputReader::getGlobalMetaState() {
-    { // acquire state lock
-        AutoMutex _l(mStateLock);
-
-        return mGlobalMetaState;
-    } // release state lock
+int32_t InputReader::getGlobalMetaStateLocked() {
+    return mGlobalMetaState;
 }
 
-void InputReader::updateInputConfiguration() {
-    { // acquire state lock
-        AutoMutex _l(mStateLock);
+void InputReader::updateInputConfigurationLocked() {
+    int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+    int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+    int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+    InputDeviceInfo deviceInfo;
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        device->getDeviceInfo(& deviceInfo);
+        uint32_t sources = deviceInfo.getSources();
 
-        int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
-        int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
-        int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
-        { // acquire device registry reader lock
-            RWLock::AutoRLock _rl(mDeviceRegistryLock);
+        if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) {
+            touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+        }
+        if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) {
+            navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+        } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) {
+            navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+        }
+        if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+            keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
+        }
+    }
 
-            InputDeviceInfo deviceInfo;
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                InputDevice* device = mDevices.valueAt(i);
-                device->getDeviceInfo(& deviceInfo);
-                uint32_t sources = deviceInfo.getSources();
-
-                if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) {
-                    touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
-                }
-                if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) {
-                    navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
-                } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) {
-                    navigationConfig = InputConfiguration::NAVIGATION_DPAD;
-                }
-                if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
-                    keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
-                }
-            }
-        } // release device registry reader lock
-
-        mInputConfiguration.touchScreen = touchScreenConfig;
-        mInputConfiguration.keyboard = keyboardConfig;
-        mInputConfiguration.navigation = navigationConfig;
-    } // release state lock
+    mInputConfiguration.touchScreen = touchScreenConfig;
+    mInputConfiguration.keyboard = keyboardConfig;
+    mInputConfiguration.navigation = navigationConfig;
 }
 
-void InputReader::disableVirtualKeysUntil(nsecs_t time) {
+void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) {
     mDisableVirtualKeysTimeout = time;
 }
 
-bool InputReader::shouldDropVirtualKey(nsecs_t now,
+bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now,
         InputDevice* device, int32_t keyCode, int32_t scanCode) {
     if (now < mDisableVirtualKeysTimeout) {
         LOGI("Dropping virtual key from device %s because virtual keys are "
@@ -550,153 +524,141 @@
     }
 }
 
-void InputReader::fadePointer() {
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-        for (size_t i = 0; i < mDevices.size(); i++) {
-            InputDevice* device = mDevices.valueAt(i);
-            device->fadePointer();
-        }
-    } // release device registry reader lock
+void InputReader::fadePointerLocked() {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        device->fadePointer();
+    }
 }
 
-void InputReader::requestTimeoutAtTime(nsecs_t when) {
+void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
     if (when < mNextTimeout) {
         mNextTimeout = when;
     }
 }
 
 void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
-    { // acquire state lock
-        AutoMutex _l(mStateLock);
+    AutoMutex _l(mLock);
 
-        *outConfiguration = mInputConfiguration;
-    } // release state lock
+    *outConfiguration = mInputConfiguration;
 }
 
 status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+    AutoMutex _l(mLock);
 
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex < 0) {
-            return NAME_NOT_FOUND;
-        }
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        return NAME_NOT_FOUND;
+    }
 
-        InputDevice* device = mDevices.valueAt(deviceIndex);
-        if (device->isIgnored()) {
-            return NAME_NOT_FOUND;
-        }
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    if (device->isIgnored()) {
+        return NAME_NOT_FOUND;
+    }
 
-        device->getDeviceInfo(outDeviceInfo);
-        return OK;
-    } // release device registy reader lock
+    device->getDeviceInfo(outDeviceInfo);
+    return OK;
 }
 
 void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+    AutoMutex _l(mLock);
+
     outDeviceIds.clear();
 
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
-            if (! device->isIgnored()) {
-                outDeviceIds.add(device->getId());
-            }
+    size_t numDevices = mDevices.size();
+    for (size_t i = 0; i < numDevices; i++) {
+        InputDevice* device = mDevices.valueAt(i);
+        if (!device->isIgnored()) {
+            outDeviceIds.add(device->getId());
         }
-    } // release device registy reader lock
+    }
 }
 
 int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
         int32_t keyCode) {
-    return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState);
+    AutoMutex _l(mLock);
+
+    return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState);
 }
 
 int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
         int32_t scanCode) {
-    return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState);
+    AutoMutex _l(mLock);
+
+    return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState);
 }
 
 int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
-    return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState);
+    AutoMutex _l(mLock);
+
+    return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState);
 }
 
-int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
         GetStateFunc getStateFunc) {
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-        int32_t result = AKEY_STATE_UNKNOWN;
-        if (deviceId >= 0) {
-            ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-            if (deviceIndex >= 0) {
-                InputDevice* device = mDevices.valueAt(deviceIndex);
-                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                    result = (device->*getStateFunc)(sourceMask, code);
-                }
+    int32_t result = AKEY_STATE_UNKNOWN;
+    if (deviceId >= 0) {
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex >= 0) {
+            InputDevice* device = mDevices.valueAt(deviceIndex);
+            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result = (device->*getStateFunc)(sourceMask, code);
             }
-        } else {
-            size_t numDevices = mDevices.size();
-            for (size_t i = 0; i < numDevices; i++) {
-                InputDevice* device = mDevices.valueAt(i);
-                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                    result = (device->*getStateFunc)(sourceMask, code);
-                    if (result >= AKEY_STATE_DOWN) {
-                        return result;
-                    }
+        }
+    } else {
+        size_t numDevices = mDevices.size();
+        for (size_t i = 0; i < numDevices; i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result = (device->*getStateFunc)(sourceMask, code);
+                if (result >= AKEY_STATE_DOWN) {
+                    return result;
                 }
             }
         }
-        return result;
-    } // release device registy reader lock
+    }
+    return result;
 }
 
 bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
         size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+    AutoMutex _l(mLock);
+
     memset(outFlags, 0, numCodes);
-    return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+    return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags);
 }
 
-bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
-        const int32_t* keyCodes, uint8_t* outFlags) {
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
-        bool result = false;
-        if (deviceId >= 0) {
-            ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-            if (deviceIndex >= 0) {
-                InputDevice* device = mDevices.valueAt(deviceIndex);
-                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                    result = device->markSupportedKeyCodes(sourceMask,
-                            numCodes, keyCodes, outFlags);
-                }
-            }
-        } else {
-            size_t numDevices = mDevices.size();
-            for (size_t i = 0; i < numDevices; i++) {
-                InputDevice* device = mDevices.valueAt(i);
-                if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                    result |= device->markSupportedKeyCodes(sourceMask,
-                            numCodes, keyCodes, outFlags);
-                }
+bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask,
+        size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+    bool result = false;
+    if (deviceId >= 0) {
+        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+        if (deviceIndex >= 0) {
+            InputDevice* device = mDevices.valueAt(deviceIndex);
+            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result = device->markSupportedKeyCodes(sourceMask,
+                        numCodes, keyCodes, outFlags);
             }
         }
-        return result;
-    } // release device registy reader lock
+    } else {
+        size_t numDevices = mDevices.size();
+        for (size_t i = 0; i < numDevices; i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+                result |= device->markSupportedKeyCodes(sourceMask,
+                        numCodes, keyCodes, outFlags);
+            }
+        }
+    }
+    return result;
 }
 
 void InputReader::requestRefreshConfiguration(uint32_t changes) {
-    if (changes) {
-        bool needWake;
-        { // acquire lock
-            AutoMutex _l(mStateLock);
+    AutoMutex _l(mLock);
 
-            needWake = !mConfigurationChangesToRefresh;
-            mConfigurationChangesToRefresh |= changes;
-        } // release lock
+    if (changes) {
+        bool needWake = !mConfigurationChangesToRefresh;
+        mConfigurationChangesToRefresh |= changes;
 
         if (needWake) {
             mEventHub->wake();
@@ -705,18 +667,16 @@
 }
 
 void InputReader::dump(String8& dump) {
+    AutoMutex _l(mLock);
+
     mEventHub->dump(dump);
     dump.append("\n");
 
     dump.append("Input Reader State:\n");
 
-    { // acquire device registry reader lock
-        RWLock::AutoRLock _rl(mDeviceRegistryLock);
-
-        for (size_t i = 0; i < mDevices.size(); i++) {
-            mDevices.valueAt(i)->dump(dump);
-        }
-    } // release device registy reader lock
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        mDevices.valueAt(i)->dump(dump);
+    }
 
     dump.append(INDENT "Configuration:\n");
     dump.append(INDENT2 "ExcludedDeviceNames: [");
@@ -772,6 +732,56 @@
 }
 
 
+// --- InputReader::ContextImpl ---
+
+InputReader::ContextImpl::ContextImpl(InputReader* reader) :
+        mReader(reader) {
+}
+
+void InputReader::ContextImpl::updateGlobalMetaState() {
+    // lock is already held by the input loop
+    mReader->updateGlobalMetaStateLocked();
+}
+
+int32_t InputReader::ContextImpl::getGlobalMetaState() {
+    // lock is already held by the input loop
+    return mReader->getGlobalMetaStateLocked();
+}
+
+void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
+    // lock is already held by the input loop
+    mReader->disableVirtualKeysUntilLocked(time);
+}
+
+bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now,
+        InputDevice* device, int32_t keyCode, int32_t scanCode) {
+    // lock is already held by the input loop
+    return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
+}
+
+void InputReader::ContextImpl::fadePointer() {
+    // lock is already held by the input loop
+    mReader->fadePointerLocked();
+}
+
+void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
+    // lock is already held by the input loop
+    mReader->requestTimeoutAtTimeLocked(when);
+}
+
+InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() {
+    return mReader->mPolicy.get();
+}
+
+InputListenerInterface* InputReader::ContextImpl::getListener() {
+    return mReader->mQueuedListener.get();
+}
+
+EventHubInterface* InputReader::ContextImpl::getEventHub() {
+    return mReader->mEventHub.get();
+}
+
+
 // --- InputReaderThread ---
 
 InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
@@ -1170,6 +1180,96 @@
 }
 
 
+// --- RawPointerAxes ---
+
+RawPointerAxes::RawPointerAxes() {
+    clear();
+}
+
+void RawPointerAxes::clear() {
+    x.clear();
+    y.clear();
+    pressure.clear();
+    touchMajor.clear();
+    touchMinor.clear();
+    toolMajor.clear();
+    toolMinor.clear();
+    orientation.clear();
+    distance.clear();
+    trackingId.clear();
+    slot.clear();
+}
+
+
+// --- RawPointerData ---
+
+RawPointerData::RawPointerData() {
+    clear();
+}
+
+void RawPointerData::clear() {
+    pointerCount = 0;
+    clearIdBits();
+}
+
+void RawPointerData::copyFrom(const RawPointerData& other) {
+    pointerCount = other.pointerCount;
+    hoveringIdBits = other.hoveringIdBits;
+    touchingIdBits = other.touchingIdBits;
+
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointers[i] = other.pointers[i];
+
+        int id = pointers[i].id;
+        idToIndex[id] = other.idToIndex[id];
+    }
+}
+
+void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const {
+    float x = 0, y = 0;
+    uint32_t count = touchingIdBits.count();
+    if (count) {
+        for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty(); ) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const Pointer& pointer = pointerForId(id);
+            x += pointer.x;
+            y += pointer.y;
+        }
+        x /= count;
+        y /= count;
+    }
+    *outX = x;
+    *outY = y;
+}
+
+
+// --- CookedPointerData ---
+
+CookedPointerData::CookedPointerData() {
+    clear();
+}
+
+void CookedPointerData::clear() {
+    pointerCount = 0;
+    hoveringIdBits.clear();
+    touchingIdBits.clear();
+}
+
+void CookedPointerData::copyFrom(const CookedPointerData& other) {
+    pointerCount = other.pointerCount;
+    hoveringIdBits = other.hoveringIdBits;
+    touchingIdBits = other.touchingIdBits;
+
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(other.pointerProperties[i]);
+        pointerCoords[i].copyFrom(other.pointerCoords[i]);
+
+        int id = pointerProperties[i].id;
+        idToIndex[id] = other.idToIndex[id];
+    }
+}
+
+
 // --- SingleTouchMotionAccumulator ---
 
 SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() {
@@ -1298,6 +1398,10 @@
                 slot->mInUse = true;
                 slot->mAbsMTPressure = rawEvent->value;
                 break;
+            case ABS_MT_DISTANCE:
+                slot->mInUse = true;
+                slot->mAbsMTDistance = rawEvent->value;
+                break;
             case ABS_MT_TOOL_TYPE:
                 slot->mInUse = true;
                 slot->mAbsMTToolType = rawEvent->value;
@@ -1338,8 +1442,8 @@
     mAbsMTOrientation = 0;
     mAbsMTTrackingId = -1;
     mAbsMTPressure = 0;
-    mAbsMTToolType = 0;
     mAbsMTDistance = 0;
+    mAbsMTToolType = 0;
 }
 
 int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
@@ -1404,6 +1508,10 @@
 void InputMapper::fadePointer() {
 }
 
+status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
+    return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
+}
+
 void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
         const RawAbsoluteAxisInfo& axis, const char* name) {
     if (axis.valid) {
@@ -1437,7 +1545,8 @@
 }
 
 void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) {
-    getDispatcher()->notifySwitch(when, switchCode, switchValue, 0);
+    NotifySwitchArgs args(when, 0, switchCode, switchValue);
+    getListener()->notifySwitch(&args);
 }
 
 int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
@@ -1451,15 +1560,15 @@
         uint32_t source, int32_t keyboardType) :
         InputMapper(device), mSource(source),
         mKeyboardType(keyboardType) {
-    initializeLocked();
+    initialize();
 }
 
 KeyboardInputMapper::~KeyboardInputMapper() {
 }
 
-void KeyboardInputMapper::initializeLocked() {
-    mLocked.metaState = AMETA_NONE;
-    mLocked.downTime = 0;
+void KeyboardInputMapper::initialize() {
+    mMetaState = AMETA_NONE;
+    mDownTime = 0;
 }
 
 uint32_t KeyboardInputMapper::getSources() {
@@ -1473,15 +1582,12 @@
 }
 
 void KeyboardInputMapper::dump(String8& dump) {
-    { // acquire lock
-        AutoMutex _l(mLock);
-        dump.append(INDENT2 "Keyboard Input Mapper:\n");
-        dumpParameters(dump);
-        dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
-        dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
-        dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
-        dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
-    } // release lock
+    dump.append(INDENT2 "Keyboard Input Mapper:\n");
+    dumpParameters(dump);
+    dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
+    dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mKeyDowns.size());
+    dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mMetaState);
+    dump.appendFormat(INDENT3 "DownTime: %lld\n", mDownTime);
 }
 
 
@@ -1493,10 +1599,7 @@
         configureParameters();
 
         // Reset LEDs.
-        {
-            AutoMutex _l(mLock);
-            resetLedStateLocked();
-        }
+        resetLedState();
     }
 }
 
@@ -1520,27 +1623,16 @@
 }
 
 void KeyboardInputMapper::reset() {
-    for (;;) {
-        int32_t keyCode, scanCode;
-        { // acquire lock
-            AutoMutex _l(mLock);
-
-            // Synthesize key up event on reset if keys are currently down.
-            if (mLocked.keyDowns.isEmpty()) {
-                initializeLocked();
-                resetLedStateLocked();
-                break; // done
-            }
-
-            const KeyDown& keyDown = mLocked.keyDowns.top();
-            keyCode = keyDown.keyCode;
-            scanCode = keyDown.scanCode;
-        } // release lock
-
+    // Synthesize key up event on reset if keys are currently down.
+    while (!mKeyDowns.isEmpty()) {
+        const KeyDown& keyDown = mKeyDowns.top();
         nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
-        processKey(when, false, keyCode, scanCode, 0);
+        processKey(when, false, keyDown.keyCode, keyDown.scanCode, 0);
     }
 
+    initialize();
+    resetLedState();
+
     InputMapper::reset();
     getContext()->updateGlobalMetaState();
 }
@@ -1567,72 +1659,66 @@
 
 void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
         int32_t scanCode, uint32_t policyFlags) {
-    int32_t newMetaState;
-    nsecs_t downTime;
-    bool metaStateChanged = false;
 
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        if (down) {
-            // Rotate key codes according to orientation if needed.
-            // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
-            if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) {
-                int32_t orientation;
-                if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
-                        false /*external*/, NULL, NULL, & orientation)) {
-                    orientation = DISPLAY_ORIENTATION_0;
-                }
-
-                keyCode = rotateKeyCode(keyCode, orientation);
+    if (down) {
+        // Rotate key codes according to orientation if needed.
+        // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+        if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) {
+            int32_t orientation;
+            if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+                    false /*external*/, NULL, NULL, & orientation)) {
+                orientation = DISPLAY_ORIENTATION_0;
             }
 
-            // Add key down.
-            ssize_t keyDownIndex = findKeyDownLocked(scanCode);
-            if (keyDownIndex >= 0) {
-                // key repeat, be sure to use same keycode as before in case of rotation
-                keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
-            } else {
-                // key down
-                if ((policyFlags & POLICY_FLAG_VIRTUAL)
-                        && mContext->shouldDropVirtualKey(when,
-                                getDevice(), keyCode, scanCode)) {
-                    return;
-                }
+            keyCode = rotateKeyCode(keyCode, orientation);
+        }
 
-                mLocked.keyDowns.push();
-                KeyDown& keyDown = mLocked.keyDowns.editTop();
-                keyDown.keyCode = keyCode;
-                keyDown.scanCode = scanCode;
-            }
-
-            mLocked.downTime = when;
+        // Add key down.
+        ssize_t keyDownIndex = findKeyDown(scanCode);
+        if (keyDownIndex >= 0) {
+            // key repeat, be sure to use same keycode as before in case of rotation
+            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
         } else {
-            // Remove key down.
-            ssize_t keyDownIndex = findKeyDownLocked(scanCode);
-            if (keyDownIndex >= 0) {
-                // key up, be sure to use same keycode as before in case of rotation
-                keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
-                mLocked.keyDowns.removeAt(size_t(keyDownIndex));
-            } else {
-                // key was not actually down
-                LOGI("Dropping key up from device %s because the key was not down.  "
-                        "keyCode=%d, scanCode=%d",
-                        getDeviceName().string(), keyCode, scanCode);
+            // key down
+            if ((policyFlags & POLICY_FLAG_VIRTUAL)
+                    && mContext->shouldDropVirtualKey(when,
+                            getDevice(), keyCode, scanCode)) {
                 return;
             }
+
+            mKeyDowns.push();
+            KeyDown& keyDown = mKeyDowns.editTop();
+            keyDown.keyCode = keyCode;
+            keyDown.scanCode = scanCode;
         }
 
-        int32_t oldMetaState = mLocked.metaState;
-        newMetaState = updateMetaState(keyCode, down, oldMetaState);
-        if (oldMetaState != newMetaState) {
-            mLocked.metaState = newMetaState;
-            metaStateChanged = true;
-            updateLedStateLocked(false);
+        mDownTime = when;
+    } else {
+        // Remove key down.
+        ssize_t keyDownIndex = findKeyDown(scanCode);
+        if (keyDownIndex >= 0) {
+            // key up, be sure to use same keycode as before in case of rotation
+            keyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;
+            mKeyDowns.removeAt(size_t(keyDownIndex));
+        } else {
+            // key was not actually down
+            LOGI("Dropping key up from device %s because the key was not down.  "
+                    "keyCode=%d, scanCode=%d",
+                    getDeviceName().string(), keyCode, scanCode);
+            return;
         }
+    }
 
-        downTime = mLocked.downTime;
-    } // release lock
+    bool metaStateChanged = false;
+    int32_t oldMetaState = mMetaState;
+    int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
+    if (oldMetaState != newMetaState) {
+        mMetaState = newMetaState;
+        metaStateChanged = true;
+        updateLedState(false);
+    }
+
+    nsecs_t downTime = mDownTime;
 
     // Key down on external an keyboard should wake the device.
     // We don't do this for internal keyboards to prevent them from waking up in your pocket.
@@ -1652,15 +1738,16 @@
         getContext()->fadePointer();
     }
 
-    getDispatcher()->notifyKey(when, getDeviceId(), mSource, policyFlags,
+    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
+    getListener()->notifyKey(&args);
 }
 
-ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) {
-    size_t n = mLocked.keyDowns.size();
+ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
+    size_t n = mKeyDowns.size();
     for (size_t i = 0; i < n; i++) {
-        if (mLocked.keyDowns[i].scanCode == scanCode) {
+        if (mKeyDowns[i].scanCode == scanCode) {
             return i;
         }
     }
@@ -1681,38 +1768,35 @@
 }
 
 int32_t KeyboardInputMapper::getMetaState() {
-    { // acquire lock
-        AutoMutex _l(mLock);
-        return mLocked.metaState;
-    } // release lock
+    return mMetaState;
 }
 
-void KeyboardInputMapper::resetLedStateLocked() {
-    initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
-    initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
-    initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
+void KeyboardInputMapper::resetLedState() {
+    initializeLedState(mCapsLockLedState, LED_CAPSL);
+    initializeLedState(mNumLockLedState, LED_NUML);
+    initializeLedState(mScrollLockLedState, LED_SCROLLL);
 
-    updateLedStateLocked(true);
+    updateLedState(true);
 }
 
-void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
+void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
     ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
     ledState.on = false;
 }
 
-void KeyboardInputMapper::updateLedStateLocked(bool reset) {
-    updateLedStateForModifierLocked(mLocked.capsLockLedState, LED_CAPSL,
+void KeyboardInputMapper::updateLedState(bool reset) {
+    updateLedStateForModifier(mCapsLockLedState, LED_CAPSL,
             AMETA_CAPS_LOCK_ON, reset);
-    updateLedStateForModifierLocked(mLocked.numLockLedState, LED_NUML,
+    updateLedStateForModifier(mNumLockLedState, LED_NUML,
             AMETA_NUM_LOCK_ON, reset);
-    updateLedStateForModifierLocked(mLocked.scrollLockLedState, LED_SCROLLL,
+    updateLedStateForModifier(mScrollLockLedState, LED_SCROLLL,
             AMETA_SCROLL_LOCK_ON, reset);
 }
 
-void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState& ledState,
+void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState,
         int32_t led, int32_t modifier, bool reset) {
     if (ledState.avail) {
-        bool desiredState = (mLocked.metaState & modifier) != 0;
+        bool desiredState = (mMetaState & modifier) != 0;
         if (reset || ledState.on != desiredState) {
             getEventHub()->setLedState(getDeviceId(), led, desiredState);
             ledState.on = desiredState;
@@ -1725,7 +1809,7 @@
 
 CursorInputMapper::CursorInputMapper(InputDevice* device) :
         InputMapper(device) {
-    initializeLocked();
+    initialize();
 }
 
 CursorInputMapper::~CursorInputMapper() {
@@ -1759,24 +1843,21 @@
 }
 
 void CursorInputMapper::dump(String8& dump) {
-    { // acquire lock
-        AutoMutex _l(mLock);
-        dump.append(INDENT2 "Cursor Input Mapper:\n");
-        dumpParameters(dump);
-        dump.appendFormat(INDENT3 "XScale: %0.3f\n", mXScale);
-        dump.appendFormat(INDENT3 "YScale: %0.3f\n", mYScale);
-        dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
-        dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
-        dump.appendFormat(INDENT3 "HaveVWheel: %s\n",
-                toString(mCursorMotionAccumulator.haveRelativeVWheel()));
-        dump.appendFormat(INDENT3 "HaveHWheel: %s\n",
-                toString(mCursorMotionAccumulator.haveRelativeHWheel()));
-        dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
-        dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
-        dump.appendFormat(INDENT3 "ButtonState: 0x%08x\n", mLocked.buttonState);
-        dump.appendFormat(INDENT3 "Down: %s\n", toString(isPointerDown(mLocked.buttonState)));
-        dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
-    } // release lock
+    dump.append(INDENT2 "Cursor Input Mapper:\n");
+    dumpParameters(dump);
+    dump.appendFormat(INDENT3 "XScale: %0.3f\n", mXScale);
+    dump.appendFormat(INDENT3 "YScale: %0.3f\n", mYScale);
+    dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+    dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+    dump.appendFormat(INDENT3 "HaveVWheel: %s\n",
+            toString(mCursorMotionAccumulator.haveRelativeVWheel()));
+    dump.appendFormat(INDENT3 "HaveHWheel: %s\n",
+            toString(mCursorMotionAccumulator.haveRelativeHWheel()));
+    dump.appendFormat(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
+    dump.appendFormat(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
+    dump.appendFormat(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
+    dump.appendFormat(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
+    dump.appendFormat(INDENT3 "DownTime: %lld\n", mDownTime);
 }
 
 void CursorInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) {
@@ -1859,38 +1940,27 @@
             toString(mParameters.orientationAware));
 }
 
-void CursorInputMapper::initializeLocked() {
+void CursorInputMapper::initialize() {
     mCursorButtonAccumulator.clearButtons();
     mCursorMotionAccumulator.clearRelativeAxes();
 
-    mLocked.buttonState = 0;
-    mLocked.downTime = 0;
+    mButtonState = 0;
+    mDownTime = 0;
 }
 
 void CursorInputMapper::reset() {
-    for (;;) {
-        int32_t buttonState;
-        { // acquire lock
-            AutoMutex _l(mLock);
+    // Reset velocity.
+    mPointerVelocityControl.reset();
+    mWheelXVelocityControl.reset();
+    mWheelYVelocityControl.reset();
 
-            buttonState = mLocked.buttonState;
-            if (!buttonState) {
-                initializeLocked();
-                break; // done
-            }
-        } // release lock
+    // Synthesize button up event on reset.
+    nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+    mCursorButtonAccumulator.clearButtons();
+    mCursorMotionAccumulator.clearRelativeAxes();
+    sync(when);
 
-        // Reset velocity.
-        mPointerVelocityControl.reset();
-        mWheelXVelocityControl.reset();
-        mWheelYVelocityControl.reset();
-
-        // Synthesize button up event on reset.
-        nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
-        mCursorButtonAccumulator.clearButtons();
-        mCursorMotionAccumulator.clearRelativeAxes();
-        sync(when);
-    }
+    initialize();
 
     InputMapper::reset();
 }
@@ -1905,98 +1975,84 @@
 }
 
 void CursorInputMapper::sync(nsecs_t when) {
-    int32_t motionEventAction;
-    int32_t lastButtonState, currentButtonState;
+    int32_t lastButtonState = mButtonState;
+    int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
+    mButtonState = currentButtonState;
+
+    bool wasDown = isPointerDown(lastButtonState);
+    bool down = isPointerDown(currentButtonState);
+    bool downChanged;
+    if (!wasDown && down) {
+        mDownTime = when;
+        downChanged = true;
+    } else if (wasDown && !down) {
+        downChanged = true;
+    } else {
+        downChanged = false;
+    }
+    nsecs_t downTime = mDownTime;
+    bool buttonsChanged = currentButtonState != lastButtonState;
+
+    float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
+    float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
+    bool moved = deltaX != 0 || deltaY != 0;
+
+    if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0
+            && (deltaX != 0.0f || deltaY != 0.0f)) {
+        // Rotate motion based on display orientation if needed.
+        // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+        int32_t orientation;
+        if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
+                false /*external*/, NULL, NULL, & orientation)) {
+            orientation = DISPLAY_ORIENTATION_0;
+        }
+
+        rotateDelta(orientation, &deltaX, &deltaY);
+    }
+
     PointerProperties pointerProperties;
+    pointerProperties.clear();
+    pointerProperties.id = 0;
+    pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
+
     PointerCoords pointerCoords;
-    nsecs_t downTime;
-    float vscroll, hscroll;
-    { // acquire lock
-        AutoMutex _l(mLock);
+    pointerCoords.clear();
 
-        lastButtonState = mLocked.buttonState;
-        currentButtonState = mCursorButtonAccumulator.getButtonState();
-        mLocked.buttonState = currentButtonState;
+    float vscroll = mCursorMotionAccumulator.getRelativeVWheel();
+    float hscroll = mCursorMotionAccumulator.getRelativeHWheel();
+    bool scrolled = vscroll != 0 || hscroll != 0;
 
-        bool wasDown = isPointerDown(lastButtonState);
-        bool down = isPointerDown(currentButtonState);
-        bool downChanged;
-        if (!wasDown && down) {
-            mLocked.downTime = when;
-            downChanged = true;
-        } else if (wasDown && !down) {
-            downChanged = true;
-        } else {
-            downChanged = false;
-        }
-        downTime = mLocked.downTime;
+    mWheelYVelocityControl.move(when, NULL, &vscroll);
+    mWheelXVelocityControl.move(when, &hscroll, NULL);
 
-        if (downChanged) {
-            motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
-        } else if (down || mPointerController == NULL) {
-            motionEventAction = AMOTION_EVENT_ACTION_MOVE;
-        } else {
-            motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
-        }
+    mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
-        float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
-        float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
+    if (mPointerController != NULL) {
+        if (moved || scrolled || buttonsChanged) {
+            mPointerController->setPresentation(
+                    PointerControllerInterface::PRESENTATION_POINTER);
 
-        if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0
-                && (deltaX != 0.0f || deltaY != 0.0f)) {
-            // Rotate motion based on display orientation if needed.
-            // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
-            int32_t orientation;
-            if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
-                    false /*external*/, NULL, NULL, & orientation)) {
-                orientation = DISPLAY_ORIENTATION_0;
+            if (moved) {
+                mPointerController->move(deltaX, deltaY);
             }
 
-            rotateDelta(orientation, &deltaX, &deltaY);
-        }
-
-        pointerProperties.clear();
-        pointerProperties.id = 0;
-        pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
-
-        pointerCoords.clear();
-
-        vscroll = mCursorMotionAccumulator.getRelativeVWheel();
-        hscroll = mCursorMotionAccumulator.getRelativeHWheel();
-
-        mWheelYVelocityControl.move(when, NULL, &vscroll);
-        mWheelXVelocityControl.move(when, &hscroll, NULL);
-
-        mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
-        if (mPointerController != NULL) {
-            if (deltaX != 0 || deltaY != 0 || vscroll != 0 || hscroll != 0
-                    || currentButtonState != lastButtonState) {
-                mPointerController->setPresentation(
-                        PointerControllerInterface::PRESENTATION_POINTER);
-
-                if (deltaX != 0 || deltaY != 0) {
-                    mPointerController->move(deltaX, deltaY);
-                }
-
-                if (currentButtonState != lastButtonState) {
-                    mPointerController->setButtonState(currentButtonState);
-                }
-
-                mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+            if (buttonsChanged) {
+                mPointerController->setButtonState(currentButtonState);
             }
 
-            float x, y;
-            mPointerController->getPosition(&x, &y);
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        } else {
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
-            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
+            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
         }
 
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
-    } // release lock
+        float x, y;
+        mPointerController->getPosition(&x, &y);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    } else {
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
+    }
+
+    pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
 
     // Moving an external trackball or mouse should wake the device.
     // We don't do this for internal cursor devices to prevent them from waking up
@@ -2012,29 +2068,43 @@
             policyFlags, lastButtonState, currentButtonState);
 
     // Send motion event.
-    int32_t metaState = mContext->getGlobalMetaState();
-    getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
-            motionEventAction, 0, metaState, currentButtonState, 0,
-            1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+    if (downChanged || moved || scrolled || buttonsChanged) {
+        int32_t metaState = mContext->getGlobalMetaState();
+        int32_t motionEventAction;
+        if (downChanged) {
+            motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+        } else if (down || mPointerController == NULL) {
+            motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+        } else {
+            motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
+        }
 
-    // Send hover move after UP to tell the application that the mouse is hovering now.
-    if (motionEventAction == AMOTION_EVENT_ACTION_UP
-            && mPointerController != NULL) {
-        getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
-                metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+        NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
+                motionEventAction, 0, metaState, currentButtonState, 0,
                 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
-    }
+        getListener()->notifyMotion(&args);
 
-    // Send scroll events.
-    if (vscroll != 0 || hscroll != 0) {
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+        // Send hover move after UP to tell the application that the mouse is hovering now.
+        if (motionEventAction == AMOTION_EVENT_ACTION_UP
+                && mPointerController != NULL) {
+            NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
+                    AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+                    metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                    1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+            getListener()->notifyMotion(&hoverArgs);
+        }
 
-        getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
-                AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState,
-                AMOTION_EVENT_EDGE_FLAG_NONE,
-                1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+        // Send scroll events.
+        if (scrolled) {
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
+            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
+
+            NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
+                    AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState,
+                    AMOTION_EVENT_EDGE_FLAG_NONE,
+                    1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+            getListener()->notifyMotion(&scrollArgs);
+        }
     }
 
     // Synthesize key up from buttons if needed.
@@ -2053,24 +2123,18 @@
 }
 
 void CursorInputMapper::fadePointer() {
-    { // acquire lock
-        AutoMutex _l(mLock);
-        if (mPointerController != NULL) {
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-        }
-    } // release lock
+    if (mPointerController != NULL) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+    }
 }
 
 
 // --- TouchInputMapper ---
 
 TouchInputMapper::TouchInputMapper(InputDevice* device) :
-        InputMapper(device) {
-    mLocked.surfaceOrientation = -1;
-    mLocked.surfaceWidth = -1;
-    mLocked.surfaceHeight = -1;
-
-    initializeLocked();
+        InputMapper(device),
+        mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) {
+    initialize();
 }
 
 TouchInputMapper::~TouchInputMapper() {
@@ -2083,126 +2147,148 @@
 void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    { // acquire lock
-        AutoMutex _l(mLock);
+    // Ensure surface information is up to date so that orientation changes are
+    // noticed immediately.
+    if (!configureSurface()) {
+        return;
+    }
 
-        // Ensure surface information is up to date so that orientation changes are
-        // noticed immediately.
-        if (!configureSurfaceLocked()) {
-            return;
+    info->addMotionRange(mOrientedRanges.x);
+    info->addMotionRange(mOrientedRanges.y);
+
+    if (mOrientedRanges.havePressure) {
+        info->addMotionRange(mOrientedRanges.pressure);
+    }
+
+    if (mOrientedRanges.haveSize) {
+        info->addMotionRange(mOrientedRanges.size);
+    }
+
+    if (mOrientedRanges.haveTouchSize) {
+        info->addMotionRange(mOrientedRanges.touchMajor);
+        info->addMotionRange(mOrientedRanges.touchMinor);
+    }
+
+    if (mOrientedRanges.haveToolSize) {
+        info->addMotionRange(mOrientedRanges.toolMajor);
+        info->addMotionRange(mOrientedRanges.toolMinor);
+    }
+
+    if (mOrientedRanges.haveOrientation) {
+        info->addMotionRange(mOrientedRanges.orientation);
+    }
+
+    if (mOrientedRanges.haveDistance) {
+        info->addMotionRange(mOrientedRanges.distance);
+    }
+
+    if (mPointerController != NULL) {
+        float minX, minY, maxX, maxY;
+        if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+            info->addMotionRange(AMOTION_EVENT_AXIS_X, mPointerSource,
+                    minX, maxX, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mPointerSource,
+                    minY, maxY, 0.0f, 0.0f);
         }
-
-        info->addMotionRange(mLocked.orientedRanges.x);
-        info->addMotionRange(mLocked.orientedRanges.y);
-
-        if (mLocked.orientedRanges.havePressure) {
-            info->addMotionRange(mLocked.orientedRanges.pressure);
-        }
-
-        if (mLocked.orientedRanges.haveSize) {
-            info->addMotionRange(mLocked.orientedRanges.size);
-        }
-
-        if (mLocked.orientedRanges.haveTouchSize) {
-            info->addMotionRange(mLocked.orientedRanges.touchMajor);
-            info->addMotionRange(mLocked.orientedRanges.touchMinor);
-        }
-
-        if (mLocked.orientedRanges.haveToolSize) {
-            info->addMotionRange(mLocked.orientedRanges.toolMajor);
-            info->addMotionRange(mLocked.orientedRanges.toolMinor);
-        }
-
-        if (mLocked.orientedRanges.haveOrientation) {
-            info->addMotionRange(mLocked.orientedRanges.orientation);
-        }
-
-        if (mLocked.orientedRanges.haveDistance) {
-            info->addMotionRange(mLocked.orientedRanges.distance);
-        }
-
-        if (mPointerController != NULL) {
-            float minX, minY, maxX, maxY;
-            if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-                info->addMotionRange(AMOTION_EVENT_AXIS_X, mPointerSource,
-                        minX, maxX, 0.0f, 0.0f);
-                info->addMotionRange(AMOTION_EVENT_AXIS_Y, mPointerSource,
-                        minY, maxY, 0.0f, 0.0f);
-            }
-            info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mPointerSource,
-                    0.0f, 1.0f, 0.0f, 0.0f);
-        }
-    } // release lock
+        info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mPointerSource,
+                0.0f, 1.0f, 0.0f, 0.0f);
+    }
 }
 
 void TouchInputMapper::dump(String8& dump) {
-    { // acquire lock
-        AutoMutex _l(mLock);
-        dump.append(INDENT2 "Touch Input Mapper:\n");
-        dumpParameters(dump);
-        dumpVirtualKeysLocked(dump);
-        dumpRawAxes(dump);
-        dumpCalibration(dump);
-        dumpSurfaceLocked(dump);
+    dump.append(INDENT2 "Touch Input Mapper:\n");
+    dumpParameters(dump);
+    dumpVirtualKeys(dump);
+    dumpRawPointerAxes(dump);
+    dumpCalibration(dump);
+    dumpSurface(dump);
 
-        dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
-        dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
-        dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale);
-        dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision);
-        dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mLocked.yPrecision);
-        dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mLocked.geometricScale);
-        dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mLocked.toolSizeLinearScale);
-        dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mLocked.toolSizeLinearBias);
-        dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mLocked.toolSizeAreaScale);
-        dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias);
-        dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale);
-        dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale);
-        dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mLocked.orientationScale);
-        dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale);
+    dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
+    dump.appendFormat(INDENT4 "XScale: %0.3f\n", mXScale);
+    dump.appendFormat(INDENT4 "YScale: %0.3f\n", mYScale);
+    dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
+    dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mYPrecision);
+    dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale);
+    dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mToolSizeLinearScale);
+    dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mToolSizeLinearBias);
+    dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mToolSizeAreaScale);
+    dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mToolSizeAreaBias);
+    dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mPressureScale);
+    dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mSizeScale);
+    dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale);
+    dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale);
 
-        dump.appendFormat(INDENT3 "Last Touch:\n");
-        dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState);
-        dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
-        for (uint32_t i = 0; i < mLastTouch.pointerCount; i++) {
-            const PointerData& pointer = mLastTouch.pointers[i];
-            dump.appendFormat(INDENT5 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
-                    "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
-                    "orientation=%d, distance=%d, toolType=%d, isHovering=%s\n", i,
-                    pointer.id, pointer.x, pointer.y, pointer.pressure,
-                    pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, pointer.toolMinor,
-                    pointer.orientation, pointer.distance,
-                    pointer.toolType, toString(pointer.isHovering));
-        }
+    dump.appendFormat(INDENT3 "Last Button State: 0x%08x\n", mLastButtonState);
 
-        if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
-            dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
-            dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n",
-                    mLocked.pointerGestureXMovementScale);
-            dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n",
-                    mLocked.pointerGestureYMovementScale);
-            dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n",
-                    mLocked.pointerGestureXZoomScale);
-            dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n",
-                    mLocked.pointerGestureYZoomScale);
-            dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
-                    mLocked.pointerGestureMaxSwipeWidth);
-        }
-    } // release lock
+    dump.appendFormat(INDENT3 "Last Raw Touch: pointerCount=%d\n",
+            mLastRawPointerData.pointerCount);
+    for (uint32_t i = 0; i < mLastRawPointerData.pointerCount; i++) {
+        const RawPointerData::Pointer& pointer = mLastRawPointerData.pointers[i];
+        dump.appendFormat(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
+                "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
+                "orientation=%d, distance=%d, toolType=%d, isHovering=%s\n", i,
+                pointer.id, pointer.x, pointer.y, pointer.pressure,
+                pointer.touchMajor, pointer.touchMinor,
+                pointer.toolMajor, pointer.toolMinor,
+                pointer.orientation, pointer.distance,
+                pointer.toolType, toString(pointer.isHovering));
+    }
+
+    dump.appendFormat(INDENT3 "Last Cooked Touch: pointerCount=%d\n",
+            mLastCookedPointerData.pointerCount);
+    for (uint32_t i = 0; i < mLastCookedPointerData.pointerCount; i++) {
+        const PointerProperties& pointerProperties = mLastCookedPointerData.pointerProperties[i];
+        const PointerCoords& pointerCoords = mLastCookedPointerData.pointerCoords[i];
+        dump.appendFormat(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
+                "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, "
+                "orientation=%0.3f, distance=%0.3f, toolType=%d, isHovering=%s\n", i,
+                pointerProperties.id,
+                pointerCoords.getX(),
+                pointerCoords.getY(),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+                pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE),
+                pointerProperties.toolType,
+                toString(mLastCookedPointerData.isHovering(i)));
+    }
+
+    if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
+        dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
+        dump.appendFormat(INDENT4 "XMovementScale: %0.3f\n",
+                mPointerGestureXMovementScale);
+        dump.appendFormat(INDENT4 "YMovementScale: %0.3f\n",
+                mPointerGestureYMovementScale);
+        dump.appendFormat(INDENT4 "XZoomScale: %0.3f\n",
+                mPointerGestureXZoomScale);
+        dump.appendFormat(INDENT4 "YZoomScale: %0.3f\n",
+                mPointerGestureYZoomScale);
+        dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
+                mPointerGestureMaxSwipeWidth);
+    }
 }
 
-void TouchInputMapper::initializeLocked() {
-    mCurrentTouch.clear();
-    mLastTouch.clear();
+void TouchInputMapper::initialize() {
+    mCurrentRawPointerData.clear();
+    mLastRawPointerData.clear();
+    mCurrentCookedPointerData.clear();
+    mLastCookedPointerData.clear();
+    mCurrentButtonState = 0;
+    mLastButtonState = 0;
+    mSentHoverEnter = false;
     mDownTime = 0;
 
-    mLocked.currentVirtualKey.down = false;
+    mCurrentVirtualKey.down = false;
 
-    mLocked.orientedRanges.havePressure = false;
-    mLocked.orientedRanges.haveSize = false;
-    mLocked.orientedRanges.haveTouchSize = false;
-    mLocked.orientedRanges.haveToolSize = false;
-    mLocked.orientedRanges.haveOrientation = false;
-    mLocked.orientedRanges.haveDistance = false;
+    mOrientedRanges.havePressure = false;
+    mOrientedRanges.haveSize = false;
+    mOrientedRanges.haveTouchSize = false;
+    mOrientedRanges.haveToolSize = false;
+    mOrientedRanges.haveOrientation = false;
+    mOrientedRanges.haveDistance = false;
 
     mPointerGesture.reset();
 }
@@ -2235,18 +2321,14 @@
         }
 
         // Configure absolute axis information.
-        configureRawAxes();
+        configureRawPointerAxes();
 
         // Prepare input device calibration.
         parseCalibration();
         resolveCalibration();
 
-        { // acquire lock
-            AutoMutex _l(mLock);
-
-             // Configure surface dimensions and orientation.
-            configureSurfaceLocked();
-        } // release lock
+         // Configure surface dimensions and orientation.
+        configureSurface();
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
@@ -2359,38 +2441,28 @@
             toString(mParameters.orientationAware));
 }
 
-void TouchInputMapper::configureRawAxes() {
-    mRawAxes.x.clear();
-    mRawAxes.y.clear();
-    mRawAxes.pressure.clear();
-    mRawAxes.touchMajor.clear();
-    mRawAxes.touchMinor.clear();
-    mRawAxes.toolMajor.clear();
-    mRawAxes.toolMinor.clear();
-    mRawAxes.orientation.clear();
-    mRawAxes.distance.clear();
-    mRawAxes.trackingId.clear();
-    mRawAxes.slot.clear();
+void TouchInputMapper::configureRawPointerAxes() {
+    mRawPointerAxes.clear();
 }
 
-void TouchInputMapper::dumpRawAxes(String8& dump) {
-    dump.append(INDENT3 "Raw Axes:\n");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.distance, "Distance");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.trackingId, "TrackingId");
-    dumpRawAbsoluteAxisInfo(dump, mRawAxes.slot, "Slot");
+void TouchInputMapper::dumpRawPointerAxes(String8& dump) {
+    dump.append(INDENT3 "Raw Touch Axes:\n");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId");
+    dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot");
 }
 
-bool TouchInputMapper::configureSurfaceLocked() {
+bool TouchInputMapper::configureSurface() {
     // Ensure we have valid X and Y axes.
-    if (!mRawAxes.x.valid || !mRawAxes.y.valid) {
+    if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
         LOGW(INDENT "Touch device '%s' did not report support for X or Y axis!  "
                 "The device will be inoperable.", getDeviceName().string());
         return false;
@@ -2398,27 +2470,27 @@
 
     // Update orientation and dimensions if needed.
     int32_t orientation = DISPLAY_ORIENTATION_0;
-    int32_t width = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
-    int32_t height = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
+    int32_t width = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
+    int32_t height = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
 
     if (mParameters.associatedDisplayId >= 0) {
         // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
         if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
                 mParameters.associatedDisplayIsExternal,
-                &mLocked.associatedDisplayWidth, &mLocked.associatedDisplayHeight,
-                &mLocked.associatedDisplayOrientation)) {
+                &mAssociatedDisplayWidth, &mAssociatedDisplayHeight,
+                &mAssociatedDisplayOrientation)) {
             return false;
         }
 
         // A touch screen inherits the dimensions of the display.
         if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
-            width = mLocked.associatedDisplayWidth;
-            height = mLocked.associatedDisplayHeight;
+            width = mAssociatedDisplayWidth;
+            height = mAssociatedDisplayHeight;
         }
 
         // The device inherits the orientation of the display if it is orientation aware.
         if (mParameters.orientationAware) {
-            orientation = mLocked.associatedDisplayOrientation;
+            orientation = mAssociatedDisplayOrientation;
         }
     }
 
@@ -2427,118 +2499,120 @@
         mPointerController = getPolicy()->obtainPointerController(getDeviceId());
     }
 
-    bool orientationChanged = mLocked.surfaceOrientation != orientation;
+    bool orientationChanged = mSurfaceOrientation != orientation;
     if (orientationChanged) {
-        mLocked.surfaceOrientation = orientation;
+        mSurfaceOrientation = orientation;
     }
 
-    bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
+    bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height;
     if (sizeChanged) {
         LOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d",
                 getDeviceId(), getDeviceName().string(), width, height);
 
-        mLocked.surfaceWidth = width;
-        mLocked.surfaceHeight = height;
+        mSurfaceWidth = width;
+        mSurfaceHeight = height;
 
         // Configure X and Y factors.
-        mLocked.xScale = float(width) / (mRawAxes.x.maxValue - mRawAxes.x.minValue + 1);
-        mLocked.yScale = float(height) / (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1);
-        mLocked.xPrecision = 1.0f / mLocked.xScale;
-        mLocked.yPrecision = 1.0f / mLocked.yScale;
+        mXScale = float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1);
+        mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1);
+        mXPrecision = 1.0f / mXScale;
+        mYPrecision = 1.0f / mYScale;
 
-        mLocked.orientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
-        mLocked.orientedRanges.x.source = mTouchSource;
-        mLocked.orientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
-        mLocked.orientedRanges.y.source = mTouchSource;
+        mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;
+        mOrientedRanges.x.source = mTouchSource;
+        mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
+        mOrientedRanges.y.source = mTouchSource;
 
-        configureVirtualKeysLocked();
+        configureVirtualKeys();
 
         // Scale factor for terms that are not oriented in a particular axis.
         // If the pixels are square then xScale == yScale otherwise we fake it
         // by choosing an average.
-        mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale);
+        mGeometricScale = avg(mXScale, mYScale);
 
         // Size of diagonal axis.
         float diagonalSize = hypotf(width, height);
 
         // TouchMajor and TouchMinor factors.
         if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
-            mLocked.orientedRanges.haveTouchSize = true;
+            mOrientedRanges.haveTouchSize = true;
 
-            mLocked.orientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
-            mLocked.orientedRanges.touchMajor.source = mTouchSource;
-            mLocked.orientedRanges.touchMajor.min = 0;
-            mLocked.orientedRanges.touchMajor.max = diagonalSize;
-            mLocked.orientedRanges.touchMajor.flat = 0;
-            mLocked.orientedRanges.touchMajor.fuzz = 0;
+            mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR;
+            mOrientedRanges.touchMajor.source = mTouchSource;
+            mOrientedRanges.touchMajor.min = 0;
+            mOrientedRanges.touchMajor.max = diagonalSize;
+            mOrientedRanges.touchMajor.flat = 0;
+            mOrientedRanges.touchMajor.fuzz = 0;
 
-            mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor;
-            mLocked.orientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
+            mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
+            mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
         }
 
         // ToolMajor and ToolMinor factors.
-        mLocked.toolSizeLinearScale = 0;
-        mLocked.toolSizeLinearBias = 0;
-        mLocked.toolSizeAreaScale = 0;
-        mLocked.toolSizeAreaBias = 0;
+        mToolSizeLinearScale = 0;
+        mToolSizeLinearBias = 0;
+        mToolSizeAreaScale = 0;
+        mToolSizeAreaBias = 0;
         if (mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
             if (mCalibration.toolSizeCalibration == Calibration::TOOL_SIZE_CALIBRATION_LINEAR) {
                 if (mCalibration.haveToolSizeLinearScale) {
-                    mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
-                } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
-                    mLocked.toolSizeLinearScale = float(min(width, height))
-                            / mRawAxes.toolMajor.maxValue;
+                    mToolSizeLinearScale = mCalibration.toolSizeLinearScale;
+                } else if (mRawPointerAxes.toolMajor.valid
+                        && mRawPointerAxes.toolMajor.maxValue != 0) {
+                    mToolSizeLinearScale = float(min(width, height))
+                            / mRawPointerAxes.toolMajor.maxValue;
                 }
 
                 if (mCalibration.haveToolSizeLinearBias) {
-                    mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
+                    mToolSizeLinearBias = mCalibration.toolSizeLinearBias;
                 }
             } else if (mCalibration.toolSizeCalibration ==
                     Calibration::TOOL_SIZE_CALIBRATION_AREA) {
                 if (mCalibration.haveToolSizeLinearScale) {
-                    mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
+                    mToolSizeLinearScale = mCalibration.toolSizeLinearScale;
                 } else {
-                    mLocked.toolSizeLinearScale = min(width, height);
+                    mToolSizeLinearScale = min(width, height);
                 }
 
                 if (mCalibration.haveToolSizeLinearBias) {
-                    mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
+                    mToolSizeLinearBias = mCalibration.toolSizeLinearBias;
                 }
 
                 if (mCalibration.haveToolSizeAreaScale) {
-                    mLocked.toolSizeAreaScale = mCalibration.toolSizeAreaScale;
-                } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
-                    mLocked.toolSizeAreaScale = 1.0f / mRawAxes.toolMajor.maxValue;
+                    mToolSizeAreaScale = mCalibration.toolSizeAreaScale;
+                } else if (mRawPointerAxes.toolMajor.valid
+                        && mRawPointerAxes.toolMajor.maxValue != 0) {
+                    mToolSizeAreaScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
                 }
 
                 if (mCalibration.haveToolSizeAreaBias) {
-                    mLocked.toolSizeAreaBias = mCalibration.toolSizeAreaBias;
+                    mToolSizeAreaBias = mCalibration.toolSizeAreaBias;
                 }
             }
 
-            mLocked.orientedRanges.haveToolSize = true;
+            mOrientedRanges.haveToolSize = true;
 
-            mLocked.orientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
-            mLocked.orientedRanges.toolMajor.source = mTouchSource;
-            mLocked.orientedRanges.toolMajor.min = 0;
-            mLocked.orientedRanges.toolMajor.max = diagonalSize;
-            mLocked.orientedRanges.toolMajor.flat = 0;
-            mLocked.orientedRanges.toolMajor.fuzz = 0;
+            mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR;
+            mOrientedRanges.toolMajor.source = mTouchSource;
+            mOrientedRanges.toolMajor.min = 0;
+            mOrientedRanges.toolMajor.max = diagonalSize;
+            mOrientedRanges.toolMajor.flat = 0;
+            mOrientedRanges.toolMajor.fuzz = 0;
 
-            mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor;
-            mLocked.orientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
+            mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
+            mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
         }
 
         // Pressure factors.
-        mLocked.pressureScale = 0;
+        mPressureScale = 0;
         if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) {
             RawAbsoluteAxisInfo rawPressureAxis;
             switch (mCalibration.pressureSource) {
             case Calibration::PRESSURE_SOURCE_PRESSURE:
-                rawPressureAxis = mRawAxes.pressure;
+                rawPressureAxis = mRawPointerAxes.pressure;
                 break;
             case Calibration::PRESSURE_SOURCE_TOUCH:
-                rawPressureAxis = mRawAxes.touchMajor;
+                rawPressureAxis = mRawPointerAxes.touchMajor;
                 break;
             default:
                 rawPressureAxis.clear();
@@ -2548,84 +2622,84 @@
                     || mCalibration.pressureCalibration
                             == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
                 if (mCalibration.havePressureScale) {
-                    mLocked.pressureScale = mCalibration.pressureScale;
+                    mPressureScale = mCalibration.pressureScale;
                 } else if (rawPressureAxis.valid && rawPressureAxis.maxValue != 0) {
-                    mLocked.pressureScale = 1.0f / rawPressureAxis.maxValue;
+                    mPressureScale = 1.0f / rawPressureAxis.maxValue;
                 }
             }
 
-            mLocked.orientedRanges.havePressure = true;
+            mOrientedRanges.havePressure = true;
 
-            mLocked.orientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
-            mLocked.orientedRanges.pressure.source = mTouchSource;
-            mLocked.orientedRanges.pressure.min = 0;
-            mLocked.orientedRanges.pressure.max = 1.0;
-            mLocked.orientedRanges.pressure.flat = 0;
-            mLocked.orientedRanges.pressure.fuzz = 0;
+            mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE;
+            mOrientedRanges.pressure.source = mTouchSource;
+            mOrientedRanges.pressure.min = 0;
+            mOrientedRanges.pressure.max = 1.0;
+            mOrientedRanges.pressure.flat = 0;
+            mOrientedRanges.pressure.fuzz = 0;
         }
 
         // Size factors.
-        mLocked.sizeScale = 0;
+        mSizeScale = 0;
         if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
             if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) {
-                if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
-                    mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue;
+                if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
+                    mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
                 }
             }
 
-            mLocked.orientedRanges.haveSize = true;
+            mOrientedRanges.haveSize = true;
 
-            mLocked.orientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
-            mLocked.orientedRanges.size.source = mTouchSource;
-            mLocked.orientedRanges.size.min = 0;
-            mLocked.orientedRanges.size.max = 1.0;
-            mLocked.orientedRanges.size.flat = 0;
-            mLocked.orientedRanges.size.fuzz = 0;
+            mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE;
+            mOrientedRanges.size.source = mTouchSource;
+            mOrientedRanges.size.min = 0;
+            mOrientedRanges.size.max = 1.0;
+            mOrientedRanges.size.flat = 0;
+            mOrientedRanges.size.fuzz = 0;
         }
 
         // Orientation
-        mLocked.orientationScale = 0;
+        mOrientationScale = 0;
         if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) {
             if (mCalibration.orientationCalibration
                     == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
-                if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) {
-                    mLocked.orientationScale = float(M_PI_2) / mRawAxes.orientation.maxValue;
+                if (mRawPointerAxes.orientation.valid && mRawPointerAxes.orientation.maxValue != 0) {
+                    mOrientationScale = float(M_PI_2) / mRawPointerAxes.orientation.maxValue;
                 }
             }
 
-            mLocked.orientedRanges.haveOrientation = true;
+            mOrientedRanges.haveOrientation = true;
 
-            mLocked.orientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
-            mLocked.orientedRanges.orientation.source = mTouchSource;
-            mLocked.orientedRanges.orientation.min = - M_PI_2;
-            mLocked.orientedRanges.orientation.max = M_PI_2;
-            mLocked.orientedRanges.orientation.flat = 0;
-            mLocked.orientedRanges.orientation.fuzz = 0;
+            mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
+            mOrientedRanges.orientation.source = mTouchSource;
+            mOrientedRanges.orientation.min = - M_PI_2;
+            mOrientedRanges.orientation.max = M_PI_2;
+            mOrientedRanges.orientation.flat = 0;
+            mOrientedRanges.orientation.fuzz = 0;
         }
 
         // Distance
-        mLocked.distanceScale = 0;
+        mDistanceScale = 0;
         if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
             if (mCalibration.distanceCalibration
                     == Calibration::DISTANCE_CALIBRATION_SCALED) {
                 if (mCalibration.haveDistanceScale) {
-                    mLocked.distanceScale = mCalibration.distanceScale;
+                    mDistanceScale = mCalibration.distanceScale;
                 } else {
-                    mLocked.distanceScale = 1.0f;
+                    mDistanceScale = 1.0f;
                 }
             }
 
-            mLocked.orientedRanges.haveDistance = true;
+            mOrientedRanges.haveDistance = true;
 
-            mLocked.orientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
-            mLocked.orientedRanges.distance.source = mTouchSource;
-            mLocked.orientedRanges.distance.min =
-                    mRawAxes.distance.minValue * mLocked.distanceScale;
-            mLocked.orientedRanges.distance.max =
-                    mRawAxes.distance.minValue * mLocked.distanceScale;
-            mLocked.orientedRanges.distance.flat = 0;
-            mLocked.orientedRanges.distance.fuzz =
-                    mRawAxes.distance.fuzz * mLocked.distanceScale;
+            mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
+            mOrientedRanges.distance.source = mTouchSource;
+            mOrientedRanges.distance.min =
+                    mRawPointerAxes.distance.minValue * mDistanceScale;
+            mOrientedRanges.distance.max =
+                    mRawPointerAxes.distance.minValue * mDistanceScale;
+            mOrientedRanges.distance.flat = 0;
+            mOrientedRanges.distance.fuzz =
+                    mRawPointerAxes.distance.fuzz * mDistanceScale;
         }
     }
 
@@ -2633,78 +2707,77 @@
         // Compute oriented surface dimensions, precision, scales and ranges.
         // Note that the maximum value reported is an inclusive maximum value so it is one
         // unit less than the total width or height of surface.
-        switch (mLocked.surfaceOrientation) {
+        switch (mSurfaceOrientation) {
         case DISPLAY_ORIENTATION_90:
         case DISPLAY_ORIENTATION_270:
-            mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
-            mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
+            mOrientedSurfaceWidth = mSurfaceHeight;
+            mOrientedSurfaceHeight = mSurfaceWidth;
 
-            mLocked.orientedXPrecision = mLocked.yPrecision;
-            mLocked.orientedYPrecision = mLocked.xPrecision;
+            mOrientedXPrecision = mYPrecision;
+            mOrientedYPrecision = mXPrecision;
 
-            mLocked.orientedRanges.x.min = 0;
-            mLocked.orientedRanges.x.max = (mRawAxes.y.maxValue - mRawAxes.y.minValue)
-                    * mLocked.yScale;
-            mLocked.orientedRanges.x.flat = 0;
-            mLocked.orientedRanges.x.fuzz = mLocked.yScale;
+            mOrientedRanges.x.min = 0;
+            mOrientedRanges.x.max = (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue)
+                    * mYScale;
+            mOrientedRanges.x.flat = 0;
+            mOrientedRanges.x.fuzz = mYScale;
 
-            mLocked.orientedRanges.y.min = 0;
-            mLocked.orientedRanges.y.max = (mRawAxes.x.maxValue - mRawAxes.x.minValue)
-                    * mLocked.xScale;
-            mLocked.orientedRanges.y.flat = 0;
-            mLocked.orientedRanges.y.fuzz = mLocked.xScale;
+            mOrientedRanges.y.min = 0;
+            mOrientedRanges.y.max = (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue)
+                    * mXScale;
+            mOrientedRanges.y.flat = 0;
+            mOrientedRanges.y.fuzz = mXScale;
             break;
 
         default:
-            mLocked.orientedSurfaceWidth = mLocked.surfaceWidth;
-            mLocked.orientedSurfaceHeight = mLocked.surfaceHeight;
+            mOrientedSurfaceWidth = mSurfaceWidth;
+            mOrientedSurfaceHeight = mSurfaceHeight;
 
-            mLocked.orientedXPrecision = mLocked.xPrecision;
-            mLocked.orientedYPrecision = mLocked.yPrecision;
+            mOrientedXPrecision = mXPrecision;
+            mOrientedYPrecision = mYPrecision;
 
-            mLocked.orientedRanges.x.min = 0;
-            mLocked.orientedRanges.x.max = (mRawAxes.x.maxValue - mRawAxes.x.minValue)
-                    * mLocked.xScale;
-            mLocked.orientedRanges.x.flat = 0;
-            mLocked.orientedRanges.x.fuzz = mLocked.xScale;
+            mOrientedRanges.x.min = 0;
+            mOrientedRanges.x.max = (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue)
+                    * mXScale;
+            mOrientedRanges.x.flat = 0;
+            mOrientedRanges.x.fuzz = mXScale;
 
-            mLocked.orientedRanges.y.min = 0;
-            mLocked.orientedRanges.y.max = (mRawAxes.y.maxValue - mRawAxes.y.minValue)
-                    * mLocked.yScale;
-            mLocked.orientedRanges.y.flat = 0;
-            mLocked.orientedRanges.y.fuzz = mLocked.yScale;
+            mOrientedRanges.y.min = 0;
+            mOrientedRanges.y.max = (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue)
+                    * mYScale;
+            mOrientedRanges.y.flat = 0;
+            mOrientedRanges.y.fuzz = mYScale;
             break;
         }
 
         // Compute pointer gesture detection parameters.
-        // TODO: These factors should not be hardcoded.
         if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
-            int32_t rawWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
-            int32_t rawHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
+            int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
+            int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
             float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(mLocked.associatedDisplayWidth,
-                    mLocked.associatedDisplayHeight);
+            float displayDiagonal = hypotf(mAssociatedDisplayWidth,
+                    mAssociatedDisplayHeight);
 
             // Scale movements such that one whole swipe of the touch pad covers a
             // given area relative to the diagonal size of the display when no acceleration
             // is applied.
             // Assume that the touch pad has a square aspect ratio such that movements in
             // X and Y of the same number of raw units cover the same physical distance.
-            mLocked.pointerGestureXMovementScale = mConfig.pointerGestureMovementSpeedRatio
+            mPointerGestureXMovementScale = mConfig.pointerGestureMovementSpeedRatio
                     * displayDiagonal / rawDiagonal;
-            mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale;
+            mPointerGestureYMovementScale = mPointerGestureXMovementScale;
 
             // Scale zooms to cover a smaller range of the display than movements do.
             // This value determines the area around the pointer that is affected by freeform
             // pointer gestures.
-            mLocked.pointerGestureXZoomScale = mConfig.pointerGestureZoomSpeedRatio
+            mPointerGestureXZoomScale = mConfig.pointerGestureZoomSpeedRatio
                     * displayDiagonal / rawDiagonal;
-            mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale;
+            mPointerGestureYZoomScale = mPointerGestureXZoomScale;
 
             // Max width between pointers to detect a swipe gesture is more than some fraction
             // of the diagonal axis of the touch pad.  Touches that are wider than this are
             // translated into freeform gestures.
-            mLocked.pointerGestureMaxSwipeWidth =
+            mPointerGestureMaxSwipeWidth =
                     mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
 
             // Reset the current pointer gesture.
@@ -2720,35 +2793,35 @@
     return true;
 }
 
-void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
-    dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth);
-    dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight);
-    dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation);
+void TouchInputMapper::dumpSurface(String8& dump) {
+    dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
+    dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+    dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
 }
 
-void TouchInputMapper::configureVirtualKeysLocked() {
+void TouchInputMapper::configureVirtualKeys() {
     Vector<VirtualKeyDefinition> virtualKeyDefinitions;
     getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
 
-    mLocked.virtualKeys.clear();
+    mVirtualKeys.clear();
 
     if (virtualKeyDefinitions.size() == 0) {
         return;
     }
 
-    mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size());
+    mVirtualKeys.setCapacity(virtualKeyDefinitions.size());
 
-    int32_t touchScreenLeft = mRawAxes.x.minValue;
-    int32_t touchScreenTop = mRawAxes.y.minValue;
-    int32_t touchScreenWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
-    int32_t touchScreenHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
+    int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
+    int32_t touchScreenTop = mRawPointerAxes.y.minValue;
+    int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
+    int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
         const VirtualKeyDefinition& virtualKeyDefinition =
                 virtualKeyDefinitions[i];
 
-        mLocked.virtualKeys.add();
-        VirtualKey& virtualKey = mLocked.virtualKeys.editTop();
+        mVirtualKeys.add();
+        VirtualKey& virtualKey = mVirtualKeys.editTop();
 
         virtualKey.scanCode = virtualKeyDefinition.scanCode;
         int32_t keyCode;
@@ -2757,7 +2830,7 @@
                 & keyCode, & flags)) {
             LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
                     virtualKey.scanCode);
-            mLocked.virtualKeys.pop(); // drop the key
+            mVirtualKeys.pop(); // drop the key
             continue;
         }
 
@@ -2769,22 +2842,22 @@
         int32_t halfHeight = virtualKeyDefinition.height / 2;
 
         virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
-                * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
+                * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
         virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
-                * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
+                * touchScreenWidth / mSurfaceWidth + touchScreenLeft;
         virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
-                * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
+                * touchScreenHeight / mSurfaceHeight + touchScreenTop;
         virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
-                * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
+                * touchScreenHeight / mSurfaceHeight + touchScreenTop;
     }
 }
 
-void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
-    if (!mLocked.virtualKeys.isEmpty()) {
+void TouchInputMapper::dumpVirtualKeys(String8& dump) {
+    if (!mVirtualKeys.isEmpty()) {
         dump.append(INDENT3 "Virtual Keys:\n");
 
-        for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) {
-            const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i);
+        for (size_t i = 0; i < mVirtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mVirtualKeys.itemAt(i);
             dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
                     "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
                     i, virtualKey.scanCode, virtualKey.keyCode,
@@ -2927,22 +3000,22 @@
     // Pressure
     switch (mCalibration.pressureSource) {
     case Calibration::PRESSURE_SOURCE_DEFAULT:
-        if (mRawAxes.pressure.valid) {
+        if (mRawPointerAxes.pressure.valid) {
             mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
-        } else if (mRawAxes.touchMajor.valid) {
+        } else if (mRawPointerAxes.touchMajor.valid) {
             mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
         }
         break;
 
     case Calibration::PRESSURE_SOURCE_PRESSURE:
-        if (! mRawAxes.pressure.valid) {
+        if (! mRawPointerAxes.pressure.valid) {
             LOGW("Calibration property touch.pressure.source is 'pressure' but "
                     "the pressure axis is not available.");
         }
         break;
 
     case Calibration::PRESSURE_SOURCE_TOUCH:
-        if (! mRawAxes.touchMajor.valid) {
+        if (! mRawPointerAxes.touchMajor.valid) {
             LOGW("Calibration property touch.pressure.source is 'touch' but "
                     "the touchMajor axis is not available.");
         }
@@ -2968,7 +3041,7 @@
     // Tool Size
     switch (mCalibration.toolSizeCalibration) {
     case Calibration::TOOL_SIZE_CALIBRATION_DEFAULT:
-        if (mRawAxes.toolMajor.valid) {
+        if (mRawPointerAxes.toolMajor.valid) {
             mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
         } else {
             mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
@@ -2997,7 +3070,7 @@
     // Size
     switch (mCalibration.sizeCalibration) {
     case Calibration::SIZE_CALIBRATION_DEFAULT:
-        if (mRawAxes.toolMajor.valid) {
+        if (mRawPointerAxes.toolMajor.valid) {
             mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
         } else {
             mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
@@ -3011,7 +3084,7 @@
     // Orientation
     switch (mCalibration.orientationCalibration) {
     case Calibration::ORIENTATION_CALIBRATION_DEFAULT:
-        if (mRawAxes.orientation.valid) {
+        if (mRawPointerAxes.orientation.valid) {
             mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
         } else {
             mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
@@ -3025,7 +3098,7 @@
     // Distance
     switch (mCalibration.distanceCalibration) {
     case Calibration::DISTANCE_CALIBRATION_DEFAULT:
-        if (mRawAxes.distance.valid) {
+        if (mRawPointerAxes.distance.valid) {
             mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
         } else {
             mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
@@ -3177,50 +3250,58 @@
 }
 
 void TouchInputMapper::reset() {
-    // Synthesize touch up event if touch is currently down.
+    // Synthesize touch up event.
     // This will also take care of finishing virtual key processing if needed.
-    if (mLastTouch.pointerCount != 0) {
-        nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
-        mCurrentTouch.clear();
-        syncTouch(when, true);
+    nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+    mCurrentRawPointerData.clear();
+    mCurrentButtonState = 0;
+    syncTouch(when, true);
+
+    initialize();
+
+    if (mPointerController != NULL
+            && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+        mPointerController->clearSpots();
     }
 
-    { // acquire lock
-        AutoMutex _l(mLock);
-        initializeLocked();
-
-        if (mPointerController != NULL
-                && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-            mPointerController->clearSpots();
-        }
-    } // release lock
-
     InputMapper::reset();
 }
 
 void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
 #if DEBUG_RAW_EVENTS
     if (!havePointerIds) {
-        LOGD("syncTouch: pointerCount=%d, no pointer ids", mCurrentTouch.pointerCount);
+        LOGD("syncTouch: pointerCount %d -> %d, no pointer ids",
+                mLastRawPointerData.pointerCount,
+                mCurrentRawPointerData.pointerCount);
     } else {
-        LOGD("syncTouch: pointerCount=%d, up=0x%08x, down=0x%08x, move=0x%08x, "
-                "last=0x%08x, current=0x%08x", mCurrentTouch.pointerCount,
-                mLastTouch.idBits.value & ~mCurrentTouch.idBits.value,
-                mCurrentTouch.idBits.value & ~mLastTouch.idBits.value,
-                mLastTouch.idBits.value & mCurrentTouch.idBits.value,
-                mLastTouch.idBits.value, mCurrentTouch.idBits.value);
+        LOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+                "hovering ids 0x%08x -> 0x%08x",
+                mLastRawPointerData.pointerCount,
+                mCurrentRawPointerData.pointerCount,
+                mLastRawPointerData.touchingIdBits.value,
+                mCurrentRawPointerData.touchingIdBits.value,
+                mLastRawPointerData.hoveringIdBits.value,
+                mCurrentRawPointerData.hoveringIdBits.value);
     }
 #endif
 
-    // Preprocess pointer data.
-    if (!havePointerIds) {
-        calculatePointerIds();
+    // Configure the surface now, if possible.
+    if (!configureSurface()) {
+        mLastRawPointerData.clear();
+        mLastCookedPointerData.clear();
+        mLastButtonState = 0;
+        return;
     }
 
-    // Handle initial down events.
+    // Preprocess pointer data.
+    if (!havePointerIds) {
+        assignPointerIds();
+    }
+
+    // Handle policy on initial down or hover events.
     uint32_t policyFlags = 0;
-    if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+    if (mLastRawPointerData.pointerCount == 0 && mCurrentRawPointerData.pointerCount != 0) {
         if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
             // If this is a touch screen, hide the pointer on an initial down.
             getContext()->fadePointer();
@@ -3235,40 +3316,31 @@
         }
     }
 
-    // Synthesize key down from buttons if needed.
+    // Synthesize key down from raw buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mTouchSource,
-            policyFlags, mLastTouch.buttonState, mCurrentTouch.buttonState);
+            policyFlags, mLastButtonState, mCurrentButtonState);
 
-    // Send motion events.
-    TouchResult touchResult;
-    if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount == 0
-            && mLastTouch.buttonState == mCurrentTouch.buttonState) {
-        // Drop spurious syncs.
-        touchResult = DROP_STROKE;
-    } else {
-        // Process touches and virtual keys.
-        touchResult = consumeOffScreenTouches(when, policyFlags);
-        if (touchResult == DISPATCH_TOUCH) {
-            suppressSwipeOntoVirtualKeys(when);
-            if (mPointerController != NULL && mConfig.pointerGesturesEnabled) {
-                dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
-            }
-            dispatchTouches(when, policyFlags);
-        }
+    if (consumeRawTouches(when, policyFlags)) {
+        mCurrentRawPointerData.clear();
     }
 
-    // Synthesize key up from buttons if needed.
+    if (mPointerController != NULL && mConfig.pointerGesturesEnabled) {
+        dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+    }
+
+    cookPointerData();
+    dispatchHoverExit(when, policyFlags);
+    dispatchTouches(when, policyFlags);
+    dispatchHoverEnterAndMove(when, policyFlags);
+
+    // Synthesize key up from raw buttons if needed.
     synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mTouchSource,
-            policyFlags, mLastTouch.buttonState, mCurrentTouch.buttonState);
+            policyFlags, mLastButtonState, mCurrentButtonState);
 
     // Copy current touch to last touch in preparation for the next cycle.
-    // Keep the button state so we can track edge-triggered button state changes.
-    if (touchResult == DROP_STROKE) {
-        mLastTouch.clear();
-        mLastTouch.buttonState = mCurrentTouch.buttonState;
-    } else {
-        mLastTouch.copyFrom(mCurrentTouch);
-    }
+    mLastRawPointerData.copyFrom(mCurrentRawPointerData);
+    mLastCookedPointerData.copyFrom(mCurrentCookedPointerData);
+    mLastButtonState = mCurrentButtonState;
 }
 
 void TouchInputMapper::timeoutExpired(nsecs_t when) {
@@ -3277,125 +3349,88 @@
     }
 }
 
-TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
-        nsecs_t when, uint32_t policyFlags) {
-    int32_t keyEventAction, keyEventFlags;
-    int32_t keyCode, scanCode, downTime;
-    TouchResult touchResult;
-
-    { // acquire lock
-        AutoMutex _l(mLock);
-
-        // Update surface size and orientation, including virtual key positions.
-        if (! configureSurfaceLocked()) {
-            return DROP_STROKE;
-        }
-
-        // Check for virtual key press.
-        if (mLocked.currentVirtualKey.down) {
-            if (mCurrentTouch.pointerCount == 0) {
-                // Pointer went up while virtual key was down.
-                mLocked.currentVirtualKey.down = false;
+bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
+    // Check for release of a virtual key.
+    if (mCurrentVirtualKey.down) {
+        if (mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+            // Pointer went up while virtual key was down.
+            mCurrentVirtualKey.down = false;
+            if (!mCurrentVirtualKey.ignored) {
 #if DEBUG_VIRTUAL_KEYS
                 LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
-                        mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
+                        mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
 #endif
-                keyEventAction = AKEY_EVENT_ACTION_UP;
-                keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
-                touchResult = SKIP_TOUCH;
-                goto DispatchVirtualKey;
+                dispatchVirtualKey(when, policyFlags,
+                        AKEY_EVENT_ACTION_UP,
+                        AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
             }
-
-            if (mCurrentTouch.pointerCount == 1) {
-                int32_t x = mCurrentTouch.pointers[0].x;
-                int32_t y = mCurrentTouch.pointers[0].y;
-                const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
-                if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.keyCode) {
-                    // Pointer is still within the space of the virtual key.
-                    return SKIP_TOUCH;
-                }
-            }
-
-            // Pointer left virtual key area or another pointer also went down.
-            // Send key cancellation and drop the stroke so subsequent motions will be
-            // considered fresh downs.  This is useful when the user swipes away from the
-            // virtual key area into the main display surface.
-            mLocked.currentVirtualKey.down = false;
-#if DEBUG_VIRTUAL_KEYS
-            LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
-                    mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
-#endif
-            keyEventAction = AKEY_EVENT_ACTION_UP;
-            keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
-                    | AKEY_EVENT_FLAG_CANCELED;
-
-            // Check whether the pointer moved inside the display area where we should
-            // start a new stroke.
-            int32_t x = mCurrentTouch.pointers[0].x;
-            int32_t y = mCurrentTouch.pointers[0].y;
-            if (isPointInsideSurfaceLocked(x, y)) {
-                mLastTouch.clear();
-                touchResult = DISPATCH_TOUCH;
-            } else {
-                touchResult = DROP_STROKE;
-            }
-        } else {
-            if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) {
-                // Pointer just went down.  Handle off-screen touches, if needed.
-                int32_t x = mCurrentTouch.pointers[0].x;
-                int32_t y = mCurrentTouch.pointers[0].y;
-                if (! isPointInsideSurfaceLocked(x, y)) {
-                    // If exactly one pointer went down, check for virtual key hit.
-                    // Otherwise we will drop the entire stroke.
-                    if (mCurrentTouch.pointerCount == 1) {
-                        const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
-                        if (virtualKey) {
-                            if (mContext->shouldDropVirtualKey(when, getDevice(),
-                                    virtualKey->keyCode, virtualKey->scanCode)) {
-                                return DROP_STROKE;
-                            }
-
-                            mLocked.currentVirtualKey.down = true;
-                            mLocked.currentVirtualKey.downTime = when;
-                            mLocked.currentVirtualKey.keyCode = virtualKey->keyCode;
-                            mLocked.currentVirtualKey.scanCode = virtualKey->scanCode;
-#if DEBUG_VIRTUAL_KEYS
-                            LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
-                                    mLocked.currentVirtualKey.keyCode,
-                                    mLocked.currentVirtualKey.scanCode);
-#endif
-                            keyEventAction = AKEY_EVENT_ACTION_DOWN;
-                            keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM
-                                    | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
-                            touchResult = SKIP_TOUCH;
-                            goto DispatchVirtualKey;
-                        }
-                    }
-                    return DROP_STROKE;
-                }
-            }
-            return DISPATCH_TOUCH;
+            return true;
         }
 
-    DispatchVirtualKey:
-        // Collect remaining state needed to dispatch virtual key.
-        keyCode = mLocked.currentVirtualKey.keyCode;
-        scanCode = mLocked.currentVirtualKey.scanCode;
-        downTime = mLocked.currentVirtualKey.downTime;
-    } // release lock
+        if (mCurrentRawPointerData.touchingIdBits.count() == 1) {
+            uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
+            const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
+            const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
+            if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) {
+                // Pointer is still within the space of the virtual key.
+                return true;
+            }
+        }
 
-    // Dispatch virtual key.
-    int32_t metaState = mContext->getGlobalMetaState();
-    policyFlags |= POLICY_FLAG_VIRTUAL;
-    getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
-            keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
-    return touchResult;
-}
+        // Pointer left virtual key area or another pointer also went down.
+        // Send key cancellation but do not consume the touch yet.
+        // This is useful when the user swipes through from the virtual key area
+        // into the main display surface.
+        mCurrentVirtualKey.down = false;
+        if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+            LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+                    mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+#endif
+            dispatchVirtualKey(when, policyFlags,
+                    AKEY_EVENT_ACTION_UP,
+                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+                            | AKEY_EVENT_FLAG_CANCELED);
+        }
+    }
 
-void TouchInputMapper::suppressSwipeOntoVirtualKeys(nsecs_t when) {
+    if (mLastRawPointerData.touchingIdBits.isEmpty()
+            && !mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+        // Pointer just went down.  Check for virtual key press or off-screen touches.
+        uint32_t id = mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
+        const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
+        if (!isPointInsideSurface(pointer.x, pointer.y)) {
+            // If exactly one pointer went down, check for virtual key hit.
+            // Otherwise we will drop the entire stroke.
+            if (mCurrentRawPointerData.touchingIdBits.count() == 1) {
+                const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y);
+                if (virtualKey) {
+                    mCurrentVirtualKey.down = true;
+                    mCurrentVirtualKey.downTime = when;
+                    mCurrentVirtualKey.keyCode = virtualKey->keyCode;
+                    mCurrentVirtualKey.scanCode = virtualKey->scanCode;
+                    mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey(
+                            when, getDevice(), virtualKey->keyCode, virtualKey->scanCode);
+
+                    if (!mCurrentVirtualKey.ignored) {
+#if DEBUG_VIRTUAL_KEYS
+                        LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+                                mCurrentVirtualKey.keyCode,
+                                mCurrentVirtualKey.scanCode);
+#endif
+                        dispatchVirtualKey(when, policyFlags,
+                                AKEY_EVENT_ACTION_DOWN,
+                                AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
     // Disable all virtual key touches that happen within a short time interval of the
-    // most recent touch.  The idea is to filter out stray virtual key presses when
-    // interacting with the touch screen.
+    // most recent touch within the screen area.  The idea is to filter out stray
+    // virtual key presses when interacting with the touch screen.
     //
     // Problems we're trying to solve:
     //
@@ -3407,37 +3442,44 @@
     //    area and accidentally triggers a virtual key.  This often happens when virtual keys
     //    are layed out below the screen near to where the on screen keyboard's space bar
     //    is displayed.
-    if (mConfig.virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) {
+    if (mConfig.virtualKeyQuietTime > 0 && !mCurrentRawPointerData.touchingIdBits.isEmpty()) {
         mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
     }
+    return false;
+}
+
+void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+        int32_t keyEventAction, int32_t keyEventFlags) {
+    int32_t keyCode = mCurrentVirtualKey.keyCode;
+    int32_t scanCode = mCurrentVirtualKey.scanCode;
+    nsecs_t downTime = mCurrentVirtualKey.downTime;
+    int32_t metaState = mContext->getGlobalMetaState();
+    policyFlags |= POLICY_FLAG_VIRTUAL;
+
+    NotifyKeyArgs args(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+            keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+    getListener()->notifyKey(&args);
 }
 
 void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
-    uint32_t currentPointerCount = mCurrentTouch.pointerCount;
-    uint32_t lastPointerCount = mLastTouch.pointerCount;
-    if (currentPointerCount == 0 && lastPointerCount == 0) {
-        return; // nothing to do!
-    }
-
-    // Update current touch coordinates.
-    float xPrecision, yPrecision;
-    prepareTouches(&xPrecision, &yPrecision);
-
-    // Dispatch motions.
-    BitSet32 currentIdBits = mCurrentTouch.idBits;
-    BitSet32 lastIdBits = mLastTouch.idBits;
+    BitSet32 currentIdBits = mCurrentCookedPointerData.touchingIdBits;
+    BitSet32 lastIdBits = mLastCookedPointerData.touchingIdBits;
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mCurrentTouch.buttonState;
+    int32_t buttonState = mCurrentButtonState;
 
     if (currentIdBits == lastIdBits) {
-        // No pointer id changes so this is a move event.
-        // The dispatcher takes care of batching moves so we don't have to deal with that here.
-        dispatchMotion(when, policyFlags, mTouchSource,
-                AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
-                AMOTION_EVENT_EDGE_FLAG_NONE,
-                mCurrentTouchProperties, mCurrentTouchCoords,
-                mCurrentTouch.idToIndex, currentIdBits, -1,
-                xPrecision, yPrecision, mDownTime);
+        if (!currentIdBits.isEmpty()) {
+            // No pointer id changes so this is a move event.
+            // The listener takes care of batching moves so we don't have to deal with that here.
+            dispatchMotion(when, policyFlags, mTouchSource,
+                    AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
+                    AMOTION_EVENT_EDGE_FLAG_NONE,
+                    mCurrentCookedPointerData.pointerProperties,
+                    mCurrentCookedPointerData.pointerCoords,
+                    mCurrentCookedPointerData.idToIndex,
+                    currentIdBits, -1,
+                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        }
     } else {
         // There may be pointers going up and pointers going down and pointers moving
         // all at the same time.
@@ -3449,23 +3491,28 @@
         // Update last coordinates of pointers that have moved so that we observe the new
         // pointer positions at the same time as other pointers that have just gone up.
         bool moveNeeded = updateMovedPointers(
-                mCurrentTouchProperties, mCurrentTouchCoords, mCurrentTouch.idToIndex,
-                mLastTouchProperties, mLastTouchCoords, mLastTouch.idToIndex,
+                mCurrentCookedPointerData.pointerProperties,
+                mCurrentCookedPointerData.pointerCoords,
+                mCurrentCookedPointerData.idToIndex,
+                mLastCookedPointerData.pointerProperties,
+                mLastCookedPointerData.pointerCoords,
+                mLastCookedPointerData.idToIndex,
                 moveIdBits);
-        if (buttonState != mLastTouch.buttonState) {
+        if (buttonState != mLastButtonState) {
             moveNeeded = true;
         }
 
         // Dispatch pointer up events.
         while (!upIdBits.isEmpty()) {
-            uint32_t upId = upIdBits.firstMarkedBit();
-            upIdBits.clearBit(upId);
+            uint32_t upId = upIdBits.clearFirstMarkedBit();
 
             dispatchMotion(when, policyFlags, mTouchSource,
                     AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, buttonState, 0,
-                    mLastTouchProperties, mLastTouchCoords,
-                    mLastTouch.idToIndex, dispatchedIdBits, upId,
-                    xPrecision, yPrecision, mDownTime);
+                    mLastCookedPointerData.pointerProperties,
+                    mLastCookedPointerData.pointerCoords,
+                    mLastCookedPointerData.idToIndex,
+                    dispatchedIdBits, upId,
+                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
             dispatchedIdBits.clearBit(upId);
         }
 
@@ -3476,15 +3523,16 @@
             LOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mTouchSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0,
-                    mCurrentTouchProperties, mCurrentTouchCoords,
-                    mCurrentTouch.idToIndex, dispatchedIdBits, -1,
-                    xPrecision, yPrecision, mDownTime);
+                    mCurrentCookedPointerData.pointerProperties,
+                    mCurrentCookedPointerData.pointerCoords,
+                    mCurrentCookedPointerData.idToIndex,
+                    dispatchedIdBits, -1,
+                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
         }
 
         // Dispatch pointer down events using the new pointer locations.
         while (!downIdBits.isEmpty()) {
-            uint32_t downId = downIdBits.firstMarkedBit();
-            downIdBits.clearBit(downId);
+            uint32_t downId = downIdBits.clearFirstMarkedBit();
             dispatchedIdBits.markBit(downId);
 
             if (dispatchedIdBits.count() == 1) {
@@ -3494,49 +3542,88 @@
 
             dispatchMotion(when, policyFlags, mTouchSource,
                     AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
-                    mCurrentTouchProperties, mCurrentTouchCoords,
-                    mCurrentTouch.idToIndex, dispatchedIdBits, downId,
-                    xPrecision, yPrecision, mDownTime);
+                    mCurrentCookedPointerData.pointerProperties,
+                    mCurrentCookedPointerData.pointerCoords,
+                    mCurrentCookedPointerData.idToIndex,
+                    dispatchedIdBits, downId,
+                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
         }
     }
-
-    // Update state for next time.
-    for (uint32_t i = 0; i < currentPointerCount; i++) {
-        mLastTouchProperties[i].copyFrom(mCurrentTouchProperties[i]);
-        mLastTouchCoords[i].copyFrom(mCurrentTouchCoords[i]);
-    }
 }
 
-void TouchInputMapper::prepareTouches(float* outXPrecision, float* outYPrecision) {
-    uint32_t currentPointerCount = mCurrentTouch.pointerCount;
-    uint32_t lastPointerCount = mLastTouch.pointerCount;
+void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
+    if (mSentHoverEnter &&
+            (mCurrentCookedPointerData.hoveringIdBits.isEmpty()
+                    || !mCurrentCookedPointerData.touchingIdBits.isEmpty())) {
+        int32_t metaState = getContext()->getGlobalMetaState();
+        dispatchMotion(when, policyFlags, mTouchSource,
+                AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0,
+                mLastCookedPointerData.pointerProperties,
+                mLastCookedPointerData.pointerCoords,
+                mLastCookedPointerData.idToIndex,
+                mLastCookedPointerData.hoveringIdBits, -1,
+                mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+        mSentHoverEnter = false;
+    }
+}
 
-    AutoMutex _l(mLock);
+void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
+    if (mCurrentCookedPointerData.touchingIdBits.isEmpty()
+            && !mCurrentCookedPointerData.hoveringIdBits.isEmpty()) {
+        int32_t metaState = getContext()->getGlobalMetaState();
+        if (!mSentHoverEnter) {
+            dispatchMotion(when, policyFlags, mTouchSource,
+                    AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0,
+                    mCurrentCookedPointerData.pointerProperties,
+                    mCurrentCookedPointerData.pointerCoords,
+                    mCurrentCookedPointerData.idToIndex,
+                    mCurrentCookedPointerData.hoveringIdBits, -1,
+                    mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+            mSentHoverEnter = true;
+        }
 
-    // Walk through the the active pointers and map touch screen coordinates (TouchData) into
-    // display or surface coordinates (PointerCoords) and adjust for display orientation.
+        dispatchMotion(when, policyFlags, mTouchSource,
+                AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0,
+                mCurrentCookedPointerData.pointerProperties,
+                mCurrentCookedPointerData.pointerCoords,
+                mCurrentCookedPointerData.idToIndex,
+                mCurrentCookedPointerData.hoveringIdBits, -1,
+                mOrientedXPrecision, mOrientedYPrecision, mDownTime);
+    }
+}
+
+void TouchInputMapper::cookPointerData() {
+    uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
+
+    mCurrentCookedPointerData.clear();
+    mCurrentCookedPointerData.pointerCount = currentPointerCount;
+    mCurrentCookedPointerData.hoveringIdBits = mCurrentRawPointerData.hoveringIdBits;
+    mCurrentCookedPointerData.touchingIdBits = mCurrentRawPointerData.touchingIdBits;
+
+    // Walk through the the active pointers and map device coordinates onto
+    // surface coordinates and adjust for display orientation.
     for (uint32_t i = 0; i < currentPointerCount; i++) {
-        const PointerData& in = mCurrentTouch.pointers[i];
+        const RawPointerData::Pointer& in = mCurrentRawPointerData.pointers[i];
 
         // ToolMajor and ToolMinor
         float toolMajor, toolMinor;
         switch (mCalibration.toolSizeCalibration) {
         case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
-            toolMajor = in.toolMajor * mLocked.geometricScale;
-            if (mRawAxes.toolMinor.valid) {
-                toolMinor = in.toolMinor * mLocked.geometricScale;
+            toolMajor = in.toolMajor * mGeometricScale;
+            if (mRawPointerAxes.toolMinor.valid) {
+                toolMinor = in.toolMinor * mGeometricScale;
             } else {
                 toolMinor = toolMajor;
             }
             break;
         case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
             toolMajor = in.toolMajor != 0
-                    ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias
+                    ? in.toolMajor * mToolSizeLinearScale + mToolSizeLinearBias
                     : 0;
-            if (mRawAxes.toolMinor.valid) {
+            if (mRawPointerAxes.toolMinor.valid) {
                 toolMinor = in.toolMinor != 0
-                        ? in.toolMinor * mLocked.toolSizeLinearScale
-                                + mLocked.toolSizeLinearBias
+                        ? in.toolMinor * mToolSizeLinearScale
+                                + mToolSizeLinearBias
                         : 0;
             } else {
                 toolMinor = toolMajor;
@@ -3545,8 +3632,8 @@
         case Calibration::TOOL_SIZE_CALIBRATION_AREA:
             if (in.toolMajor != 0) {
                 float diameter = sqrtf(in.toolMajor
-                        * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias);
-                toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias;
+                        * mToolSizeAreaScale + mToolSizeAreaBias);
+                toolMajor = diameter * mToolSizeLinearScale + mToolSizeLinearBias;
             } else {
                 toolMajor = 0;
             }
@@ -3559,8 +3646,9 @@
         }
 
         if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) {
-            toolMajor /= currentPointerCount;
-            toolMinor /= currentPointerCount;
+            uint32_t touchingCount = mCurrentRawPointerData.touchingIdBits.count();
+            toolMajor /= touchingCount;
+            toolMinor /= touchingCount;
         }
 
         // Pressure
@@ -3580,10 +3668,10 @@
         switch (mCalibration.pressureCalibration) {
         case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
         case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-            pressure = rawPressure * mLocked.pressureScale;
+            pressure = rawPressure * mPressureScale;
             break;
         default:
-            pressure = 1;
+            pressure = in.isHovering ? 0 : 1;
             break;
         }
 
@@ -3591,9 +3679,9 @@
         float touchMajor, touchMinor;
         switch (mCalibration.touchSizeCalibration) {
         case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
-            touchMajor = in.touchMajor * mLocked.geometricScale;
-            if (mRawAxes.touchMinor.valid) {
-                touchMinor = in.touchMinor * mLocked.geometricScale;
+            touchMajor = in.touchMajor * mGeometricScale;
+            if (mRawPointerAxes.touchMinor.valid) {
+                touchMinor = in.touchMinor * mGeometricScale;
             } else {
                 touchMinor = touchMajor;
             }
@@ -3619,10 +3707,10 @@
         float size;
         switch (mCalibration.sizeCalibration) {
         case Calibration::SIZE_CALIBRATION_NORMALIZED: {
-            float rawSize = mRawAxes.toolMinor.valid
+            float rawSize = mRawPointerAxes.toolMinor.valid
                     ? avg(in.toolMajor, in.toolMinor)
                     : in.toolMajor;
-            size = rawSize * mLocked.sizeScale;
+            size = rawSize * mSizeScale;
             break;
         }
         default:
@@ -3634,7 +3722,7 @@
         float orientation;
         switch (mCalibration.orientationCalibration) {
         case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-            orientation = in.orientation * mLocked.orientationScale;
+            orientation = in.orientation * mOrientationScale;
             break;
         case Calibration::ORIENTATION_CALIBRATION_VECTOR: {
             int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
@@ -3659,7 +3747,7 @@
         float distance;
         switch (mCalibration.distanceCalibration) {
         case Calibration::DISTANCE_CALIBRATION_SCALED:
-            distance = in.distance * mLocked.distanceScale;
+            distance = in.distance * mDistanceScale;
             break;
         default:
             distance = 0;
@@ -3668,35 +3756,35 @@
         // X and Y
         // Adjust coords for surface orientation.
         float x, y;
-        switch (mLocked.surfaceOrientation) {
+        switch (mSurfaceOrientation) {
         case DISPLAY_ORIENTATION_90:
-            x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
-            y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
+            x = float(in.y - mRawPointerAxes.y.minValue) * mYScale;
+            y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;
             orientation -= M_PI_2;
             if (orientation < - M_PI_2) {
                 orientation += M_PI;
             }
             break;
         case DISPLAY_ORIENTATION_180:
-            x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
-            y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
+            x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;
+            y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;
             break;
         case DISPLAY_ORIENTATION_270:
-            x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
-            y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
+            x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;
+            y = float(in.x - mRawPointerAxes.x.minValue) * mXScale;
             orientation += M_PI_2;
             if (orientation > M_PI_2) {
                 orientation -= M_PI;
             }
             break;
         default:
-            x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
-            y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
+            x = float(in.x - mRawPointerAxes.x.minValue) * mXScale;
+            y = float(in.y - mRawPointerAxes.y.minValue) * mYScale;
             break;
         }
 
         // Write output coords.
-        PointerCoords& out = mCurrentTouchCoords[i];
+        PointerCoords& out = mCurrentCookedPointerData.pointerCoords[i];
         out.clear();
         out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
@@ -3707,19 +3795,18 @@
         out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
         out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
         out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
-        if (distance != 0) {
-            out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
-        }
+        out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
 
         // Write output properties.
-        PointerProperties& properties = mCurrentTouchProperties[i];
+        PointerProperties& properties = mCurrentCookedPointerData.pointerProperties[i];
+        uint32_t id = in.id;
         properties.clear();
-        properties.id = mCurrentTouch.pointers[i].id;
-        properties.toolType = mCurrentTouch.pointers[i].toolType;
-    }
+        properties.id = id;
+        properties.toolType = in.toolType;
 
-    *outXPrecision = mLocked.orientedXPrecision;
-    *outYPrecision = mLocked.orientedYPrecision;
+        // Write id index.
+        mCurrentCookedPointerData.idToIndex[id] = i;
+    }
 }
 
 void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags,
@@ -3782,7 +3869,7 @@
 
     // Send events!
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t buttonState = mCurrentTouch.buttonState;
+    int32_t buttonState = mCurrentButtonState;
 
     // Update last coordinates of pointers that have moved so that we observe the new
     // pointer positions at the same time as other pointers that have just gone up.
@@ -3803,7 +3890,7 @@
                 mPointerGesture.lastGestureProperties,
                 mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex,
                 movedGestureIdBits);
-        if (buttonState != mLastTouch.buttonState) {
+        if (buttonState != mLastButtonState) {
             moveNeeded = true;
         }
     }
@@ -3830,8 +3917,7 @@
                         & ~mPointerGesture.currentGestureIdBits.value;
             }
             while (!upGestureIdBits.isEmpty()) {
-                uint32_t id = upGestureIdBits.firstMarkedBit();
-                upGestureIdBits.clearBit(id);
+                uint32_t id = upGestureIdBits.clearFirstMarkedBit();
 
                 dispatchMotion(when, policyFlags, mPointerSource,
                         AMOTION_EVENT_ACTION_POINTER_UP, 0,
@@ -3861,8 +3947,7 @@
         BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value
                 & ~dispatchedGestureIdBits.value);
         while (!downGestureIdBits.isEmpty()) {
-            uint32_t id = downGestureIdBits.firstMarkedBit();
-            downGestureIdBits.clearBit(id);
+            uint32_t id = downGestureIdBits.clearFirstMarkedBit();
             dispatchedGestureIdBits.markBit(id);
 
             if (dispatchedGestureIdBits.count() == 1) {
@@ -3906,10 +3991,11 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
-        getDispatcher()->notifyMotion(when, getDeviceId(), mPointerSource, policyFlags,
+        NotifyMotionArgs args(when, getDeviceId(), mPointerSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
                 metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                 1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime);
+        getListener()->notifyMotion(&args);
     }
 
     // Update state.
@@ -3919,8 +4005,7 @@
     } else {
         mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits;
         for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) {
-            uint32_t id = idBits.firstMarkedBit();
-            idBits.clearBit(id);
+            uint32_t id = idBits.clearFirstMarkedBit();
             uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
             mPointerGesture.lastGestureProperties[index].copyFrom(
                     mPointerGesture.currentGestureProperties[index]);
@@ -3936,8 +4021,6 @@
     *outCancelPreviousGesture = false;
     *outFinishPreviousGesture = false;
 
-    AutoMutex _l(mLock);
-
     // Handle TAP timeout.
     if (isTimeout) {
 #if DEBUG_GESTURES
@@ -3973,16 +4056,14 @@
     {
         VelocityTracker::Position positions[MAX_POINTERS];
         uint32_t count = 0;
-        for (BitSet32 idBits(mCurrentTouch.idBits); !idBits.isEmpty(); count++) {
-            uint32_t id = idBits.firstMarkedBit();
-            idBits.clearBit(id);
-            uint32_t index = mCurrentTouch.idToIndex[id];
-            positions[count].x = mCurrentTouch.pointers[index].x
-                    * mLocked.pointerGestureXMovementScale;
-            positions[count].y = mCurrentTouch.pointers[index].y
-                    * mLocked.pointerGestureYMovementScale;
+        for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); count++) {
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
+            positions[count].x = pointer.x * mPointerGestureXMovementScale;
+            positions[count].y = pointer.y * mPointerGestureYMovementScale;
         }
-        mPointerGesture.velocityTracker.addMovement(when, mCurrentTouch.idBits, positions);
+        mPointerGesture.velocityTracker.addMovement(when,
+                mCurrentRawPointerData.touchingIdBits, positions);
     }
 
     // Pick a new active touch id if needed.
@@ -3994,20 +4075,25 @@
     int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
     int32_t activeTouchId = lastActiveTouchId;
     if (activeTouchId < 0) {
-        if (!mCurrentTouch.idBits.isEmpty()) {
+        if (!mCurrentRawPointerData.touchingIdBits.isEmpty()) {
             activeTouchChanged = true;
-            activeTouchId = mPointerGesture.activeTouchId = mCurrentTouch.idBits.firstMarkedBit();
+            activeTouchId = mPointerGesture.activeTouchId =
+                    mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
             mPointerGesture.firstTouchTime = when;
         }
-    } else if (!mCurrentTouch.idBits.hasBit(activeTouchId)) {
+    } else if (!mCurrentRawPointerData.touchingIdBits.hasBit(activeTouchId)) {
         activeTouchChanged = true;
-        if (!mCurrentTouch.idBits.isEmpty()) {
-            activeTouchId = mPointerGesture.activeTouchId = mCurrentTouch.idBits.firstMarkedBit();
+        if (!mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+            activeTouchId = mPointerGesture.activeTouchId =
+                    mCurrentRawPointerData.touchingIdBits.firstMarkedBit();
         } else {
             activeTouchId = mPointerGesture.activeTouchId = -1;
         }
     }
 
+    uint32_t currentTouchingPointerCount = mCurrentRawPointerData.touchingIdBits.count();
+    uint32_t lastTouchingPointerCount = mLastRawPointerData.touchingIdBits.count();
+
     // Determine whether we are in quiet time.
     bool isQuietTime = false;
     if (activeTouchId < 0) {
@@ -4018,14 +4104,14 @@
             if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS
                     || mPointerGesture.lastGestureMode == PointerGesture::SWIPE
                     || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM)
-                    && mCurrentTouch.pointerCount < 2) {
+                    && currentTouchingPointerCount < 2) {
                 // Enter quiet time when exiting swipe or freeform state.
                 // This is to prevent accidentally entering the hover state and flinging the
                 // pointer when finishing a swipe and there is still one pointer left onscreen.
                 isQuietTime = true;
             } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG
-                    && mCurrentTouch.pointerCount >= 2
-                    && !isPointerDown(mCurrentTouch.buttonState)) {
+                    && currentTouchingPointerCount >= 2
+                    && !isPointerDown(mCurrentButtonState)) {
                 // Enter quiet time when releasing the button and there are still two or more
                 // fingers down.  This may indicate that one finger was used to press the button
                 // but it has not gone up yet.
@@ -4053,7 +4139,7 @@
         mPointerGesture.currentGestureIdBits.clear();
 
         mPointerGesture.pointerVelocityControl.reset();
-    } else if (isPointerDown(mCurrentTouch.buttonState)) {
+    } else if (isPointerDown(mCurrentButtonState)) {
         // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
         // The pointer follows the active touch point.
         // Emit DOWN, MOVE, UP events at the pointer location.
@@ -4069,7 +4155,7 @@
         // being dragged.
 #if DEBUG_GESTURES
         LOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
-                "currentTouchPointerCount=%d", activeTouchId, mCurrentTouch.pointerCount);
+                "currentTouchingPointerCount=%d", activeTouchId, currentTouchingPointerCount);
 #endif
         // Reset state when just starting.
         if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) {
@@ -4079,11 +4165,11 @@
 
         // Switch pointers if needed.
         // Find the fastest pointer and follow it.
-        if (activeTouchId >= 0 && mCurrentTouch.pointerCount > 1) {
+        if (activeTouchId >= 0 && currentTouchingPointerCount > 1) {
             int32_t bestId = -1;
             float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
-            for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                uint32_t id = mCurrentTouch.pointers[i].id;
+            for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) {
+                uint32_t id = idBits.clearFirstMarkedBit();
                 float vx, vy;
                 if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
                     float speed = hypotf(vx, vy);
@@ -4103,17 +4189,15 @@
             }
         }
 
-        if (activeTouchId >= 0 && mLastTouch.idBits.hasBit(activeTouchId)) {
-            const PointerData& currentPointer =
-                    mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
-            const PointerData& lastPointer =
-                    mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]];
-            float deltaX = (currentPointer.x - lastPointer.x)
-                    * mLocked.pointerGestureXMovementScale;
-            float deltaY = (currentPointer.y - lastPointer.y)
-                    * mLocked.pointerGestureYMovementScale;
+        if (activeTouchId >= 0 && mLastRawPointerData.touchingIdBits.hasBit(activeTouchId)) {
+            const RawPointerData::Pointer& currentPointer =
+                    mCurrentRawPointerData.pointerForId(activeTouchId);
+            const RawPointerData::Pointer& lastPointer =
+                    mLastRawPointerData.pointerForId(activeTouchId);
+            float deltaX = (currentPointer.x - lastPointer.x) * mPointerGestureXMovementScale;
+            float deltaY = (currentPointer.y - lastPointer.y) * mPointerGestureYMovementScale;
 
-            rotateDelta(mLocked.surfaceOrientation, &deltaX, &deltaY);
+            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
             mPointerGesture.pointerVelocityControl.move(when, &deltaX, &deltaY);
 
             // Move the pointer using a relative motion.
@@ -4138,7 +4222,7 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-    } else if (mCurrentTouch.pointerCount == 0) {
+    } else if (currentTouchingPointerCount == 0) {
         // Case 3. No fingers down and button is not pressed. (NEUTRAL)
         if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
             *outFinishPreviousGesture = true;
@@ -4149,7 +4233,7 @@
         bool tapped = false;
         if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER
                 || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG)
-                && mLastTouch.pointerCount == 1) {
+                && lastTouchingPointerCount == 1) {
             if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
                 float x, y;
                 mPointerController->getPosition(&x, &y);
@@ -4209,7 +4293,7 @@
             mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
         }
-    } else if (mCurrentTouch.pointerCount == 1) {
+    } else if (currentTouchingPointerCount == 1) {
         // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
         // The pointer follows the active touch point.
         // When in HOVER, emit HOVER_MOVE events at the pointer location.
@@ -4241,17 +4325,17 @@
             mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG;
         }
 
-        if (mLastTouch.idBits.hasBit(activeTouchId)) {
-            const PointerData& currentPointer =
-                    mCurrentTouch.pointers[mCurrentTouch.idToIndex[activeTouchId]];
-            const PointerData& lastPointer =
-                    mLastTouch.pointers[mLastTouch.idToIndex[activeTouchId]];
+        if (mLastRawPointerData.touchingIdBits.hasBit(activeTouchId)) {
+            const RawPointerData::Pointer& currentPointer =
+                    mCurrentRawPointerData.pointerForId(activeTouchId);
+            const RawPointerData::Pointer& lastPointer =
+                    mLastRawPointerData.pointerForId(activeTouchId);
             float deltaX = (currentPointer.x - lastPointer.x)
-                    * mLocked.pointerGestureXMovementScale;
+                    * mPointerGestureXMovementScale;
             float deltaY = (currentPointer.y - lastPointer.y)
-                    * mLocked.pointerGestureYMovementScale;
+                    * mPointerGestureYMovementScale;
 
-            rotateDelta(mLocked.surfaceOrientation, &deltaX, &deltaY);
+            rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
             mPointerGesture.pointerVelocityControl.move(when, &deltaX, &deltaY);
 
             // Move the pointer using a relative motion.
@@ -4294,7 +4378,7 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
                 down ? 1.0f : 0.0f);
 
-        if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+        if (lastTouchingPointerCount == 0 && currentTouchingPointerCount != 0) {
             mPointerGesture.resetTap();
             mPointerGesture.tapDownTime = when;
             mPointerGesture.tapX = x;
@@ -4322,7 +4406,7 @@
                 && mPointerGesture.lastGestureMode != PointerGesture::SWIPE
                 && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) {
             *outFinishPreviousGesture = true;
-        } else if (!settled && mCurrentTouch.pointerCount > mLastTouch.pointerCount) {
+        } else if (!settled && currentTouchingPointerCount > lastTouchingPointerCount) {
             // Additional pointers have gone down but not yet settled.
             // Reset the gesture.
 #if DEBUG_GESTURES
@@ -4350,33 +4434,31 @@
                             + mConfig.pointerGestureMultitouchSettleInterval - when)
                             * 0.000001f);
 #endif
-            mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
+            mCurrentRawPointerData.getCentroidOfTouchingPointers(
+                    &mPointerGesture.referenceTouchX,
                     &mPointerGesture.referenceTouchY);
             mPointerController->getPosition(&mPointerGesture.referenceGestureX,
                     &mPointerGesture.referenceGestureY);
         }
 
         // Clear the reference deltas for fingers not yet included in the reference calculation.
-        for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
-                !idBits.isEmpty(); ) {
-            uint32_t id = idBits.firstMarkedBit();
-            idBits.clearBit(id);
-
+        for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits.value
+                & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) {
+            uint32_t id = idBits.clearFirstMarkedBit();
             mPointerGesture.referenceDeltas[id].dx = 0;
             mPointerGesture.referenceDeltas[id].dy = 0;
         }
-        mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
+        mPointerGesture.referenceIdBits = mCurrentRawPointerData.touchingIdBits;
 
         // Add delta for all fingers and calculate a common movement delta.
         float commonDeltaX = 0, commonDeltaY = 0;
-        BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
+        BitSet32 commonIdBits(mLastRawPointerData.touchingIdBits.value
+                & mCurrentRawPointerData.touchingIdBits.value);
         for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
             bool first = (idBits == commonIdBits);
-            uint32_t id = idBits.firstMarkedBit();
-            idBits.clearBit(id);
-
-            const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
-            const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
+            uint32_t id = idBits.clearFirstMarkedBit();
+            const RawPointerData::Pointer& cpd = mCurrentRawPointerData.pointerForId(id);
+            const RawPointerData::Pointer& lpd = mLastRawPointerData.pointerForId(id);
             PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
             delta.dx += cpd.x - lpd.x;
             delta.dy += cpd.y - lpd.y;
@@ -4395,12 +4477,10 @@
             float dist[MAX_POINTER_ID + 1];
             int32_t distOverThreshold = 0;
             for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.firstMarkedBit();
-                idBits.clearBit(id);
-
+                uint32_t id = idBits.clearFirstMarkedBit();
                 PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale,
-                        delta.dy * mLocked.pointerGestureYZoomScale);
+                dist[id] = hypotf(delta.dx * mPointerGestureXZoomScale,
+                        delta.dy * mPointerGestureYZoomScale);
                 if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) {
                     distOverThreshold += 1;
                 }
@@ -4409,71 +4489,74 @@
             // Only transition when at least two pointers have moved further than
             // the minimum distance threshold.
             if (distOverThreshold >= 2) {
-                float d;
-                if (mCurrentTouch.pointerCount > 2) {
+                if (currentTouchingPointerCount > 2) {
                     // There are more than two pointers, switch to FREEFORM.
 #if DEBUG_GESTURES
                     LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
-                            mCurrentTouch.pointerCount);
-#endif
-                    *outCancelPreviousGesture = true;
-                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-                } else if (((d = distance(
-                        mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
-                        mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
-                                > mLocked.pointerGestureMaxSwipeWidth)) {
-                    // There are two pointers but they are too far apart for a SWIPE,
-                    // switch to FREEFORM.
-#if DEBUG_GESTURES
-                    LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
-                            d, mLocked.pointerGestureMaxSwipeWidth);
+                            currentTouchingPointerCount);
 #endif
                     *outCancelPreviousGesture = true;
                     mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
                 } else {
-                    // There are two pointers.  Wait for both pointers to start moving
-                    // before deciding whether this is a SWIPE or FREEFORM gesture.
-                    uint32_t id1 = mCurrentTouch.pointers[0].id;
-                    uint32_t id2 = mCurrentTouch.pointers[1].id;
-                    float dist1 = dist[id1];
-                    float dist2 = dist[id2];
-                    if (dist1 >= mConfig.pointerGestureMultitouchMinDistance
-                            && dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
-                        // Calculate the dot product of the displacement vectors.
-                        // When the vectors are oriented in approximately the same direction,
-                        // the angle betweeen them is near zero and the cosine of the angle
-                        // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
-                        PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
-                        PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
-                        float dx1 = delta1.dx * mLocked.pointerGestureXZoomScale;
-                        float dy1 = delta1.dy * mLocked.pointerGestureYZoomScale;
-                        float dx2 = delta2.dx * mLocked.pointerGestureXZoomScale;
-                        float dy2 = delta2.dy * mLocked.pointerGestureYZoomScale;
-                        float dot = dx1 * dx2 + dy1 * dy2;
-                        float cosine = dot / (dist1 * dist2); // denominator always > 0
-                        if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
-                            // Pointers are moving in the same direction.  Switch to SWIPE.
+                    // There are exactly two pointers.
+                    BitSet32 idBits(mCurrentRawPointerData.touchingIdBits);
+                    uint32_t id1 = idBits.clearFirstMarkedBit();
+                    uint32_t id2 = idBits.firstMarkedBit();
+                    const RawPointerData::Pointer& p1 = mCurrentRawPointerData.pointerForId(id1);
+                    const RawPointerData::Pointer& p2 = mCurrentRawPointerData.pointerForId(id2);
+                    float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y);
+                    if (mutualDistance > mPointerGestureMaxSwipeWidth) {
+                        // There are two pointers but they are too far apart for a SWIPE,
+                        // switch to FREEFORM.
 #if DEBUG_GESTURES
-                            LOGD("Gestures: PRESS transitioned to SWIPE, "
-                                    "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
-                                    "cosine %0.3f >= %0.3f",
-                                    dist1, mConfig.pointerGestureMultitouchMinDistance,
-                                    dist2, mConfig.pointerGestureMultitouchMinDistance,
-                                    cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
+                        LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+                                mutualDistance, mPointerGestureMaxSwipeWidth);
 #endif
-                            mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
-                        } else {
-                            // Pointers are moving in different directions.  Switch to FREEFORM.
+                        *outCancelPreviousGesture = true;
+                        mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                    } else {
+                        // There are two pointers.  Wait for both pointers to start moving
+                        // before deciding whether this is a SWIPE or FREEFORM gesture.
+                        float dist1 = dist[id1];
+                        float dist2 = dist[id2];
+                        if (dist1 >= mConfig.pointerGestureMultitouchMinDistance
+                                && dist2 >= mConfig.pointerGestureMultitouchMinDistance) {
+                            // Calculate the dot product of the displacement vectors.
+                            // When the vectors are oriented in approximately the same direction,
+                            // the angle betweeen them is near zero and the cosine of the angle
+                            // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
+                            PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+                            PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+                            float dx1 = delta1.dx * mPointerGestureXZoomScale;
+                            float dy1 = delta1.dy * mPointerGestureYZoomScale;
+                            float dx2 = delta2.dx * mPointerGestureXZoomScale;
+                            float dy2 = delta2.dy * mPointerGestureYZoomScale;
+                            float dot = dx1 * dx2 + dy1 * dy2;
+                            float cosine = dot / (dist1 * dist2); // denominator always > 0
+                            if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
+                                // Pointers are moving in the same direction.  Switch to SWIPE.
 #if DEBUG_GESTURES
-                            LOGD("Gestures: PRESS transitioned to FREEFORM, "
-                                    "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
-                                    "cosine %0.3f < %0.3f",
-                                    dist1, mConfig.pointerGestureMultitouchMinDistance,
-                                    dist2, mConfig.pointerGestureMultitouchMinDistance,
-                                    cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
+                                LOGD("Gestures: PRESS transitioned to SWIPE, "
+                                        "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                        "cosine %0.3f >= %0.3f",
+                                        dist1, mConfig.pointerGestureMultitouchMinDistance,
+                                        dist2, mConfig.pointerGestureMultitouchMinDistance,
+                                        cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
 #endif
-                            *outCancelPreviousGesture = true;
-                            mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                                mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+                            } else {
+                                // Pointers are moving in different directions.  Switch to FREEFORM.
+#if DEBUG_GESTURES
+                                LOGD("Gestures: PRESS transitioned to FREEFORM, "
+                                        "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                        "cosine %0.3f < %0.3f",
+                                        dist1, mConfig.pointerGestureMultitouchMinDistance,
+                                        dist2, mConfig.pointerGestureMultitouchMinDistance,
+                                        cosine, mConfig.pointerGestureSwipeTransitionAngleCosine);
+#endif
+                                *outCancelPreviousGesture = true;
+                                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                            }
                         }
                     }
                 }
@@ -4481,10 +4564,10 @@
         } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
             // Switch from SWIPE to FREEFORM if additional pointers go down.
             // Cancel previous gesture.
-            if (mCurrentTouch.pointerCount > 2) {
+            if (currentTouchingPointerCount > 2) {
 #if DEBUG_GESTURES
                 LOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
-                        mCurrentTouch.pointerCount);
+                        currentTouchingPointerCount);
 #endif
                 *outCancelPreviousGesture = true;
                 mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
@@ -4496,9 +4579,7 @@
         if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
                 && (commonDeltaX || commonDeltaY)) {
             for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
-                uint32_t id = idBits.firstMarkedBit();
-                idBits.clearBit(id);
-
+                uint32_t id = idBits.clearFirstMarkedBit();
                 PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
                 delta.dx = 0;
                 delta.dy = 0;
@@ -4507,10 +4588,10 @@
             mPointerGesture.referenceTouchX += commonDeltaX;
             mPointerGesture.referenceTouchY += commonDeltaY;
 
-            commonDeltaX *= mLocked.pointerGestureXMovementScale;
-            commonDeltaY *= mLocked.pointerGestureYMovementScale;
+            commonDeltaX *= mPointerGestureXMovementScale;
+            commonDeltaY *= mPointerGestureYMovementScale;
 
-            rotateDelta(mLocked.surfaceOrientation, &commonDeltaX, &commonDeltaY);
+            rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
             mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
 
             mPointerGesture.referenceGestureX += commonDeltaX;
@@ -4524,7 +4605,7 @@
 #if DEBUG_GESTURES
             LOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
                     "activeGestureId=%d, currentTouchPointerCount=%d",
-                    activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount);
+                    activeTouchId, mPointerGesture.activeGestureId, currentTouchingPointerCount);
 #endif
             LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
@@ -4546,7 +4627,7 @@
 #if DEBUG_GESTURES
             LOGD("Gestures: FREEFORM activeTouchId=%d,"
                     "activeGestureId=%d, currentTouchPointerCount=%d",
-                    activeTouchId, mPointerGesture.activeGestureId, mCurrentTouch.pointerCount);
+                    activeTouchId, mPointerGesture.activeGestureId, currentTouchingPointerCount);
 #endif
             LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
@@ -4568,15 +4649,16 @@
             } else {
                 // Otherwise, assume we mapped all touches from the previous frame.
                 // Reuse all mappings that are still applicable.
-                mappedTouchIdBits.value = mLastTouch.idBits.value & mCurrentTouch.idBits.value;
+                mappedTouchIdBits.value = mLastRawPointerData.touchingIdBits.value
+                        & mCurrentRawPointerData.touchingIdBits.value;
                 usedGestureIdBits = mPointerGesture.lastGestureIdBits;
 
                 // Check whether we need to choose a new active gesture id because the
                 // current went went up.
-                for (BitSet32 upTouchIdBits(mLastTouch.idBits.value & ~mCurrentTouch.idBits.value);
+                for (BitSet32 upTouchIdBits(mLastRawPointerData.touchingIdBits.value
+                        & ~mCurrentRawPointerData.touchingIdBits.value);
                         !upTouchIdBits.isEmpty(); ) {
-                    uint32_t upTouchId = upTouchIdBits.firstMarkedBit();
-                    upTouchIdBits.clearBit(upTouchId);
+                    uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit();
                     uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId];
                     if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) {
                         mPointerGesture.activeGestureId = -1;
@@ -4593,12 +4675,12 @@
                     mPointerGesture.activeGestureId);
 #endif
 
-            for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                uint32_t touchId = mCurrentTouch.pointers[i].id;
+            BitSet32 idBits(mCurrentRawPointerData.touchingIdBits);
+            for (uint32_t i = 0; i < currentTouchingPointerCount; i++) {
+                uint32_t touchId = idBits.clearFirstMarkedBit();
                 uint32_t gestureId;
                 if (!mappedTouchIdBits.hasBit(touchId)) {
-                    gestureId = usedGestureIdBits.firstUnmarkedBit();
-                    usedGestureIdBits.markBit(gestureId);
+                    gestureId = usedGestureIdBits.markFirstUnmarkedBit();
                     mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
 #if DEBUG_GESTURES
                     LOGD("Gestures: FREEFORM "
@@ -4616,11 +4698,13 @@
                 mPointerGesture.currentGestureIdBits.markBit(gestureId);
                 mPointerGesture.currentGestureIdToIndex[gestureId] = i;
 
-                float deltaX = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX)
-                        * mLocked.pointerGestureXZoomScale;
-                float deltaY = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY)
-                        * mLocked.pointerGestureYZoomScale;
-                rotateDelta(mLocked.surfaceOrientation, &deltaX, &deltaY);
+                const RawPointerData::Pointer& pointer =
+                        mCurrentRawPointerData.pointerForId(touchId);
+                float deltaX = (pointer.x - mPointerGesture.referenceTouchX)
+                        * mPointerGestureXZoomScale;
+                float deltaY = (pointer.y - mPointerGesture.referenceTouchY)
+                        * mPointerGestureYZoomScale;
+                rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
 
                 mPointerGesture.currentGestureProperties[i].clear();
                 mPointerGesture.currentGestureProperties[i].id = gestureId;
@@ -4646,7 +4730,7 @@
         }
     }
 
-    mPointerController->setButtonState(mCurrentTouch.buttonState);
+    mPointerController->setButtonState(mCurrentButtonState);
 
 #if DEBUG_GESTURES
     LOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
@@ -4656,8 +4740,7 @@
             mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
             mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
     for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
+        uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
         const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
         const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
@@ -4669,8 +4752,7 @@
                 coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
     }
     for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
+        uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
         const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
         const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
@@ -4694,8 +4776,7 @@
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
     while (!idBits.isEmpty()) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
+        uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t index = idToIndex[id];
         pointerProperties[pointerCount].copyFrom(properties[index]);
         pointerCoords[pointerCount].copyFrom(coords[index]);
@@ -4723,9 +4804,10 @@
         }
     }
 
-    getDispatcher()->notifyMotion(when, getDeviceId(), source, policyFlags,
+    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
             action, flags, metaState, buttonState, edgeFlags,
             pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime);
+    getListener()->notifyMotion(&args);
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -4734,9 +4816,7 @@
         BitSet32 idBits) const {
     bool changed = false;
     while (!idBits.isEmpty()) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
-
+        uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t inIndex = inIdToIndex[id];
         uint32_t outIndex = outIdToIndex[id];
 
@@ -4759,24 +4839,21 @@
 }
 
 void TouchInputMapper::fadePointer() {
-    { // acquire lock
-        AutoMutex _l(mLock);
-        if (mPointerController != NULL) {
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-        }
-    } // release lock
+    if (mPointerController != NULL) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+    }
 }
 
-bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
-    return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue
-            && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue;
+bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+    return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue
+            && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue;
 }
 
-const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked(
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(
         int32_t x, int32_t y) {
-    size_t numVirtualKeys = mLocked.virtualKeys.size();
+    size_t numVirtualKeys = mVirtualKeys.size();
     for (size_t i = 0; i < numVirtualKeys; i++) {
-        const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+        const VirtualKey& virtualKey = mVirtualKeys[i];
 
 #if DEBUG_VIRTUAL_KEYS
         LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
@@ -4795,43 +4872,59 @@
     return NULL;
 }
 
-void TouchInputMapper::calculatePointerIds() {
-    uint32_t currentPointerCount = mCurrentTouch.pointerCount;
-    uint32_t lastPointerCount = mLastTouch.pointerCount;
+void TouchInputMapper::assignPointerIds() {
+    uint32_t currentPointerCount = mCurrentRawPointerData.pointerCount;
+    uint32_t lastPointerCount = mLastRawPointerData.pointerCount;
+
+    mCurrentRawPointerData.clearIdBits();
 
     if (currentPointerCount == 0) {
         // No pointers to assign.
-        mCurrentTouch.idBits.clear();
-    } else if (lastPointerCount == 0) {
-        // All pointers are new.
-        mCurrentTouch.idBits.clear();
-        for (uint32_t i = 0; i < currentPointerCount; i++) {
-            mCurrentTouch.pointers[i].id = i;
-            mCurrentTouch.idToIndex[i] = i;
-            mCurrentTouch.idBits.markBit(i);
-        }
-    } else if (currentPointerCount == 1 && lastPointerCount == 1) {
-        // Only one pointer and no change in count so it must have the same id as before.
-        uint32_t id = mLastTouch.pointers[0].id;
-        mCurrentTouch.pointers[0].id = id;
-        mCurrentTouch.idToIndex[id] = 0;
-        mCurrentTouch.idBits.value = BitSet32::valueForBit(id);
-    } else {
-        // General case.
-        // We build a heap of squared euclidean distances between current and last pointers
-        // associated with the current and last pointer indices.  Then, we find the best
-        // match (by distance) for each current pointer.
-        PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+        return;
+    }
 
-        uint32_t heapSize = 0;
-        for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
-                currentPointerIndex++) {
-            for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
-                    lastPointerIndex++) {
-                int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x
-                        - mLastTouch.pointers[lastPointerIndex].x;
-                int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y
-                        - mLastTouch.pointers[lastPointerIndex].y;
+    if (lastPointerCount == 0) {
+        // All pointers are new.
+        for (uint32_t i = 0; i < currentPointerCount; i++) {
+            uint32_t id = i;
+            mCurrentRawPointerData.pointers[i].id = id;
+            mCurrentRawPointerData.idToIndex[id] = i;
+            mCurrentRawPointerData.markIdBit(id, mCurrentRawPointerData.isHovering(i));
+        }
+        return;
+    }
+
+    if (currentPointerCount == 1 && lastPointerCount == 1
+            && mCurrentRawPointerData.pointers[0].toolType
+                    == mLastRawPointerData.pointers[0].toolType) {
+        // Only one pointer and no change in count so it must have the same id as before.
+        uint32_t id = mLastRawPointerData.pointers[0].id;
+        mCurrentRawPointerData.pointers[0].id = id;
+        mCurrentRawPointerData.idToIndex[id] = 0;
+        mCurrentRawPointerData.markIdBit(id, mCurrentRawPointerData.isHovering(0));
+        return;
+    }
+
+    // General case.
+    // We build a heap of squared euclidean distances between current and last pointers
+    // associated with the current and last pointer indices.  Then, we find the best
+    // match (by distance) for each current pointer.
+    // The pointers must have the same tool type but it is possible for them to
+    // transition from hovering to touching or vice-versa while retaining the same id.
+    PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+    uint32_t heapSize = 0;
+    for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+            currentPointerIndex++) {
+        for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+                lastPointerIndex++) {
+            const RawPointerData::Pointer& currentPointer =
+                    mCurrentRawPointerData.pointers[currentPointerIndex];
+            const RawPointerData::Pointer& lastPointer =
+                    mLastRawPointerData.pointers[lastPointerIndex];
+            if (currentPointer.toolType == lastPointer.toolType) {
+                int64_t deltaX = currentPointer.x - lastPointer.x;
+                int64_t deltaY = currentPointer.y - lastPointer.y;
 
                 uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
 
@@ -4842,193 +4935,174 @@
                 heapSize += 1;
             }
         }
+    }
 
-        // Heapify
-        for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
-            startIndex -= 1;
-            for (uint32_t parentIndex = startIndex; ;) {
-                uint32_t childIndex = parentIndex * 2 + 1;
-                if (childIndex >= heapSize) {
-                    break;
-                }
-
-                if (childIndex + 1 < heapSize
-                        && heap[childIndex + 1].distance < heap[childIndex].distance) {
-                    childIndex += 1;
-                }
-
-                if (heap[parentIndex].distance <= heap[childIndex].distance) {
-                    break;
-                }
-
-                swap(heap[parentIndex], heap[childIndex]);
-                parentIndex = childIndex;
-            }
-        }
-
-#if DEBUG_POINTER_ASSIGNMENT
-        LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
-        for (size_t i = 0; i < heapSize; i++) {
-            LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
-                    i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
-                    heap[i].distance);
-        }
-#endif
-
-        // Pull matches out by increasing order of distance.
-        // To avoid reassigning pointers that have already been matched, the loop keeps track
-        // of which last and current pointers have been matched using the matchedXXXBits variables.
-        // It also tracks the used pointer id bits.
-        BitSet32 matchedLastBits(0);
-        BitSet32 matchedCurrentBits(0);
-        BitSet32 usedIdBits(0);
-        bool first = true;
-        for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
-            for (;;) {
-                if (first) {
-                    // The first time through the loop, we just consume the root element of
-                    // the heap (the one with smallest distance).
-                    first = false;
-                } else {
-                    // Previous iterations consumed the root element of the heap.
-                    // Pop root element off of the heap (sift down).
-                    heapSize -= 1;
-                    LOG_ASSERT(heapSize > 0);
-
-                    // Sift down.
-                    heap[0] = heap[heapSize];
-                    for (uint32_t parentIndex = 0; ;) {
-                        uint32_t childIndex = parentIndex * 2 + 1;
-                        if (childIndex >= heapSize) {
-                            break;
-                        }
-
-                        if (childIndex + 1 < heapSize
-                                && heap[childIndex + 1].distance < heap[childIndex].distance) {
-                            childIndex += 1;
-                        }
-
-                        if (heap[parentIndex].distance <= heap[childIndex].distance) {
-                            break;
-                        }
-
-                        swap(heap[parentIndex], heap[childIndex]);
-                        parentIndex = childIndex;
-                    }
-
-#if DEBUG_POINTER_ASSIGNMENT
-                    LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
-                    for (size_t i = 0; i < heapSize; i++) {
-                        LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
-                                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
-                                heap[i].distance);
-                    }
-#endif
-                }
-
-                uint32_t currentPointerIndex = heap[0].currentPointerIndex;
-                if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
-
-                uint32_t lastPointerIndex = heap[0].lastPointerIndex;
-                if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
-
-                matchedCurrentBits.markBit(currentPointerIndex);
-                matchedLastBits.markBit(lastPointerIndex);
-
-                uint32_t id = mLastTouch.pointers[lastPointerIndex].id;
-                mCurrentTouch.pointers[currentPointerIndex].id = id;
-                mCurrentTouch.idToIndex[id] = currentPointerIndex;
-                usedIdBits.markBit(id);
-
-#if DEBUG_POINTER_ASSIGNMENT
-                LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
-                        lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
+    // Heapify
+    for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+        startIndex -= 1;
+        for (uint32_t parentIndex = startIndex; ;) {
+            uint32_t childIndex = parentIndex * 2 + 1;
+            if (childIndex >= heapSize) {
                 break;
             }
+
+            if (childIndex + 1 < heapSize
+                    && heap[childIndex + 1].distance < heap[childIndex].distance) {
+                childIndex += 1;
+            }
+
+            if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                break;
+            }
+
+            swap(heap[parentIndex], heap[childIndex]);
+            parentIndex = childIndex;
         }
-
-        // Assign fresh ids to new pointers.
-        if (currentPointerCount > lastPointerCount) {
-            for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
-                uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
-                uint32_t id = usedIdBits.firstUnmarkedBit();
-
-                mCurrentTouch.pointers[currentPointerIndex].id = id;
-                mCurrentTouch.idToIndex[id] = currentPointerIndex;
-                usedIdBits.markBit(id);
+    }
 
 #if DEBUG_POINTER_ASSIGNMENT
-                LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
-                        currentPointerIndex, id);
+    LOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
+    for (size_t i = 0; i < heapSize; i++) {
+        LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                heap[i].distance);
+    }
 #endif
 
-                if (--i == 0) break; // done
-                matchedCurrentBits.markBit(currentPointerIndex);
-            }
-        }
+    // Pull matches out by increasing order of distance.
+    // To avoid reassigning pointers that have already been matched, the loop keeps track
+    // of which last and current pointers have been matched using the matchedXXXBits variables.
+    // It also tracks the used pointer id bits.
+    BitSet32 matchedLastBits(0);
+    BitSet32 matchedCurrentBits(0);
+    BitSet32 usedIdBits(0);
+    bool first = true;
+    for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) {
+        while (heapSize > 0) {
+            if (first) {
+                // The first time through the loop, we just consume the root element of
+                // the heap (the one with smallest distance).
+                first = false;
+            } else {
+                // Previous iterations consumed the root element of the heap.
+                // Pop root element off of the heap (sift down).
+                heap[0] = heap[heapSize];
+                for (uint32_t parentIndex = 0; ;) {
+                    uint32_t childIndex = parentIndex * 2 + 1;
+                    if (childIndex >= heapSize) {
+                        break;
+                    }
 
-        // Fix id bits.
-        mCurrentTouch.idBits = usedIdBits;
+                    if (childIndex + 1 < heapSize
+                            && heap[childIndex + 1].distance < heap[childIndex].distance) {
+                        childIndex += 1;
+                    }
+
+                    if (heap[parentIndex].distance <= heap[childIndex].distance) {
+                        break;
+                    }
+
+                    swap(heap[parentIndex], heap[childIndex]);
+                    parentIndex = childIndex;
+                }
+
+#if DEBUG_POINTER_ASSIGNMENT
+                LOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
+                for (size_t i = 0; i < heapSize; i++) {
+                    LOGD("  heap[%d]: cur=%d, last=%d, distance=%lld",
+                            i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+                            heap[i].distance);
+                }
+#endif
+            }
+
+            heapSize -= 1;
+
+            uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+            if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+            uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+            if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+            matchedCurrentBits.markBit(currentPointerIndex);
+            matchedLastBits.markBit(lastPointerIndex);
+
+            uint32_t id = mLastRawPointerData.pointers[lastPointerIndex].id;
+            mCurrentRawPointerData.pointers[currentPointerIndex].id = id;
+            mCurrentRawPointerData.idToIndex[id] = currentPointerIndex;
+            mCurrentRawPointerData.markIdBit(id,
+                    mCurrentRawPointerData.isHovering(currentPointerIndex));
+            usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+            LOGD("assignPointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+                    lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+            break;
+        }
+    }
+
+    // Assign fresh ids to pointers that were not matched in the process.
+    for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) {
+        uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit();
+        uint32_t id = usedIdBits.markFirstUnmarkedBit();
+
+        mCurrentRawPointerData.pointers[currentPointerIndex].id = id;
+        mCurrentRawPointerData.idToIndex[id] = currentPointerIndex;
+        mCurrentRawPointerData.markIdBit(id,
+                mCurrentRawPointerData.isHovering(currentPointerIndex));
+
+#if DEBUG_POINTER_ASSIGNMENT
+        LOGD("assignPointerIds - assigned: cur=%d, id=%d",
+                currentPointerIndex, id);
+#endif
     }
 }
 
 int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
-    { // acquire lock
-        AutoMutex _l(mLock);
+    if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) {
+        return AKEY_STATE_VIRTUAL;
+    }
 
-        if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) {
-            return AKEY_STATE_VIRTUAL;
+    size_t numVirtualKeys = mVirtualKeys.size();
+    for (size_t i = 0; i < numVirtualKeys; i++) {
+        const VirtualKey& virtualKey = mVirtualKeys[i];
+        if (virtualKey.keyCode == keyCode) {
+            return AKEY_STATE_UP;
         }
-
-        size_t numVirtualKeys = mLocked.virtualKeys.size();
-        for (size_t i = 0; i < numVirtualKeys; i++) {
-            const VirtualKey& virtualKey = mLocked.virtualKeys[i];
-            if (virtualKey.keyCode == keyCode) {
-                return AKEY_STATE_UP;
-            }
-        }
-    } // release lock
+    }
 
     return AKEY_STATE_UNKNOWN;
 }
 
 int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    { // acquire lock
-        AutoMutex _l(mLock);
+    if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) {
+        return AKEY_STATE_VIRTUAL;
+    }
 
-        if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) {
-            return AKEY_STATE_VIRTUAL;
+    size_t numVirtualKeys = mVirtualKeys.size();
+    for (size_t i = 0; i < numVirtualKeys; i++) {
+        const VirtualKey& virtualKey = mVirtualKeys[i];
+        if (virtualKey.scanCode == scanCode) {
+            return AKEY_STATE_UP;
         }
-
-        size_t numVirtualKeys = mLocked.virtualKeys.size();
-        for (size_t i = 0; i < numVirtualKeys; i++) {
-            const VirtualKey& virtualKey = mLocked.virtualKeys[i];
-            if (virtualKey.scanCode == scanCode) {
-                return AKEY_STATE_UP;
-            }
-        }
-    } // release lock
+    }
 
     return AKEY_STATE_UNKNOWN;
 }
 
 bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
         const int32_t* keyCodes, uint8_t* outFlags) {
-    { // acquire lock
-        AutoMutex _l(mLock);
+    size_t numVirtualKeys = mVirtualKeys.size();
+    for (size_t i = 0; i < numVirtualKeys; i++) {
+        const VirtualKey& virtualKey = mVirtualKeys[i];
 
-        size_t numVirtualKeys = mLocked.virtualKeys.size();
-        for (size_t i = 0; i < numVirtualKeys; i++) {
-            const VirtualKey& virtualKey = mLocked.virtualKeys[i];
-
-            for (size_t i = 0; i < numCodes; i++) {
-                if (virtualKey.keyCode == keyCodes[i]) {
-                    outFlags[i] = 1;
-                }
+        for (size_t i = 0; i < numCodes; i++) {
+            if (virtualKey.keyCode == keyCodes[i]) {
+                outFlags[i] = 1;
             }
         }
-    } // release lock
+    }
 
     return true;
 }
@@ -5067,21 +5141,18 @@
 }
 
 void SingleTouchInputMapper::sync(nsecs_t when) {
-    mCurrentTouch.clear();
+    mCurrentRawPointerData.clear();
+    mCurrentButtonState = 0;
 
     if (mTouchButtonAccumulator.isActive()) {
-        uint32_t buttonState = mTouchButtonAccumulator.getButtonState();
-        bool isHovering = mTouchButtonAccumulator.isHovering();
-        if (mSingleTouchMotionAccumulator.getAbsoluteDistance() > 0) {
-            isHovering = true;
-        }
+        mCurrentRawPointerData.pointerCount = 1;
+        mCurrentRawPointerData.idToIndex[0] = 0;
 
-        mCurrentTouch.pointerCount = 1;
-        mCurrentTouch.idToIndex[0] = 0;
-        mCurrentTouch.idBits.markBit(0);
-        mCurrentTouch.buttonState = buttonState;
+        bool isHovering = mTouchButtonAccumulator.isHovering()
+                || mSingleTouchMotionAccumulator.getAbsoluteDistance() > 0;
+        mCurrentRawPointerData.markIdBit(0, isHovering);
 
-        PointerData& outPointer = mCurrentTouch.pointers[0];
+        RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[0];
         outPointer.id = 0;
         outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX();
         outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY();
@@ -5097,21 +5168,24 @@
             outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
         }
         outPointer.isHovering = isHovering;
+
+        mCurrentButtonState = mTouchButtonAccumulator.getButtonState()
+                | mCursorButtonAccumulator.getButtonState();
     }
 
     syncTouch(when, true);
 }
 
-void SingleTouchInputMapper::configureRawAxes() {
-    TouchInputMapper::configureRawAxes();
+void SingleTouchInputMapper::configureRawPointerAxes() {
+    TouchInputMapper::configureRawPointerAxes();
 
     mTouchButtonAccumulator.configure(getDevice());
 
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_DISTANCE, & mRawAxes.distance);
+    getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
+    getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
+    getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
+    getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
+    getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
 }
 
 
@@ -5172,8 +5246,9 @@
     size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
     size_t outCount = 0;
     bool havePointerIds = true;
+    BitSet32 newPointerIdBits;
 
-    mCurrentTouch.clear();
+    mCurrentRawPointerData.clear();
 
     for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
         const MultiTouchMotionAccumulator::Slot* inSlot =
@@ -5191,7 +5266,7 @@
             break; // too many fingers!
         }
 
-        PointerData& outPointer = mCurrentTouch.pointers[outCount];
+        RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[outCount];
         outPointer.x = inSlot->getX();
         outPointer.y = inSlot->getY();
         outPointer.pressure = inSlot->getPressure();
@@ -5210,8 +5285,9 @@
             }
         }
 
-        outPointer.isHovering = mTouchButtonAccumulator.isHovering()
+        bool isHovering = mTouchButtonAccumulator.isHovering()
                 || inSlot->getDistance() > 0;
+        outPointer.isHovering = isHovering;
 
         // Assign pointer id using tracking id if available.
         if (havePointerIds) {
@@ -5219,37 +5295,37 @@
             int32_t id = -1;
             if (trackingId >= 0) {
                 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
-                    uint32_t n = idBits.firstMarkedBit();
-                    idBits.clearBit(n);
-
+                    uint32_t n = idBits.clearFirstMarkedBit();
                     if (mPointerTrackingIdMap[n] == trackingId) {
                         id = n;
                     }
                 }
 
                 if (id < 0 && !mPointerIdBits.isFull()) {
-                    id = mPointerIdBits.firstUnmarkedBit();
-                    mPointerIdBits.markBit(id);
+                    id = mPointerIdBits.markFirstUnmarkedBit();
                     mPointerTrackingIdMap[id] = trackingId;
                 }
             }
             if (id < 0) {
                 havePointerIds = false;
-                mCurrentTouch.idBits.clear();
+                mCurrentRawPointerData.clearIdBits();
+                newPointerIdBits.clear();
             } else {
                 outPointer.id = id;
-                mCurrentTouch.idToIndex[id] = outCount;
-                mCurrentTouch.idBits.markBit(id);
+                mCurrentRawPointerData.idToIndex[id] = outCount;
+                mCurrentRawPointerData.markIdBit(id, isHovering);
+                newPointerIdBits.markBit(id);
             }
         }
 
         outCount += 1;
     }
 
-    mCurrentTouch.pointerCount = outCount;
-    mCurrentTouch.buttonState = mTouchButtonAccumulator.getButtonState();
+    mCurrentRawPointerData.pointerCount = outCount;
+    mCurrentButtonState = mTouchButtonAccumulator.getButtonState()
+            | mCursorButtonAccumulator.getButtonState();
 
-    mPointerIdBits = mCurrentTouch.idBits;
+    mPointerIdBits = newPointerIdBits;
 
     syncTouch(when, havePointerIds);
 
@@ -5258,26 +5334,27 @@
     }
 }
 
-void MultiTouchInputMapper::configureRawAxes() {
-    TouchInputMapper::configureRawAxes();
+void MultiTouchInputMapper::configureRawPointerAxes() {
+    TouchInputMapper::configureRawPointerAxes();
 
     mTouchButtonAccumulator.configure(getDevice());
 
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, &mRawAxes.x);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, &mRawAxes.y);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, &mRawAxes.touchMajor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, &mRawAxes.touchMinor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, &mRawAxes.toolMajor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, &mRawAxes.toolMinor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, &mRawAxes.orientation);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, &mRawAxes.pressure);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_DISTANCE, &mRawAxes.distance);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TRACKING_ID, &mRawAxes.trackingId);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_SLOT, &mRawAxes.slot);
+    getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
+    getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
+    getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
+    getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
+    getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
+    getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
+    getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
+    getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
+    getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
+    getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
+    getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
 
-    if (mRawAxes.trackingId.valid
-            && mRawAxes.slot.valid && mRawAxes.slot.minValue == 0 && mRawAxes.slot.maxValue > 0) {
-        size_t slotCount = mRawAxes.slot.maxValue + 1;
+    if (mRawPointerAxes.trackingId.valid
+            && mRawPointerAxes.slot.valid
+            && mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
+        size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
         if (slotCount > MAX_SLOTS) {
             LOGW("MultiTouch Device %s reported %d slots but the framework "
                     "only supports a maximum of %d slots at this time.",
@@ -5364,7 +5441,7 @@
         // Collect all axes.
         for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
             RawAbsoluteAxisInfo rawAxisInfo;
-            getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo);
+            getAbsoluteAxisInfo(abs, &rawAxisInfo);
             if (rawAxisInfo.valid) {
                 // Map axis.
                 AxisInfo axisInfo;
@@ -5581,9 +5658,10 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
+    NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
             AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
             1, &pointerProperties, &pointerCoords, 0, 0, 0);
+    getListener()->notifyMotion(&args);
 }
 
 bool JoystickInputMapper::filterAxes(bool force) {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index ee6990b..f5d095d 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -18,8 +18,8 @@
 #define _UI_INPUT_READER_H
 
 #include "EventHub.h"
-#include "InputDispatcher.h"
 #include "PointerController.h"
+#include "InputListener.h"
 
 #include <ui/Input.h>
 #include <ui/DisplayInfo.h>
@@ -164,6 +164,9 @@
  *
  * The actual implementation is partially supported by callbacks into the DVM
  * via JNI.  This interface is also mocked in the unit tests.
+ *
+ * These methods must NOT re-enter the input reader since they may be called while
+ * holding the input reader lock.
  */
 class InputReaderPolicyInterface : public virtual RefBase {
 protected:
@@ -195,7 +198,7 @@
 };
 
 
-/* Processes raw input events and sends cooked event data to an input dispatcher. */
+/* Processes raw input events and sends cooked event data to an input listener. */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -270,25 +273,27 @@
     virtual void requestTimeoutAtTime(nsecs_t when) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
-    virtual InputDispatcherInterface* getDispatcher() = 0;
+    virtual InputListenerInterface* getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 };
 
 
 /* The input reader reads raw event data from the event hub and processes it into input events
- * that it sends to the input dispatcher.  Some functions of the input reader, such as early
+ * that it sends to the input listener.  Some functions of the input reader, such as early
  * event filtering in low power states, are controlled by a separate policy object.
  *
- * IMPORTANT INVARIANT:
- *     Because the policy and dispatcher can potentially block or cause re-entrance into
- *     the input reader, the input reader never calls into other components while holding
- *     an exclusive internal lock whenever re-entrance can happen.
+ * The InputReader owns a collection of InputMappers.  Most of the work it does happens
+ * on the input reader thread but the InputReader can receive queries from other system
+ * components running on arbitrary threads.  To keep things manageable, the InputReader
+ * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
+ * EventHub or the InputReaderPolicy but it is never held while calling into the
+ * InputListener.
  */
-class InputReader : public InputReaderInterface, protected InputReaderContext {
+class InputReader : public InputReaderInterface {
 public:
     InputReader(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
-            const sp<InputDispatcherInterface>& dispatcher);
+            const sp<InputListenerInterface>& listener);
     virtual ~InputReader();
 
     virtual void dump(String8& dump);
@@ -313,74 +318,80 @@
     virtual void requestRefreshConfiguration(uint32_t changes);
 
 protected:
-    // These methods are protected virtual so they can be overridden and instrumented
-    // by test cases.
-    virtual InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
+    // These members are protected so they can be instrumented by test cases.
+    virtual InputDevice* createDeviceLocked(int32_t deviceId,
+            const String8& name, uint32_t classes);
+
+    class ContextImpl : public InputReaderContext {
+        InputReader* mReader;
+
+    public:
+        ContextImpl(InputReader* reader);
+
+        virtual void updateGlobalMetaState();
+        virtual int32_t getGlobalMetaState();
+        virtual void disableVirtualKeysUntil(nsecs_t time);
+        virtual bool shouldDropVirtualKey(nsecs_t now,
+                InputDevice* device, int32_t keyCode, int32_t scanCode);
+        virtual void fadePointer();
+        virtual void requestTimeoutAtTime(nsecs_t when);
+        virtual InputReaderPolicyInterface* getPolicy();
+        virtual InputListenerInterface* getListener();
+        virtual EventHubInterface* getEventHub();
+    } mContext;
+
+    friend class ContextImpl;
 
 private:
+    Mutex mLock;
+
     sp<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
-    sp<InputDispatcherInterface> mDispatcher;
+    sp<QueuedInputListener> mQueuedListener;
 
     InputReaderConfiguration mConfig;
 
-    virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); }
-    virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); }
-    virtual EventHubInterface* getEventHub() { return mEventHub.get(); }
-
     // The event queue.
     static const int EVENT_BUFFER_SIZE = 256;
     RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
 
-    // This reader/writer lock guards the list of input devices.
-    // The writer lock must be held whenever the list of input devices is modified
-    //   and then promptly released.
-    // The reader lock must be held whenever the list of input devices is traversed or an
-    //   input device in the list is accessed.
-    // This lock only protects the registry and prevents inadvertent deletion of device objects
-    // that are in use.  Individual devices are responsible for guarding their own internal state
-    // as needed for concurrent operation.
-    RWLock mDeviceRegistryLock;
     KeyedVector<int32_t, InputDevice*> mDevices;
 
     // low-level input event decoding and device management
-    void processEvents(const RawEvent* rawEvents, size_t count);
+    void processEventsLocked(const RawEvent* rawEvents, size_t count);
 
-    void addDevice(int32_t deviceId);
-    void removeDevice(int32_t deviceId);
-    void processEventsForDevice(int32_t deviceId, const RawEvent* rawEvents, size_t count);
-    void timeoutExpired(nsecs_t when);
+    void addDeviceLocked(int32_t deviceId);
+    void removeDeviceLocked(int32_t deviceId);
+    void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
+    void timeoutExpiredLocked(nsecs_t when);
 
-    void handleConfigurationChanged(nsecs_t when);
+    void handleConfigurationChangedLocked(nsecs_t when);
 
-    // state management for all devices
-    Mutex mStateLock;
+    int32_t mGlobalMetaState;
+    void updateGlobalMetaStateLocked();
+    int32_t getGlobalMetaStateLocked();
 
-    int32_t mGlobalMetaState; // guarded by mStateLock
-    virtual void updateGlobalMetaState();
-    virtual int32_t getGlobalMetaState();
+    void fadePointerLocked();
 
-    virtual void fadePointer();
+    InputConfiguration mInputConfiguration;
+    void updateInputConfigurationLocked();
 
-    InputConfiguration mInputConfiguration; // guarded by mStateLock
-    void updateInputConfiguration();
-
-    nsecs_t mDisableVirtualKeysTimeout; // only accessed by reader thread
-    virtual void disableVirtualKeysUntil(nsecs_t time);
-    virtual bool shouldDropVirtualKey(nsecs_t now,
+    nsecs_t mDisableVirtualKeysTimeout;
+    void disableVirtualKeysUntilLocked(nsecs_t time);
+    bool shouldDropVirtualKeyLocked(nsecs_t now,
             InputDevice* device, int32_t keyCode, int32_t scanCode);
 
-    nsecs_t mNextTimeout; // only accessed by reader thread, not guarded
-    virtual void requestTimeoutAtTime(nsecs_t when);
+    nsecs_t mNextTimeout;
+    void requestTimeoutAtTimeLocked(nsecs_t when);
 
-    uint32_t mConfigurationChangesToRefresh; // guarded by mStateLock
-    void refreshConfiguration(uint32_t changes);
+    uint32_t mConfigurationChangesToRefresh;
+    void refreshConfigurationLocked(uint32_t changes);
 
     // state queries
     typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code);
-    int32_t getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+    int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code,
             GetStateFunc getStateFunc);
-    bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+    bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
 };
 
@@ -530,6 +541,93 @@
 };
 
 
+/* Raw axis information from the driver. */
+struct RawPointerAxes {
+    RawAbsoluteAxisInfo x;
+    RawAbsoluteAxisInfo y;
+    RawAbsoluteAxisInfo pressure;
+    RawAbsoluteAxisInfo touchMajor;
+    RawAbsoluteAxisInfo touchMinor;
+    RawAbsoluteAxisInfo toolMajor;
+    RawAbsoluteAxisInfo toolMinor;
+    RawAbsoluteAxisInfo orientation;
+    RawAbsoluteAxisInfo distance;
+    RawAbsoluteAxisInfo trackingId;
+    RawAbsoluteAxisInfo slot;
+
+    RawPointerAxes();
+    void clear();
+};
+
+
+/* Raw data for a collection of pointers including a pointer id mapping table. */
+struct RawPointerData {
+    struct Pointer {
+        uint32_t id;
+        int32_t x;
+        int32_t y;
+        int32_t pressure;
+        int32_t touchMajor;
+        int32_t touchMinor;
+        int32_t toolMajor;
+        int32_t toolMinor;
+        int32_t orientation;
+        int32_t distance;
+        int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant
+        bool isHovering;
+    };
+
+    uint32_t pointerCount;
+    Pointer pointers[MAX_POINTERS];
+    BitSet32 hoveringIdBits, touchingIdBits;
+    uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+    RawPointerData();
+    void clear();
+    void copyFrom(const RawPointerData& other);
+    void getCentroidOfTouchingPointers(float* outX, float* outY) const;
+
+    inline void markIdBit(uint32_t id, bool isHovering) {
+        if (isHovering) {
+            hoveringIdBits.markBit(id);
+        } else {
+            touchingIdBits.markBit(id);
+        }
+    }
+
+    inline void clearIdBits() {
+        hoveringIdBits.clear();
+        touchingIdBits.clear();
+    }
+
+    inline const Pointer& pointerForId(uint32_t id) const {
+        return pointers[idToIndex[id]];
+    }
+
+    inline bool isHovering(uint32_t pointerIndex) {
+        return pointers[pointerIndex].isHovering;
+    }
+};
+
+
+/* Cooked data for a collection of pointers including a pointer id mapping table. */
+struct CookedPointerData {
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+    BitSet32 hoveringIdBits, touchingIdBits;
+    uint32_t idToIndex[MAX_POINTER_ID + 1];
+
+    CookedPointerData();
+    void clear();
+    void copyFrom(const CookedPointerData& other);
+
+    inline bool isHovering(uint32_t pointerIndex) {
+        return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
+    }
+};
+
+
 /* Keeps track of the state of single-touch protocol. */
 class SingleTouchMotionAccumulator {
 public:
@@ -590,8 +688,8 @@
         int32_t mAbsMTOrientation;
         int32_t mAbsMTTrackingId;
         int32_t mAbsMTPressure;
-        int32_t mAbsMTToolType;
         int32_t mAbsMTDistance;
+        int32_t mAbsMTToolType;
 
         Slot();
         void clearIfInUse();
@@ -632,7 +730,7 @@
     inline const String8 getDeviceName() { return mDevice->getName(); }
     inline InputReaderContext* getContext() { return mContext; }
     inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
-    inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); }
+    inline InputListenerInterface* getListener() { return mContext->getListener(); }
     inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
 
     virtual uint32_t getSources() = 0;
@@ -657,6 +755,8 @@
     InputDevice* mDevice;
     InputReaderContext* mContext;
 
+    status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
+
     static void dumpRawAbsoluteAxisInfo(String8& dump,
             const RawAbsoluteAxisInfo& axis, const char* name);
 };
@@ -707,27 +807,25 @@
     uint32_t mSource;
     int32_t mKeyboardType;
 
+    Vector<KeyDown> mKeyDowns; // keys that are down
+    int32_t mMetaState;
+    nsecs_t mDownTime; // time of most recent key down
+
+    struct LedState {
+        bool avail; // led is available
+        bool on;    // we think the led is currently on
+    };
+    LedState mCapsLockLedState;
+    LedState mNumLockLedState;
+    LedState mScrollLockLedState;
+
     // Immutable configuration parameters.
     struct Parameters {
         int32_t associatedDisplayId;
         bool orientationAware;
     } mParameters;
 
-    struct LockedState {
-        Vector<KeyDown> keyDowns; // keys that are down
-        int32_t metaState;
-        nsecs_t downTime; // time of most recent key down
-
-        struct LedState {
-            bool avail; // led is available
-            bool on;    // we think the led is currently on
-        };
-        LedState capsLockLedState;
-        LedState numLockLedState;
-        LedState scrollLockLedState;
-    } mLocked;
-
-    void initializeLocked();
+    void initialize();
 
     void configureParameters();
     void dumpParameters(String8& dump);
@@ -737,12 +835,12 @@
     void processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode,
             uint32_t policyFlags);
 
-    ssize_t findKeyDownLocked(int32_t scanCode);
+    ssize_t findKeyDown(int32_t scanCode);
 
-    void resetLedStateLocked();
-    void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
-    void updateLedStateLocked(bool reset);
-    void updateLedStateForModifierLocked(LockedState::LedState& ledState, int32_t led,
+    void resetLedState();
+    void initializeLedState(LedState& ledState, int32_t led);
+    void updateLedState(bool reset);
+    void updateLedStateForModifier(LedState& ledState, int32_t led,
             int32_t modifier, bool reset);
 };
 
@@ -767,8 +865,6 @@
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
 
-    Mutex mLock;
-
     // Immutable configuration parameters.
     struct Parameters {
         enum Mode {
@@ -801,12 +897,10 @@
 
     sp<PointerControllerInterface> mPointerController;
 
-    struct LockedState {
-        int32_t buttonState;
-        nsecs_t downTime;
-    } mLocked;
+    int32_t mButtonState;
+    nsecs_t mDownTime;
 
-    void initializeLocked();
+    void initialize();
 
     void configureParameters();
     void dumpParameters(String8& dump);
@@ -835,8 +929,6 @@
     virtual void timeoutExpired(nsecs_t when);
 
 protected:
-    Mutex mLock;
-
     struct VirtualKey {
         int32_t keyCode;
         int32_t scanCode;
@@ -853,82 +945,6 @@
         }
     };
 
-    // Raw data for a single pointer.
-    struct PointerData {
-        uint32_t id;
-        int32_t x;
-        int32_t y;
-        int32_t pressure;
-        int32_t touchMajor;
-        int32_t touchMinor;
-        int32_t toolMajor;
-        int32_t toolMinor;
-        int32_t orientation;
-        int32_t distance;
-        int32_t toolType; // AMOTION_EVENT_TOOL_TYPE constant
-        bool isHovering;
-
-        inline bool operator== (const PointerData& other) const {
-            return id == other.id
-                    && x == other.x
-                    && y == other.y
-                    && pressure == other.pressure
-                    && touchMajor == other.touchMajor
-                    && touchMinor == other.touchMinor
-                    && toolMajor == other.toolMajor
-                    && toolMinor == other.toolMinor
-                    && orientation == other.orientation
-                    && distance == other.distance
-                    && toolType == other.toolType
-                    && isHovering == other.isHovering;
-        }
-        inline bool operator!= (const PointerData& other) const {
-            return !(*this == other);
-        }
-    };
-
-    // Raw data for a collection of pointers including a pointer id mapping table.
-    struct TouchData {
-        uint32_t pointerCount;
-        PointerData pointers[MAX_POINTERS];
-        BitSet32 idBits;
-        uint32_t idToIndex[MAX_POINTER_ID + 1];
-        int32_t buttonState;
-
-        void copyFrom(const TouchData& other) {
-            pointerCount = other.pointerCount;
-            idBits = other.idBits;
-            buttonState = other.buttonState;
-
-            for (uint32_t i = 0; i < pointerCount; i++) {
-                pointers[i] = other.pointers[i];
-
-                int id = pointers[i].id;
-                idToIndex[id] = other.idToIndex[id];
-            }
-        }
-
-        inline void clear() {
-            pointerCount = 0;
-            idBits.clear();
-            buttonState = 0;
-        }
-
-        void getCentroid(float* outX, float* outY) {
-            float x = 0, y = 0;
-            if (pointerCount != 0) {
-                for (uint32_t i = 0; i < pointerCount; i++) {
-                    x += pointers[i].x;
-                    y += pointers[i].y;
-                }
-                x /= pointerCount;
-                y /= pointerCount;
-            }
-            *outX = x;
-            *outY = y;
-        }
-    };
-
     // Input sources supported by the device.
     uint32_t mTouchSource; // sources when reporting touch data
     uint32_t mPointerSource; // sources when reporting pointer gestures
@@ -1038,29 +1054,23 @@
         float distanceScale;
     } mCalibration;
 
-    // Raw axis information from the driver.
-    struct RawAxes {
-        RawAbsoluteAxisInfo x;
-        RawAbsoluteAxisInfo y;
-        RawAbsoluteAxisInfo pressure;
-        RawAbsoluteAxisInfo touchMajor;
-        RawAbsoluteAxisInfo touchMinor;
-        RawAbsoluteAxisInfo toolMajor;
-        RawAbsoluteAxisInfo toolMinor;
-        RawAbsoluteAxisInfo orientation;
-        RawAbsoluteAxisInfo distance;
-        RawAbsoluteAxisInfo trackingId;
-        RawAbsoluteAxisInfo slot;
-    } mRawAxes;
+    // Raw pointer axis information from the driver.
+    RawPointerAxes mRawPointerAxes;
 
-    // Current and previous touch sample data.
-    TouchData mCurrentTouch;
-    PointerProperties mCurrentTouchProperties[MAX_POINTERS];
-    PointerCoords mCurrentTouchCoords[MAX_POINTERS];
+    // Raw pointer sample data.
+    RawPointerData mCurrentRawPointerData;
+    RawPointerData mLastRawPointerData;
 
-    TouchData mLastTouch;
-    PointerProperties mLastTouchProperties[MAX_POINTERS];
-    PointerCoords mLastTouchCoords[MAX_POINTERS];
+    // Cooked pointer sample data.
+    CookedPointerData mCurrentCookedPointerData;
+    CookedPointerData mLastCookedPointerData;
+
+    // Button state.
+    int32_t mCurrentButtonState;
+    int32_t mLastButtonState;
+
+    // True if we sent a HOVER_ENTER event.
+    bool mSentHoverEnter;
 
     // The time the primary pointer last went down.
     nsecs_t mDownTime;
@@ -1068,113 +1078,106 @@
     // The pointer controller, or null if the device is not a pointer.
     sp<PointerControllerInterface> mPointerController;
 
-    struct LockedState {
-        Vector<VirtualKey> virtualKeys;
-
-        // The surface orientation and width and height set by configureSurfaceLocked().
-        int32_t surfaceOrientation;
-        int32_t surfaceWidth, surfaceHeight;
-
-        // The associated display orientation and width and height set by configureSurfaceLocked().
-        int32_t associatedDisplayOrientation;
-        int32_t associatedDisplayWidth, associatedDisplayHeight;
-
-        // Translation and scaling factors, orientation-independent.
-        float xScale;
-        float xPrecision;
-
-        float yScale;
-        float yPrecision;
-
-        float geometricScale;
-
-        float toolSizeLinearScale;
-        float toolSizeLinearBias;
-        float toolSizeAreaScale;
-        float toolSizeAreaBias;
-
-        float pressureScale;
-
-        float sizeScale;
-
-        float orientationScale;
-
-        float distanceScale;
-
-        // Oriented motion ranges for input device info.
-        struct OrientedRanges {
-            InputDeviceInfo::MotionRange x;
-            InputDeviceInfo::MotionRange y;
-
-            bool havePressure;
-            InputDeviceInfo::MotionRange pressure;
-
-            bool haveSize;
-            InputDeviceInfo::MotionRange size;
-
-            bool haveTouchSize;
-            InputDeviceInfo::MotionRange touchMajor;
-            InputDeviceInfo::MotionRange touchMinor;
-
-            bool haveToolSize;
-            InputDeviceInfo::MotionRange toolMajor;
-            InputDeviceInfo::MotionRange toolMinor;
-
-            bool haveOrientation;
-            InputDeviceInfo::MotionRange orientation;
-
-            bool haveDistance;
-            InputDeviceInfo::MotionRange distance;
-        } orientedRanges;
-
-        // Oriented dimensions and precision.
-        float orientedSurfaceWidth, orientedSurfaceHeight;
-        float orientedXPrecision, orientedYPrecision;
-
-        struct CurrentVirtualKeyState {
-            bool down;
-            nsecs_t downTime;
-            int32_t keyCode;
-            int32_t scanCode;
-        } currentVirtualKey;
-
-        // Scale factor for gesture based pointer movements.
-        float pointerGestureXMovementScale;
-        float pointerGestureYMovementScale;
-
-        // Scale factor for gesture based zooming and other freeform motions.
-        float pointerGestureXZoomScale;
-        float pointerGestureYZoomScale;
-
-        // The maximum swipe width.
-        float pointerGestureMaxSwipeWidth;
-    } mLocked;
+    Vector<VirtualKey> mVirtualKeys;
 
     virtual void configureParameters();
     virtual void dumpParameters(String8& dump);
-    virtual void configureRawAxes();
-    virtual void dumpRawAxes(String8& dump);
-    virtual bool configureSurfaceLocked();
-    virtual void dumpSurfaceLocked(String8& dump);
-    virtual void configureVirtualKeysLocked();
-    virtual void dumpVirtualKeysLocked(String8& dump);
+    virtual void configureRawPointerAxes();
+    virtual void dumpRawPointerAxes(String8& dump);
+    virtual bool configureSurface();
+    virtual void dumpSurface(String8& dump);
+    virtual void configureVirtualKeys();
+    virtual void dumpVirtualKeys(String8& dump);
     virtual void parseCalibration();
     virtual void resolveCalibration();
     virtual void dumpCalibration(String8& dump);
 
-    enum TouchResult {
-        // Dispatch the touch normally.
-        DISPATCH_TOUCH,
-        // Do not dispatch the touch, but keep tracking the current stroke.
-        SKIP_TOUCH,
-        // Do not dispatch the touch, and drop all information associated with the current stoke
-        // so the next movement will appear as a new down.
-        DROP_STROKE
-    };
-
     void syncTouch(nsecs_t when, bool havePointerIds);
 
 private:
+    // The surface orientation and width and height set by configureSurface().
+    int32_t mSurfaceOrientation;
+    int32_t mSurfaceWidth;
+    int32_t mSurfaceHeight;
+
+    // The associated display orientation and width and height set by configureSurface().
+    int32_t mAssociatedDisplayOrientation;
+    int32_t mAssociatedDisplayWidth;
+    int32_t mAssociatedDisplayHeight;
+
+    // Translation and scaling factors, orientation-independent.
+    float mXScale;
+    float mXPrecision;
+
+    float mYScale;
+    float mYPrecision;
+
+    float mGeometricScale;
+
+    float mToolSizeLinearScale;
+    float mToolSizeLinearBias;
+    float mToolSizeAreaScale;
+    float mToolSizeAreaBias;
+
+    float mPressureScale;
+
+    float mSizeScale;
+
+    float mOrientationScale;
+
+    float mDistanceScale;
+
+    // Oriented motion ranges for input device info.
+    struct OrientedRanges {
+        InputDeviceInfo::MotionRange x;
+        InputDeviceInfo::MotionRange y;
+
+        bool havePressure;
+        InputDeviceInfo::MotionRange pressure;
+
+        bool haveSize;
+        InputDeviceInfo::MotionRange size;
+
+        bool haveTouchSize;
+        InputDeviceInfo::MotionRange touchMajor;
+        InputDeviceInfo::MotionRange touchMinor;
+
+        bool haveToolSize;
+        InputDeviceInfo::MotionRange toolMajor;
+        InputDeviceInfo::MotionRange toolMinor;
+
+        bool haveOrientation;
+        InputDeviceInfo::MotionRange orientation;
+
+        bool haveDistance;
+        InputDeviceInfo::MotionRange distance;
+    } mOrientedRanges;
+
+    // Oriented dimensions and precision.
+    float mOrientedSurfaceWidth;
+    float mOrientedSurfaceHeight;
+    float mOrientedXPrecision;
+    float mOrientedYPrecision;
+
+    struct CurrentVirtualKeyState {
+        bool down;
+        bool ignored;
+        nsecs_t downTime;
+        int32_t keyCode;
+        int32_t scanCode;
+    } mCurrentVirtualKey;
+
+    // Scale factor for gesture based pointer movements.
+    float mPointerGestureXMovementScale;
+    float mPointerGestureYMovementScale;
+
+    // Scale factor for gesture based zooming and other freeform motions.
+    float mPointerGestureXZoomScale;
+    float mPointerGestureYZoomScale;
+
+    // The maximum swipe width.
+    float mPointerGestureMaxSwipeWidth;
+
     struct PointerDistanceHeapElement {
         uint32_t currentPointerIndex : 8;
         uint32_t lastPointerIndex : 8;
@@ -1319,11 +1322,17 @@
         }
     } mPointerGesture;
 
-    void initializeLocked();
+    void initialize();
 
-    TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags);
+    bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
+    void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+            int32_t keyEventAction, int32_t keyEventFlags);
+
     void dispatchTouches(nsecs_t when, uint32_t policyFlags);
-    void prepareTouches(float* outXPrecision, float* outYPrecision);
+    void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
+    void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
+    void cookPointerData();
+
     void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
     bool preparePointerGestures(nsecs_t when,
             bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
@@ -1346,12 +1355,10 @@
             PointerProperties* outProperties, PointerCoords* outCoords,
             const uint32_t* outIdToIndex, BitSet32 idBits) const;
 
-    void suppressSwipeOntoVirtualKeys(nsecs_t when);
+    bool isPointInsideSurface(int32_t x, int32_t y);
+    const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
 
-    bool isPointInsideSurfaceLocked(int32_t x, int32_t y);
-    const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y);
-
-    void calculatePointerIds();
+    void assignPointerIds();
 };
 
 
@@ -1364,7 +1371,7 @@
     virtual void process(const RawEvent* rawEvent);
 
 protected:
-    virtual void configureRawAxes();
+    virtual void configureRawPointerAxes();
 
 private:
     CursorButtonAccumulator mCursorButtonAccumulator;
@@ -1386,7 +1393,7 @@
     virtual void process(const RawEvent* rawEvent);
 
 protected:
-    virtual void configureRawAxes();
+    virtual void configureRawPointerAxes();
 
 private:
     CursorButtonAccumulator mCursorButtonAccumulator;
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index 12c7cba..1d1730d 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -261,9 +261,7 @@
 
     // Add or move spots for fingers that are down.
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
-        uint32_t id = idBits.firstMarkedBit();
-        idBits.clearBit(id);
-
+        uint32_t id = idBits.clearFirstMarkedBit();
         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
         const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
                 ? mResources.spotTouch : mResources.spotHover;
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 8533743..4a866a8 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -48,13 +48,16 @@
 class FakePointerController : public PointerControllerInterface {
     bool mHaveBounds;
     float mMinX, mMinY, mMaxX, mMaxY;
+    float mX, mY;
+    int32_t mButtonState;
 
 protected:
     virtual ~FakePointerController() { }
 
 public:
     FakePointerController() :
-        mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0) {
+        mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
+        mButtonState(0) {
     }
 
     void setBounds(float minX, float minY, float maxX, float maxY) {
@@ -65,6 +68,24 @@
         mMaxY = maxY;
     }
 
+    virtual void setPosition(float x, float y) {
+        mX = x;
+        mY = y;
+    }
+
+    virtual void setButtonState(int32_t buttonState) {
+        mButtonState = buttonState;
+    }
+
+    virtual int32_t getButtonState() const {
+        return mButtonState;
+    }
+
+    virtual void getPosition(float* outX, float* outY) const {
+        *outX = mX;
+        *outY = mY;
+    }
+
 private:
     virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
         *outMinX = mMinX;
@@ -75,21 +96,12 @@
     }
 
     virtual void move(float deltaX, float deltaY) {
-    }
-
-    virtual void setButtonState(int32_t buttonState) {
-    }
-
-    virtual int32_t getButtonState() const {
-        return 0;
-    }
-
-    virtual void setPosition(float x, float y) {
-    }
-
-    virtual void getPosition(float* outX, float* outY) const {
-        *outX = 0;
-        *outY = 0;
+        mX += deltaX;
+        if (mX < mMinX) mX = mMinX;
+        if (mX > mMaxX) mX = mMaxX;
+        mY += deltaY;
+        if (mY < mMinY) mY = mMinY;
+        if (mY > mMaxY) mY = mMaxY;
     }
 
     virtual void fade(Transition transition) {
@@ -186,221 +198,84 @@
 };
 
 
-// --- FakeInputDispatcher ---
+// --- FakeInputListener ---
 
-class FakeInputDispatcher : public InputDispatcherInterface {
-public:
-    struct NotifyConfigurationChangedArgs {
-        NotifyConfigurationChangedArgs() : eventTime(0) { }
-
-        nsecs_t eventTime;
-    };
-
-    struct NotifyKeyArgs {
-        nsecs_t eventTime;
-        int32_t deviceId;
-        uint32_t source;
-        uint32_t policyFlags;
-        int32_t action;
-        int32_t flags;
-        int32_t keyCode;
-        int32_t scanCode;
-        int32_t metaState;
-        nsecs_t downTime;
-    };
-
-    struct NotifyMotionArgs {
-        nsecs_t eventTime;
-        int32_t deviceId;
-        uint32_t source;
-        uint32_t policyFlags;
-        int32_t action;
-        int32_t flags;
-        int32_t metaState;
-        int32_t buttonState;
-        int32_t edgeFlags;
-        uint32_t pointerCount;
-        Vector<PointerProperties> pointerProperties;
-        Vector<PointerCoords> pointerCoords;
-        float xPrecision;
-        float yPrecision;
-        nsecs_t downTime;
-    };
-
-    struct NotifySwitchArgs {
-        nsecs_t when;
-        int32_t switchCode;
-        int32_t switchValue;
-        uint32_t policyFlags;
-    };
-
+class FakeInputListener : public InputListenerInterface {
 private:
-    List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgs;
-    List<NotifyKeyArgs> mNotifyKeyArgs;
-    List<NotifyMotionArgs> mNotifyMotionArgs;
-    List<NotifySwitchArgs> mNotifySwitchArgs;
+    List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
+    List<NotifyKeyArgs> mNotifyKeyArgsQueue;
+    List<NotifyMotionArgs> mNotifyMotionArgsQueue;
+    List<NotifySwitchArgs> mNotifySwitchArgsQueue;
 
 protected:
-    virtual ~FakeInputDispatcher() { }
+    virtual ~FakeInputListener() { }
 
 public:
-    FakeInputDispatcher() {
+    FakeInputListener() {
     }
 
-    void assertNotifyConfigurationChangedWasCalled(NotifyConfigurationChangedArgs* outArgs = NULL) {
-        ASSERT_FALSE(mNotifyConfigurationChangedArgs.empty())
+    void assertNotifyConfigurationChangedWasCalled(
+            NotifyConfigurationChangedArgs* outEventArgs = NULL) {
+        ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
                 << "Expected notifyConfigurationChanged() to have been called.";
-        if (outArgs) {
-            *outArgs = *mNotifyConfigurationChangedArgs.begin();
+        if (outEventArgs) {
+            *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
         }
-        mNotifyConfigurationChangedArgs.erase(mNotifyConfigurationChangedArgs.begin());
+        mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
     }
 
-    void assertNotifyKeyWasCalled(NotifyKeyArgs* outArgs = NULL) {
-        ASSERT_FALSE(mNotifyKeyArgs.empty())
+    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) {
+        ASSERT_FALSE(mNotifyKeyArgsQueue.empty())
                 << "Expected notifyKey() to have been called.";
-        if (outArgs) {
-            *outArgs = *mNotifyKeyArgs.begin();
+        if (outEventArgs) {
+            *outEventArgs = *mNotifyKeyArgsQueue.begin();
         }
-        mNotifyKeyArgs.erase(mNotifyKeyArgs.begin());
+        mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
     }
 
     void assertNotifyKeyWasNotCalled() {
-        ASSERT_TRUE(mNotifyKeyArgs.empty())
+        ASSERT_TRUE(mNotifyKeyArgsQueue.empty())
                 << "Expected notifyKey() to not have been called.";
     }
 
-    void assertNotifyMotionWasCalled(NotifyMotionArgs* outArgs = NULL) {
-        ASSERT_FALSE(mNotifyMotionArgs.empty())
+    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = NULL) {
+        ASSERT_FALSE(mNotifyMotionArgsQueue.empty())
                 << "Expected notifyMotion() to have been called.";
-        if (outArgs) {
-            *outArgs = *mNotifyMotionArgs.begin();
+        if (outEventArgs) {
+            *outEventArgs = *mNotifyMotionArgsQueue.begin();
         }
-        mNotifyMotionArgs.erase(mNotifyMotionArgs.begin());
+        mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
     }
 
     void assertNotifyMotionWasNotCalled() {
-        ASSERT_TRUE(mNotifyMotionArgs.empty())
+        ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
                 << "Expected notifyMotion() to not have been called.";
     }
 
-    void assertNotifySwitchWasCalled(NotifySwitchArgs* outArgs = NULL) {
-        ASSERT_FALSE(mNotifySwitchArgs.empty())
+    void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = NULL) {
+        ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
                 << "Expected notifySwitch() to have been called.";
-        if (outArgs) {
-            *outArgs = *mNotifySwitchArgs.begin();
+        if (outEventArgs) {
+            *outEventArgs = *mNotifySwitchArgsQueue.begin();
         }
-        mNotifySwitchArgs.erase(mNotifySwitchArgs.begin());
+        mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
     }
 
 private:
-    virtual void notifyConfigurationChanged(nsecs_t eventTime) {
-        NotifyConfigurationChangedArgs args;
-        args.eventTime = eventTime;
-        mNotifyConfigurationChangedArgs.push_back(args);
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
+        mNotifyConfigurationChangedArgsQueue.push_back(*args);
     }
 
-    virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-            int32_t scanCode, int32_t metaState, nsecs_t downTime) {
-        NotifyKeyArgs args;
-        args.eventTime = eventTime;
-        args.deviceId = deviceId;
-        args.source = source;
-        args.policyFlags = policyFlags;
-        args.action = action;
-        args.flags = flags;
-        args.keyCode = keyCode;
-        args.scanCode = scanCode;
-        args.metaState = metaState;
-        args.downTime = downTime;
-        mNotifyKeyArgs.push_back(args);
+    virtual void notifyKey(const NotifyKeyArgs* args) {
+        mNotifyKeyArgsQueue.push_back(*args);
     }
 
-    virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            uint32_t policyFlags, int32_t action, int32_t flags,
-            int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-            uint32_t pointerCount, const PointerProperties* pointerProperties,
-            const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime) {
-        NotifyMotionArgs args;
-        args.eventTime = eventTime;
-        args.deviceId = deviceId;
-        args.source = source;
-        args.policyFlags = policyFlags;
-        args.action = action;
-        args.flags = flags;
-        args.metaState = metaState;
-        args.buttonState = buttonState;
-        args.edgeFlags = edgeFlags;
-        args.pointerCount = pointerCount;
-        args.pointerProperties.clear();
-        args.pointerProperties.appendArray(pointerProperties, pointerCount);
-        args.pointerCoords.clear();
-        args.pointerCoords.appendArray(pointerCoords, pointerCount);
-        args.xPrecision = xPrecision;
-        args.yPrecision = yPrecision;
-        args.downTime = downTime;
-        mNotifyMotionArgs.push_back(args);
+    virtual void notifyMotion(const NotifyMotionArgs* args) {
+        mNotifyMotionArgsQueue.push_back(*args);
     }
 
-    virtual void notifySwitch(nsecs_t when,
-            int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {
-        NotifySwitchArgs args;
-        args.when = when;
-        args.switchCode = switchCode;
-        args.switchValue = switchValue;
-        args.policyFlags = policyFlags;
-        mNotifySwitchArgs.push_back(args);
-    }
-
-    virtual void dump(String8& dump) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-    }
-
-    virtual void dispatchOnce() {
-        ADD_FAILURE() << "Should never be called by input reader.";
-    }
-
-    virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
-            uint32_t policyFlags) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-        return INPUT_EVENT_INJECTION_FAILED;
-    }
-
-    virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-    }
-
-    virtual void setFocusedApplication(
-            const sp<InputApplicationHandle>& inputApplicationHandle) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-    }
-
-    virtual void setInputDispatchMode(bool enabled, bool frozen) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-    }
-
-    virtual void setInputFilterEnabled(bool enabled) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-    }
-
-    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
-            const sp<InputChannel>& toChannel) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-        return 0;
-    }
-
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-        return 0;
-    }
-
-    virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) {
-        ADD_FAILURE() << "Should never be called by input reader.";
-        return 0;
+    virtual void notifySwitch(const NotifySwitchArgs* args) {
+        mNotifySwitchArgsQueue.push_back(*args);
     }
 };
 
@@ -551,6 +426,10 @@
         event.value = value;
         event.flags = flags;
         mEvents.push_back(event);
+
+        if (type == EV_ABS) {
+            setAbsoluteAxisValue(deviceId, scanCode, value);
+        }
     }
 
     void assertQueueIsEmpty() {
@@ -765,15 +644,15 @@
 class FakeInputReaderContext : public InputReaderContext {
     sp<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
-    sp<InputDispatcherInterface> mDispatcher;
+    sp<InputListenerInterface> mListener;
     int32_t mGlobalMetaState;
     bool mUpdateGlobalMetaStateWasCalled;
 
 public:
     FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
-            const sp<InputDispatcherInterface>& dispatcher) :
-            mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+            const sp<InputListenerInterface>& listener) :
+            mEventHub(eventHub), mPolicy(policy), mListener(listener),
             mGlobalMetaState(0) {
     }
 
@@ -806,8 +685,8 @@
         return mPolicy.get();
     }
 
-    virtual InputDispatcherInterface* getDispatcher() {
-        return mDispatcher.get();
+    virtual InputListenerInterface* getListener() {
+        return mListener.get();
     }
 
     virtual void disableVirtualKeysUntil(nsecs_t time) {
@@ -969,8 +848,8 @@
 public:
     InstrumentedInputReader(const sp<EventHubInterface>& eventHub,
             const sp<InputReaderPolicyInterface>& policy,
-            const sp<InputDispatcherInterface>& dispatcher) :
-            InputReader(eventHub, policy, dispatcher),
+            const sp<InputListenerInterface>& listener) :
+            InputReader(eventHub, policy, listener),
             mNextDevice(NULL) {
     }
 
@@ -984,14 +863,19 @@
         mNextDevice = device;
     }
 
+    InputDevice* newDevice(int32_t deviceId, const String8& name) {
+        return new InputDevice(&mContext, deviceId, name);
+    }
+
 protected:
-    virtual InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+    virtual InputDevice* createDeviceLocked(int32_t deviceId,
+            const String8& name, uint32_t classes) {
         if (mNextDevice) {
             InputDevice* device = mNextDevice;
             mNextDevice = NULL;
             return device;
         }
-        return InputReader::createDevice(deviceId, name, classes);
+        return InputReader::createDeviceLocked(deviceId, name, classes);
     }
 
     friend class InputReaderTest;
@@ -1002,7 +886,7 @@
 
 class InputReaderTest : public testing::Test {
 protected:
-    sp<FakeInputDispatcher> mFakeDispatcher;
+    sp<FakeInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<FakeEventHub> mFakeEventHub;
     sp<InstrumentedInputReader> mReader;
@@ -1010,15 +894,15 @@
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeDispatcher = new FakeInputDispatcher();
+        mFakeListener = new FakeInputListener();
 
-        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
     }
 
     virtual void TearDown() {
         mReader.clear();
 
-        mFakeDispatcher.clear();
+        mFakeListener.clear();
         mFakePolicy.clear();
         mFakeEventHub.clear();
     }
@@ -1038,7 +922,7 @@
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId,
             const String8& name, uint32_t classes, uint32_t sources,
             const PropertyMap* configuration) {
-        InputDevice* device = new InputDevice(mReader.get(), deviceId, name);
+        InputDevice* device = mReader->newDevice(deviceId, name);
         FakeInputMapper* mapper = new FakeInputMapper(device, sources);
         device->addMapper(mapper);
         mReader->setNextDevice(device);
@@ -1304,8 +1188,9 @@
 TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
     addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD, NULL);
 
-    FakeInputDispatcher::NotifyConfigurationChangedArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyConfigurationChangedWasCalled(&args));
+    NotifyConfigurationChangedArgs args;
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
 }
 
@@ -1339,7 +1224,7 @@
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeInputDispatcher> mFakeDispatcher;
+    sp<FakeInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
 
     InputDevice* mDevice;
@@ -1347,8 +1232,8 @@
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeDispatcher = new FakeInputDispatcher();
-        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+        mFakeListener = new FakeInputListener();
+        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
         mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
@@ -1358,7 +1243,7 @@
         delete mDevice;
 
         delete mFakeContext;
-        mFakeDispatcher.clear();
+        mFakeListener.clear();
         mFakePolicy.clear();
         mFakeEventHub.clear();
     }
@@ -1509,15 +1394,15 @@
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeInputDispatcher> mFakeDispatcher;
+    sp<FakeInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
     virtual void SetUp() {
         mFakeEventHub = new FakeEventHub();
         mFakePolicy = new FakeInputReaderPolicy();
-        mFakeDispatcher = new FakeInputDispatcher();
-        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+        mFakeListener = new FakeInputListener();
+        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
 
         mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
@@ -1526,7 +1411,7 @@
     virtual void TearDown() {
         delete mDevice;
         delete mFakeContext;
-        mFakeDispatcher.clear();
+        mFakeListener.clear();
         mFakePolicy.clear();
         mFakeEventHub.clear();
     }
@@ -1570,7 +1455,7 @@
     static void assertPointerCoords(const PointerCoords& coords,
             float x, float y, float pressure, float size,
             float touchMajor, float touchMinor, float toolMajor, float toolMinor,
-            float orientation) {
+            float orientation, float distance) {
         ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), 1);
         ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
         ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
@@ -1580,6 +1465,14 @@
         ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), 1);
         ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), 1);
         ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
+        ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
+    }
+
+    static void assertPosition(const sp<FakePointerController>& controller, float x, float y) {
+        float actualX, actualY;
+        controller->getPosition(&actualX, &actualY);
+        ASSERT_NEAR(x, actualX, 1);
+        ASSERT_NEAR(y, actualY, 1);
     }
 };
 
@@ -1617,9 +1510,9 @@
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 0, 1, 0);
 
-    FakeInputDispatcher::NotifySwitchArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifySwitchWasCalled(&args));
-    ASSERT_EQ(ARBITRARY_TIME, args.when);
+    NotifySwitchArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
+    ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(SW_LID, args.switchCode);
     ASSERT_EQ(1, args.switchValue);
     ASSERT_EQ(uint32_t(0), args.policyFlags);
@@ -1636,16 +1529,16 @@
 
 void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
         int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
-    FakeInputDispatcher::NotifyKeyArgs args;
+    NotifyKeyArgs args;
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 1, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
@@ -1668,8 +1561,8 @@
     // Key down.
     process(mapper, ARBITRARY_TIME, DEVICE_ID,
             EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE);
-    FakeInputDispatcher::NotifyKeyArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
@@ -1684,7 +1577,7 @@
     // Key up.
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
             EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
     ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
@@ -1705,16 +1598,16 @@
     // Key down.
     process(mapper, ARBITRARY_TIME, DEVICE_ID,
             EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Key up.
     process(mapper, ARBITRARY_TIME, DEVICE_ID,
             EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Reset.  Since no keys still down, should not synthesize any key ups.
     mapper->reset();
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
 }
 
 TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) {
@@ -1725,18 +1618,18 @@
     // Metakey down.
     process(mapper, ARBITRARY_TIME, DEVICE_ID,
             EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Key down.
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
             EV_KEY, KEY_A, AKEYCODE_A, 1, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Reset.  Since two keys are still down, should synthesize two key ups in reverse order.
     mapper->reset();
 
-    FakeInputDispatcher::NotifyKeyArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -1747,7 +1640,7 @@
     ASSERT_EQ(uint32_t(0), args.policyFlags);
     ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -1759,7 +1652,7 @@
     ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime);
 
     // And that's it.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
@@ -1773,8 +1666,8 @@
     // Metakey down.
     process(mapper, ARBITRARY_TIME, DEVICE_ID,
             EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0);
-    FakeInputDispatcher::NotifyKeyArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
@@ -1782,21 +1675,21 @@
     // Key down.
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
             EV_KEY, KEY_A, AKEYCODE_A, 1, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
 
     // Key up.
     process(mapper, ARBITRARY_TIME + 2, DEVICE_ID,
             EV_KEY, KEY_A, AKEYCODE_A, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
 
     // Metakey up.
     process(mapper, ARBITRARY_TIME + 3, DEVICE_ID,
             EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
@@ -1876,13 +1769,13 @@
 
     // Special case: if orientation changes while key is down, we still emit the same keycode
     // in the key up as we did in the key down.
-    FakeInputDispatcher::NotifyKeyArgs args;
+    NotifyKeyArgs args;
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_270);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 1, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
@@ -1891,7 +1784,7 @@
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_180);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
     ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
@@ -2034,17 +1927,17 @@
 
 void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper,
         int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, originalX, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, originalY, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD,
             float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD,
-            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
@@ -2120,13 +2013,13 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Button press.
     // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
@@ -2140,7 +2033,7 @@
     ASSERT_EQ(0, args.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
     ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
     ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
@@ -2148,7 +2041,7 @@
     // Button release.  Should have same down time.
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
     process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
@@ -2162,7 +2055,7 @@
     ASSERT_EQ(0, args.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
     ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
     ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
@@ -2173,23 +2066,23 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Motion in X but not Y.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Motion in Y but not X.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
@@ -2197,23 +2090,23 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
-    // Button press without following sync.
+    // Button press.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    // Button release without following sync.
+    // Button release.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
@@ -2221,36 +2114,36 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Combined X, Y and Button.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
-            1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Move X, Y a bit while pressed.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 2, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
-            1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Release Button.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) {
@@ -2258,22 +2151,24 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Button press.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
     // Button release.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 
     // Reset.  Should not synthesize button up since button is not pressed.
     mapper->reset();
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(CursorInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) {
@@ -2281,20 +2176,20 @@
     addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Button press.
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
 
     // Reset.  Should synthesize button up.
     mapper->reset();
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
@@ -2366,6 +2261,203 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));
 }
 
+TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "pointer");
+    addMapperAndConfigure(mapper);
+
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    NotifyMotionArgs motionArgs;
+    NotifyKeyArgs keyArgs;
+
+    // press BTN_LEFT, release BTN_LEFT
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_LEFT, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            mFakePointerController->getButtonState());
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_RIGHT, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MIDDLE, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    // press BTN_BACK, release BTN_BACK
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_BACK, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_SIDE, release BTN_SIDE
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_SIDE, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_FORWARD, release BTN_FORWARD
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_FORWARD, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    // press BTN_EXTRA, release BTN_EXTRA
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_EXTRA, 0, 0, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, mFakePointerController->getButtonState());
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+}
+
+TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "pointer");
+    addMapperAndConfigure(mapper);
+
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+
+    NotifyMotionArgs args;
+
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 10, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, 20, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+}
+
 
 // --- TouchInputMapperTest ---
 
@@ -2383,8 +2475,12 @@
     static const int32_t RAW_PRESSURE_MAX;
     static const int32_t RAW_ORIENTATION_MIN;
     static const int32_t RAW_ORIENTATION_MAX;
+    static const int32_t RAW_DISTANCE_MIN;
+    static const int32_t RAW_DISTANCE_MAX;
     static const int32_t RAW_ID_MIN;
     static const int32_t RAW_ID_MAX;
+    static const int32_t RAW_SLOT_MIN;
+    static const int32_t RAW_SLOT_MAX;
     static const float X_PRECISION;
     static const float Y_PRECISION;
 
@@ -2398,6 +2494,9 @@
         ORIENTATION = 1 << 4,
         MINOR = 1 << 5,
         ID = 1 << 6,
+        DISTANCE = 1 << 7,
+        SLOT = 1 << 8,
+        TOOL_TYPE = 1 << 9,
     };
 
     void prepareDisplay(int32_t orientation);
@@ -2420,8 +2519,12 @@
 const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = RAW_TOUCH_MAX;
 const int32_t TouchInputMapperTest::RAW_ORIENTATION_MIN = -7;
 const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7;
+const int32_t TouchInputMapperTest::RAW_DISTANCE_MIN = 0;
+const int32_t TouchInputMapperTest::RAW_DISTANCE_MAX = 7;
 const int32_t TouchInputMapperTest::RAW_ID_MIN = 0;
 const int32_t TouchInputMapperTest::RAW_ID_MAX = 9;
+const int32_t TouchInputMapperTest::RAW_SLOT_MIN = 0;
+const int32_t TouchInputMapperTest::RAW_SLOT_MAX = 9;
 const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH;
 const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT;
 
@@ -2470,6 +2573,8 @@
     void processUp(SingleTouchInputMapper* mappery);
     void processPressure(SingleTouchInputMapper* mapper, int32_t pressure);
     void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor);
+    void processDistance(SingleTouchInputMapper* mapper, int32_t distance);
+    void processKey(SingleTouchInputMapper* mapper, int32_t code, int32_t value);
     void processSync(SingleTouchInputMapper* mapper);
 };
 
@@ -2492,6 +2597,10 @@
         mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH,
                 RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
     }
+    if (axes & DISTANCE) {
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_DISTANCE,
+                RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
+    }
 }
 
 void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
@@ -2519,6 +2628,16 @@
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, 0, toolMajor, 0);
 }
 
+void SingleTouchInputMapperTest::processDistance(
+        SingleTouchInputMapper* mapper, int32_t distance) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_DISTANCE, 0, distance, 0);
+}
+
+void SingleTouchInputMapperTest::processKey(
+        SingleTouchInputMapper* mapper, int32_t code, int32_t value) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, 0, value, 0);
+}
+
 void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
 }
@@ -2581,14 +2700,14 @@
     int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
     processDown(mapper, x, y);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 }
@@ -2610,14 +2729,14 @@
     int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
     processDown(mapper, x, y);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 }
@@ -2656,13 +2775,13 @@
     int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
     processDown(mapper, x, y);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Reset.  Since key is down, synthesize key up.
     mapper->reset();
 
-    FakeInputDispatcher::NotifyKeyArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     //ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2689,18 +2808,18 @@
     int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
     processDown(mapper, x, y);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Release virtual key.
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
     // Reset.  Since no key is down, nothing happens.
     mapper->reset();
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
@@ -2714,7 +2833,7 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyKeyArgs args;
+    NotifyKeyArgs args;
 
     // Press virtual key.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -2722,7 +2841,7 @@
     processDown(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2738,7 +2857,7 @@
     processUp(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -2751,7 +2870,7 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Should not have sent any motions.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
@@ -2765,7 +2884,7 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyKeyArgs keyArgs;
+    NotifyKeyArgs keyArgs;
 
     // Press virtual key.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -2773,7 +2892,7 @@
     processDown(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, keyArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source);
@@ -2791,7 +2910,7 @@
     processMove(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, keyArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source);
@@ -2804,8 +2923,8 @@
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState);
     ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime);
 
-    FakeInputDispatcher::NotifyMotionArgs motionArgs;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    NotifyMotionArgs motionArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -2819,7 +2938,7 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -2829,7 +2948,7 @@
     processMove(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -2843,7 +2962,7 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -2852,7 +2971,7 @@
     processUp(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -2866,14 +2985,14 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
     // Should not have sent any more keys or motions.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
@@ -2887,7 +3006,7 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyMotionArgs motionArgs;
+    NotifyMotionArgs motionArgs;
 
     // Initially go down out of bounds.
     int32_t x = -10;
@@ -2895,7 +3014,7 @@
     processDown(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 
     // Move into the display area.  Should generate a pointer down.
     x = 50;
@@ -2903,7 +3022,7 @@
     processMove(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -2917,7 +3036,7 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -2926,7 +3045,7 @@
     processUp(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -2940,14 +3059,14 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
     // Should not have sent any more keys or motions.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
@@ -2961,7 +3080,7 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyMotionArgs motionArgs;
+    NotifyMotionArgs motionArgs;
 
     // Down.
     int32_t x = 100;
@@ -2969,7 +3088,7 @@
     processDown(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -2983,7 +3102,7 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -2994,7 +3113,7 @@
     processMove(mapper, x, y);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3008,7 +3127,7 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3017,7 +3136,7 @@
     processUp(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3031,14 +3150,14 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
     // Should not have sent any more keys or motions.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
@@ -3049,20 +3168,20 @@
     addConfigurationProperty("touch.orientationAware", "0");
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Rotation 90.
     prepareDisplay(DISPLAY_ORIENTATION_90);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
     ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
@@ -3072,59 +3191,59 @@
     prepareAxes(POSITION);
     addMapperAndConfigure(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
+    NotifyMotionArgs args;
 
     // Rotation 0.
     prepareDisplay(DISPLAY_ORIENTATION_0);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
     ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 90.
     prepareDisplay(DISPLAY_ORIENTATION_90);
     processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
     ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 180.
     prepareDisplay(DISPLAY_ORIENTATION_180);
     processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
     ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 
     // Rotation 270.
     prepareDisplay(DISPLAY_ORIENTATION_270);
     processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
     ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
@@ -3132,7 +3251,7 @@
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
-    prepareAxes(POSITION | PRESSURE | TOOL);
+    prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE);
     addMapperAndConfigure(mapper);
 
     // These calculations are based on the input device calibration documentation.
@@ -3140,6 +3259,7 @@
     int32_t rawY = 200;
     int32_t rawPressure = 10;
     int32_t rawToolMajor = 12;
+    int32_t rawDistance = 0;
 
     float x = toDisplayX(rawX);
     float y = toDisplayY(rawY);
@@ -3147,16 +3267,388 @@
     float size = float(rawToolMajor) / RAW_TOOL_MAX;
     float tool = min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * size;
     float touch = min(tool * pressure, tool);
+    float distance = float(rawDistance);
 
     processDown(mapper, rawX, rawY);
     processPressure(mapper, rawPressure);
     processToolMajor(mapper, rawToolMajor);
+    processDistance(mapper, rawDistance);
     processSync(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            x, y, pressure, size, touch, touch, tool, tool, 0));
+            x, y, pressure, size, touch, touch, tool, tool, 0, distance));
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+    NotifyKeyArgs keyArgs;
+
+    processDown(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
+    // press BTN_LEFT, release BTN_LEFT
+    processKey(mapper, BTN_LEFT, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
+    processKey(mapper, BTN_LEFT, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+    processKey(mapper, BTN_RIGHT, 1);
+    processKey(mapper, BTN_MIDDLE, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            motionArgs.buttonState);
+
+    processKey(mapper, BTN_RIGHT, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_MIDDLE, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // press BTN_BACK, release BTN_BACK
+    processKey(mapper, BTN_BACK, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_BACK, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_SIDE, release BTN_SIDE
+    processKey(mapper, BTN_SIDE, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_SIDE, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_FORWARD, release BTN_FORWARD
+    processKey(mapper, BTN_FORWARD, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_FORWARD, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    // press BTN_EXTRA, release BTN_EXTRA
+    processKey(mapper, BTN_EXTRA, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_EXTRA, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    // press BTN_STYLUS, release BTN_STYLUS
+    processKey(mapper, BTN_STYLUS, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+
+    processKey(mapper, BTN_STYLUS, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // press BTN_STYLUS2, release BTN_STYLUS2
+    processKey(mapper, BTN_STYLUS2, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+    processKey(mapper, BTN_STYLUS2, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // release touch
+    processUp(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+
+    // default tool type is finger
+    processDown(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // eraser
+    processKey(mapper, BTN_TOOL_RUBBER, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
+
+    // stylus
+    processKey(mapper, BTN_TOOL_RUBBER, 0);
+    processKey(mapper, BTN_TOOL_PEN, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
+
+    // finger
+    processKey(mapper, BTN_TOOL_PEN, 0);
+    processKey(mapper, BTN_TOOL_FINGER, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // stylus trumps finger
+    processKey(mapper, BTN_TOOL_PEN, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
+
+    // eraser trumps stylus
+    processKey(mapper, BTN_TOOL_RUBBER, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
+
+    // back to default tool type
+    processKey(mapper, BTN_TOOL_RUBBER, 0);
+    processKey(mapper, BTN_TOOL_PEN, 0);
+    processKey(mapper, BTN_TOOL_FINGER, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, AKEYCODE_UNKNOWN, 0);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+
+    // initially hovering because BTN_TOUCH not sent yet, pressure defaults to 0
+    processKey(mapper, BTN_TOOL_FINGER, 1);
+    processMove(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // move a little
+    processMove(mapper, 150, 250);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // down when BTN_TOUCH is pressed, pressure defaults to 1
+    processKey(mapper, BTN_TOUCH, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // up when BTN_TOUCH is released, hover restored
+    processKey(mapper, BTN_TOUCH, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // exit hover when pointer goes away
+    processKey(mapper, BTN_TOOL_FINGER, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenAbsDistanceIsPresent_HoversIfItsValueIsGreaterThanZero) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION | DISTANCE);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+
+    // initially hovering because distance is 1, pressure defaults to 0
+    processDown(mapper, 100, 200);
+    processDistance(mapper, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    // move a little
+    processMove(mapper, 150, 250);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    // down when distance goes to 0, pressure defaults to 1
+    processDistance(mapper, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // up when distance goes to 1, hover restored
+    processDistance(mapper, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    // exit hover when pointer goes away
+    processUp(mapper);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
 }
 
 
@@ -3173,7 +3665,11 @@
     void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor);
     void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation);
     void processPressure(MultiTouchInputMapper* mapper, int32_t pressure);
+    void processDistance(MultiTouchInputMapper* mapper, int32_t distance);
     void processId(MultiTouchInputMapper* mapper, int32_t id);
+    void processSlot(MultiTouchInputMapper* mapper, int32_t slot);
+    void processToolType(MultiTouchInputMapper* mapper, int32_t toolType);
+    void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value);
     void processMTSync(MultiTouchInputMapper* mapper);
     void processSync(MultiTouchInputMapper* mapper);
 };
@@ -3209,10 +3705,23 @@
         mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE,
                 RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
     }
+    if (axes & DISTANCE) {
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_DISTANCE,
+                RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
+    }
     if (axes & ID) {
         mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
                 RAW_ID_MIN, RAW_ID_MAX, 0, 0);
     }
+    if (axes & SLOT) {
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_SLOT,
+                RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0);
+        mFakeEventHub->setAbsoluteAxisValue(DEVICE_ID, ABS_MT_SLOT, 0);
+    }
+    if (axes & TOOL_TYPE) {
+        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOOL_TYPE,
+                0, MT_TOOL_MAX, 0, 0);
+    }
 }
 
 void MultiTouchInputMapperTest::processPosition(
@@ -3251,11 +3760,31 @@
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, 0, pressure, 0);
 }
 
+void MultiTouchInputMapperTest::processDistance(
+        MultiTouchInputMapper* mapper, int32_t distance) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_DISTANCE, 0, distance, 0);
+}
+
 void MultiTouchInputMapperTest::processId(
         MultiTouchInputMapper* mapper, int32_t id) {
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, 0, id, 0);
 }
 
+void MultiTouchInputMapperTest::processSlot(
+        MultiTouchInputMapper* mapper, int32_t slot) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_SLOT, 0, slot, 0);
+}
+
+void MultiTouchInputMapperTest::processToolType(
+        MultiTouchInputMapper* mapper, int32_t toolType) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOOL_TYPE, 0, toolType, 0);
+}
+
+void MultiTouchInputMapperTest::processKey(
+        MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
+    process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, code, 0, value, 0);
+}
+
 void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0, 0, 0);
 }
@@ -3275,7 +3804,7 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyMotionArgs motionArgs;
+    NotifyMotionArgs motionArgs;
 
     // Two fingers down at once.
     int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -3285,7 +3814,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3299,12 +3828,12 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3321,9 +3850,9 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3336,7 +3865,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3352,9 +3881,9 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3365,7 +3894,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3382,14 +3911,14 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3403,7 +3932,7 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3414,7 +3943,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3428,7 +3957,7 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3441,7 +3970,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3458,9 +3987,9 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3471,7 +4000,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3488,14 +4017,14 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3509,7 +4038,7 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
@@ -3518,7 +4047,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
@@ -3532,14 +4061,14 @@
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
     ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
     ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
 
     // Should not have sent any more keys or motions.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
@@ -3552,7 +4081,7 @@
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
-    FakeInputDispatcher::NotifyMotionArgs motionArgs;
+    NotifyMotionArgs motionArgs;
 
     // Two fingers down at once.
     int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
@@ -3564,15 +4093,15 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(size_t(1), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
@@ -3581,9 +4110,9 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // Move.
     x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
@@ -3595,7 +4124,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
@@ -3603,9 +4132,9 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // First finger up.
     x2 += 15; y2 -= 20;
@@ -3614,7 +4143,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
@@ -3623,17 +4152,17 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(size_t(1), motionArgs.pointerCount);
     ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // Move.
     x2 += 20; y2 -= 25;
@@ -3642,13 +4171,13 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(size_t(1), motionArgs.pointerCount);
     ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // New finger down.
     int32_t x3 = 700, y3 = 300;
@@ -3660,7 +4189,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
@@ -3669,9 +4198,9 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // Second finger up.
     x3 += 30; y3 -= 20;
@@ -3680,7 +4209,7 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             motionArgs.action);
     ASSERT_EQ(size_t(2), motionArgs.pointerCount);
@@ -3689,40 +4218,211 @@
     ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
-            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(size_t(1), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // Last finger up.
     processMTSync(mapper);
     processSync(mapper);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
     ASSERT_EQ(size_t(1), motionArgs.pointerCount);
     ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
-            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
 
     // Should not have sent any more keys or motions.
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    prepareVirtualKeys();
+    addMapperAndConfigure(mapper);
+
+    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+    NotifyMotionArgs motionArgs;
+
+    // Two fingers down at once.
+    int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
+    processPosition(mapper, x1, y1);
+    processId(mapper, 1);
+    processSlot(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processId(mapper, 2);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            motionArgs.action);
+    ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // Move.
+    x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
+    processSlot(mapper, 0);
+    processPosition(mapper, x1, y1);
+    processSlot(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // First finger up.
+    x2 += 15; y2 -= 20;
+    processSlot(mapper, 0);
+    processId(mapper, -1);
+    processSlot(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            motionArgs.action);
+    ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // Move.
+    x2 += 20; y2 -= 25;
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // New finger down.
+    int32_t x3 = 700, y3 = 300;
+    processPosition(mapper, x2, y2);
+    processSlot(mapper, 0);
+    processId(mapper, 3);
+    processPosition(mapper, x3, y3);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            motionArgs.action);
+    ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // Second finger up.
+    x3 += 30; y3 -= 20;
+    processSlot(mapper, 1);
+    processId(mapper, -1);
+    processSlot(mapper, 0);
+    processPosition(mapper, x3, y3);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            motionArgs.action);
+    ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+            toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // Last finger up.
+    processId(mapper, -1);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // Should not have sent any more keys or motions.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR);
+    prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
     addMapperAndConfigure(mapper);
 
     // These calculations are based on the input device calibration documentation.
@@ -3733,6 +4433,7 @@
     int32_t rawToolMajor = 9;
     int32_t rawToolMinor = 8;
     int32_t rawPressure = 11;
+    int32_t rawDistance = 0;
     int32_t rawOrientation = 3;
     int32_t id = 5;
 
@@ -3745,6 +4446,7 @@
     float touchMajor = min(toolMajor * pressure, toolMajor);
     float touchMinor = min(toolMinor * pressure, toolMinor);
     float orientation = float(rawOrientation) / RAW_ORIENTATION_MAX * M_PI_2;
+    float distance = float(rawDistance);
 
     processPosition(mapper, rawX, rawY);
     processTouchMajor(mapper, rawTouchMajor);
@@ -3753,15 +4455,16 @@
     processToolMinor(mapper, rawToolMinor);
     processPressure(mapper, rawPressure);
     processOrientation(mapper, rawOrientation);
+    processDistance(mapper, rawDistance);
     processId(mapper, id);
     processMTSync(mapper);
     processSync(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(0, args.pointerProperties[0].id);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation));
+            x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation, distance));
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
@@ -3800,10 +4503,10 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, 0));
+            x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, 0, 0));
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) {
@@ -3850,18 +4553,18 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
 
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             args.action);
     ASSERT_EQ(size_t(2), args.pointerCount);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            x, y, pressure, size, touch, touch, tool, tool, 0));
+            x, y, pressure, size, touch, touch, tool, tool, 0, 0));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1],
-            x2, y2, pressure, size, touch, touch, tool, tool, 0));
+            x2, y2, pressure, size, touch, touch, tool, tool, 0, 0));
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) {
@@ -3899,10 +4602,395 @@
     processMTSync(mapper);
     processSync(mapper);
 
-    FakeInputDispatcher::NotifyMotionArgs args;
-    ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
-            x, y, pressure, size, touch, touch, tool, tool, 0));
+            x, y, pressure, size, touch, touch, tool, tool, 0, 0));
 }
 
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+    NotifyKeyArgs keyArgs;
+
+    processId(mapper, 1);
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+
+    // press BTN_LEFT, release BTN_LEFT
+    processKey(mapper, BTN_LEFT, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
+
+    processKey(mapper, BTN_LEFT, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+    processKey(mapper, BTN_RIGHT, 1);
+    processKey(mapper, BTN_MIDDLE, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
+            motionArgs.buttonState);
+
+    processKey(mapper, BTN_RIGHT, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_MIDDLE, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // press BTN_BACK, release BTN_BACK
+    processKey(mapper, BTN_BACK, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_BACK, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_SIDE, release BTN_SIDE
+    processKey(mapper, BTN_SIDE, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_SIDE, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
+
+    // press BTN_FORWARD, release BTN_FORWARD
+    processKey(mapper, BTN_FORWARD, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_FORWARD, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    // press BTN_EXTRA, release BTN_EXTRA
+    processKey(mapper, BTN_EXTRA, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    processKey(mapper, BTN_EXTRA, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
+
+    // press BTN_STYLUS, release BTN_STYLUS
+    processKey(mapper, BTN_STYLUS, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY, motionArgs.buttonState);
+
+    processKey(mapper, BTN_STYLUS, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // press BTN_STYLUS2, release BTN_STYLUS2
+    processKey(mapper, BTN_STYLUS2, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
+
+    processKey(mapper, BTN_STYLUS2, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+
+    // release touch
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.buttonState);
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+
+    // default tool type is finger
+    processId(mapper, 1);
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // eraser
+    processKey(mapper, BTN_TOOL_RUBBER, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
+
+    // stylus
+    processKey(mapper, BTN_TOOL_RUBBER, 0);
+    processKey(mapper, BTN_TOOL_PEN, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
+
+    // finger
+    processKey(mapper, BTN_TOOL_PEN, 0);
+    processKey(mapper, BTN_TOOL_FINGER, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // stylus trumps finger
+    processKey(mapper, BTN_TOOL_PEN, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
+
+    // eraser trumps stylus
+    processKey(mapper, BTN_TOOL_RUBBER, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_ERASER, motionArgs.pointerProperties[0].toolType);
+
+    // MT tool type trumps BTN tool types: MT_TOOL_FINGER
+    processToolType(mapper, MT_TOOL_FINGER); // this is the first time we send MT_TOOL_TYPE
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // MT tool type trumps BTN tool types: MT_TOOL_PEN
+    processToolType(mapper, MT_TOOL_PEN);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_STYLUS, motionArgs.pointerProperties[0].toolType);
+
+    // back to default tool type
+    processToolType(mapper, -1); // use a deliberately undefined tool type, for testing
+    processKey(mapper, BTN_TOOL_RUBBER, 0);
+    processKey(mapper, BTN_TOOL_PEN, 0);
+    processKey(mapper, BTN_TOOL_FINGER, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT);
+    mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, AKEYCODE_UNKNOWN, 0);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+
+    // initially hovering because BTN_TOUCH not sent yet, pressure defaults to 0
+    processId(mapper, 1);
+    processPosition(mapper, 100, 200);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // move a little
+    processPosition(mapper, 150, 250);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // down when BTN_TOUCH is pressed, pressure defaults to 1
+    processKey(mapper, BTN_TOUCH, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // up when BTN_TOUCH is released, hover restored
+    processKey(mapper, BTN_TOUCH, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+
+    // exit hover when pointer goes away
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTDistanceIsPresent_HoversIfItsValueIsGreaterThanZero) {
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | DISTANCE);
+    addMapperAndConfigure(mapper);
+
+    NotifyMotionArgs motionArgs;
+
+    // initially hovering because distance is 1, pressure defaults to 0
+    processId(mapper, 1);
+    processPosition(mapper, 100, 200);
+    processDistance(mapper, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(100), toDisplayY(200), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    // move a little
+    processPosition(mapper, 150, 250);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    // down when distance goes to 0, pressure defaults to 1
+    processDistance(mapper, 0);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    // up when distance goes to 1, hover restored
+    processDistance(mapper, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 1, 0, 0, 0, 0, 0, 0, 0));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_ENTER, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+
+    // exit hover when pointer goes away
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_EXIT, motionArgs.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 1));
+}
+
+
 } // namespace android
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 3ec2a96..bdd8938 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -307,7 +307,8 @@
         public ParcelFileDescriptor fd;
         public final AtomicBoolean latch;
         public IFullBackupRestoreObserver observer;
-        public String password;     // filled in by the confirmation step
+        public String curPassword;     // filled in by the confirmation step
+        public String encryptPassword;
 
         FullParams() {
             latch = new AtomicBoolean(false);
@@ -458,8 +459,8 @@
             {
                 FullBackupParams params = (FullBackupParams)msg.obj;
                 (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
-                        params.includeShared, params.password, params.allApps, params.packages,
-                        params.latch)).run();
+                        params.includeShared, params.curPassword, params.encryptPassword,
+                        params.allApps, params.packages, params.latch)).run();
                 break;
             }
 
@@ -476,7 +477,7 @@
             case MSG_RUN_FULL_RESTORE:
             {
                 FullRestoreParams params = (FullRestoreParams)msg.obj;
-                (new PerformFullRestoreTask(params.fd, params.password,
+                (new PerformFullRestoreTask(params.fd, params.curPassword, params.encryptPassword,
                         params.observer, params.latch)).run();
                 break;
             }
@@ -1908,7 +1909,8 @@
         boolean mIncludeShared;
         boolean mAllApps;
         String[] mPackages;
-        String mUserPassword;
+        String mCurrentPassword;
+        String mEncryptPassword;
         AtomicBoolean mLatchObject;
         File mFilesDir;
         File mManifestFile;
@@ -1963,15 +1965,25 @@
         }
 
         PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 
-                boolean includeApks, boolean includeShared, String password,
-                boolean doAllApps, String[] packages, AtomicBoolean latch) {
+                boolean includeApks, boolean includeShared, String curPassword,
+                String encryptPassword, boolean doAllApps, String[] packages,
+                AtomicBoolean latch) {
             mOutputFile = fd;
             mObserver = observer;
             mIncludeApks = includeApks;
             mIncludeShared = includeShared;
             mAllApps = doAllApps;
             mPackages = packages;
-            mUserPassword = password;
+            mCurrentPassword = curPassword;
+            // when backing up, if there is a current backup password, we require that
+            // the user use a nonempty encryption password as well.  if one is supplied
+            // in the UI we use that, but if the UI was left empty we fall back to the
+            // current backup password (which was supplied by the user as well).
+            if (encryptPassword == null || "".equals(encryptPassword)) {
+                mEncryptPassword = curPassword;
+            } else {
+                mEncryptPassword = encryptPassword;
+            }
             mLatchObject = latch;
 
             mFilesDir = new File("/data/system");
@@ -2016,7 +2028,7 @@
 
             PackageInfo pkg = null;
             try {
-                boolean encrypting = (mUserPassword != null && mUserPassword.length() > 0);
+                boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
                 boolean compressing = COMPRESS_FULL_BACKUPS;
                 OutputStream finalOutput = ofstream;
 
@@ -2057,7 +2069,7 @@
                         // Verify that the given password matches the currently-active
                         // backup password, if any
                         if (hasBackupPassword()) {
-                            if (!passwordMatchesSaved(mUserPassword, PBKDF2_HASH_ROUNDS)) {
+                            if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) {
                                 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
                                 return;
                             }
@@ -2122,7 +2134,7 @@
                 OutputStream ofstream) throws Exception {
             // User key will be used to encrypt the master key.
             byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
-            SecretKey userKey = buildPasswordKey(mUserPassword, newUserSalt,
+            SecretKey userKey = buildPasswordKey(mEncryptPassword, newUserSalt,
                     PBKDF2_HASH_ROUNDS);
 
             // the master key is random for each backup
@@ -2445,7 +2457,8 @@
 
     class PerformFullRestoreTask implements Runnable {
         ParcelFileDescriptor mInputFile;
-        String mUserPassword;
+        String mCurrentPassword;
+        String mDecryptPassword;
         IFullBackupRestoreObserver mObserver;
         AtomicBoolean mLatchObject;
         IBackupAgent mAgent;
@@ -2469,10 +2482,11 @@
         // Packages we've already wiped data on when restoring their first file
         final HashSet<String> mClearedPackages = new HashSet<String>();
 
-        PerformFullRestoreTask(ParcelFileDescriptor fd, String password,
+        PerformFullRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
                 IFullBackupRestoreObserver observer, AtomicBoolean latch) {
             mInputFile = fd;
-            mUserPassword = password;
+            mCurrentPassword = curPassword;
+            mDecryptPassword = decryptPassword;
             mObserver = observer;
             mLatchObject = latch;
             mAgent = null;
@@ -2531,6 +2545,13 @@
             FileInputStream rawInStream = null;
             DataInputStream rawDataIn = null;
             try {
+                if (hasBackupPassword()) {
+                    if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) {
+                        if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
+                        return;
+                    }
+                }
+
                 mBytes = 0;
                 byte[] buffer = new byte[32 * 1024];
                 rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
@@ -2557,7 +2578,7 @@
                         if (s.equals("none")) {
                             // no more header to parse; we're good to go
                             okay = true;
-                        } else if (mUserPassword != null && mUserPassword.length() > 0) {
+                        } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
                             preCompressStream = decodeAesHeaderAndInitialize(s, rawInStream);
                             if (preCompressStream != null) {
                                 okay = true;
@@ -2635,7 +2656,7 @@
 
                     // decrypt the master key blob
                     Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
-                    SecretKey userKey = buildPasswordKey(mUserPassword, userSalt,
+                    SecretKey userKey = buildPasswordKey(mDecryptPassword, userSalt,
                             rounds);
                     byte[] IV = hexToByteArray(userIvHex);
                     IvParameterSpec ivSpec = new IvParameterSpec(IV);
@@ -4453,7 +4474,7 @@
     // is used to require a user-facing disclosure about the operation.
     @Override
     public void acknowledgeFullBackupOrRestore(int token, boolean allow,
-            String password, IFullBackupRestoreObserver observer) {
+            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
         if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
                 + " allow=" + allow);
 
@@ -4472,12 +4493,14 @@
                     mFullConfirmations.delete(token);
 
                     if (allow) {
-                        params.observer = observer;
-                        params.password = password;
                         final int verb = params instanceof FullBackupParams
                                 ? MSG_RUN_FULL_BACKUP
                                 : MSG_RUN_FULL_RESTORE;
 
+                        params.observer = observer;
+                        params.curPassword = curPassword;
+                        params.encryptPassword = encPpassword;
+
                         if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(verb, params);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9a9cc8f..2a724d4 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2484,7 +2484,7 @@
         synchronized (this) {
             if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
             if (mDefaultProxy == proxy) return;
-            if (!TextUtils.isEmpty(proxy.getHost())) {
+            if (proxy != null && !TextUtils.isEmpty(proxy.getHost())) {
                 mDefaultProxy = proxy;
             } else {
                 mDefaultProxy = null;
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 39d2b1c..a59b6c0 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -21,6 +21,8 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.provider.Settings.Secure.NETSTATS_ENABLED;
+import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -54,7 +56,6 @@
 import java.io.InputStreamReader;
 import java.net.Inet4Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -89,8 +90,10 @@
     private static final String KEY_IFACE = "iface";
     private static final String KEY_TAG_HEX = "acct_tag_hex";
     private static final String KEY_UID = "uid_tag_int";
-    private static final String KEY_RX = "rx_bytes";
-    private static final String KEY_TX = "tx_bytes";
+    private static final String KEY_RX_BYTES = "rx_bytes";
+    private static final String KEY_RX_PACKETS = "rx_packets";
+    private static final String KEY_TX_BYTES = "tx_bytes";
+    private static final String KEY_TX_PACKETS = "tx_packets";
 
     class NetdResponseCode {
         /* Keep in sync with system/netd/ResponseCode.h */
@@ -203,8 +206,7 @@
             Slog.d(TAG, "not enabling bandwidth control");
         }
 
-        SystemProperties.set(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED,
-                mBandwidthControlEnabled ? "1" : "0");
+        SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
     }
 
     public void registerObserver(INetworkManagementEventObserver obs) {
@@ -1249,6 +1251,9 @@
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
         final NetworkStats.Entry entry = new NetworkStats.Entry();
 
+        // TODO: remove knownLines check once 5087722 verified
+        final HashSet<String> knownLines = Sets.newHashSet();
+
         final ArrayList<String> keys = Lists.newArrayList();
         final ArrayList<String> values = Lists.newArrayList();
         final HashMap<String, String> parsed = Maps.newHashMap();
@@ -1266,14 +1271,18 @@
                 splitLine(line, values);
                 parseLine(keys, values, parsed);
 
+                if (!knownLines.add(line)) {
+                    throw new IllegalStateException("encountered duplicate proc entry");
+                }
+
                 try {
-                    // TODO: add rxPackets/txPackets once kernel exports
                     entry.iface = parsed.get(KEY_IFACE);
-                    entry.tag = NetworkManagementSocketTagger.kernelToTag(
-                            parsed.get(KEY_TAG_HEX));
-                    entry.uid = Integer.parseInt(parsed.get(KEY_UID));
-                    entry.rxBytes = Long.parseLong(parsed.get(KEY_RX));
-                    entry.txBytes = Long.parseLong(parsed.get(KEY_TX));
+                    entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX));
+                    entry.uid = getParsedInt(parsed, KEY_UID);
+                    entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES);
+                    entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS);
+                    entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES);
+                    entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS);
 
                     if (limitUid == UID_ALL || limitUid == entry.uid) {
                         stats.addValues(entry);
@@ -1291,6 +1300,16 @@
         return stats;
     }
 
+    private static int getParsedInt(HashMap<String, String> parsed, String key) {
+        final String value = parsed.get(key);
+        return value != null ? Integer.parseInt(value) : 0;
+    }
+
+    private static long getParsedLong(HashMap<String, String> parsed, String key) {
+        final String value = parsed.get(key);
+        return value != null ? Long.parseLong(value) : 0;
+    }
+
     /**
      * Build {@link NetworkStats} with detailed UID statistics.
      *
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 4ced83c..ca3b12d 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -69,8 +69,8 @@
     int mDisabled = 0;
 
     Object mLock = new Object();
-    // We usually call it lights out mode, but double negatives are annoying
-    boolean mLightsOn = true;
+    // encompasses lights-out mode and other flags defined on View
+    int mSystemUiVisibility = 0;
     boolean mMenuVisible = false;
     int mImeWindowVis = 0;
     int mImeBackDisposition;
@@ -301,22 +301,23 @@
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
+        if (SPEW) Slog.d(TAG, "setSystemUiVisibility(" + vis + ")");
+
         synchronized (mLock) {
-            final boolean lightsOn = (vis & View.STATUS_BAR_HIDDEN) == 0;
-            updateLightsOnLocked(lightsOn);
+            updateUiVisibilityLocked(vis);
             disableLocked(vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken,
                     "WindowManager.LayoutParams");
         }
     }
 
-    private void updateLightsOnLocked(final boolean lightsOn) {
-        if (mLightsOn != lightsOn) {
-            mLightsOn = lightsOn;
+    private void updateUiVisibilityLocked(final int vis) {
+        if (mSystemUiVisibility != vis) {
+            mSystemUiVisibility = vis;
             mHandler.post(new Runnable() {
                     public void run() {
                         if (mBar != null) {
                             try {
-                                mBar.setLightsOn(lightsOn);
+                                mBar.setSystemUiVisibility(vis);
                             } catch (RemoteException ex) {
                             }
                         }
@@ -392,7 +393,7 @@
         }
         synchronized (mLock) {
             switches[0] = gatherDisableActionsLocked();
-            switches[1] = mLightsOn ? 1 : 0;
+            switches[1] = mSystemUiVisibility;
             switches[2] = mMenuVisible ? 1 : 0;
             switches[3] = mImeWindowVis;
             switches[4] = mImeBackDisposition;
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index b2d9917..e97df84 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -73,6 +73,16 @@
         synchronized (mSpellCheckerMap) {
             buildSpellCheckerMapLocked(context, mSpellCheckerList, mSpellCheckerMap);
         }
+        SpellCheckerInfo sci = getCurrentSpellChecker(null);
+        if (sci == null) {
+            sci = findAvailSpellCheckerLocked(null, null);
+            if (sci != null) {
+                // Set the current spell checker if there is one or more spell checkers
+                // available. In this case, "sci" is the first one in the available spell
+                // checkers.
+                setCurrentSpellCheckerLocked(sci.getId());
+            }
+        }
     }
 
     private class TextServicesMonitor extends PackageMonitor {
@@ -85,12 +95,14 @@
                 if (sci == null) return;
                 final String packageName = sci.getPackageName();
                 final int change = isPackageDisappearing(packageName);
-                if (change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE) {
-                    // Package disappearing
-                    setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName));
-                } else if (isPackageModified(packageName)) {
-                    // Package modified
-                    setCurrentSpellChecker(findAvailSpellCheckerLocked(null, packageName));
+                if (// Package disappearing
+                        change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE
+                        // Package modified
+                        || isPackageModified(packageName)) {
+                    sci = findAvailSpellCheckerLocked(null, packageName);
+                    if (sci != null) {
+                        setCurrentSpellCheckerLocked(sci.getId());
+                    }
                 }
             }
         }
@@ -160,39 +172,34 @@
                 Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId);
             }
             if (TextUtils.isEmpty(curSpellCheckerId)) {
-                final SpellCheckerInfo sci = findAvailSpellCheckerLocked(null, null);
-                if (sci == null) return null;
-                // Set the current spell checker if there is one or more spell checkers
-                // available. In this case, "sci" is the first one in the available spell
-                // checkers.
-                setCurrentSpellChecker(sci);
-                return sci;
+                return null;
             }
             return mSpellCheckerMap.get(curSpellCheckerId);
         }
     }
 
     @Override
-    public void getSpellCheckerService(SpellCheckerInfo info, String locale,
+    public void getSpellCheckerService(String sciId, String locale,
             ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) {
         if (!mSystemReady) {
             return;
         }
-        if (info == null || tsListener == null || scListener == null) {
+        if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) {
             Slog.e(TAG, "getSpellCheckerService: Invalid input.");
             return;
         }
-        final String sciId = info.getId();
         synchronized(mSpellCheckerMap) {
             if (!mSpellCheckerMap.containsKey(sciId)) {
                 return;
             }
+            final SpellCheckerInfo sci = mSpellCheckerMap.get(sciId);
+            final int uid = Binder.getCallingUid();
             if (mSpellCheckerBindGroups.containsKey(sciId)) {
                 final SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId);
                 if (bindGroup != null) {
                     final InternalDeathRecipient recipient =
                             mSpellCheckerBindGroups.get(sciId).addListener(
-                                    tsListener, locale, scListener);
+                                    tsListener, locale, scListener, uid);
                     if (recipient == null) {
                         if (DBG) {
                             Slog.w(TAG, "Didn't create a death recipient.");
@@ -204,14 +211,22 @@
                         bindGroup.removeAll();
                     } else if (bindGroup.mSpellChecker != null) {
                         if (DBG) {
-                            Slog.w(TAG, "Existing bind found. Return a spell checker session now.");
+                            Slog.w(TAG, "Existing bind found. Return a spell checker session now. "
+                                    + "Listeners count = " + bindGroup.mListeners.size());
                         }
                         try {
                             final ISpellCheckerSession session =
                                     bindGroup.mSpellChecker.getISpellCheckerSession(
                                             recipient.mScLocale, recipient.mScListener);
-                            tsListener.onServiceConnected(session);
-                            return;
+                            if (session != null) {
+                                tsListener.onServiceConnected(session);
+                                return;
+                            } else {
+                                if (DBG) {
+                                    Slog.w(TAG, "Existing bind already expired. ");
+                                }
+                                bindGroup.removeAll();
+                            }
                         } catch (RemoteException e) {
                             Slog.e(TAG, "Exception in getting spell checker session: " + e);
                             bindGroup.removeAll();
@@ -221,7 +236,7 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                startSpellCheckerServiceInnerLocked(info, locale, tsListener, scListener);
+                startSpellCheckerServiceInnerLocked(sci, locale, tsListener, scListener, uid);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -230,7 +245,11 @@
     }
 
     private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale,
-            ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) {
+            ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
+            int uid) {
+        if (DBG) {
+            Slog.w(TAG, "Start spell checker session inner locked.");
+        }
         final String sciId = info.getId();
         final InternalServiceConnection connection = new InternalServiceConnection(
                 sciId, locale, scListener);
@@ -244,7 +263,7 @@
             return;
         }
         final SpellCheckerBindGroup group = new SpellCheckerBindGroup(
-                connection, tsListener, locale, scListener);
+                connection, tsListener, locale, scListener, uid);
         mSpellCheckerBindGroups.put(sciId, group);
     }
 
@@ -272,19 +291,39 @@
         }
     }
 
-    private void setCurrentSpellChecker(SpellCheckerInfo sci) {
-        if (DBG) {
-            Slog.w(TAG, "setCurrentSpellChecker: " + sci.getId());
+    @Override
+    public void setCurrentSpellChecker(String sciId) {
+        synchronized(mSpellCheckerMap) {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Requires permission "
+                        + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+            }
+            setCurrentSpellCheckerLocked(sciId);
         }
-        if (sci == null || mSpellCheckerMap.containsKey(sci.getId())) return;
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.SPELL_CHECKER_SERVICE, sci == null ? "" : sci.getId());
+    }
+
+    private void setCurrentSpellCheckerLocked(String sciId) {
+        if (DBG) {
+            Slog.w(TAG, "setCurrentSpellChecker: " + sciId);
+        }
+        if (TextUtils.isEmpty(sciId) || !mSpellCheckerMap.containsKey(sciId)) return;
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.SPELL_CHECKER_SERVICE, sciId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     // SpellCheckerBindGroup contains active text service session listeners.
     // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from
     // mSpellCheckerBindGroups
     private class SpellCheckerBindGroup {
+        private final String TAG = SpellCheckerBindGroup.class.getSimpleName();
         private final InternalServiceConnection mInternalConnection;
         private final ArrayList<InternalDeathRecipient> mListeners =
                 new ArrayList<InternalDeathRecipient>();
@@ -293,10 +332,10 @@
 
         public SpellCheckerBindGroup(InternalServiceConnection connection,
                 ITextServicesSessionListener listener, String locale,
-                ISpellCheckerSessionListener scListener) {
+                ISpellCheckerSessionListener scListener, int uid) {
             mInternalConnection = connection;
             mConnected = false;
-            addListener(listener, locale, scListener);
+            addListener(listener, locale, scListener, uid);
         }
 
         public void onServiceConnected(ISpellCheckerService spellChecker) {
@@ -310,7 +349,7 @@
                                 listener.mScLocale, listener.mScListener);
                         listener.mTsListener.onServiceConnected(session);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Exception in getting spell checker session: " + e);
+                        Slog.e(TAG, "Exception in getting the spell checker session: " + e);
                         removeAll();
                         return;
                     }
@@ -321,7 +360,7 @@
         }
 
         public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener,
-                String locale, ISpellCheckerSessionListener scListener) {
+                String locale, ISpellCheckerSessionListener scListener, int uid) {
             if (DBG) {
                 Slog.d(TAG, "addListener: " + locale);
             }
@@ -336,10 +375,9 @@
                         }
                     }
                     recipient = new InternalDeathRecipient(
-                            this, tsListener, locale, scListener);
+                            this, tsListener, locale, scListener, uid);
                     scListener.asBinder().linkToDeath(recipient, 0);
-                    mListeners.add(new InternalDeathRecipient(
-                            this, tsListener, locale, scListener));
+                    mListeners.add(recipient);
                 } catch(RemoteException e) {
                     // do nothing
                 }
@@ -350,7 +388,7 @@
 
         public void removeListener(ISpellCheckerSessionListener listener) {
             if (DBG) {
-                Slog.d(TAG, "remove listener");
+                Slog.w(TAG, "remove listener: " + listener.hashCode());
             }
             synchronized(mSpellCheckerMap) {
                 final int size = mListeners.size();
@@ -359,11 +397,17 @@
                 for (int i = 0; i < size; ++i) {
                     final InternalDeathRecipient tempRecipient = mListeners.get(i);
                     if(tempRecipient.hasSpellCheckerListener(listener)) {
+                        if (DBG) {
+                            Slog.w(TAG, "found existing listener.");
+                        }
                         removeList.add(tempRecipient);
                     }
                 }
                 final int removeSize = removeList.size();
                 for (int i = 0; i < removeSize; ++i) {
+                    if (DBG) {
+                        Slog.w(TAG, "Remove " + removeList.get(i));
+                    }
                     mListeners.remove(removeList.get(i));
                 }
                 cleanLocked();
@@ -385,8 +429,10 @@
 
         public void removeAll() {
             Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
-            mListeners.clear();
-            cleanLocked();
+            synchronized(mSpellCheckerMap) {
+                mListeners.clear();
+                cleanLocked();
+            }
         }
     }
 
@@ -426,17 +472,19 @@
         public final ISpellCheckerSessionListener mScListener;
         public final String mScLocale;
         private final SpellCheckerBindGroup mGroup;
+        public final int mUid;
         public InternalDeathRecipient(SpellCheckerBindGroup group,
                 ITextServicesSessionListener tsListener, String scLocale,
-                ISpellCheckerSessionListener scListener) {
+                ISpellCheckerSessionListener scListener, int uid) {
             mTsListener = tsListener;
             mScListener = scListener;
             mScLocale = scLocale;
             mGroup = group;
+            mUid = uid;
         }
 
         public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) {
-            return mScListener.equals(listener);
+            return listener.asBinder().equals(mScListener.asBinder());
         }
 
         @Override
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index c129b97..2460fd6 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -68,7 +68,6 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.service.wallpaper.ImageWallpaper;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 
@@ -150,8 +149,8 @@
      * Name of the component used to display bitmap wallpapers from either the gallery or
      * built-in wallpapers.
      */
-    ComponentName mImageWallpaperComponent = new ComponentName("android", 
-            ImageWallpaper.class.getName());
+    ComponentName mImageWallpaperComponent = new ComponentName("com.android.systemui",
+            "com.android.systemui.ImageWallpaper");
     
     WallpaperConnection mWallpaperConnection;
     long mLastDiedTime;
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index d0f8843..af01c5b 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -288,6 +288,7 @@
                         } else {
                             unbindAllServicesLocked();
                         }
+                        updateInputFilterLocked();
                         sendStateToClientsLocked();
                     }
                 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 66f88fc..14c6306 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -53,7 +53,7 @@
 import android.app.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -8067,6 +8067,21 @@
 
             if (needSep) pw.println(" ");
             needSep = true;
+            pw.println("  OOM levels:");
+            pw.print("    SYSTEM_ADJ: "); pw.println(SYSTEM_ADJ);
+            pw.print("    CORE_SERVER_ADJ: "); pw.println(CORE_SERVER_ADJ);
+            pw.print("    FOREGROUND_APP_ADJ: "); pw.println(FOREGROUND_APP_ADJ);
+            pw.print("    VISIBLE_APP_ADJ: "); pw.println(VISIBLE_APP_ADJ);
+            pw.print("    PERCEPTIBLE_APP_ADJ: "); pw.println(PERCEPTIBLE_APP_ADJ);
+            pw.print("    HEAVY_WEIGHT_APP_ADJ: "); pw.println(HEAVY_WEIGHT_APP_ADJ);
+            pw.print("    BACKUP_APP_ADJ: "); pw.println(BACKUP_APP_ADJ);
+            pw.print("    SECONDARY_SERVER_ADJ: "); pw.println(SECONDARY_SERVER_ADJ);
+            pw.print("    HOME_APP_ADJ: "); pw.println(HOME_APP_ADJ);
+            pw.print("    HIDDEN_APP_MIN_ADJ: "); pw.println(HIDDEN_APP_MIN_ADJ);
+            pw.print("    EMPTY_APP_ADJ: "); pw.println(EMPTY_APP_ADJ);
+
+            if (needSep) pw.println(" ");
+            needSep = true;
             pw.println("  Process OOM control:");
             dumpProcessOomList(pw, this, procs, "    ",
                     "Proc", "PERS", true);
@@ -8814,7 +8829,8 @@
                 pw.print("    ");
                 pw.print("keeping="); pw.print(r.keeping);
                 pw.print(" hidden="); pw.print(r.hidden);
-                pw.print(" empty="); pw.println(r.empty);
+                pw.print(" empty="); pw.print(r.empty);
+                pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
 
                 if (!r.keeping) {
                     if (r.lastWakeTime != 0) {
@@ -9226,6 +9242,7 @@
         app.foregroundServices = false;
         app.foregroundActivities = false;
         app.hasShownUi = false;
+        app.hasAboveClient = false;
 
         killServicesLocked(app, allowRestart);
 
@@ -10452,6 +10469,9 @@
                 activity.connections.add(c);
             }
             b.client.connections.add(c);
+            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                b.client.hasAboveClient = true;
+            }
             clist = mServiceConnections.get(binder);
             if (clist == null) {
                 clist = new ArrayList<ConnectionRecord>();
@@ -10523,6 +10543,9 @@
         }
         if (b.client != skipApp) {
             b.client.connections.remove(c);
+            if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                b.client.updateHasAboveClientLocked();
+            }
         }
         clist = mServiceConnections.get(binder);
         if (clist != null) {
@@ -12577,9 +12600,9 @@
             // an earlier hidden adjustment that isn't really for us... if
             // so, use the new hidden adjustment.
             if (!recursed && app.hidden) {
-                app.curAdj = hiddenAdj;
+                app.curAdj = app.curRawAdj = hiddenAdj;
             }
-            return app.curAdj;
+            return app.curRawAdj;
         }
 
         if (app.thread == null) {
@@ -12588,28 +12611,47 @@
             return (app.curAdj=EMPTY_APP_ADJ);
         }
 
+        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
+        app.adjSource = null;
+        app.adjTarget = null;
+        app.empty = false;
+        app.hidden = false;
+
+        final int activitiesSize = app.activities.size();
+
         if (app.maxAdj <= FOREGROUND_APP_ADJ) {
             // The max adjustment doesn't allow this app to be anything
             // below foreground, so it is not worth doing work for it.
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
             app.curRawAdj = app.maxAdj;
+            app.foregroundActivities = false;
             app.keeping = true;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+            // System process can do UI, and when they do we want to have
+            // them trim their memory after the user leaves the UI.  To
+            // facilitate this, here we need to determine whether or not it
+            // is currently showing UI.
+            app.systemNoUi = true;
+            if (app == TOP_APP) {
+                app.systemNoUi = false;
+            } else if (activitiesSize > 0) {
+                for (int j = 0; j < activitiesSize; j++) {
+                    final ActivityRecord r = app.activities.get(j);
+                    if (r.visible) {
+                        app.systemNoUi = false;
+                        break;
+                    }
+                }
+            }
             return (app.curAdj=app.maxAdj);
         }
 
         final boolean hadForegroundActivities = app.foregroundActivities;
 
-        app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
-        app.adjSource = null;
-        app.adjTarget = null;
-        app.keeping = false;
-        app.empty = false;
-        app.hidden = false;
         app.foregroundActivities = false;
-
-        final int activitiesSize = app.activities.size();
+        app.keeping = false;
+        app.systemNoUi = false;
 
         // Determine the importance of the process, starting with most
         // important to least, and assign an appropriate OOM adjustment.
@@ -12784,7 +12826,7 @@
                                 // Binding to ourself is not interesting.
                                 continue;
                             }
-                            if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                            if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                                 ProcessRecord client = cr.binding.client;
                                 int clientAdj = adj;
                                 int myHiddenAdj = hiddenAdj;
@@ -12825,15 +12867,32 @@
                                     }
                                 }
                                 if (adj > clientAdj) {
-                                    adj = clientAdj >= VISIBLE_APP_ADJ
-                                            ? clientAdj : VISIBLE_APP_ADJ;
-                                    if (!client.hidden) {
-                                        app.hidden = false;
+                                    // If this process has recently shown UI, and
+                                    // the process that is binding to it is less
+                                    // important than being visible, then we don't
+                                    // care about the binding as much as we care
+                                    // about letting this process get into the LRU
+                                    // list to be killed and restarted if needed for
+                                    // memory.
+                                    if (app.hasShownUi && clientAdj > PERCEPTIBLE_APP_ADJ) {
+                                        adjType = "bound-bg-ui-services";
+                                    } else {
+                                        if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+                                                |Context.BIND_IMPORTANT)) != 0) {
+                                            adj = clientAdj;
+                                        } else if (clientAdj >= VISIBLE_APP_ADJ) {
+                                            adj = clientAdj;
+                                        } else {
+                                            adj = VISIBLE_APP_ADJ;
+                                        }
+                                        if (!client.hidden) {
+                                            app.hidden = false;
+                                        }
+                                        if (client.keeping) {
+                                            app.keeping = true;
+                                        }
+                                        adjType = "service";
                                     }
-                                    if (client.keeping) {
-                                        app.keeping = true;
-                                    }
-                                    adjType = "service";
                                 }
                                 if (adjType != null) {
                                     app.adjType = adjType;
@@ -12848,21 +12907,22 @@
                                     }
                                 }
                             }
-                            ActivityRecord a = cr.activity;
-                            //if (a != null) {
-                            //    Slog.i(TAG, "Connection to " + a ": state=" + a.state);
-                            //}
-                            if (a != null && adj > FOREGROUND_APP_ADJ &&
-                                    (a.state == ActivityState.RESUMED
-                                     || a.state == ActivityState.PAUSING)) {
-                                adj = FOREGROUND_APP_ADJ;
-                                schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                app.hidden = false;
-                                app.adjType = "service";
-                                app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                        .REASON_SERVICE_IN_USE;
-                                app.adjSource = a;
-                                app.adjTarget = s.name;
+                            if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+                                ActivityRecord a = cr.activity;
+                                if (a != null && adj > FOREGROUND_APP_ADJ &&
+                                        (a.visible || a.state == ActivityState.RESUMED
+                                         || a.state == ActivityState.PAUSING)) {
+                                    adj = FOREGROUND_APP_ADJ;
+                                    if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                        schedGroup = Process.THREAD_GROUP_DEFAULT;
+                                    }
+                                    app.hidden = false;
+                                    app.adjType = "service";
+                                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                            .REASON_SERVICE_IN_USE;
+                                    app.adjSource = a;
+                                    app.adjTarget = s.name;
+                                }
                             }
                         }
                     }
@@ -12906,15 +12966,19 @@
                         int clientAdj = computeOomAdjLocked(
                             client, myHiddenAdj, TOP_APP, true);
                         if (adj > clientAdj) {
-                            adj = clientAdj > FOREGROUND_APP_ADJ
-                                    ? clientAdj : FOREGROUND_APP_ADJ;
+                            if (app.hasShownUi && clientAdj > PERCEPTIBLE_APP_ADJ) {
+                                app.adjType = "bg-ui-provider";
+                            } else {
+                                adj = clientAdj > FOREGROUND_APP_ADJ
+                                        ? clientAdj : FOREGROUND_APP_ADJ;
+                                app.adjType = "provider";
+                            }
                             if (!client.hidden) {
                                 app.hidden = false;
                             }
                             if (client.keeping) {
                                 app.keeping = true;
                             }
-                            app.adjType = "provider";
                             app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                                     .REASON_PROVIDER_IN_USE;
                             app.adjSource = client;
@@ -12955,6 +13019,25 @@
             app.keeping = true;
         }
 
+        if (app.hasAboveClient) {
+            // If this process has bound to any services with BIND_ABOVE_CLIENT,
+            // then we need to drop its adjustment to be lower than the service's
+            // in order to honor the request.  We want to drop it by one adjustment
+            // level...  but there is special meaning applied to various levels so
+            // we will skip some of them.
+            if (adj < FOREGROUND_APP_ADJ) {
+                // System process will not get dropped, ever
+            } else if (adj < VISIBLE_APP_ADJ) {
+                adj = VISIBLE_APP_ADJ;
+            } else if (adj < PERCEPTIBLE_APP_ADJ) {
+                adj = PERCEPTIBLE_APP_ADJ;
+            } else if (adj < HIDDEN_APP_MIN_ADJ) {
+                adj = HIDDEN_APP_MIN_ADJ;
+            } else if (adj < EMPTY_APP_ADJ) {
+                adj++;
+            }
+        }
+
         app.curAdj = adj;
         app.curSchedGroup = schedGroup;
 
@@ -12963,7 +13046,7 @@
                     app.foregroundActivities).sendToTarget();
         }
 
-        return adj;
+        return app.curRawAdj;
     }
 
     /**
@@ -13204,7 +13287,7 @@
 
         final boolean wasKeeping = app.keeping;
 
-        int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
+        computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
 
         if (app.curRawAdj != app.setRawAdj) {
             if (app.curRawAdj > FOREGROUND_APP_ADJ
@@ -13233,14 +13316,14 @@
 
             app.setRawAdj = app.curRawAdj;
         }
-        if (adj != app.setAdj) {
-            if (Process.setOomAdj(app.pid, adj)) {
+        if (app.curAdj != app.setAdj) {
+            if (Process.setOomAdj(app.pid, app.curAdj)) {
                 if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
                     TAG, "Set app " + app.processName +
-                    " oom adj to " + adj + " because " + app.adjType);
-                app.setAdj = adj;
+                    " oom adj to " + app.curAdj + " because " + app.adjType);
+                app.setAdj = app.curAdj;
             } else {
-                Slog.w(TAG, "Failed setting oom adj of " + app + " to " + adj);
+                Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
             }
         }
         if (app.setSchedGroup != app.curSchedGroup) {
@@ -13377,7 +13460,7 @@
             final int N = mLruProcesses.size();
             factor = numBg/3;
             step = 0;
-            int curLevel = ComponentCallbacks.TRIM_MEMORY_COMPLETE;
+            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (i=0; i<N; i++) {
                 ProcessRecord app = mLruProcesses.get(i);
                 if (app.curAdj >= HIDDEN_APP_MIN_ADJ && !app.killedBackground) {
@@ -13386,7 +13469,7 @@
                             app.thread.scheduleTrimMemory(curLevel);
                         } catch (RemoteException e) {
                         }
-                        if (curLevel >= ComponentCallbacks.TRIM_MEMORY_COMPLETE) {
+                        if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
                             // For these apps we will also finish their activities
                             // to help them free memory.
                             mMainStack.destroyActivitiesLocked(app, false);
@@ -13396,23 +13479,36 @@
                     step++;
                     if (step >= factor) {
                         switch (curLevel) {
-                            case ComponentCallbacks.TRIM_MEMORY_COMPLETE:
-                                curLevel = ComponentCallbacks.TRIM_MEMORY_MODERATE;
+                            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
                                 break;
-                            case ComponentCallbacks.TRIM_MEMORY_MODERATE:
-                                curLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND;
+                            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                                 break;
                         }
                     }
                 } else if (app.curAdj == HEAVY_WEIGHT_APP_ADJ) {
-                    if (app.trimMemoryLevel < ComponentCallbacks.TRIM_MEMORY_BACKGROUND
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
                             && app.thread != null) {
                         try {
-                            app.thread.scheduleTrimMemory(ComponentCallbacks.TRIM_MEMORY_BACKGROUND);
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                         } catch (RemoteException e) {
                         }
                     }
-                    app.trimMemoryLevel = ComponentCallbacks.TRIM_MEMORY_BACKGROUND;
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+                } else if ((app.curAdj > VISIBLE_APP_ADJ || app.systemNoUi)
+                        && app.pendingUiClean) {
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+                            && app.thread != null) {
+                        try {
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+                    app.pendingUiClean = false;
                 } else {
                     app.trimMemoryLevel = 0;
                 }
@@ -13421,7 +13517,21 @@
             final int N = mLruProcesses.size();
             for (i=0; i<N; i++) {
                 ProcessRecord app = mLruProcesses.get(i);
-                app.trimMemoryLevel = 0;
+                if ((app.curAdj > VISIBLE_APP_ADJ || app.systemNoUi)
+                        && app.pendingUiClean) {
+                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+                            && app.thread != null) {
+                        try {
+                            app.thread.scheduleTrimMemory(
+                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+                    app.pendingUiClean = false;
+                } else {
+                    app.trimMemoryLevel = 0;
+                }
             }
         }
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index cc58eaf..33b21ab 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -560,6 +560,7 @@
             showAskCompatModeDialogLocked(r);
             r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
             app.hasShownUi = true;
+            app.pendingUiClean = true;
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                     System.identityHashCode(r),
                     r.info, r.compat, r.icicle, results, newIntents, !andResume,
diff --git a/services/java/com/android/server/am/AppBindRecord.java b/services/java/com/android/server/am/AppBindRecord.java
index 9c57360..f1c54fa 100644
--- a/services/java/com/android/server/am/AppBindRecord.java
+++ b/services/java/com/android/server/am/AppBindRecord.java
@@ -26,7 +26,7 @@
 class AppBindRecord {
     final ServiceRecord service;    // The running service.
     final IntentBindRecord intent;  // The intent we are bound to.
-    final ProcessRecord client; // Who has started/bound the service.
+    final ProcessRecord client;     // Who has started/bound the service.
 
     final HashSet<ConnectionRecord> connections = new HashSet<ConnectionRecord>();
                                     // All ConnectionRecord for this client.
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 5b59363..a896ce4 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -23,6 +23,7 @@
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
@@ -66,7 +67,10 @@
     boolean setIsForeground;    // Running foreground UI when last set?
     boolean foregroundServices; // Running any services that are foreground?
     boolean foregroundActivities; // Running any activities that are foreground?
+    boolean systemNoUi;         // This is a system process, but not currently showing UI.
     boolean hasShownUi;         // Has UI been shown in this process since it was started?
+    boolean pendingUiClean;     // Want to clean up resources from showing UI?
+    boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
     boolean bad;                // True if disabled in the bad process list
     boolean killedBackground;   // True when proc has been killed due to too many bg
     String waitingToKill;       // Process is waiting to be killed when in the bg; reason
@@ -185,8 +189,11 @@
                 pw.print(" set="); pw.println(setAdj);
         pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
+                pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
-                pw.print(" hasShownUi="); pw.println(hasShownUi);
+        pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
+                pw.print(" pendingUiClean="); pw.print(pendingUiClean);
+                pw.print(" hasAboveClient="); pw.println(hasAboveClient);
         pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);
                 pw.print(" foregroundServices="); pw.print(foregroundServices);
                 pw.print(" forcingToForeground="); pw.println(forcingToForeground);
@@ -307,6 +314,18 @@
         deathRecipient = null;
     }
 
+    void updateHasAboveClientLocked() {
+        hasAboveClient = false;
+        if (connections.size() > 0) {
+            for (ConnectionRecord cr : connections) {
+                if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                    hasAboveClient = true;
+                    break;
+                }
+            }
+        }
+    }
+
     public String toShortString() {
         if (shortStringName != null) {
             return shortStringName;
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 6cc01f4..24188ca 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -17,6 +17,8 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.content.Intent.ACTION_SHUTDOWN;
@@ -56,6 +58,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
+import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -65,6 +68,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.NtpTrustedTime;
 import android.util.Slog;
@@ -150,9 +154,9 @@
 
     /** Set of currently active ifaces. */
     private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
-    /** Set of historical stats for known networks. */
+    /** Set of historical network layer stats for known networks. */
     private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
-    /** Set of historical stats for known UIDs. */
+    /** Set of historical network layer stats for known UIDs. */
     private HashMap<NetworkIdentitySet, LongSparseArray<NetworkStatsHistory>> mUidStats =
             Maps.newHashMap();
 
@@ -164,6 +168,10 @@
 
     private NetworkStats mLastUidSnapshot;
 
+    /** Data layer operation counters for splicing into other structures. */
+    private NetworkStats mOperations = new NetworkStats(0L, 10);
+    private NetworkStats mLastOperationsSnapshot;
+
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
 
@@ -381,9 +389,13 @@
                             entry.uid = uid;
                             entry.tag = tag;
                             entry.rxBytes = historyEntry.rxBytes;
+                            entry.rxPackets = historyEntry.rxPackets;
                             entry.txBytes = historyEntry.txBytes;
+                            entry.txPackets = historyEntry.txPackets;
+                            entry.operations = historyEntry.operations;
 
-                            if (entry.rxBytes > 0 || entry.txBytes > 0) {
+                            if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
+                                    || entry.txPackets > 0 || entry.operations > 0) {
                                 stats.combineValues(entry);
                             }
                         }
@@ -396,6 +408,41 @@
     }
 
     @Override
+    public NetworkStats getDataLayerSnapshotForUid(int uid) throws RemoteException {
+        if (Binder.getCallingUid() != uid) {
+            mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
+        }
+
+        // TODO: switch to data layer stats once kernel exports
+        // for now, read network layer stats and flatten across all ifaces
+        final NetworkStats networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
+        final NetworkStats dataLayer = new NetworkStats(
+                networkLayer.getElapsedRealtime(), networkLayer.size());
+
+        NetworkStats.Entry entry = null;
+        for (int i = 0; i < networkLayer.size(); i++) {
+            entry = networkLayer.getValues(i, entry);
+            entry.iface = IFACE_ALL;
+            dataLayer.combineValues(entry);
+        }
+
+        // splice in operation counts
+        dataLayer.spliceOperationsFrom(mOperations);
+        return dataLayer;
+    }
+
+    @Override
+    public void incrementOperationCount(int uid, int tag, int operationCount) {
+        if (Binder.getCallingUid() != uid) {
+            mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        }
+
+        synchronized (mStatsLock) {
+            mOperations.combineValues(IFACE_ALL, uid, tag, 0L, 0L, 0L, 0L, operationCount);
+        }
+    }
+
+    @Override
     public void forceUpdate() {
         mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
 
@@ -533,7 +580,7 @@
         final NetworkStats uidSnapshot;
         try {
             networkSnapshot = mNetworkManager.getNetworkStatsSummary();
-            uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
+            uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsUidDetail(UID_ALL) : null;
         } catch (IllegalStateException e) {
             Slog.w(TAG, "problem reading network stats: " + e);
             return;
@@ -592,7 +639,7 @@
             }
 
             final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident);
-            history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes);
+            history.recordData(timeStart, currentTime, entry);
         }
 
         // trim any history beyond max
@@ -615,9 +662,12 @@
         ensureUidStatsLoadedLocked();
 
         final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot);
+        final NetworkStats operationsDelta = computeStatsDelta(
+                mLastOperationsSnapshot, mOperations);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
+        NetworkStats.Entry operationsEntry = null;
         for (int i = 0; i < delta.size(); i++) {
             entry = delta.getValues(i, entry);
             final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
@@ -625,9 +675,16 @@
                 continue;
             }
 
+            // splice in operation counts since last poll
+            final int j = operationsDelta.findIndex(IFACE_ALL, entry.uid, entry.tag);
+            if (j != -1) {
+                operationsEntry = operationsDelta.getValues(j, operationsEntry);
+                entry.operations = operationsEntry.operations;
+            }
+
             final NetworkStatsHistory history = findOrCreateUidStatsLocked(
                     ident, entry.uid, entry.tag);
-            history.recordData(timeStart, currentTime, entry.rxBytes, entry.txBytes);
+            history.recordData(timeStart, currentTime, entry);
         }
 
         // trim any history beyond max
@@ -648,6 +705,8 @@
         }
 
         mLastUidSnapshot = uidSnapshot;
+        mLastOperationsSnapshot = mOperations;
+        mOperations = new NetworkStats(0L, 10);
     }
 
     /**
@@ -980,7 +1039,7 @@
                         final int tag = unpackTag(packed);
                         final NetworkStatsHistory history = uidStats.valueAt(i);
                         pw.print("    UID="); pw.print(uid);
-                        pw.print(" tag="); pw.println(tag);
+                        pw.print(" tag=0x"); pw.println(Integer.toHexString(tag));
                         history.dump("    ", pw, fullHistory);
                     }
                 }
@@ -1028,7 +1087,9 @@
         if (before != null) {
             return current.subtractClamped(before);
         } else {
-            return current;
+            // this is first snapshot; to prevent from double-counting we only
+            // observe traffic occuring between known snapshots.
+            return new NetworkStats(0L, 10);
         }
     }
 
@@ -1114,5 +1175,4 @@
             return DAY_IN_MILLIS;
         }
     }
-
 }
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 11cb555..dcf040a 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -357,7 +357,7 @@
             // wait for the transition to complete.
             // give up after 1 second.
             for (int i = 0; i < 20; i++) {
-                // State transition is done when sys.usb.conf.done is set to the new configuration
+                // State transition is done when sys.usb.state is set to the new configuration
                 if (state.equals(SystemProperties.get("sys.usb.state"))) return true;
                 try {
                     // try again in 50ms
@@ -376,42 +376,64 @@
             return waitForState(config);
         }
 
-        private void doSetCurrentFunctions(String functions) {
-            if (!mCurrentFunctions.equals(functions)) {
-                if (!setUsbConfig("none") || !setUsbConfig(functions)) {
-                    Slog.e(TAG, "Failed to switch USB configuration to " + functions);
-                    // revert to previous configuration if we fail
-                    setUsbConfig(mCurrentFunctions);
-                } else {
-                    mCurrentFunctions = functions;
-                }
-            }
-        }
-
         private void setAdbEnabled(boolean enable) {
             if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
             if (enable != mAdbEnabled) {
                 mAdbEnabled = enable;
-                String functions;
                 // Due to the persist.sys.usb.config property trigger, changing adb state requires
                 // switching to default function
-                if (enable) {
-                    functions = addFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB);
-                } else {
-                    functions = removeFunction(mDefaultFunctions, UsbManager.USB_FUNCTION_ADB);
-                }
-                setCurrentFunction(functions, true);
+                setEnabledFunctions(mDefaultFunctions, false);
                 updateAdbNotification();
             }
         }
 
-        private void setEnabledFunctions(String functionList) {
+        private void setEnabledFunctions(String functions, boolean makeDefault) {
             if (mAdbEnabled) {
-                functionList = addFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+                functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
             } else {
-                functionList = removeFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+                functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
             }
-            doSetCurrentFunctions(functionList);
+
+            if (functions != null && makeDefault) {
+                if (!mDefaultFunctions.equals(functions)) {
+                    if (!setUsbConfig("none")) {
+                        Slog.e(TAG, "Failed to disable USB");
+                        // revert to previous configuration if we fail
+                        setUsbConfig(mCurrentFunctions);
+                        return;
+                    }
+                    // setting this property will also change the current USB state
+                    // via a property trigger
+                    SystemProperties.set("persist.sys.usb.config", functions);
+                    if (waitForState(functions)) {
+                        mCurrentFunctions = functions;
+                        mDefaultFunctions = functions;
+                    } else {
+                        Slog.e(TAG, "Failed to switch persistent USB config to " + functions);
+                        // revert to previous configuration if we fail
+                        SystemProperties.set("persist.sys.usb.config", mDefaultFunctions);
+                    }
+                }
+            } else {
+                if (functions == null) {
+                    functions = mDefaultFunctions;
+                }
+                if (!mCurrentFunctions.equals(functions)) {
+                    if (!setUsbConfig("none")) {
+                        Slog.e(TAG, "Failed to disable USB");
+                        // revert to previous configuration if we fail
+                        setUsbConfig(mCurrentFunctions);
+                        return;
+                    }
+                    if (setUsbConfig(functions)) {
+                        mCurrentFunctions = functions;
+                    } else {
+                        Slog.e(TAG, "Failed to switch USB config to " + functions);
+                        // revert to previous configuration if we fail
+                        setUsbConfig(mCurrentFunctions);
+                    }
+                }
+            }
         }
 
         private void updateCurrentAccessory() {
@@ -433,7 +455,7 @@
                 // make sure accessory mode is off
                 // and restore default functions
                 Slog.d(TAG, "exited USB accessory mode");
-                setEnabledFunctions(mDefaultFunctions);
+                setEnabledFunctions(mDefaultFunctions, false);
 
                 if (mCurrentAccessory != null) {
                     if (mBootCompleted) {
@@ -476,7 +498,7 @@
 
                     if (!mConnected) {
                         // restore defaults when USB is disconnected
-                        doSetCurrentFunctions(mDefaultFunctions);
+                        setEnabledFunctions(mDefaultFunctions, false);
                     }
                     if (mBootCompleted) {
                         updateUsbState();
@@ -488,25 +510,7 @@
                 case MSG_SET_CURRENT_FUNCTION:
                     String function = (String)msg.obj;
                     boolean makeDefault = (msg.arg1 == 1);
-                    if (function != null && makeDefault) {
-                        if (mAdbEnabled) {
-                            function = addFunction(function, UsbManager.USB_FUNCTION_ADB);
-                        }
-
-                        setUsbConfig("none");
-                        // setting this property will change the current USB state
-                        // via a property trigger
-                        SystemProperties.set("persist.sys.usb.config", function);
-                        if (waitForState(function)) {
-                            mCurrentFunctions = function;
-                            mDefaultFunctions = function;
-                        }
-                    } else {
-                        if (function == null) {
-                            function = mDefaultFunctions;
-                        }
-                        setEnabledFunctions(function);
-                    }
+                    setEnabledFunctions(function, makeDefault);
                     break;
                 case MSG_SYSTEM_READY:
                     updateUsbNotification();
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f67c82e..b178e49 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -22,7 +22,7 @@
 	LOCAL_CFLAGS += -DNO_RGBX_8888
 endif
 ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
-	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
 endif
 
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7d6a14d..886bb2a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -105,6 +105,7 @@
 // in the purgatory list
 void Layer::onRemoved()
 {
+    mSurfaceTexture->abandon();
 }
 
 sp<ISurface> Layer::createSurface()
@@ -326,8 +327,9 @@
 {
     // if we don't have a buffer yet, we're translucent regardless of the
     // layer's opaque flag.
-    if (mActiveBuffer == 0)
+    if (mActiveBuffer == 0) {
         return false;
+    }
 
     // if the layer has the opaque flag, then we're always opaque,
     // otherwise we use the current buffer's format.
@@ -411,6 +413,8 @@
 void Layer::lockPageFlip(bool& recomputeVisibleRegions)
 {
     if (mQueuedFrames > 0) {
+        const bool oldOpacity = isOpaque();
+
         // signal another event if we have more frames pending
         if (android_atomic_dec(&mQueuedFrames) > 1) {
             mFlinger->signalEvent();
@@ -438,9 +442,8 @@
             mFlinger->invalidateHwcGeometry();
         }
 
-        const bool opacity(getOpacityForFormat(mActiveBuffer->format));
-        if (opacity != mCurrentOpacity) {
-            mCurrentOpacity = opacity;
+        mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
+        if (oldOpacity != isOpaque()) {
             recomputeVisibleRegions = true;
         }
 
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 91e010f..5973e76 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -86,6 +86,32 @@
     return res;
 }
 
+status_t SurfaceTextureLayer::connect(int api) {
+    status_t err = SurfaceTexture::connect(api);
+    if (err == NO_ERROR) {
+        switch(api) {
+            case NATIVE_WINDOW_API_MEDIA:
+            case NATIVE_WINDOW_API_CAMERA:
+                // Camera preview and videos are rate-limited on the producer
+                // side.  If enabled for this build, we use async mode to always
+                // show the most recent frame at the cost of requiring an
+                // additional buffer.
+#ifndef NEVER_DEFAULT_TO_ASYNC_MODE
+                err = setSynchronousMode(false);
+                break;
+#endif
+                // fall through to set synchronous mode when not defaulting to
+                // async mode.
+            deafult:
+                err = setSynchronousMode(true);
+                break;
+        }
+        if (err != NO_ERROR) {
+            disconnect(api);
+        }
+    }
+    return err;
+}
 
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/SurfaceTextureLayer.h b/services/surfaceflinger/SurfaceTextureLayer.h
index 29a9cbe..5d328b7 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.h
+++ b/services/surfaceflinger/SurfaceTextureLayer.h
@@ -50,6 +50,8 @@
 
     virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
             uint32_t format, uint32_t usage);
+
+    virtual status_t connect(int api);
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1620405..60be35a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -26,10 +26,12 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index 56ef995a..f628977 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -50,10 +50,9 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        final File canonicalFilesDir = getContext().getFilesDir().getCanonicalFile();
-        mTestProc = new File(canonicalFilesDir, "proc");
+        mTestProc = new File(getContext().getFilesDir(), "proc");
         if (mTestProc.exists()) {
-            Files.deleteRecursively(mTestProc);
+            IoUtils.deleteContents(mTestProc);
         }
 
         mService = NetworkManagementService.createForTest(mContext, mTestProc, true);
@@ -64,7 +63,7 @@
         mService = null;
 
         if (mTestProc.exists()) {
-            Files.deleteRecursively(mTestProc);
+            IoUtils.deleteContents(mTestProc);
         }
 
         super.tearDown();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index aab09ca..91fef22 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -197,9 +197,6 @@
         expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
         expectTime(System.currentTimeMillis());
 
-        // default behavior is background data enabled
-        expect(mConnManager.getBackgroundDataSetting()).andReturn(true);
-
         replay();
         mService.systemReady();
         verifyAndReset();
@@ -471,7 +468,7 @@
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 2L, 256L, 2L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 2L, 256L, 2L, 11);
         expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
                 .andReturn(stats).atLeastOnce();
 
@@ -547,7 +544,7 @@
         elapsedRealtime += MINUTE_IN_MILLIS;
         currentTime = TIME_MAR_10 + elapsedRealtime;
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
         state = new NetworkState[] { buildWifi() };
 
         {
@@ -574,7 +571,7 @@
         elapsedRealtime += MINUTE_IN_MILLIS;
         currentTime = TIME_MAR_10 + elapsedRealtime;
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1536L, 15L, 0L, 0L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1536L, 15L, 0L, 0L, 11);
 
         {
             expectTime(currentTime);
@@ -595,7 +592,7 @@
         elapsedRealtime += MINUTE_IN_MILLIS;
         currentTime = TIME_MAR_10 + elapsedRealtime;
         stats = new NetworkStats(elapsedRealtime, 1)
-                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 5120L, 512L, 0L, 0L);
+                .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 5120L, 512L, 0L, 0L, 22);
 
         {
             expectTime(currentTime);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index bd80af9..cf69fd5 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -160,22 +160,25 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
 
         // verify service has empty history for wifi
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // modify some number on wifi, and trigger poll event
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 1L, 2048L, 2L));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
+        assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
         verifyAndReset();
 
         // and bump forward again, with counters going higher. this is
@@ -185,13 +188,13 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 4096L, 4L, 8192L, 8L));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 4096L, 8192L);
+        assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
         verifyAndReset();
 
     }
@@ -211,26 +214,32 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
 
         // verify service has empty history for wifi
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // modify some number on wifi, and trigger poll event
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 8L, 2048L, 16L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 2)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 512L, 4L, 256L, 2L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1L, 128L, 1L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 20);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 10);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
-        assertUidTotal(sTemplateWifi, UID_RED, 512L, 256L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 128L);
+        assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 512L, 4L, 256L, 2L, 20);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 10);
         verifyAndReset();
 
         // graceful shutdown system, which should trigger persist of stats, and
@@ -241,7 +250,7 @@
         // we persisted them to file.
         expectDefaultSettings();
         replay();
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
         verifyAndReset();
 
         assertStatsFilesExist(true);
@@ -254,9 +263,9 @@
         mService.systemReady();
 
         // after systemReady(), we should have historical stats loaded again
-        assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
-        assertUidTotal(sTemplateWifi, UID_RED, 512L, 256L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 128L);
+        assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 512L, 4L, 256L, 2L, 20);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 10);
         verifyAndReset();
 
     }
@@ -278,20 +287,23 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // modify some number on wifi, and trigger poll event
         elapsedRealtime += 2 * HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 512L, 4L, 512L, 4L));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
         history = mService.getHistoryForNetwork(sTemplateWifi);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
         assertEquals(HOUR_IN_MILLIS, history.getBucketDuration());
         assertEquals(2, history.size());
         verifyAndReset();
@@ -301,14 +313,14 @@
         expectTime(TEST_START + elapsedRealtime);
         expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify identical stats, but spread across 4 buckets now
         history = mService.getHistoryForNetwork(sTemplateWifi);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
         assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration());
         assertEquals(4, history.size());
         verifyAndReset();
@@ -328,25 +340,32 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic on first network
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 2048L, 16L, 512L, 4L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 3)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 3)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 1536L, 12L, 512L, 4L)
                 .addValues(TEST_IFACE, UID_RED, 0xF00D, 512L, 4L, 512L, 4L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 512L, 4L, 0L, 0L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 15);
+        mService.incrementOperationCount(UID_RED, 0xF00D, 10);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateImsi1, 2048L, 512L);
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
-        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 512L);
-        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 0L);
+        assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 15);
+        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 5);
         verifyAndReset();
 
         // now switch networks; this also tests that we're okay with interfaces
@@ -356,7 +375,7 @@
         expectDefaultSettings();
         expectNetworkState(buildMobile3gState(IMSI_2));
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -369,22 +388,24 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 128L, 1L, 1024L, 8L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1L, 1024L, 8L));
 
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 10);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify original history still intact
-        assertNetworkTotal(sTemplateImsi1, 2048L, 512L);
-        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 512L);
-        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 0L);
+        assertNetworkTotal(sTemplateImsi1, 2048L, 16L, 512L, 4L, 0);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 512L, 4L, 15);
+        assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 4L, 0L, 0L, 5);
 
         // and verify new history also recorded under different template, which
         // verifies that we didn't cross the streams.
-        assertNetworkTotal(sTemplateImsi2, 128L, 1024L);
-        assertNetworkTotal(sTemplateWifi, 0L, 0L);
-        assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1024L);
+        assertNetworkTotal(sTemplateImsi2, 128L, 1L, 1024L, 8L, 0);
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1L, 1024L, 8L, 10);
         verifyAndReset();
 
     }
@@ -402,25 +423,32 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_ALL, TAG_NONE, 4128L, 258L, 544L, 34L));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 16L, 1L, 16L, 1L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 4096L, 258L, 512L, 32L)
                 .addValues(TEST_IFACE, UID_GREEN, TAG_NONE, 16L, 1L, 16L, 1L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 10);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 15);
+        mService.incrementOperationCount(UID_GREEN, TAG_NONE, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 4128L, 544L);
-        assertUidTotal(sTemplateWifi, UID_RED, 16L, 16L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 512L);
-        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 16L);
+        assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 16L, 1L, 16L, 1L, 10);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 258L, 512L, 32L, 15);
+        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 5);
         verifyAndReset();
 
         // now pretend two UIDs are uninstalled, which should migrate stats to
@@ -435,11 +463,11 @@
 
         // existing uid and total should remain unchanged; but removed UID
         // should be gone completely.
-        assertNetworkTotal(sTemplateWifi, 4128L, 544L);
-        assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L);
-        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 16L);
-        assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 528L);
+        assertNetworkTotal(sTemplateWifi, 4128L, 258L, 544L, 34L, 0);
+        assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L, 0L, 0L, 0);
+        assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 1L, 16L, 1L, 5);
+        assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 259L, 528L, 33L, 25);
         verifyAndReset();
 
     }
@@ -457,20 +485,26 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 1024L, 8L, 1024L, 8L)
                 .addValues(TEST_IFACE, UID_RED, 0xF00D, 512L, 4L, 512L, 4L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 10);
+        mService.incrementOperationCount(UID_RED, 0xF00D, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 1024L);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 10);
         verifyAndReset();
 
         // now switch over to 4g network
@@ -479,7 +513,7 @@
         expectDefaultSettings();
         expectNetworkState(buildMobile4gState());
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -491,14 +525,16 @@
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 512L, 4L, 256L, 2L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 5);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify that ALL_MOBILE template combines both
-        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 1280L);
+        assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 15);
 
         verifyAndReset();
 
@@ -537,32 +573,41 @@
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
         verifyAndReset();
 
+        // bootstrap with full polling event to prime stats
+        performBootstrapPoll(TEST_START, elapsedRealtime);
+
         // create some traffic for two apps
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L)
                 .addValues(TEST_IFACE, UID_RED, 0xF00D, 10L, 1L, 10L, 1L)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L));
 
+        mService.incrementOperationCount(UID_RED, TAG_NONE, 5);
+        mService.incrementOperationCount(UID_RED, 0xF00D, 1);
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 10);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        assertUidTotal(sTemplateWifi, UID_RED, 50L, 50L);
-        assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 512L);
+        assertUidTotal(sTemplateWifi, UID_RED, 50L, 5L, 50L, 5L, 5);
+        assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 8L, 512L, 4L, 10);
         verifyAndReset();
-        
+
         // now create more traffic in next hour, but only for one app
         elapsedRealtime += HOUR_IN_MILLIS;
         expectTime(TEST_START + elapsedRealtime);
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
-        expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+        expectNetworkStatsUidDetail(new NetworkStats(elapsedRealtime, 1)
                 .addValues(TEST_IFACE, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L));
 
+        mService.incrementOperationCount(UID_BLUE, TAG_NONE, 15);
+
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
@@ -570,28 +615,32 @@
         NetworkStats stats = mService.getSummaryForAllUid(
                 sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
         assertEquals(3, stats.size());
-        assertValues(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L);
-        assertValues(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L);
-        assertValues(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L);
+        assertValues(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 5L, 50L, 5L, 5);
+        assertValues(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 1L, 10L, 1L, 1);
+        assertValues(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 16L, 1024L, 8L, 15);
 
         // now verify that recent history only contains one uid
         final long currentTime = TEST_START + elapsedRealtime;
         stats = mService.getSummaryForAllUid(
                 sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
         assertEquals(1, stats.size());
-        assertValues(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L);
+        assertValues(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 8L, 512L, 4L, 5);
 
         verifyAndReset();
     }
 
-    private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long txBytes) {
+    private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
         final NetworkStatsHistory history = mService.getHistoryForNetwork(template);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
     }
 
-    private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long txBytes) {
+    private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
+            long txBytes, long txPackets, int operations) {
         final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE);
-        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, txBytes);
+        assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
     }
 
     private void expectSystemReady() throws Exception {
@@ -611,8 +660,8 @@
         expect(mNetManager.getNetworkStatsSummary()).andReturn(summary).atLeastOnce();
     }
 
-    private void expectNetworkStatsDetail(NetworkStats detail) throws Exception {
-        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+    private void expectNetworkStatsUidDetail(NetworkStats detail) throws Exception {
+        expect(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL))).andReturn(detail).atLeastOnce();
     }
 
     private void expectDefaultSettings() throws Exception {
@@ -639,6 +688,17 @@
         expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
     }
 
+    private void performBootstrapPoll(long testStart, long elapsedRealtime) throws Exception {
+        expectTime(testStart + elapsedRealtime);
+        expectDefaultSettings();
+        expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+        expectNetworkStatsUidDetail(buildEmptyStats(elapsedRealtime));
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+        verifyAndReset();
+    }
+
     private void assertStatsFilesExist(boolean exist) {
         final File networkFile = new File(mStatsDir, "netstats.bin");
         final File uidFile = new File(mStatsDir, "netstats_uid.bin");
@@ -652,23 +712,26 @@
     }
 
     private static void assertValues(NetworkStats stats, int i, String iface, int uid, int tag,
-            long rxBytes, long rxPackets, long txBytes, long txPackets) {
+            long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStats.Entry entry = stats.getValues(i, null);
-        assertEquals(iface, entry.iface);
-        assertEquals(uid, entry.uid);
-        assertEquals(tag, entry.tag);
-        assertEquals(rxBytes, entry.rxBytes);
-        // TODO: enable testing packet counts once stored in history
-//        assertEquals(rxPackets, entry.rxPackets);
-        assertEquals(txBytes, entry.txBytes);
-//        assertEquals(txPackets, entry.txPackets);
+        assertEquals("unexpected iface", iface, entry.iface);
+        assertEquals("unexpected uid", uid, entry.uid);
+        assertEquals("unexpected tag", tag, entry.tag);
+        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+        assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+        assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+        assertEquals("unexpected operations", operations, entry.operations);
     }
 
-    private static void assertValues(
-            NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
+    private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes,
+            long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+        assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+        assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+        assertEquals("unexpected operations", operations, entry.operations);
     }
 
     private static NetworkState buildWifiState() {
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index 50c18f0..c0870c7 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -289,7 +289,7 @@
     public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
         // TODO: provide elapsedRealtime mock to match TimeAuthority
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        stats.addValues(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, 0L, tx, 0L);
+        stats.addValues(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, 0L, tx, 0L, 0);
 
         expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
     }
diff --git a/test-runner/src/android/test/ActivityInstrumentationTestCase2.java b/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
index e8570bd..24b125e 100644
--- a/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
+++ b/test-runner/src/android/test/ActivityInstrumentationTestCase2.java
@@ -147,8 +147,8 @@
     protected void setUp() throws Exception {
         super.setUp();
         
-        boolean mInitialTouchMode = false;
-        Intent mActivityIntent = null;
+        mInitialTouchMode = false;
+        mActivityIntent = null;
     }
 
     @Override
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
index 833b61c..c241a62 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
@@ -101,6 +101,7 @@
             Canvas canvas = new Canvas(mBitmap);
             canvas.translate(-mPathBounds.left + mOffset * 1.5f, -mPathBounds.top + mOffset * 1.5f);
             canvas.drawPath(mPath, mMediumPaint);
+            canvas.setBitmap(null);
         }
 
         @Override
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index e75a079..da5f488 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -48,6 +48,8 @@
     StatusBarManager mStatusBarManager;
     NotificationManager mNotificationManager;
     Handler mHandler = new Handler();
+    int mUiVisibility = 0;
+    View mListView;
 
     View.OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener
             = new View.OnSystemUiVisibilityChangeListener() {
@@ -69,32 +71,52 @@
         return mTests;
     }
 
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        mListView = findViewById(android.R.id.list);
+        mListView.setOnSystemUiVisibilityChangeListener(mOnSystemUiVisibilityChangeListener);
+    }
+
     private Test[] mTests = new Test[] {
+        new Test("toggle LOW_PROFILE (lights out)") {
+            public void run() {
+                if (0 != (mUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
+                    mUiVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                } else {
+                    mUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
+                }
+                mListView.setSystemUiVisibility(mUiVisibility);
+            }
+        },
+        new Test("toggle HIDE_NAVIGATION") {
+            public void run() {
+                if (0 != (mUiVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) {
+                    mUiVisibility &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                } else {
+                    mUiVisibility |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+                }
+                mListView.setSystemUiVisibility(mUiVisibility);
+
+            }
+        },
+        new Test("clear SYSTEM_UI_FLAGs") {
+            public void run() {
+                mUiVisibility = 0;
+                mListView.setSystemUiVisibility(mUiVisibility);
+            }
+        },
+//        new Test("no setSystemUiVisibility") {
+//            public void run() {
+//                View v = findViewById(android.R.id.list);
+//                v.setOnSystemUiVisibilityChangeListener(null);
+//                v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+//            }
+//        },
         new Test("DISABLE_NAVIGATION") {
             public void run() {
-                View v = findViewById(android.R.id.list);
-                v.setSystemUiVisibility(View.STATUS_BAR_DISABLE_NAVIGATION);
-            }
-        },
-        new Test("STATUS_BAR_HIDDEN") {
-            public void run() {
-                View v = findViewById(android.R.id.list);
-                v.setOnSystemUiVisibilityChangeListener(mOnSystemUiVisibilityChangeListener);
-                v.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
-            }
-        },
-        new Test("STATUS_BAR_VISIBLE") {
-            public void run() {
-                View v = findViewById(android.R.id.list);
-                v.setOnSystemUiVisibilityChangeListener(mOnSystemUiVisibilityChangeListener);
-                v.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
-            }
-        },
-        new Test("no setSystemUiVisibility") {
-            public void run() {
-                View v = findViewById(android.R.id.list);
-                v.setOnSystemUiVisibilityChangeListener(null);
-                v.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+                mListView.setSystemUiVisibility(View.STATUS_BAR_DISABLE_NAVIGATION);
             }
         },
         new Test("Double Remove") {