Merge "Updated assets in status bar. DO NOT MERGE." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 17f824b..876fe00 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -143144,6 +143144,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_MANAGE_APPLICATIONS_SETTINGS"
  type="java.lang.String"
  transient="false"
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a180837..89812ab 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1272,6 +1272,15 @@
             return true;
         }
 
+        case GET_PROVIDER_MIME_TYPE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            String type = getProviderMimeType(uri);
+            reply.writeNoException();
+            reply.writeString(type);
+            return true;
+        }
+
         case NEW_URI_PERMISSION_OWNER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             String name = data.readString();
@@ -2847,6 +2856,20 @@
         reply.recycle();
     }
 
+    public String getProviderMimeType(Uri uri)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        uri.writeToParcel(data, 0);
+        mRemote.transact(GET_PROVIDER_MIME_TYPE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        String res = reply.readString();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     public IBinder newUriPermissionOwner(String name)
             throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6d1bf96..a10a823 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3287,12 +3287,20 @@
         }
     }
 
-    private final IContentProvider getProvider(Context context, String name) {
+    private final IContentProvider getExistingProvider(Context context, String name) {
         synchronized(mProviderMap) {
             final ProviderClientRecord pr = mProviderMap.get(name);
             if (pr != null) {
                 return pr.mProvider;
             }
+            return null;
+        }
+    }
+
+    private final IContentProvider getProvider(Context context, String name) {
+        IContentProvider existing = getExistingProvider(context, name);
+        if (existing != null) {
+            return existing;
         }
 
         IActivityManager.ContentProviderHolder holder = null;
@@ -3337,6 +3345,22 @@
         return provider;
     }
 
+    public final IContentProvider acquireExistingProvider(Context c, String name) {
+        IContentProvider provider = getExistingProvider(c, name);
+        if(provider == null)
+            return null;
+        IBinder jBinder = provider.asBinder();
+        synchronized(mProviderMap) {
+            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
+            if(prc == null) {
+                mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
+            } else {
+                prc.count++;
+            } //end else
+        } //end synchronized
+        return provider;
+    }
+
     public final boolean releaseProvider(IContentProvider provider) {
         if(provider == null) {
             return false;
@@ -3345,7 +3369,7 @@
         synchronized(mProviderMap) {
             ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
             if(prc == null) {
-                if(localLOGV) Slog.v(TAG, "releaseProvider::Weird shouldnt be here");
+                if(localLOGV) Slog.v(TAG, "releaseProvider::Weird shouldn't be here");
                 return false;
             } else {
                 prc.count--;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 09ef710..1bbf9ea 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1626,22 +1626,23 @@
     // ----------------------------------------------------------------------
 
     private static final class ApplicationContentResolver extends ContentResolver {
-        public ApplicationContentResolver(Context context,
-                                          ActivityThread mainThread)
-        {
+        public ApplicationContentResolver(Context context, ActivityThread mainThread) {
             super(context);
             mMainThread = mainThread;
         }
 
         @Override
-        protected IContentProvider acquireProvider(Context context, String name)
-        {
+        protected IContentProvider acquireProvider(Context context, String name) {
             return mMainThread.acquireProvider(context, name);
         }
 
         @Override
-        public boolean releaseProvider(IContentProvider provider)
-        {
+        protected IContentProvider acquireExistingProvider(Context context, String name) {
+            return mMainThread.acquireExistingProvider(context, name);
+        }
+
+        @Override
+        public boolean releaseProvider(IContentProvider provider) {
             return mMainThread.releaseProvider(provider);
         }
         
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f9bd461..28af0d3 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -313,6 +313,8 @@
 
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException;
+
+    public String getProviderMimeType(Uri uri) throws RemoteException;
     
     public IBinder newUriPermissionOwner(String name) throws RemoteException;
     public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg,
@@ -526,7 +528,8 @@
     int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111;
     int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112;
     int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113;
-    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
-    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
-    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+    int GET_PROVIDER_MIME_TYPE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114;
+    int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
+    int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
+    int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
 }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index dc4e9c4..1d6e8b8 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -544,6 +544,12 @@
      * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
      * Processes and Threads</a>.
      *
+     * <p>Note that there are no permissions needed for an application to
+     * access this information; if your content provider requires read and/or
+     * write permissions, or is not exported, all applications can still call
+     * this method regardless of their access permissions.  This allows them
+     * to retrieve the MIME type for a URI when dispatching intents.
+     *
      * @param uri the URI to query.
      * @return a MIME type string, or null if there is no type.
      */
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 69f7611..81ff414 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.accounts.Account;
+import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -176,6 +177,12 @@
 
     /** @hide */
     protected abstract IContentProvider acquireProvider(Context c, String name);
+    /** Providing a default implementation of this, to avoid having to change
+     * a lot of other things, but implementations of ContentResolver should
+     * implement it. @hide */
+    protected IContentProvider acquireExistingProvider(Context c, String name) {
+        return acquireProvider(c, name);
+    }
     /** @hide */
     public abstract boolean releaseProvider(IContentProvider icp);
 
@@ -186,20 +193,29 @@
      * using the content:// scheme.
      * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
      */
-    public final String getType(Uri url)
-    {
-        IContentProvider provider = acquireProvider(url);
-        if (provider == null) {
+    public final String getType(Uri url) {
+        IContentProvider provider = acquireExistingProvider(url);
+        if (provider != null) {
+            try {
+                return provider.getType(url);
+            } catch (RemoteException e) {
+                return null;
+            } catch (java.lang.Exception e) {
+                return null;
+            } finally {
+                releaseProvider(provider);
+            }
+        }
+
+        if (!SCHEME_CONTENT.equals(url.getScheme())) {
             return null;
         }
+
         try {
-            return provider.getType(url);
+            String type = ActivityManagerNative.getDefault().getProviderMimeType(url);
+            return type;
         } catch (RemoteException e) {
             return null;
-        } catch (java.lang.Exception e) {
-            return null;
-        } finally {
-            releaseProvider(provider);
         }
     }
 
@@ -717,14 +733,13 @@
     }
 
     /**
-     * Returns the content provider for the given content URI..
+     * Returns the content provider for the given content URI.
      *
      * @param uri The URI to a content provider
      * @return The ContentProvider for the given URI, or null if no content provider is found.
      * @hide
      */
-    public final IContentProvider acquireProvider(Uri uri)
-    {
+    public final IContentProvider acquireProvider(Uri uri) {
         if (!SCHEME_CONTENT.equals(uri.getScheme())) {
             return null;
         }
@@ -736,6 +751,25 @@
     }
 
     /**
+     * Returns the content provider for the given content URI if the process
+     * already has a reference on it.
+     *
+     * @param uri The URI to a content provider
+     * @return The ContentProvider for the given URI, or null if no content provider is found.
+     * @hide
+     */
+    public final IContentProvider acquireExistingProvider(Uri uri) {
+        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+            return null;
+        }
+        String auth = uri.getAuthority();
+        if (auth != null) {
+            return acquireExistingProvider(mContext, uri.getAuthority());
+        }
+        return null;
+    }
+
+    /**
      * @hide
      */
     public final IContentProvider acquireProvider(String name) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1f21672..e853120 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.util.XmlUtils;
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -336,7 +337,7 @@
         try {
             // We must read the stream for the JarEntry to retrieve
             // its certificates.
-            InputStream is = jarFile.getInputStream(je);
+            InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
             while (is.read(readBuffer, 0, readBuffer.length) != -1) {
                 // not using
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 69151df..1dd2420 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -355,6 +355,20 @@
             "android.settings.MANAGE_APPLICATIONS_SETTINGS";
 
     /**
+     * Activity Action: Show settings to manage all applications.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS =
+            "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
+
+    /**
      * Activity Action: Show screen of details about a particular application.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 733b535..220e023 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -23,19 +23,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.TextView;
-import android.widget.TextView.CursorController;
 
 // XXX this doesn't extend MetaKeyKeyListener because the signatures
 // don't match.  Need to figure that out.  Meanwhile the meta keys
 // won't work in fields that don't take input.
 
 public class ArrowKeyMovementMethod implements MovementMethod {
-    /**
-     * An optional controller for the cursor.
-     * Use {@link #setCursorController(CursorController)} to set this field.
-     */
-    private CursorController mCursorController;
-
     private boolean isCap(Spannable buffer) {
         return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
                 (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
@@ -192,21 +185,10 @@
     }
 
     public boolean onTrackballEvent(TextView widget, Spannable text, MotionEvent event) {
-        if (mCursorController != null) {
-            mCursorController.hide();
-        }
         return false;
     }
 
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
-        if (mCursorController != null) {
-            return onTouchEventCursor(widget, buffer, event);
-        } else {
-            return onTouchEventStandard(widget, buffer, event);
-        }
-    }
-
-    private boolean onTouchEventStandard(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1, initialScrollY = -1;
         if (event.getAction() == MotionEvent.ACTION_UP) {
             initialScrollX = Touch.getInitialScrollX(widget, buffer);
@@ -278,49 +260,6 @@
         return handled;
     }
 
-    private boolean onTouchEventCursor(TextView widget, Spannable buffer, MotionEvent event) {
-        if (widget.isFocused() && !widget.didTouchFocusSelect()) {
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_MOVE:
-                    widget.cancelLongPress();
-
-                    // Offset the current touch position (from controller to cursor)
-                    final float x = event.getX() + mCursorController.getOffsetX();
-                    final float y = event.getY() + mCursorController.getOffsetY();
-                    mCursorController.updatePosition((int) x, (int) y);
-                    return true;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    mCursorController = null;
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Defines the cursor controller.
-     *
-     * When set, this object can be used to handle touch events, that can be translated into cursor
-     * updates.
-     *
-     * {@link MotionEvent#ACTION_MOVE} events will call back the 
-     * {@link CursorController#updatePosition(int, int)} controller's method, passing the current
-     * finger coordinates (offset by {@link CursorController#getOffsetX()} and
-     * {@link CursorController#getOffsetY()}) as parameters. 
-     *
-     * When the gesture is finished (on a {@link MotionEvent#ACTION_UP} or
-     * {@link MotionEvent#ACTION_CANCEL} event), the controller is reset to null.
-     *
-     * @param cursorController A cursor controller implementation
-     *
-     * @hide
-     */
-    public void setCursorController(CursorController cursorController) {
-        mCursorController = cursorController;
-    }
-
     public boolean canSelectArbitrarily() {
         return true;
     }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index eebbc93..9c4aefe 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -583,6 +583,19 @@
          * also been set.
          */
         public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
+        
+        /** Window flag: when set the window will accept for touch events
+         * outside of its bounds to be sent to other windows that also
+         * support split touch.  When this flag is not set, the first pointer
+         * that goes down determines the window to which all subsequent touches
+         * go until all pointers go up.  When this flag is set, each pointer
+         * (not necessarily the first) that goes down determines the window
+         * to which all subsequent touches of that pointer will go until that
+         * pointer goes up thereby enabling touches with multiple pointers
+         * to be split across multiple windows.
+         * 
+         * {@hide} */
+        public static final int FLAG_SPLIT_TOUCH = 0x00800000;
 
         /** Window flag: *sigh* The lock screen wants to continue running its
          * animation while it is fading.  A kind-of hack to allow this.  Maybe
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index a10d647..7fe6190 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -85,6 +85,7 @@
     private boolean mTouchable = true;
     private boolean mOutsideTouchable = false;
     private boolean mClippingEnabled = true;
+    private boolean mSplitTouchEnabled;
 
     private OnTouchListener mTouchInterceptor;
     
@@ -565,6 +566,36 @@
     }
 
     /**
+     * <p>Indicates whether the popup window supports splitting touches.</p>
+     * 
+     * @return true if the touch splitting is enabled, false otherwise
+     * 
+     * @see #setSplitTouchEnabled(boolean)
+     * @hide
+     */
+    public boolean isSplitTouchEnabled() {
+        return mSplitTouchEnabled;
+    }
+
+    /**
+     * <p>Allows the popup window to split touches across other windows that also
+     * support split touch.  When this flag is not set, the first pointer
+     * that goes down determines the window to which all subsequent touches
+     * go until all pointers go up.  When this flag is set, each pointer
+     * (not necessarily the first) that goes down determines the window
+     * to which all subsequent touches of that pointer will go until that
+     * pointer goes up thereby enabling touches with multiple pointers
+     * to be split across multiple windows.</p>
+     *
+     * @param enabled true if the split touches should be enabled, false otherwise
+     * @see #isSplitTouchEnabled()
+     * @hide
+     */
+    public void setSplitTouchEnabled(boolean enabled) {
+        mSplitTouchEnabled = enabled;
+    }
+
+    /**
      * <p>Change the width and height measure specs that are given to the
      * window manager by the popup.  By default these are 0, meaning that
      * the current width or height is requested as an explicit size from
@@ -889,6 +920,9 @@
         if (!mClippingEnabled) {
             curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
         }
+        if (mSplitTouchEnabled) {
+            curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+        }
         return curFlags;
     }
     
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 034c617..6278192 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7601,16 +7601,10 @@
 
     /**
      * A CursorController instance can be used to control a cursor in the text.
-     *
-     * It can be passed to an {@link ArrowKeyMovementMethod} which can intercepts events
-     * and send them to this object instead of the cursor.
-     *
+     * It is not used outside of {@link TextView}.
      * @hide
      */
-    public interface CursorController {
-        /* Cursor fade-out animation duration, in milliseconds. */
-        static final int FADE_OUT_DURATION = 400;
-
+    private interface CursorController {
         /**
          * Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
          * See also {@link #hide()}.
@@ -7631,23 +7625,11 @@
         /**
          * Update the controller's position.
          */
-        public void updatePosition(int x, int y);
+        public void updatePosition(HandleView handle, int x, int y);
 
         public void updatePosition();
 
         /**
-         * The controller and the cursor's positions can be link by a fixed offset,
-         * computed when the controller is touched, and then maintained as it moves
-         * @return Horizontal offset between the controller and the cursor.
-         */
-        public float getOffsetX();
-
-        /**
-         * @return Vertical offset between the controller and the cursor.
-         */
-        public float getOffsetY();
-
-        /**
          * This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
          * a chance to become active and/or visible.
          * @param event The touch event
@@ -7670,6 +7652,7 @@
             mDrawable = handle;
             mContainer = new PopupWindow(TextView.this.mContext, null,
                     com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
         }
 
         @Override
@@ -7768,7 +7751,7 @@
                 TextView.this.getLocationOnScreen(coords);
                 final int x = (int) (rawX - coords[0] + 0.5f);
                 final int y = (int) (rawY - coords[1] + 0.5f);
-                mController.updatePosition(x, y);
+                mController.updatePosition(this, x, y);
                 break;
 
             case MotionEvent.ACTION_UP:
@@ -7802,13 +7785,11 @@
         }
     }
 
-    class InsertionPointCursorController implements CursorController {
+    private class InsertionPointCursorController implements CursorController {
         private static final int DELAY_BEFORE_FADE_OUT = 4100;
 
         // The cursor controller image
         private final HandleView mHandle;
-        // Offset between finger hot point on cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
 
         private final Runnable mHider = new Runnable() {
             public void run() {
@@ -7841,7 +7822,7 @@
             return mHandle.isShowing();
         }
 
-        public void updatePosition(int x, int y) {
+        public void updatePosition(HandleView handle, int x, int y) {
             final int previousOffset = getSelectionStart();
             int offset = getHysteresisOffset(x, y, previousOffset);
 
@@ -7865,24 +7846,14 @@
             mHandle.positionAtCursor(offset, true);
         }
 
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
-
         public boolean onTouchEvent(MotionEvent ev) {
             return false;
         }
     }
 
-    class SelectionModifierCursorController implements CursorController {
+    private class SelectionModifierCursorController implements CursorController {
         // The cursor controller images
         private HandleView mStartHandle, mEndHandle;
-        // Offset between finger hot point on active cursor controller and actual cursor
-        private float mOffsetX, mOffsetY;
         // The offsets of that last touch down event. Remembered to start selection there.
         private int mMinTouchOffset, mMaxTouchOffset;
         // Whether selection anchors are active
@@ -7916,15 +7887,15 @@
             hide();
         }
 
-        public void updatePosition(int x, int y) {
+        public void updatePosition(HandleView handle, int x, int y) {
             int selectionStart = getSelectionStart();
             int selectionEnd = getSelectionEnd();
 
-            final int previousOffset = mStartHandle.isDragging() ? selectionStart : selectionEnd;
+            final int previousOffset = handle == mStartHandle ? selectionStart : selectionEnd;
             int offset = getHysteresisOffset(x, y, previousOffset);
 
             // Handle the case where start and end are swapped, making sure start <= end
-            if (mStartHandle.isDragging()) {
+            if (handle == mStartHandle) {
                 if (offset <= selectionEnd) {
                     if (selectionStart == offset) {
                         return; // no change, no need to redraw;
@@ -8021,14 +7992,6 @@
             return mMaxTouchOffset;
         }
 
-        public float getOffsetX() {
-            return mOffsetX;
-        }
-
-        public float getOffsetY() {
-            return mOffsetY;
-        }
-
         /**
          * @return true iff this controller is currently used to move the selection start.
          */
diff --git a/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
index bdcb378..4fbfa4f 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
index 0876bc6..b07c7bc 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
index fa27ee4..de2f3c3 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
index 343e4ca..b5eab83 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d58dedc..ade22c7 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umožňuje aplikaci číst všechny navštívené adresy URL a záložky Prohlížeče."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zápis do historie a záložek Prohlížeče"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Umožní aplikaci změnit historii či záložky prohlížeče uložené v telefonu. Škodlivé aplikace mohou pomocí tohoto nastavení vymazat či pozměnit data Prohlížeče."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Změnit oprávnění prohlížeče poskytovat informace o zeměpisné poloze"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Umožňuje aplikaci změnit oprávnění prohlížeče poskytovat informace o zeměpisné poloze. Škodlivé aplikace mohou toto nastavení použít k odesílání informací o umístění na libovolné webové stránky."</string>
     <string name="save_password_message" msgid="767344687139195790">"Chcete, aby si prohlížeč zapamatoval toto heslo?"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 3e6a63c..eb0fffc 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillader, at programmet læser alle de webadresser, browseren har besøgt, og alle browserens bogmærker."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Tillader, at et program ændrer browseroversigten eller bogmærker, der er gemt på din telefon. Ondsindede programmer kan bruge dette til at slette eller ændre din browsers data."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Skift browsertilladelser for geografisk placering"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Giver et program tilladelse til at ændre browserens tilladelser for geografisk placering. Skadelige programmer kan bruge dette til at tillade, at placeringsoplysninger sendes til vilkårlige websteder."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ønsker du, at browseren skal huske denne adgangskode?"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c0ac156..bacb370 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -468,7 +468,7 @@
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
     <item msgid="7390473628275490700">"Windows Live"</item>
-    <item msgid="7882877134931458217">"Yahoo"</item>
+    <item msgid="7882877134931458217">"Yahoo!"</item>
     <item msgid="5035376313200585242">"Skype"</item>
     <item msgid="7532363178459444943">"QQ"</item>
     <item msgid="3713441034299660749">"Google Talk"</item>
@@ -515,7 +515,7 @@
     <string name="imProtocolCustom" msgid="6919453836618749992">"Benutzerdefiniert"</string>
     <string name="imProtocolAim" msgid="7050360612368383417">"AIM"</string>
     <string name="imProtocolMsn" msgid="144556545420769442">"Windows Live"</string>
-    <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
+    <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo!"</string>
     <string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
     <string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
     <string name="imProtocolGoogleTalk" msgid="3808393979157698766">"Google Talk"</string>
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Ermöglicht der Anwendung, alle URLs, die mit dem Browser besucht wurden, sowie alle Lesezeichen des Browsers zu lesen."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Browserverlauf und Lesezeichen schreiben"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Ermöglicht einer Anwendung, den auf Ihrem Telefon gespeicherten Browserverlauf und die Lesezeichen zu ändern. Schädliche Anwendungen können so Ihre Browserdaten löschen oder ändern."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Ermöglicht einer Anwendung, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Anwendungen können dies nutzen, um das Senden von Standortinformationen an willkürliche Websites zuzulassen."</string>
     <string name="save_password_message" msgid="767344687139195790">"Möchten Sie, dass der Browser dieses Passwort speichert?"</string>
@@ -824,7 +828,7 @@
     <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"Die SD-Karte ist beschädigt. Sie müssen Ihre Karte eventuell neu formatieren."</string>
     <string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD-Karte unerwartet entfernt"</string>
     <string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"SD-Karte vor dem Entnehmen trennen, um Datenverlust zu vermeiden."</string>
-    <string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD-Karte\nkann entfernt werden."</string>
+    <string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD-Karte kann entfernt werden."</string>
     <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Die SD-Karte kann entfernt werden."</string>
     <string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"SD-Karte entfernt"</string>
     <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD-Karte entfernt. Legen Sie eine neue ein."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ca0eec7..78bce1a 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -563,7 +563,7 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Η κάρτα SIM είναι κλειδωμένη."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ξεκλείδωμα κάρτας SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Προσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google."\n\n" Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> \nδευτερόλεπτα."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google."\n\n" Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g>  δευτερόλεπτα."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Προσπαθήστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Ξεχάσατε το μοτίβο;"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Ξεκλείδωμα λογαριασμού"</string>
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των διευθύνσεων URL που το πρόγραμμα περιήγησης έχει επισκεφθεί και όλων των σελιδοδεικτών του προγράμματος περιήγησης."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"εγγραφή ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Επιτρέπει σε μια εφαρμογή να τροποποιήσει το ιστορικό ή τους σελιδοδείκτες του προγράμματος περιήγησης που βρίσκονται αποθηκευμένα στο τηλέφωνό σας. Κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να διαγράψουν ή να τροποποιήσουν τα δεδομένα του προγράμματος περιήγησης."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Τροποποίηση δικαιωμάτων γεωγραφικής θέσης προγράμματος περιήγησης"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Επιτρέπει σε μια εφαρμογή την τροποποίηση των δικαιωμάτων γεωγραφικής θέσης του προγράμματος περιήγησης. Οι κακόβουλες εφαρμογές μπορούν να το χρησιμοποιήσουν για να επιτρέψουν την αποστολή στοιχείων τοποθεσίας σε αυθαίρετους ιστότοπους."</string>
     <string name="save_password_message" msgid="767344687139195790">"Θέλετε το πρόγραμμα περιήγησης να διατηρήσει αυτόν τον κωδικό πρόσβασης;"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b0da375..6615e70 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite a la aplicación leer todas las URL que ha visitado el navegador y todos los marcadores del navegador."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir historial y marcadores del navegador"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite a una aplicación modificar el historial y los marcadores del navegador almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar tus datos."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modificar los permisos de ubicación geográfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite que una aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones maliciosas pueden utilizarlos para permitir el envío de información sobre la ubicación a sitos web de forma arbitraria."</string>
     <string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string>
@@ -871,6 +875,6 @@
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Amplia utilización de datos móviles"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
-    <string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido "</string>
+    <string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 74f5874..ae59862 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que la aplicación lea todas las URL que ha visitado el navegador y todos sus marcadores."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir en marcadores y en el historial del navegador"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que una aplicación modifique la información de los marcadores o del historial del navegador almacenada en el teléfono. Las aplicaciones malintencionadas pueden utilizar este permiso para borrar o modificar los datos del navegador."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modificar los permisos de ubicación geográfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite que una aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones malintencionadas pueden utilizar este permiso para permitir el envío de información sobre la ubicación a sitios web arbitrarios."</string>
     <string name="save_password_message" msgid="767344687139195790">"¿Deseas que el navegador recuerde esta contraseña?"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index bc69793..9dd1a11 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Autorise l\'application à lire toutes les URL auxquelles le navigateur a accédé et tous ses favoris."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"écrire dans l\'historique et les favoris du navigateur"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Autorise une application à modifier l\'historique du navigateur ou les favoris enregistrés sur votre téléphone. Des applications malveillantes peuvent utiliser cette fonction pour effacer ou modifier les données de votre navigateur."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modifier les autorisations de géolocalisation du navigateur"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permet à une application de modifier les autorisations de géolocalisation du navigateur. Les applications malveillantes peuvent se servir de cette fonctionnalité pour envoyer des informations de lieu à des sites Web arbitraires."</string>
     <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 85415bd..4c4f8f1 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Consente all\'applicazione di leggere tutti gli URL visitati e tutti i segnalibri del browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"creazione cronologia e segnalibri del browser"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Consente a un\'applicazione di modificare la cronologia o i segnalibri del browser memorizzati sul telefono. Le applicazioni dannose possono sfruttare questa possibilità per cancellare o modificare i dati del browser."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modifica le autorizzazioni di localizzazione geografica del browser"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Consente a un\'applicazione di modificare le autorizzazioni di localizzazione geografica del browser. Le applicazioni dannose possono utilizzare questa autorizzazione per consentire l\'invio di informazioni sulla posizione a siti web arbitrari."</string>
     <string name="save_password_message" msgid="767344687139195790">"Memorizzare la password nel browser?"</string>
@@ -752,7 +756,7 @@
     <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Seleziona per passare all\'applicazione"</string>
     <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Scambiare le applicazioni?"</string>
     <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Un\'altra applicazione già in esecuzione deve essere chiusa prima di poterne avviare un\'altra."</string>
-    <string name="old_app_action" msgid="493129172238566282">"Torna a <xliff:g id="OLD_APP">%1$s</xliff:g> "</string>
+    <string name="old_app_action" msgid="493129172238566282">"Torna a <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="old_app_description" msgid="942967900237208466">"Non avviare la nuova applicazione."</string>
     <string name="new_app_action" msgid="5472756926945440706">"Avvia <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="new_app_description" msgid="6830398339826789493">"Interrompi la vecchia applicazione senza salvare."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 70a3019..094b469 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ブラウザでアクセスしたすべてのURLおよびブラウザのすべてのブックマークの読み取りをアプリケーションに許可します。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"ブラウザの履歴とブックマークを書き込む"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"携帯電話に保存されているブラウザの履歴やブックマークの修正をアプリケーショに許可します。これにより悪意のあるアプリケーションが、ブラウザのデータを消去または変更する恐れがあります。"</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"ブラウザの位置情報へのアクセス権を変更"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"ブラウザの位置情報に対するアクセス権の変更をアプリケーションに許可します。この設定では、悪意のあるアプリケーションが任意のウェブサイトに位置情報を送信する可能性があります。"</string>
     <string name="save_password_message" msgid="767344687139195790">"このパスワードをブラウザで保存しますか?"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 0f33772..b16325a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"애플리케이션이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브라우저의 기록 및 북마크 쓰기"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"애플리케이션이 휴대전화에 저장된 브라우저 기록 또는 북마크를 수정할 수 있도록 허용합니다. 이 경우 악성 애플리케이션이 브라우저의 데이터를 지우거나 수정할 수 있습니다."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"브라우저 위치 정보 수정 권한"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"애플리케이션이 브라우저의 위치 정보 권한을 수정할 수 있도록 합니다. 악성 애플리케이션이 이를 사용하여 임의의 웹사이트에 위치 정보를 보낼 수도 있습니다."</string>
     <string name="save_password_message" msgid="767344687139195790">"브라우저에 이 비밀번호를 저장하시겠습니까?"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b3727b4..2ba3e9d 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Lar applikasjonen lese alle adresser nettleseren har besøkt, og alle nettleserens bokmerker."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens logg og bokmerker"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Lar applikasjonen endre nettleserens logg og bokmerker lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å fjerne eller redigere nettleserens data."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Endre nettleserens tillatelser for geografisk posisjonering"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Tillater programmet å endre nettleserens tillatelser for geografisk posisjonering. Skadelige programmer kan bruke denne funksjonen til å sende posisjonsopplysninger til vilkårlige nettsteder."</string>
     <string name="save_password_message" msgid="767344687139195790">"Ønsker du at nettleseren skal huske dette passordet?"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9c5d6bf..aa95374 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Hiermee kan een toepassing de URL\'s lezen die u via de browser heeft bezocht, evenals alle bladwijzers van de browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"browsergeschiedenis en bladwijzers schrijven"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Hiermee kan een toepassing de op uw telefoon opgeslagen browsergeschiedenis of bladwijzers wijzigen. Schadelijke toepassingen kunnen hiermee uw browsergegevens verwijderen of wijzigen."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Geolocatierechten voor browser aanpassen"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Staat een toepassing toe de geolocatierechten van de browser aan te passen. Schadelijke toepassingen kunnen dit gebruiken om locatiegegevens te verzenden naar willekeurige websites."</string>
     <string name="save_password_message" msgid="767344687139195790">"Wilt u dat de browser dit wachtwoord onthoudt?"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index f61d1d6..047a0c1 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umożliwia aplikacji odczyt wszystkich adresów URL odwiedzonych przez przeglądarkę, a także wszystkich zakładek przeglądarki."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zapis historii i zakładek przeglądarki"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Umożliwia aplikacji modyfikowanie historii lub zakładek przeglądarki zapisanych w telefonie. Złośliwe aplikacje mogą używać tej opcji do usuwania lub modyfikowania danych przeglądarki."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Zezwala aplikacji na modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej. Złośliwe aplikacje mogą używać tej opcji do wysyłania informacji o lokalizacji do dowolnych witryn internetowych."</string>
     <string name="save_password_message" msgid="767344687139195790">"Czy chcesz, aby zapamiętać to hasło w przeglądarce?"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5b44e61f..15cb833 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que a aplicação leia todos os URLs visitados pelo browser e todos os marcadores do browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e marcadores do browser"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que uma aplicação modifique o histórico e os marcadores do browser armazenados no telefone. As aplicações maliciosas podem utilizar esta permissão para apagar ou modificar os dados do browser."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modificar permissões de localização geográfica do Navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite a uma aplicação modificar as permissões de localização geográfica do Navegador. As aplicações mal intencionadas podem utilizar isto para enviar informações de localização para Web sites arbitrários."</string>
     <string name="save_password_message" msgid="767344687139195790">"Quer que o browser memorize esta palavra-passe?"</string>
@@ -790,7 +794,7 @@
     <string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar tudo"</b></string>
-    <string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento em massa USB "</string>
+    <string name="usb_storage_activity_title" msgid="2399289999608900443">"Armazenamento em massa USB"</string>
     <string name="usb_storage_title" msgid="5901459041398751495">"Ligado através de USB"</string>
     <string name="usb_storage_message" msgid="4796759646167247178">"Ligou o telefone ao computador através de USB. Seleccione o botão abaixo se pretender copiar ficheiros entre o computador e o cartão SD do Android."</string>
     <string name="usb_storage_button_mount" msgid="1052259930369508235">"Activar armazenamento USB"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 60e9108..4f9258a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que o aplicativo leia todos os URLs visitados pelo Navegador e todos os favoritos do Navegador."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e favoritos do Navegador"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que um aplicativo modifique o histórico ou os favoritos do Navegador armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar os dados do seu Navegador."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Modifique as permissões de geolocalização do seu navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Permite que um aplicativo modifique as permissões de geolocalização do navegador. Aplicativos maliciosos podem usar isso para permitir o envio de informações de localização a sites arbitrários."</string>
     <string name="save_password_message" msgid="767344687139195790">"Deseja que o navegador lembre desta senha?"</string>
@@ -650,7 +654,7 @@
   </plurals>
   <plurals name="abbrev_num_seconds_ago">
     <item quantity="one" msgid="1849036840200069118">"1 seg. atrás"</item>
-    <item quantity="other" msgid="3699169366650930415">"<xliff:g id="COUNT">%d</xliff:g> segundos\n  atrás"</item>
+    <item quantity="other" msgid="3699169366650930415">"<xliff:g id="COUNT">%d</xliff:g> segundos   atrás"</item>
   </plurals>
   <plurals name="abbrev_num_minutes_ago">
     <item quantity="one" msgid="6361490147113871545">"1 minuto atrás"</item>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 9475d2d..748474f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -563,7 +563,7 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-карта заблокирована."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Разблокировка SIM-карты…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. После <xliff:g id="NUMBER_1">%d</xliff:g> неудачных попыток вам будет предложено разблокировать телефон с помощью учетных данных Google.\n "\n\n" Повторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. После <xliff:g id="NUMBER_1">%d</xliff:g> неудачных попыток вам будет предложено разблокировать телефон с помощью учетных данных Google.  "\n\n" Повторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> с."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Забыли графический ключ?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Снятие блокировки аккаунта"</string>
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Разрешает приложению считывать все URL, посещенные браузером, и все его закладки."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"записывать историю и закладки браузера"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Разрешает приложению изменять историю и закладки браузера, сохраненные в вашем телефоне. Вредоносное ПО может пользоваться этим, чтобы стирать или изменять данные вашего браузера."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Изменить разрешения браузера для доступа к географическому местоположению"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Позволяет программе изменять разрешения браузера для доступа к географическому положению. Вредоносные программы могут пользоваться этим для отправки информации о местоположении на некоторые сайты."</string>
     <string name="save_password_message" msgid="767344687139195790">"Вы хотите, чтобы браузер запомнил этот пароль?"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 085cc29..469901a 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillåter att program läser alla webbadresser som webbläsaren har öppnat och alla webbläsarens bokmärken."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriva webbläsarhistorik och bokmärken"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Tillåter att ett program ändrar webbläsarhistoriken och bokmärkena i din telefon. Skadliga program kan använda detta för att ta bort eller ändra data i webbläsaren."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Ändra geografisk plats för webbläsaren"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Tillåter att ett program ändrar webbläsarens behörigheter för geografisk plats. Skadliga program kan använda detta för att tillåta att platsinformation skickas till godtyckliga webbplatser."</string>
     <string name="save_password_message" msgid="767344687139195790">"Vill du att webbläsaren ska komma ihåg lösenordet?"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 6efa8e0..4ce0989 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Uygulamaya Tarayıcının ziyaret etmiş olduğu tüm URL\'leri ve Tarayıcının tüm favorilerini okuma izni verir."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Tarayıcı geçmişini ve favorileri yaz"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Uygulamaya telefonunuzda depolanan Tarayıcı geçmişini veya favorileri değiştirme izni verir. Kötü amaçlı uygulamalar bunu Tarayıcı verilerinizi silmek veya değiştirmek için kullanabilir."</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"Tarayıcı\'nın coğrafi konum izinlerini değiştir"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"Bir uygulamanın, Tarayıcı\'nın coğrafi konum izinlerini değiştirmesine izin verir. Kötü amaçlı uygulamalar, bu özelliği konum bilgilerini rastgele web sitelerine göndermek için kullanabilir."</string>
     <string name="save_password_message" msgid="767344687139195790">"Tarayıcının bu şifreyi anımsamasını istiyor musunuz?"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c8f2893..02ac746 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允许应用程序读取用浏览器访问过的所有网址,以及浏览器的所有书签。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入浏览器的历史记录和书签"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"允许应用程序修改存储在手机中的浏览器历史记录或书签。恶意应用程序可借此清除或修改浏览器数据。"</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"修改浏览器的地理位置权限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"允许应用程序修改浏览器的地理位置权限。恶意应用程序会利用这一点将位置信息发送到任意网站。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否希望浏览器记住此密码?"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index fcfa464..434c2e0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -595,6 +595,10 @@
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允許應用程式讀取瀏覽器曾經造訪過的所有網址,以及瀏覽器的所有書籤。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string>
     <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"允許應用程式修改儲存在電話上的瀏覽記錄或書籤。請注意:惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
+    <!-- no translation found for permlab_setAlarm (5924401328803615165) -->
+    <skip />
+    <!-- no translation found for permdesc_setAlarm (5966966598149875082) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="4715212655598275532">"修改瀏覽器地理資訊的權限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="4011908282980861679">"允許應用程式修改瀏覽器的地理位置權限,惡意應用程式可能會透過此方式允許將您的位置資訊任意傳送給某些網站。"</string>
     <string name="save_password_message" msgid="767344687139195790">"是否記住此密碼?"</string>
diff --git a/docs/html/guide/topics/security/security.jd b/docs/html/guide/topics/security/security.jd
index dbc9866..de0c6e5 100644
--- a/docs/html/guide/topics/security/security.jd
+++ b/docs/html/guide/topics/security/security.jd
@@ -21,14 +21,15 @@
 </div>
 </div>
 
-<p>Android is a multi-process system, in which each application (and parts of the
-system) runs in its own process.  Most security between applications and
-the system is enforced at the process level through standard Linux facilities,
-such as user and group IDs that are assigned to applications.
-Additional finer-grained security features are provided
-through a "permission" mechanism that enforces restrictions on the specific
-operations that a particular process can perform, and per-URI permissions
-for granting ad-hoc access to specific pieces of data.</p>
+<p>Android is a privilege-separated operating system, in which each
+application runs with a distinct system identity (Linux user ID and group
+ID).  Parts of the system are also separated into distinct identities.
+Linux thereby isolates applications from each other and from the system.</p>
+
+<p>Additional finer-grained security features are provided through a
+"permission" mechanism that enforces restrictions on the specific operations
+that a particular process can perform, and per-URI permissions for granting
+ad-hoc access to specific pieces of data.</p>
 
 <a name="arch"></a>
 <h2>Security Architecture</h2>
@@ -38,39 +39,46 @@
 adversely impact other applications, the operating system, or the user.  This
 includes reading or writing the user's private data (such as contacts or
 e-mails), reading or writing another application's files, performing
-network access, keeping the device awake, etc.<p>
+network access, keeping the device awake, etc.</p>
 
-<p>An application's process runs in a security sandbox. The sandbox is designed
-to prevent applications from disrupting each other, except by explicitly
-declaring the <em>permissions</em> they need for additional capabilities not
-provided by the basic sandbox. The system handles requests for permissions
-in various ways, typically by automatically allowing or disallowing based on
-certificates or by prompting the user.  The permissions required by an
-application are declared statically in that application, so they can be known
-up-front at install time and will not change after that.</p>
+<p>Because the kernel sandboxes applications from each other, applications
+must explicitly share resources and data. They do this by declaring the
+<em>permissions</em> they need for additional capabilities not provided by
+the basic sandbox. Applications statically declare the permissions they
+require, and the Android system prompts the user for consent at the time the
+application is installed. Android has no mechanism for granting permissions
+dynamically (at run-time) because it complicates the user experience to the
+detriment of security.</p>
+
+<p>The kernel is solely responsible for sandboxing applications from each
+other. In particular the Dalvik VM is not a security boundary, and any app
+can run native code (see <a href="/sdk/ndk/index.html">the Android NDK</a>).
+All types of applications &mdash; Java, native, and hybrid &mdash; are
+sandboxed in the same way and have the same degree of security from each
+other.</p>
 
 <a name="signing"></a>
 <h2>Application Signing</h2>
 
-<p>All Android applications (.apk files) must be signed with a certificate whose
-private key is held by their developer.  This certificate identifies the author
-of the application.  The certificate does <em>not</em> need to be signed by
-a certificate authority: it is perfectly allowable, and typical, for Android
-applications to use self-signed certificates.  The certificate is used only
-to establish trust relationships between applications, not for wholesale
-control over whether an application can be installed.  The most significant
-ways that signatures impact security is by determining who can access
-signature-based permissions and who can share user IDs.</p>
-
+<p>All Android applications (.apk files) must be signed with a certificate
+whose private key is held by their developer.  This certificate identifies
+the author of the application.  The certificate does <em>not</em> need to be
+signed by a certificate authority: it is perfectly allowable, and typical,
+for Android applications to use self-signed certificates. The purpose of
+certificates in Android is to distinguish application authors. This allows
+the system to grant or deny applications access to <a
+href="/guide/topics/manifest/permission-element.html#plevel">signature-level
+permissions</a> and to grant or deny an application's <a
+href="/guide/topics/manifest/manifest-element.html#uid">request to be given
+the same Linux identity</a> as another application.</p>
 
 <a name="userid"></a>
 <h2>User IDs and File Access</h2>
 
-<p>Each Android package (.apk) file installed on the device is given its
-own unique Linux user ID, creating a sandbox for it and preventing it from touching
-other applications (or other applications from touching it).  This user ID is
-assigned to it when the application is installed on the device, and
-remains constant for the duration of its life on that device.</p>
+<p>At install time, Android gives each package a distinct Linux user ID. The
+identity remains constant for the duration of the package's life on that
+device. On a different device, the same package may have a different UID;
+what matters is that each package has a distinct UID on a given device.</p>
 
 <p>Because security enforcement happens at the
 process level, the code of any two packages can not normally
@@ -150,7 +158,7 @@
 <li>Both sending and receiving broadcasts, to control who can receive
 your broadcast or who can send a broadcast to you.</li>
 <li>When accessing and operating on a content provider.</li>
-<li>Binding or starting a service.</li>
+<li>Binding to or starting a service.</li>
 </ul>
 
 
diff --git a/include/ui/Input.h b/include/ui/Input.h
index b587e94..21baf32 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -40,10 +40,18 @@
 
 /*
  * Maximum number of pointers supported per motion event.
+ * Smallest number of pointers is 1.
  */
 #define MAX_POINTERS 10
 
 /*
+ * Maximum pointer id value supported in a motion event.
+ * Smallest pointer id is 0.
+ * (This is limited by our use of BitSet32 to track pointer assignments.)
+ */
+#define MAX_POINTER_ID 31
+
+/*
  * Declare a concrete type for the NDK's input event forward declaration.
  */
 struct AInputEvent {
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 96b4fae..cc16012 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -27,6 +27,7 @@
 #include <utils/String8.h>
 #include <utils/Looper.h>
 #include <utils/Pool.h>
+#include <utils/BitSet.h>
 
 #include <stddef.h>
 #include <unistd.h>
@@ -89,17 +90,13 @@
          * AMOTION_EVENT_ACTION_OUTSIDE to this target. */
         FLAG_OUTSIDE = 0x02,
 
-        /* This flag indicates that a KeyEvent or MotionEvent is being canceled.
-         * In the case of a key event, it should be delivered with flag
-         * AKEY_EVENT_FLAG_CANCELED set.
-         * In the case of a motion event, it should be delivered with action
-         * AMOTION_EVENT_ACTION_CANCEL instead. */
-        FLAG_CANCEL = 0x04,
-
         /* This flag indicates that the target of a MotionEvent is partly or wholly
          * obscured by another visible window above it.  The motion event should be
          * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
-        FLAG_WINDOW_IS_OBSCURED = 0x08,
+        FLAG_WINDOW_IS_OBSCURED = 0x04,
+
+        /* This flag indicates that a motion event is being split across multiple windows. */
+        FLAG_SPLIT = 0x08,
     };
 
     // The input channel to be targeted.
@@ -111,6 +108,13 @@
     // The x and y offset to add to a MotionEvent as it is delivered.
     // (ignored for KeyEvents)
     float xOffset, yOffset;
+
+    // The window type of the input target.
+    int32_t windowType;
+
+    // The subset of pointer ids to include in motion events dispatched to this input target
+    // if FLAG_SPLIT is set.
+    BitSet32 pointerIds;
 };
 
 
@@ -143,7 +147,7 @@
         FLAG_SHOW_WALLPAPER = 0x00100000,
         FLAG_TURN_SCREEN_ON = 0x00200000,
         FLAG_DISMISS_KEYGUARD = 0x00400000,
-        FLAG_IMMERSIVE = 0x00800000,
+        FLAG_SPLIT_TOUCH = 0x00800000,
         FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
         FLAG_COMPATIBLE_WINDOW = 0x20000000,
         FLAG_SYSTEM_ERROR = 0x40000000,
@@ -276,7 +280,7 @@
             const KeyEvent* keyEvent, uint32_t policyFlags) = 0;
 
     /* Poke user activity for an event dispatched to a window. */
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) = 0;
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
 
     /* Checks whether a given application pid/uid has permission to inject input events
      * into other applications.
@@ -415,6 +419,16 @@
         T* prev;
     };
 
+    struct InjectionState {
+        mutable int32_t refCount;
+
+        int32_t injectorPid;
+        int32_t injectorUid;
+        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
+        bool injectionIsAsync; // set to true if injection is not waiting for the result
+        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
+    };
+
     struct EventEntry : Link<EventEntry> {
         enum {
             TYPE_SENTINEL,
@@ -423,21 +437,14 @@
             TYPE_MOTION
         };
 
-        int32_t refCount;
+        mutable int32_t refCount;
         int32_t type;
         nsecs_t eventTime;
-
-        int32_t injectionResult;  // initially INPUT_EVENT_INJECTION_PENDING
-        bool    injectionIsAsync; // set to true if injection is not waiting for the result
-        int32_t injectorPid;      // -1 if not injected
-        int32_t injectorUid;      // -1 if not injected
+        InjectionState* injectionState;
 
         bool dispatchInProgress; // initially false, set to true while dispatching
-        int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
-        inline bool isInjected() { return injectorPid >= 0; }
-
-        void recycle();
+        inline bool isInjected() { return injectionState != NULL; }
     };
 
     struct ConfigurationChangedEntry : EventEntry {
@@ -463,8 +470,6 @@
             INTERCEPT_KEY_RESULT_CONTINUE,
         };
         InterceptKeyResult interceptKeyResult; // set based on the interception result
-
-        void recycle();
     };
 
     struct MotionSample {
@@ -521,6 +526,10 @@
         inline bool hasForegroundTarget() const {
             return targetFlags & InputTarget::FLAG_FOREGROUND;
         }
+
+        inline bool isSplit() const {
+            return targetFlags & InputTarget::FLAG_SPLIT;
+        }
     };
 
     // A command entry captures state and behavior for an action to be performed in the
@@ -555,7 +564,6 @@
         KeyEntry* keyEntry;
         sp<InputChannel> inputChannel;
         sp<InputApplicationHandle> inputApplicationHandle;
-        int32_t windowType;
         int32_t userActivityEventType;
     };
 
@@ -611,6 +619,7 @@
     public:
         Allocator();
 
+        InjectionState* obtainInjectionState(int32_t injectorPid, int32_t injectorUid);
         ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
         KeyEntry* obtainKeyEntry(nsecs_t eventTime,
                 int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
@@ -626,6 +635,7 @@
                 int32_t targetFlags, float xOffset, float yOffset);
         CommandEntry* obtainCommandEntry(Command command);
 
+        void releaseInjectionState(InjectionState* injectionState);
         void releaseEventEntry(EventEntry* entry);
         void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
         void releaseKeyEntry(KeyEntry* entry);
@@ -633,10 +643,13 @@
         void releaseDispatchEntry(DispatchEntry* entry);
         void releaseCommandEntry(CommandEntry* entry);
 
+        void recycleKeyEntry(KeyEntry* entry);
+
         void appendMotionSample(MotionEntry* motionEntry,
                 nsecs_t eventTime, const PointerCoords* pointerCoords);
 
     private:
+        Pool<InjectionState> mInjectionStatePool;
         Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
         Pool<KeyEntry> mKeyEntryPool;
         Pool<MotionEntry> mMotionEntryPool;
@@ -645,6 +658,7 @@
         Pool<CommandEntry> mCommandEntryPool;
 
         void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
+        void releaseEventEntryInjectionState(EventEntry* entry);
     };
 
     /* Tracks dispatched key and motion event state so that cancelation events can be
@@ -823,6 +837,7 @@
     void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
 
     Condition mInjectionSyncFinishedCondition;
+    void incrementPendingForegroundDispatchesLocked(EventEntry* entry);
     void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
 
     // Throttling state.
@@ -858,23 +873,37 @@
     // Dispatch state.
     bool mDispatchEnabled;
     bool mDispatchFrozen;
+
     Vector<InputWindow> mWindows;
-    Vector<InputWindow*> mWallpaperWindows;
+
+    const InputWindow* getWindowLocked(const sp<InputChannel>& inputChannel);
 
     // Focus tracking for keys, trackball, etc.
-    InputWindow* mFocusedWindow;
+    const InputWindow* mFocusedWindow;
 
     // Focus tracking for touch.
-    bool mTouchDown;
-    InputWindow* mTouchedWindow;                   // primary target for current down
-    bool mTouchedWindowIsObscured;                 // true if other windows may obscure the target
-    Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
-    struct OutsideTarget {
-        InputWindow* window;
-        bool obscured;
+    struct TouchedWindow {
+        const InputWindow* window;
+        int32_t targetFlags;
+        BitSet32 pointerIds;
+        sp<InputChannel> channel;
     };
-    Vector<OutsideTarget> mTempTouchedOutsideTargets; // temporary outside touch targets
-    Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+    struct TouchState {
+        bool down;
+        bool split;
+        Vector<TouchedWindow> windows;
+
+        TouchState();
+        ~TouchState();
+        void reset();
+        void copyFrom(const TouchState& other);
+        void addOrUpdateWindow(const InputWindow* window, int32_t targetFlags, BitSet32 pointerIds);
+        void removeOutsideTouchWindows();
+        const InputWindow* getFirstForegroundWindow();
+    };
+
+    TouchState mTouchState;
+    TouchState mTempTouchState;
 
     // Focused application.
     InputApplication* mFocusedApplication;
@@ -899,8 +928,6 @@
     // The input targets that were most recently identified for dispatch.
     bool mCurrentInputTargetsValid; // false while targets are being recomputed
     Vector<InputTarget> mCurrentInputTargets;
-    int32_t mCurrentInputWindowType;
-    sp<InputChannel> mCurrentInputChannel;
 
     enum InputTargetWaitCause {
         INPUT_TARGET_WAIT_CAUSE_NONE,
@@ -915,7 +942,7 @@
 
     // Finding targets for input events.
     void resetTargetsLocked();
-    void commitTargetsLocked(const InputWindow* window);
+    void commitTargetsLocked();
     int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
             const InputApplication* application, const InputWindow* window,
             nsecs_t* nextWakeupTime);
@@ -924,19 +951,19 @@
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
     void resetANRTimeoutsLocked();
 
-    int32_t findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
-            nsecs_t* nextWakeupTime, InputWindow** outWindow);
-    int32_t findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
-            nsecs_t* nextWakeupTime, InputWindow** outWindow);
+    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+            nsecs_t* nextWakeupTime);
+    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+            nsecs_t* nextWakeupTime);
 
-    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags);
+    void addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+            BitSet32 pointerIds);
     void addMonitoringTargetsLocked();
-    void pokeUserActivityLocked(nsecs_t eventTime, int32_t windowType, int32_t eventType);
-    bool checkInjectionPermission(const InputWindow* window,
-            int32_t injectorPid, int32_t injectorUid);
+    bool shouldPokeUserActivityForCurrentInputTargetsLocked();
+    void pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType);
+    bool checkInjectionPermission(const InputWindow* window, const InjectionState* injectionState);
     bool isWindowObscuredLocked(const InputWindow* window);
     bool isWindowFinishedWithPreviousInputLocked(const InputWindow* window);
-    void releaseTouchedWindowLocked();
     String8 getApplicationWindowLabelLocked(const InputApplication* application,
             const InputWindow* window);
 
@@ -955,6 +982,9 @@
     void drainOutboundQueueLocked(Connection* connection);
     static int handleReceiveCallback(int receiveFd, int events, void* data);
 
+    // Splitting motion events across windows.
+    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+
     // Dump state.
     void dumpDispatchStateLocked(String8& dump);
     void logDispatchStateLocked();
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 903c3c4..e85735a 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -549,10 +549,6 @@
             const int32_t* keyCodes, uint8_t* outFlags);
 
 protected:
-    /* Maximum pointer id value supported.
-     * (This is limited by our use of BitSet32 to track pointer assignments.) */
-    static const uint32_t MAX_POINTER_ID = 31;
-
     Mutex mLock;
 
     struct VirtualKey {
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index 19c8bf0..f5dbcd94 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -38,6 +38,9 @@
     // Clears the bit set.
     inline void clear() { value = 0; }
 
+    // Returns the number of marked bits in the set.
+    inline uint32_t count() const { return __builtin_popcount(value); }
+
     // Returns true if the bit set does not contain any marked bits.
     inline bool isEmpty() const { return ! value; }
 
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 1cf7592..5da1676 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -69,6 +69,65 @@
     return value ? "true" : "false";
 }
 
+static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+    return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+            >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static bool isValidKeyAction(int32_t action) {
+    switch (action) {
+    case AKEY_EVENT_ACTION_DOWN:
+    case AKEY_EVENT_ACTION_UP:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool validateKeyEvent(int32_t action) {
+    if (! isValidKeyAction(action)) {
+        LOGE("Key event has invalid action code 0x%x", action);
+        return false;
+    }
+    return true;
+}
+
+static bool isValidMotionAction(int32_t action) {
+    switch (action & AMOTION_EVENT_ACTION_MASK) {
+    case AMOTION_EVENT_ACTION_DOWN:
+    case AMOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_CANCEL:
+    case AMOTION_EVENT_ACTION_MOVE:
+    case AMOTION_EVENT_ACTION_POINTER_DOWN:
+    case AMOTION_EVENT_ACTION_POINTER_UP:
+    case AMOTION_EVENT_ACTION_OUTSIDE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool validateMotionEvent(int32_t action, size_t pointerCount,
+        const int32_t* pointerIds) {
+    if (! isValidMotionAction(action)) {
+        LOGE("Motion event has invalid action code 0x%x", action);
+        return false;
+    }
+    if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
+        LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.",
+                pointerCount, MAX_POINTERS);
+        return false;
+    }
+    for (size_t i = 0; i < pointerCount; i++) {
+        if (pointerIds[i] < 0 || pointerIds[i] > MAX_POINTER_ID) {
+            LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
+                    pointerIds[i], MAX_POINTER_ID);
+            return false;
+        }
+    }
+    return true;
+}
+
 
 // --- InputWindow ---
 
@@ -91,7 +150,7 @@
     mPolicy(policy),
     mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
     mDispatchEnabled(true), mDispatchFrozen(false),
-    mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+    mFocusedWindow(NULL),
     mFocusedApplication(NULL),
     mCurrentInputTargetsValid(false),
     mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
@@ -414,9 +473,10 @@
 }
 
 void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
-    if (entry->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
 #if DEBUG_DISPATCH_CYCLE
-        LOGD("Inbound event was dropped.  Setting injection result to failed.");
+        LOGD("Injected inbound event was dropped.");
 #endif
         setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
     }
@@ -424,10 +484,11 @@
 }
 
 bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) {
-    return ! entry->isInjected()
-            || entry->injectorUid == 0
+    InjectionState* injectionState = entry->injectionState;
+    return ! injectionState
+            || injectionState->injectorUid == 0
             || mPolicy->checkInjectEventsPermissionNonReentrant(
-                    entry->injectorPid, entry->injectorUid);
+                    injectionState->injectorPid, injectionState->injectorUid);
 }
 
 void InputDispatcher::resetKeyRepeatLocked() {
@@ -444,7 +505,7 @@
     // Reuse the repeated key entry if it is otherwise unreferenced.
     uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
     if (entry->refCount == 1) {
-        entry->recycle();
+        mAllocator.recycleKeyEntry(entry);
         entry->eventTime = currentTime;
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
@@ -496,8 +557,7 @@
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         bool trusted;
         if (! dropEvent && mFocusedWindow) {
-            trusted = checkInjectionPermission(mFocusedWindow,
-                    entry->injectorPid, entry->injectorUid);
+            trusted = checkInjectionPermission(mFocusedWindow, entry->injectionState);
         } else {
             trusted = isEventFromReliableSourceLocked(entry);
         }
@@ -559,9 +619,8 @@
 
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
-        InputWindow* window = NULL;
-        int32_t injectionResult = findFocusedWindowLocked(currentTime,
-                entry, nextWakeupTime, & window);
+        int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                entry, nextWakeupTime);
         if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
             return false;
         }
@@ -572,14 +631,16 @@
         }
 
         addMonitoringTargetsLocked();
-        commitTargetsLocked(window);
+        commitTargetsLocked();
     }
 
     // Dispatch the key.
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 
     // Poke user activity.
-    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT);
+    if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+        pokeUserActivityLocked(entry->eventTime, POWER_MANAGER_BUTTON_EVENT);
+    }
     return true;
 }
 
@@ -616,16 +677,15 @@
 
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
-        InputWindow* window = NULL;
         int32_t injectionResult;
         if (isPointerEvent) {
             // Pointer event.  (eg. touchscreen)
-            injectionResult = findTouchedWindowLocked(currentTime,
-                    entry, nextWakeupTime, & window);
+            injectionResult = findTouchedWindowTargetsLocked(currentTime,
+                    entry, nextWakeupTime);
         } else {
             // Non touch event.  (eg. trackball)
-            injectionResult = findFocusedWindowLocked(currentTime,
-                    entry, nextWakeupTime, & window);
+            injectionResult = findFocusedWindowTargetsLocked(currentTime,
+                    entry, nextWakeupTime);
         }
         if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
             return false;
@@ -637,34 +697,36 @@
         }
 
         addMonitoringTargetsLocked();
-        commitTargetsLocked(window);
+        commitTargetsLocked();
     }
 
     // Dispatch the motion.
     dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
 
     // Poke user activity.
-    int32_t eventType;
-    if (isPointerEvent) {
-        switch (entry->action) {
-        case AMOTION_EVENT_ACTION_DOWN:
-            eventType = POWER_MANAGER_TOUCH_EVENT;
-            break;
-        case AMOTION_EVENT_ACTION_UP:
-            eventType = POWER_MANAGER_TOUCH_UP_EVENT;
-            break;
-        default:
-            if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+    if (shouldPokeUserActivityForCurrentInputTargetsLocked()) {
+        int32_t eventType;
+        if (isPointerEvent) {
+            switch (entry->action) {
+            case AMOTION_EVENT_ACTION_DOWN:
                 eventType = POWER_MANAGER_TOUCH_EVENT;
-            } else {
-                eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+                break;
+            case AMOTION_EVENT_ACTION_UP:
+                eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+                break;
+            default:
+                if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) {
+                    eventType = POWER_MANAGER_TOUCH_EVENT;
+                } else {
+                    eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+                }
+                break;
             }
-            break;
+        } else {
+            eventType = POWER_MANAGER_BUTTON_EVENT;
         }
-    } else {
-        eventType = POWER_MANAGER_BUTTON_EVENT;
+        pokeUserActivityLocked(entry->eventTime, eventType);
     }
-    pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType);
     return true;
 }
 
@@ -735,13 +797,10 @@
 void InputDispatcher::resetTargetsLocked() {
     mCurrentInputTargetsValid = false;
     mCurrentInputTargets.clear();
-    mCurrentInputChannel.clear();
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
 
-void InputDispatcher::commitTargetsLocked(const InputWindow* window) {
-    mCurrentInputWindowType = window->layoutParamsType;
-    mCurrentInputChannel = window->inputChannel;
+void InputDispatcher::commitTargetsLocked() {
     mCurrentInputTargetsValid = true;
 }
 
@@ -803,8 +862,8 @@
         // Give up.
         mInputTargetWaitTimeoutExpired = true;
 
-        // Release the touch target.
-        releaseTouchedWindowLocked();
+        // Release the touch targets.
+        mTouchState.reset();
 
         // Input state will not be realistic.  Mark it out of sync.
         if (inputChannel.get()) {
@@ -834,9 +893,8 @@
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
 }
 
-int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry,
-        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
-    *outWindow = NULL;
+int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
+        const EventEntry* entry, nsecs_t* nextWakeupTime) {
     mCurrentInputTargets.clear();
 
     int32_t injectionResult;
@@ -861,7 +919,7 @@
     }
 
     // Check permissions.
-    if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) {
+    if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         goto Failed;
     }
@@ -888,8 +946,7 @@
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    *outWindow = mFocusedWindow;
-    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND);
+    addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
 
     // Done.
 Failed:
@@ -905,15 +962,14 @@
     return injectionResult;
 }
 
-int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry,
-        nsecs_t* nextWakeupTime, InputWindow** outWindow) {
+int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
+        const MotionEntry* entry, nsecs_t* nextWakeupTime) {
     enum InjectionPermission {
         INJECTION_PERMISSION_UNKNOWN,
         INJECTION_PERMISSION_GRANTED,
         INJECTION_PERMISSION_DENIED
     };
 
-    *outWindow = NULL;
     mCurrentInputTargets.clear();
 
     nsecs_t startTime = now();
@@ -945,25 +1001,33 @@
     bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
 
     int32_t action = entry->action;
+    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
-    int32_t injectionResult;
-    InjectionPermission injectionPermission;
-    if (action == AMOTION_EVENT_ACTION_DOWN) {
-        /* Case 1: ACTION_DOWN */
+    int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        mTempTouchState.reset();
+        mTempTouchState.down = true;
+    } else {
+        mTempTouchState.copyFrom(mTouchState);
+    }
 
-        InputWindow* newTouchedWindow = NULL;
-        mTempTouchedOutsideTargets.clear();
+    bool isSplit = mTempTouchState.split && mTempTouchState.down;
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+            || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
+        /* Case 1: New splittable pointer going down. */
 
-        int32_t x = int32_t(entry->firstSample.pointerCoords[0].x);
-        int32_t y = int32_t(entry->firstSample.pointerCoords[0].y);
-        InputWindow* topErrorWindow = NULL;
-        bool obscured = false;
+        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+        int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x);
+        int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y);
+        const InputWindow* newTouchedWindow = NULL;
+        const InputWindow* topErrorWindow = NULL;
 
         // Traverse windows from front to back to find touched window and outside targets.
         size_t numWindows = mWindows.size();
         for (size_t i = 0; i < numWindows; i++) {
-            InputWindow* window = & mWindows.editItemAt(i);
+            const InputWindow* window = & mWindows.editItemAt(i);
             int32_t flags = window->layoutParamsFlags;
 
             if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
@@ -979,17 +1043,15 @@
                     if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
                         if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
                             newTouchedWindow = window;
-                            obscured = isWindowObscuredLocked(window);
                         }
                         break; // found touched window, exit window loop
                     }
                 }
 
-                if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) {
-                    OutsideTarget outsideTarget;
-                    outsideTarget.window = window;
-                    outsideTarget.obscured = isWindowObscuredLocked(window);
-                    mTempTouchedOutsideTargets.push(outsideTarget);
+                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+                        && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
+                    mTempTouchState.addOrUpdateWindow(window,
+                            InputTarget::FLAG_OUTSIDE, BitSet32(0));
                 }
             }
         }
@@ -1007,6 +1069,21 @@
             goto Unresponsive;
         }
 
+        // Figure out whether splitting will be allowed for this window.
+        if (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+            // New window supports splitting.
+            isSplit = true;
+        } else if (isSplit) {
+            // New window does not support splitting but we have already split events.
+            // Assign the pointer to the first foreground window we find.
+            // (May be NULL which is why we put this code block before the next check.)
+            newTouchedWindow = mTempTouchState.getFirstForegroundWindow();
+        }
+        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+        if (isSplit) {
+            targetFlags |= InputTarget::FLAG_SPLIT;
+        }
+
         // If we did not find a touched window then fail.
         if (! newTouchedWindow) {
             if (mFocusedApplication) {
@@ -1017,140 +1094,127 @@
 #endif
                 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                         mFocusedApplication, NULL, nextWakeupTime);
-                injectionPermission = INJECTION_PERMISSION_UNKNOWN;
                 goto Unresponsive;
             }
 
             LOGI("Dropping event because there is no touched window or focused application.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_UNKNOWN;
             goto Failed;
         }
 
-        // Check permissions.
-        if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-            goto Failed;
+        // Update the temporary touch state.
+        BitSet32 pointerIds;
+        if (isSplit) {
+            uint32_t pointerId = entry->pointerIds[pointerIndex];
+            pointerIds.markBit(pointerId);
         }
-
-        // If the touched window is paused then keep waiting.
-        if (newTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because touched window is paused.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, newTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
-
-        // If the touched window is still working on previous events then keep waiting.
-        if (! isWindowFinishedWithPreviousInputLocked(newTouchedWindow)) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because touched window still processing previous input.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, newTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
-
-        // Success!  Update the touch dispatch state for real.
-        releaseTouchedWindowLocked();
-
-        mTouchedWindow = newTouchedWindow;
-        mTouchedWindowIsObscured = obscured;
-
-        if (newTouchedWindow->hasWallpaper) {
-            mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
-        }
+        mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
     } else {
-        /* Case 2: Everything but ACTION_DOWN */
-
-        // Check permissions.
-        if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) {
-            injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-            goto Failed;
-        }
+        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
         // If the pointer is not currently down, then ignore the event.
-        if (! mTouchDown) {
+        if (! mTempTouchState.down) {
             LOGI("Dropping event because the pointer is not down.");
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Failed;
         }
+    }
 
-        // If there is no currently touched window then fail.
-        if (! mTouchedWindow) {
+    // Check permission to inject into all touched foreground windows and ensure there
+    // is at least one touched foreground window.
+    {
+        bool haveForegroundWindow = false;
+        for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+            if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+                haveForegroundWindow = true;
+                if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) {
+                    injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+                    injectionPermission = INJECTION_PERMISSION_DENIED;
+                    goto Failed;
+                }
+            }
+        }
+        if (! haveForegroundWindow) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Dropping event because there is no touched window to receive it.");
+            LOGD("Dropping event because there is no touched foreground window to receive it.");
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
             goto Failed;
         }
 
-        // If the touched window is paused then keep waiting.
-        if (mTouchedWindow->paused) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
-            LOGD("Waiting because touched window is paused.");
-#endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, mTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
-        }
+        // Permission granted to injection into all touched foreground windows.
+        injectionPermission = INJECTION_PERMISSION_GRANTED;
+    }
 
-        // If the touched window is still working on previous events then keep waiting.
-        if (! isWindowFinishedWithPreviousInputLocked(mTouchedWindow)) {
-#if DEBUG_FOCUS
-            LOGD("Waiting because touched window still processing previous input.");
+    // Ensure all touched foreground windows are ready for new input.
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+            // If the touched window is paused then keep waiting.
+            if (touchedWindow.window->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+                LOGD("Waiting because touched window is paused.");
 #endif
-            injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
-                    NULL, mTouchedWindow, nextWakeupTime);
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-            goto Unresponsive;
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        NULL, touchedWindow.window, nextWakeupTime);
+                goto Unresponsive;
+            }
+
+            // If the touched window is still working on previous events then keep waiting.
+            if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) {
+#if DEBUG_FOCUS
+                LOGD("Waiting because touched window still processing previous input.");
+#endif
+                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+                        NULL, touchedWindow.window, nextWakeupTime);
+                goto Unresponsive;
+            }
+        }
+    }
+
+    // If this is the first pointer going down and the touched window has a wallpaper
+    // then also add the touched wallpaper windows so they are locked in for the duration
+    // of the touch gesture.
+    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+        const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
+        if (foregroundWindow->hasWallpaper) {
+            for (size_t i = 0; i < mWindows.size(); i++) {
+                const InputWindow* window = & mWindows[i];
+                if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
+                    mTempTouchState.addOrUpdateWindow(window, 0, BitSet32(0));
+                }
+            }
+        }
+    }
+
+    // If a touched window has been obscured at any point during the touch gesture, set
+    // the appropriate flag so we remember it for the entire gesture.
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+        if ((touchedWindow.targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) == 0) {
+            if (isWindowObscuredLocked(touchedWindow.window)) {
+                touchedWindow.targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+            }
         }
     }
 
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
-    injectionPermission = INJECTION_PERMISSION_GRANTED;
 
-    {
-        size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
-        for (size_t i = 0; i < numWallpaperWindows; i++) {
-            addWindowTargetLocked(mTouchedWallpaperWindows[i],
-                    InputTarget::FLAG_WINDOW_IS_OBSCURED);
-        }
-
-        size_t numOutsideTargets = mTempTouchedOutsideTargets.size();
-        for (size_t i = 0; i < numOutsideTargets; i++) {
-            const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i];
-            int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
-            if (outsideTarget.obscured) {
-                outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-            }
-            addWindowTargetLocked(outsideTarget.window, outsideTargetFlags);
-        }
-        mTempTouchedOutsideTargets.clear();
-
-        int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
-        if (mTouchedWindowIsObscured) {
-            targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
-        }
-        addWindowTargetLocked(mTouchedWindow, targetFlags);
-        *outWindow = mTouchedWindow;
+    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
+        addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags,
+                touchedWindow.pointerIds);
     }
 
+    // Drop the outside touch window since we will not care about them in the next iteration.
+    mTempTouchState.removeOutsideTouchWindows();
+
 Failed:
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow,
-                entry->injectorPid, entry->injectorUid)) {
+        if (checkInjectionPermission(NULL, entry->injectionState)) {
             injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
             injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1159,25 +1223,41 @@
 
     // Update final pieces of touch state if the injector had permission.
     if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
-        if (action == AMOTION_EVENT_ACTION_DOWN) {
-            if (mTouchDown) {
-                // This is weird.  We got a down but we thought it was already down!
+        if (maskedAction == AMOTION_EVENT_ACTION_UP
+                || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+            // All pointers up or canceled.
+            mTempTouchState.reset();
+        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+            // First pointer went down.
+            if (mTouchState.down) {
                 LOGW("Pointer down received while already down.");
-            } else {
-                mTouchDown = true;
             }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+            // One pointer went up.
+            if (isSplit) {
+                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+                uint32_t pointerId = entry->pointerIds[pointerIndex];
 
-            if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-                // Since we failed to identify a target for this touch down, we may still
-                // be holding on to an earlier target from a previous touch down.  Release it.
-                releaseTouchedWindowLocked();
+                for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+                    TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+                        touchedWindow.pointerIds.clearBit(pointerId);
+                        if (touchedWindow.pointerIds.isEmpty()) {
+                            mTempTouchState.windows.removeAt(i);
+                            continue;
+                        }
+                    }
+                    i += 1;
+                }
             }
-        } else if (action == AMOTION_EVENT_ACTION_UP) {
-            mTouchDown = false;
-            releaseTouchedWindowLocked();
         }
+
+        // Save changes to touch state.
+        mTouchState.copyFrom(mTempTouchState);
     } else {
-        LOGW("Not updating touch focus because injection was denied.");
+#if DEBUG_FOCUS
+        LOGD("Not updating touch focus because injection was denied.");
+#endif
     }
 
 Unresponsive:
@@ -1185,20 +1265,15 @@
     updateDispatchStatisticsLocked(currentTime, entry,
             injectionResult, timeSpentWaitingForApplication);
 #if DEBUG_FOCUS
-    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d,"
-            "timeSpendWaitingForApplication=%0.1fms",
+    LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+            "timeSpentWaitingForApplication=%0.1fms",
             injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
 #endif
     return injectionResult;
 }
 
-void InputDispatcher::releaseTouchedWindowLocked() {
-    mTouchedWindow = NULL;
-    mTouchedWindowIsObscured = false;
-    mTouchedWallpaperWindows.clear();
-}
-
-void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags) {
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+        BitSet32 pointerIds) {
     mCurrentInputTargets.push();
 
     InputTarget& target = mCurrentInputTargets.editTop();
@@ -1206,6 +1281,8 @@
     target.flags = targetFlags;
     target.xOffset = - window->frameLeft;
     target.yOffset = - window->frameTop;
+    target.windowType = window->layoutParamsType;
+    target.pointerIds = pointerIds;
 }
 
 void InputDispatcher::addMonitoringTargetsLocked() {
@@ -1217,22 +1294,27 @@
         target.flags = 0;
         target.xOffset = 0;
         target.yOffset = 0;
+        target.windowType = InputWindow::TYPE_SYSTEM_OVERLAY;
     }
 }
 
 bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
-        int32_t injectorPid, int32_t injectorUid) {
-    if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
-        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+        const InjectionState* injectionState) {
+    if (injectionState
+            && injectionState->injectorUid > 0
+            && (window == NULL || window->ownerUid != injectionState->injectorUid)) {
+        bool result = mPolicy->checkInjectEventsPermissionNonReentrant(
+                injectionState->injectorPid, injectionState->injectorUid);
         if (! result) {
             if (window) {
                 LOGW("Permission denied: injecting event from pid %d uid %d to window "
                         "with input channel %s owned by uid %d",
-                        injectorPid, injectorUid, window->inputChannel->getName().string(),
+                        injectionState->injectorPid, injectionState->injectorUid,
+                        window->inputChannel->getName().string(),
                         window->ownerUid);
             } else {
                 LOGW("Permission denied: injecting event from pid %d uid %d",
-                        injectorPid, injectorUid);
+                        injectionState->injectorPid, injectionState->injectorUid);
             }
             return false;
         }
@@ -1282,12 +1364,19 @@
     }
 }
 
-void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime,
-        int32_t windowType, int32_t eventType) {
+bool InputDispatcher::shouldPokeUserActivityForCurrentInputTargetsLocked() {
+    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+        if (mCurrentInputTargets[i].windowType == InputWindow::TYPE_KEYGUARD) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, int32_t eventType) {
     CommandEntry* commandEntry = postCommandLocked(
             & InputDispatcher::doPokeUserActivityLockedInterruptible);
     commandEntry->eventTime = eventTime;
-    commandEntry->windowType = windowType;
     commandEntry->userActivityEventType = eventType;
 }
 
@@ -1296,12 +1385,19 @@
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
-            "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s",
+            "xOffset=%f, yOffset=%f, "
+            "windowType=%d, pointerIds=0x%x, "
+            "resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
+            inputTarget->windowType, inputTarget->pointerIds.value,
             toString(resumeWithAppendedMotionSample));
 #endif
 
+    // Make sure we are never called for streaming when splitting across multiple windows.
+    bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
+    assert(! (resumeWithAppendedMotionSample && isSplit));
+
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
     if (connection->status != Connection::STATUS_NORMAL) {
@@ -1310,6 +1406,23 @@
         return;
     }
 
+    // Split a motion event if needed.
+    if (isSplit) {
+        assert(eventEntry->type == EventEntry::TYPE_MOTION);
+
+        MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
+        if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+            MotionEntry* splitMotionEntry = splitMotionEvent(
+                    originalMotionEntry, inputTarget->pointerIds);
+#if DEBUG_FOCUS
+            LOGD("channel '%s' ~ Split motion event.",
+                    connection->getInputChannelName());
+            logOutboundMotionDetailsLocked("  ", splitMotionEntry);
+#endif
+            eventEntry = splitMotionEntry;
+        }
+    }
+
     // Resume the dispatch cycle with a freshly appended motion sample.
     // First we check that the last dispatch entry in the outbound queue is for the same
     // motion event to which we appended the motion sample.  If we find such a dispatch
@@ -1351,7 +1464,8 @@
             // The dispatch entry is in progress and is still potentially open for streaming.
             // Try to stream the new motion sample.  This might fail if the consumer has already
             // consumed the motion event (or if the channel is broken).
-            MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+            MotionSample* appendedMotionSample = motionEntry->lastSample;
             status_t status = connection->inputPublisher.appendMotionSample(
                     appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
             if (status == OK) {
@@ -1426,7 +1540,7 @@
     DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
             inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
     if (dispatchEntry->hasForegroundTarget()) {
-        eventEntry->pendingForegroundDispatches += 1;
+        incrementPendingForegroundDispatchesLocked(eventEntry);
     }
 
     // Handle the case where we could not stream a new motion sample because the consumer has
@@ -1470,8 +1584,8 @@
     dispatchEntry->inProgress = true;
 
     // Update the connection's input state.
-    InputState::Consistency consistency = connection->inputState.trackEvent(
-            dispatchEntry->eventEntry);
+    EventEntry* eventEntry = dispatchEntry->eventEntry;
+    InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry);
 
 #if FILTER_INPUT_EVENTS
     // Filter out inconsistent sequences of input events.
@@ -1497,16 +1611,13 @@
 
     // Publish the event.
     status_t status;
-    switch (dispatchEntry->eventEntry->type) {
+    switch (eventEntry->type) {
     case EventEntry::TYPE_KEY: {
-        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
         // Apply target flags.
         int32_t action = keyEntry->action;
         int32_t flags = keyEntry->flags;
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
-            flags |= AKEY_EVENT_FLAG_CANCELED;
-        }
 
         // Publish the key event.
         status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
@@ -1524,7 +1635,7 @@
     }
 
     case EventEntry::TYPE_MOTION: {
-        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
         // Apply target flags.
         int32_t action = motionEntry->action;
@@ -1532,9 +1643,6 @@
         if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
             action = AMOTION_EVENT_ACTION_OUTSIDE;
         }
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_CANCEL) {
-            action = AMOTION_EVENT_ACTION_CANCEL;
-        }
         if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
             flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
         }
@@ -1617,7 +1725,7 @@
     }
 
     // Record information about the newly started dispatch cycle.
-    connection->lastEventTime = dispatchEntry->eventEntry->eventTime;
+    connection->lastEventTime = eventEntry->eventTime;
     connection->lastDispatchTime = currentTime;
 
     // Notify other system components.
@@ -1773,6 +1881,78 @@
     } // release lock
 }
 
+InputDispatcher::MotionEntry*
+InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+    assert(pointerIds.value != 0);
+
+    uint32_t splitPointerIndexMap[MAX_POINTERS];
+    int32_t splitPointerIds[MAX_POINTERS];
+    PointerCoords splitPointerCoords[MAX_POINTERS];
+
+    uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+    uint32_t splitPointerCount = 0;
+
+    for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
+            originalPointerIndex++) {
+        int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]);
+        if (pointerIds.hasBit(pointerId)) {
+            splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
+            splitPointerIds[splitPointerCount] = pointerId;
+            splitPointerCoords[splitPointerCount] =
+                    originalMotionEntry->firstSample.pointerCoords[originalPointerIndex];
+            splitPointerCount += 1;
+        }
+    }
+    assert(splitPointerCount == pointerIds.count());
+
+    int32_t action = originalMotionEntry->action;
+    int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+    if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+            || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+        int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+        int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex];
+        if (pointerIds.hasBit(pointerId)) {
+            if (pointerIds.count() == 1) {
+                // The first/last pointer went down/up.
+                action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+                        ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+            }
+        } else {
+            // An unrelated pointer changed.
+            action = AMOTION_EVENT_ACTION_MOVE;
+        }
+    }
+
+    MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry(
+            originalMotionEntry->eventTime,
+            originalMotionEntry->deviceId,
+            originalMotionEntry->source,
+            originalMotionEntry->policyFlags,
+            action,
+            originalMotionEntry->flags,
+            originalMotionEntry->metaState,
+            originalMotionEntry->edgeFlags,
+            originalMotionEntry->xPrecision,
+            originalMotionEntry->yPrecision,
+            originalMotionEntry->downTime,
+            splitPointerCount, splitPointerIds, splitPointerCoords);
+
+    for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next;
+            originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) {
+        for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
+                splitPointerIndex++) {
+            uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
+            splitPointerCoords[splitPointerIndex] =
+                    originalMotionSample->pointerCoords[originalPointerIndex];
+        }
+
+        mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime,
+                splitPointerCoords);
+    }
+
+    return splitMotionEntry;
+}
+
 void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
@@ -1800,6 +1980,9 @@
             eventTime, deviceId, source, policyFlags, action, flags,
             keyCode, scanCode, metaState, downTime);
 #endif
+    if (! validateKeyEvent(action)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1839,6 +2022,9 @@
                 pointerCoords[i].orientation);
     }
 #endif
+    if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+        return;
+    }
 
     bool needWake;
     { // acquire lock
@@ -1913,8 +2099,10 @@
 
                     DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
                     if (! dispatchEntry->inProgress
-                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) {
-                        // No motion event is being dispatched.
+                            || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
+                            || dispatchEntry->isSplit()) {
+                        // No motion event is being dispatched, or it is being split across
+                        // windows in which case we cannot stream.
                         continue;
                     }
 
@@ -1972,24 +2160,24 @@
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
 
-    EventEntry* injectedEntry;
+    InjectionState* injectionState;
     bool needWake;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        injectedEntry = createEntryFromInjectedInputEventLocked(event);
+        EventEntry* injectedEntry = createEntryFromInjectedInputEventLocked(event);
         if (! injectedEntry) {
             return INPUT_EVENT_INJECTION_FAILED;
         }
 
-        injectedEntry->refCount += 1;
-        injectedEntry->injectorPid = injectorPid;
-        injectedEntry->injectorUid = injectorUid;
-
+        injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid);
         if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
-            injectedEntry->injectionIsAsync = true;
+            injectionState->injectionIsAsync = true;
         }
 
+        injectionState->refCount += 1;
+        injectedEntry->injectionState = injectionState;
+
         needWake = enqueueInboundEventLocked(injectedEntry);
     } // release lock
 
@@ -2005,7 +2193,7 @@
             injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
         } else {
             for (;;) {
-                injectionResult = injectedEntry->injectionResult;
+                injectionResult = injectionState->injectionResult;
                 if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
                     break;
                 }
@@ -2025,10 +2213,10 @@
 
             if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
                     && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
-                while (injectedEntry->pendingForegroundDispatches != 0) {
+                while (injectionState->pendingForegroundDispatches != 0) {
 #if DEBUG_INJECTION
                     LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
-                            injectedEntry->pendingForegroundDispatches);
+                            injectionState->pendingForegroundDispatches);
 #endif
                     nsecs_t remainingTimeout = endTime - now();
                     if (remainingTimeout <= 0) {
@@ -2045,7 +2233,7 @@
             }
         }
 
-        mAllocator.releaseEventEntry(injectedEntry);
+        mAllocator.releaseInjectionState(injectionState);
     } // release lock
 
 #if DEBUG_INJECTION
@@ -2058,14 +2246,15 @@
 }
 
 void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
-    if (entry->isInjected()) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
 #if DEBUG_INJECTION
         LOGD("Setting input event injection result to %d.  "
                 "injectorPid=%d, injectorUid=%d",
-                 injectionResult, entry->injectorPid, entry->injectorUid);
+                 injectionResult, injectionState->injectorPid, injectionState->injectorUid);
 #endif
 
-        if (entry->injectionIsAsync) {
+        if (injectionState->injectionIsAsync) {
             // Log the outcome since the injector did not wait for the injection result.
             switch (injectionResult) {
             case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2083,41 +2272,26 @@
             }
         }
 
-        entry->injectionResult = injectionResult;
+        injectionState->injectionResult = injectionResult;
         mInjectionResultAvailableCondition.broadcast();
     }
 }
 
+void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
+        injectionState->pendingForegroundDispatches += 1;
+    }
+}
+
 void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
-    entry->pendingForegroundDispatches -= 1;
+    InjectionState* injectionState = entry->injectionState;
+    if (injectionState) {
+        injectionState->pendingForegroundDispatches -= 1;
 
-    if (entry->isInjected() && entry->pendingForegroundDispatches == 0) {
-        mInjectionSyncFinishedCondition.broadcast();
-    }
-}
-
-static bool isValidKeyAction(int32_t action) {
-    switch (action) {
-    case AKEY_EVENT_ACTION_DOWN:
-    case AKEY_EVENT_ACTION_UP:
-        return true;
-    default:
-        return false;
-    }
-}
-
-static bool isValidMotionAction(int32_t action) {
-    switch (action & AMOTION_EVENT_ACTION_MASK) {
-    case AMOTION_EVENT_ACTION_DOWN:
-    case AMOTION_EVENT_ACTION_UP:
-    case AMOTION_EVENT_ACTION_CANCEL:
-    case AMOTION_EVENT_ACTION_MOVE:
-    case AMOTION_EVENT_ACTION_POINTER_DOWN:
-    case AMOTION_EVENT_ACTION_POINTER_UP:
-    case AMOTION_EVENT_ACTION_OUTSIDE:
-        return true;
-    default:
-        return false;
+        if (injectionState->pendingForegroundDispatches == 0) {
+            mInjectionSyncFinishedCondition.broadcast();
+        }
     }
 }
 
@@ -2126,9 +2300,7 @@
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY: {
         const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
-        if (! isValidKeyAction(keyEvent->getAction())) {
-            LOGE("Dropping injected key event since it has invalid action code 0x%x",
-                    keyEvent->getAction());
+        if (! validateKeyEvent(keyEvent->getAction())) {
             return NULL;
         }
 
@@ -2144,16 +2316,10 @@
 
     case AINPUT_EVENT_TYPE_MOTION: {
         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
-        if (! isValidMotionAction(motionEvent->getAction())) {
-            LOGE("Dropping injected motion event since it has invalid action code 0x%x.",
-                    motionEvent->getAction());
+        if (! validateMotionEvent(motionEvent->getAction(),
+                motionEvent->getPointerCount(), motionEvent->getPointerIds())) {
             return NULL;
         }
-        if (motionEvent->getPointerCount() == 0
-                || motionEvent->getPointerCount() > MAX_POINTERS) {
-            LOGE("Dropping injected motion event since it has an invalid pointer count %d.",
-                    motionEvent->getPointerCount());
-        }
 
         uint32_t policyFlags = POLICY_FLAG_INJECTED;
 
@@ -2182,6 +2348,16 @@
     }
 }
 
+const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) {
+    for (size_t i = 0; i < mWindows.size(); i++) {
+        const InputWindow* window = & mWindows[i];
+        if (window->inputChannel == inputChannel) {
+            return window;
+        }
+    }
+    return NULL;
+}
+
 void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
 #if DEBUG_FOCUS
     LOGD("setInputWindows");
@@ -2189,22 +2365,8 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        // Clear old window pointers but remember their associated channels.
+        // Clear old window pointers.
         mFocusedWindow = NULL;
-
-        sp<InputChannel> touchedWindowChannel;
-        if (mTouchedWindow) {
-            touchedWindowChannel = mTouchedWindow->inputChannel;
-            mTouchedWindow = NULL;
-        }
-        size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
-        if (numTouchedWallpapers != 0) {
-            for (size_t i = 0; i < numTouchedWallpapers; i++) {
-                mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
-            }
-            mTouchedWallpaperWindows.clear();
-        }
-        mWallpaperWindows.clear();
         mWindows.clear();
 
         // Loop over new windows and rebuild the necessary window pointers for
@@ -2213,26 +2375,23 @@
 
         size_t numWindows = mWindows.size();
         for (size_t i = 0; i < numWindows; i++) {
-            InputWindow* window = & mWindows.editItemAt(i);
+            const InputWindow* window = & mWindows.itemAt(i);
             if (window->hasFocus) {
                 mFocusedWindow = window;
-            }
-
-            if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
-                mWallpaperWindows.push(window);
-
-                for (size_t j = 0; j < numTouchedWallpapers; j++) {
-                    if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
-                        mTouchedWallpaperWindows.push(window);
-                    }
-                }
-            }
-
-            if (window->inputChannel == touchedWindowChannel) {
-                mTouchedWindow = window;
+                break;
             }
         }
-        mTempTouchedWallpaperChannels.clear();
+
+        for (size_t i = 0; i < mTouchState.windows.size(); ) {
+            TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
+            const InputWindow* window = getWindowLocked(touchedWindow.channel);
+            if (window) {
+                touchedWindow.window = window;
+                i += 1;
+            } else {
+                mTouchState.windows.removeAt(i);
+            }
+        }
 
 #if DEBUG_FOCUS
         logDispatchStateLocked();
@@ -2334,12 +2493,13 @@
     }
     dump.appendFormat("  focusedWindow: name='%s'\n",
             mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
-    dump.appendFormat("  touchedWindow: name='%s', touchDown=%d\n",
-            mTouchedWindow != NULL ? mTouchedWindow->name.string() : "<null>",
-            mTouchDown);
-    for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
-        dump.appendFormat("  touchedWallpaperWindows[%d]: name='%s'\n",
-                i, mTouchedWallpaperWindows[i]->name.string());
+    dump.appendFormat("  touchState: down=%s, split=%s\n", toString(mTouchState.down),
+            toString(mTouchState.split));
+    for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+        const TouchedWindow& touchedWindow = mTouchState.windows[i];
+        dump.appendFormat("  touchedWindow[%d]: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+                i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
+                touchedWindow.targetFlags);
     }
     for (size_t i = 0; i < mWindows.size(); i++) {
         dump.appendFormat("  windows[%d]: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
@@ -2594,8 +2754,7 @@
 void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType,
-            commandEntry->userActivityEventType);
+    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
 
     mLock.lock();
 }
@@ -2627,17 +2786,32 @@
 InputDispatcher::Allocator::Allocator() {
 }
 
+InputDispatcher::InjectionState*
+InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) {
+    InjectionState* injectionState = mInjectionStatePool.alloc();
+    injectionState->refCount = 1;
+    injectionState->injectorPid = injectorPid;
+    injectionState->injectorUid = injectorUid;
+    injectionState->injectionIsAsync = false;
+    injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+    injectionState->pendingForegroundDispatches = 0;
+    return injectionState;
+}
+
 void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
         nsecs_t eventTime) {
     entry->type = type;
     entry->refCount = 1;
     entry->dispatchInProgress = false;
     entry->eventTime = eventTime;
-    entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
-    entry->injectionIsAsync = false;
-    entry->injectorPid = -1;
-    entry->injectorUid = -1;
-    entry->pendingForegroundDispatches = 0;
+    entry->injectionState = NULL;
+}
+
+void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) {
+    if (entry->injectionState) {
+        releaseInjectionState(entry->injectionState);
+        entry->injectionState = NULL;
+    }
 }
 
 InputDispatcher::ConfigurationChangedEntry*
@@ -2720,6 +2894,15 @@
     return entry;
 }
 
+void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) {
+    injectionState->refCount -= 1;
+    if (injectionState->refCount == 0) {
+        mInjectionStatePool.free(injectionState);
+    } else {
+        assert(injectionState->refCount > 0);
+    }
+}
+
 void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
     switch (entry->type) {
     case EventEntry::TYPE_CONFIGURATION_CHANGED:
@@ -2741,6 +2924,7 @@
         ConfigurationChangedEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mConfigurationChangeEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2750,6 +2934,7 @@
 void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         mKeyEntryPool.free(entry);
     } else {
         assert(entry->refCount > 0);
@@ -2759,6 +2944,7 @@
 void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
     entry->refCount -= 1;
     if (entry->refCount == 0) {
+        releaseEventEntryInjectionState(entry);
         for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
             MotionSample* next = sample->next;
             mMotionSamplePool.free(sample);
@@ -2793,22 +2979,12 @@
     motionEntry->lastSample = sample;
 }
 
+void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) {
+    releaseEventEntryInjectionState(keyEntry);
 
-// --- InputDispatcher::EventEntry ---
-
-void InputDispatcher::EventEntry::recycle() {
-    injectionResult = INPUT_EVENT_INJECTION_PENDING;
-    dispatchInProgress = false;
-    pendingForegroundDispatches = 0;
-}
-
-
-// --- InputDispatcher::KeyEntry ---
-
-void InputDispatcher::KeyEntry::recycle() {
-    EventEntry::recycle();
-    syntheticRepeat = false;
-    interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN;
+    keyEntry->dispatchInProgress = false;
+    keyEntry->syntheticRepeat = false;
+    keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
 }
 
 
@@ -3057,6 +3233,72 @@
 }
 
 
+// --- InputDispatcher::TouchState ---
+
+InputDispatcher::TouchState::TouchState() :
+    down(false), split(false) {
+}
+
+InputDispatcher::TouchState::~TouchState() {
+}
+
+void InputDispatcher::TouchState::reset() {
+    down = false;
+    split = false;
+    windows.clear();
+}
+
+void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
+    down = other.down;
+    split = other.split;
+    windows.clear();
+    windows.appendVector(other.windows);
+}
+
+void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
+        int32_t targetFlags, BitSet32 pointerIds) {
+    if (targetFlags & InputTarget::FLAG_SPLIT) {
+        split = true;
+    }
+
+    for (size_t i = 0; i < windows.size(); i++) {
+        TouchedWindow& touchedWindow = windows.editItemAt(i);
+        if (touchedWindow.window == window) {
+            touchedWindow.targetFlags |= targetFlags;
+            touchedWindow.pointerIds.value |= pointerIds.value;
+            return;
+        }
+    }
+
+    windows.push();
+
+    TouchedWindow& touchedWindow = windows.editTop();
+    touchedWindow.window = window;
+    touchedWindow.targetFlags = targetFlags;
+    touchedWindow.pointerIds = pointerIds;
+    touchedWindow.channel = window->inputChannel;
+}
+
+void InputDispatcher::TouchState::removeOutsideTouchWindows() {
+    for (size_t i = 0 ; i < windows.size(); ) {
+        if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) {
+            windows.removeAt(i);
+        } else {
+            i += 1;
+        }
+    }
+}
+
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+    for (size_t i = 0; i < windows.size(); i++) {
+        if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
+            return windows[i].window;
+        }
+    }
+    return NULL;
+}
+
+
 // --- InputDispatcherThread ---
 
 InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 783cbc4..f2b029a 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -3366,7 +3366,7 @@
                 if (id > MAX_POINTER_ID) {
 #if DEBUG_POINTERS
                     LOGD("Pointers: Ignoring driver provided pointer id %d because "
-                            "it is larger than max supported id %d for optimizations",
+                            "it is larger than max supported id %d",
                             id, MAX_POINTER_ID);
 #endif
                     havePointerIds = false;
diff --git a/packages/VpnServices/res/values-fr/strings.xml b/packages/VpnServices/res/values-fr/strings.xml
index 80fefa4..69ba34b 100644
--- a/packages/VpnServices/res/values-fr/strings.xml
+++ b/packages/VpnServices/res/values-fr/strings.xml
@@ -2,7 +2,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4589592829302498102">"Services VPN"</string>
-    <string name="vpn_notification_title_connected" msgid="8598654486956133580">"VPN \n<xliff:g id="PROFILENAME">%s</xliff:g> connecté"</string>
+    <string name="vpn_notification_title_connected" msgid="8598654486956133580">"VPN  <xliff:g id="PROFILENAME">%s</xliff:g> connecté"</string>
     <string name="vpn_notification_title_disconnected" msgid="6216572264382192027">"VPN <xliff:g id="PROFILENAME">%s</xliff:g> déconnecté"</string>
     <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Touchez l\'écran pour vous reconnecter à un VPN."</string>
 </resources>
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index ea2c5d4..58209fd 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -319,6 +319,7 @@
     // Callback is disabled by default
     mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
     mOrientation = 0;
+    mOrientationChanged = false;
     cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
     LOG1("Client::Client X (pid %d)", callingPid);
@@ -496,6 +497,7 @@
             // Force the destruction of any previous overlay
             sp<Overlay> dummy;
             mHardware->setOverlay(dummy);
+            mOverlayRef = 0;
         } else {
             mSurface->unregisterBuffers();
         }
@@ -539,11 +541,12 @@
     CameraParameters params(mHardware->getParameters());
     params.getPreviewSize(&w, &h);
 
-    if (w != mOverlayW || h != mOverlayH) {
+    if (w != mOverlayW || h != mOverlayH || mOrientationChanged) {
         // Force the destruction of any previous overlay
         sp<Overlay> dummy;
         mHardware->setOverlay(dummy);
         mOverlayRef = 0;
+        mOrientationChanged = false;
     }
 
     status_t result = NO_ERROR;
@@ -810,6 +813,7 @@
 
 status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
     LOG1("sendCommand (pid %d)", getCallingPid());
+    int orientation;
     Mutex::Autolock lock(mLock);
     status_t result = checkPidAndHardware();
     if (result != NO_ERROR) return result;
@@ -821,20 +825,24 @@
         }
         switch (arg1) {
             case 0:
-                mOrientation = ISurface::BufferHeap::ROT_0;
+                orientation = ISurface::BufferHeap::ROT_0;
                 break;
             case 90:
-                mOrientation = ISurface::BufferHeap::ROT_90;
+                orientation = ISurface::BufferHeap::ROT_90;
                 break;
             case 180:
-                mOrientation = ISurface::BufferHeap::ROT_180;
+                orientation = ISurface::BufferHeap::ROT_180;
                 break;
             case 270:
-                mOrientation = ISurface::BufferHeap::ROT_270;
+                orientation = ISurface::BufferHeap::ROT_270;
                 break;
             default:
                 return BAD_VALUE;
         }
+        if (mOrientation != orientation) {
+            mOrientation = orientation;
+            if (mOverlayRef != 0) mOrientationChanged = true;
+        }
         return OK;
     }
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 0d69836..8f0ed75 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -164,7 +164,9 @@
         int                             mOverlayW;
         int                             mOverlayH;
         int                             mPreviewCallbackFlag;
-        int                             mOrientation;
+        int                             mOrientation;     // Current display orientation
+        // True if display orientation has been changed. This is only used in overlay.
+        int                             mOrientationChanged;
 
         // Ensures atomicity among the public methods
         mutable Mutex                   mLock;
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
index 81ae26f..0f1fc78 100644
--- a/services/java/com/android/server/EntropyService.java
+++ b/services/java/com/android/server/EntropyService.java
@@ -139,6 +139,7 @@
             out.println(SystemProperties.get("ro.bootloader"));
             out.println(SystemProperties.get("ro.hardware"));
             out.println(SystemProperties.get("ro.revision"));
+            out.println(new Object().hashCode());
             out.println(System.currentTimeMillis());
             out.println(System.nanoTime());
         } catch (IOException e) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 223d77d..22cd8ff 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -5592,6 +5592,38 @@
         }
     }
 
+    /**
+     * Allows app to retrieve the MIME type of a URI without having permission
+     * to access its content provider.
+     *
+     * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+     *
+     * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+     *     src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+     */
+    public String getProviderMimeType(Uri uri) {
+        final String name = uri.getAuthority();
+        final long ident = Binder.clearCallingIdentity();
+        ContentProviderHolder holder = null;
+
+        try {
+            holder = getContentProviderExternal(name);
+            if (holder != null) {
+                return holder.provider.getType(uri);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Content provider dead retrieving " + uri, e);
+            return null;
+        } finally {
+            if (holder != null) {
+                removeContentProviderExternal(name);
+            }
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        return null;
+    }
+
     // =========================================================
     // GLOBAL MANAGEMENT
     // =========================================================
diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java
index 0cb6943..e3347cb 100644
--- a/services/java/com/android/server/am/UriPermission.java
+++ b/services/java/com/android/server/am/UriPermission.java
@@ -27,8 +27,8 @@
  *
  * CTS tests for this functionality can be run with "runtest cts-appsecurity".
  *
- * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert
- *      /src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ *      src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
  */
 class UriPermission {
     final int uid;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index e3bae56..6f52f24 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -207,7 +207,7 @@
     virtual int32_t getMaxEventsPerSecond();
     virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
             const KeyEvent* keyEvent, uint32_t policyFlags);
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType);
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
     virtual bool checkInjectEventsPermissionNonReentrant(
             int32_t injectorPid, int32_t injectorUid);
 
@@ -973,10 +973,8 @@
     return consumed && ! error;
 }
 
-void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t windowType, int32_t eventType) {
-    if (windowType != InputWindow::TYPE_KEYGUARD) {
-        android_server_PowerManagerService_userActivity(eventTime, eventType);
-    }
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+    android_server_PowerManagerService_userActivity(eventTime, eventType);
 }
 
 
diff --git a/test-runner/src/android/test/mock/MockContentResolver.java b/test-runner/src/android/test/mock/MockContentResolver.java
index ab511f8..26eb8e4 100644
--- a/test-runner/src/android/test/mock/MockContentResolver.java
+++ b/test-runner/src/android/test/mock/MockContentResolver.java
@@ -75,6 +75,12 @@
     /** @hide */
     @Override
     protected IContentProvider acquireProvider(Context context, String name) {
+        return acquireExistingProvider(context, name);
+    }
+
+    /** @hide */
+    @Override
+    protected IContentProvider acquireExistingProvider(Context context, String name) {
 
         /*
          * Gets the content provider from the local map
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
index cfab90a..d89dba9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -43,6 +43,12 @@
     }
 
     @Override
+    public IContentProvider acquireExistingProvider(Context c, String name) {
+        // ignore
+        return null;
+    }
+
+    @Override
     public boolean releaseProvider(IContentProvider icp) {
         // ignore
         return false;