Merge "Minor tweaks to GridLayout for KeyGuardScreenUnlock."
diff --git a/api/current.txt b/api/current.txt
index fc244d8..721e33e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16782,6 +16782,9 @@
     method public void addF32(android.renderscript.Float3);
     method public void addF32(android.renderscript.Float4);
     method public void addF64(double);
+    method public void addF64(android.renderscript.Double2);
+    method public void addF64(android.renderscript.Double3);
+    method public void addF64(android.renderscript.Double4);
     method public void addI16(short);
     method public void addI16(android.renderscript.Short2);
     method public void addI16(android.renderscript.Short3);
@@ -16791,6 +16794,9 @@
     method public void addI32(android.renderscript.Int3);
     method public void addI32(android.renderscript.Int4);
     method public void addI64(long);
+    method public void addI64(android.renderscript.Long2);
+    method public void addI64(android.renderscript.Long3);
+    method public void addI64(android.renderscript.Long4);
     method public void addI8(byte);
     method public void addI8(android.renderscript.Byte2);
     method public void addI8(android.renderscript.Byte3);
@@ -16808,6 +16814,9 @@
     method public void addU32(android.renderscript.Long3);
     method public void addU32(android.renderscript.Long4);
     method public void addU64(long);
+    method public void addU64(android.renderscript.Long2);
+    method public void addU64(android.renderscript.Long3);
+    method public void addU64(android.renderscript.Long4);
     method public void addU8(short);
     method public void addU8(android.renderscript.Short2);
     method public void addU8(android.renderscript.Short3);
@@ -21695,7 +21704,10 @@
     method public void dispatchDisplayHint(int);
     method public boolean dispatchDragEvent(android.view.DragEvent);
     method protected void dispatchDraw(android.graphics.Canvas);
+    method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
     method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
+    method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
+    method protected boolean dispatchHoverEvent(android.view.MotionEvent);
     method public boolean dispatchKeyEvent(android.view.KeyEvent);
     method public boolean dispatchKeyEventPreIme(android.view.KeyEvent);
     method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
@@ -21883,6 +21895,7 @@
     method public void onFinishTemporaryDetach();
     method protected void onFocusChanged(boolean, int, android.graphics.Rect);
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
+    method public void onHoverChanged(boolean);
     method public boolean onHoverEvent(android.view.MotionEvent);
     method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
@@ -22159,6 +22172,10 @@
     method public abstract boolean onGenericMotion(android.view.View, android.view.MotionEvent);
   }
 
+  public static abstract interface View.OnHoverListener {
+    method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
+  }
+
   public static abstract interface View.OnKeyListener {
     method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
   }
@@ -22329,6 +22346,7 @@
     method protected void measureChildren(int, int);
     method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect);
     method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect);
+    method public boolean onInterceptHoverEvent(android.view.MotionEvent);
     method public boolean onInterceptTouchEvent(android.view.MotionEvent);
     method protected abstract void onLayout(boolean, int, int, int, int);
     method protected boolean onRequestFocusInDescendants(int, android.graphics.Rect);
@@ -25272,10 +25290,16 @@
     method public void dismiss();
     method public android.view.Menu getMenu();
     method public android.view.MenuInflater getMenuInflater();
+    method public void inflate(int);
+    method public void setOnDismissListener(android.widget.PopupMenu.OnDismissListener);
     method public void setOnMenuItemClickListener(android.widget.PopupMenu.OnMenuItemClickListener);
     method public void show();
   }
 
+  public static abstract interface PopupMenu.OnDismissListener {
+    method public abstract void onDismiss(android.widget.PopupMenu);
+  }
+
   public static abstract interface PopupMenu.OnMenuItemClickListener {
     method public abstract boolean onMenuItemClick(android.view.MenuItem);
   }
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index e81f799..4c4bf98 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -22,8 +22,6 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
-import java.io.FileDescriptor;
-import java.io.IOException;
 import java.util.ArrayList;
 
 public final class Backup {
@@ -51,17 +49,19 @@
             return;
         }
 
+        int socketFd = Integer.parseInt(nextArg());
+
         String arg = nextArg();
         if (arg.equals("backup")) {
-            doFullBackup();
+            doFullBackup(socketFd);
         } else if (arg.equals("restore")) {
-            doFullRestore();
+            doFullRestore(socketFd);
         } else {
             Log.e(TAG, "Invalid operation '" + arg + "'");
         }
     }
 
-    private void doFullBackup() {
+    private void doFullBackup(int socketFd) {
         ArrayList<String> packages = new ArrayList<String>();
         boolean saveApks = false;
         boolean saveShared = false;
@@ -100,24 +100,20 @@
         }
 
         try {
-            ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.out);
+            ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
             String[] packArray = new String[packages.size()];
             mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything,
                     packages.toArray(packArray));
-        } catch (IOException e) {
-            Log.e(TAG, "Can't dup out");
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for backup");
         }
     }
 
-    private void doFullRestore() {
+    private void doFullRestore(int socketFd) {
         // No arguments to restore
         try {
-            ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.in);
+            ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
             mBackupManager.fullRestore(fd);
-        } catch (IOException e) {
-            Log.e(TAG, "Can't dup System.in");
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for restore");
         }
diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java
index 56c4f7a..cde7dac 100644
--- a/core/java/android/pim/EventRecurrence.java
+++ b/core/java/android/pim/EventRecurrence.java
@@ -18,36 +18,18 @@
 
 import android.text.TextUtils;
 import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
 
 import java.util.Calendar;
+import java.util.HashMap;
 
-public class EventRecurrence
-{
-    /**
-     * Thrown when a recurrence string provided can not be parsed according
-     * to RFC2445.
-     */
-    public static class InvalidFormatException extends RuntimeException
-    {
-        InvalidFormatException(String s) {
-            super(s);
-        }
-    }
+/**
+ * Event recurrence utility functions.
+ */
+public class EventRecurrence {
+    private static String TAG = "EventRecur";
 
-    public EventRecurrence()
-    {
-        wkst = MO;
-    }
-    
-    /**
-     * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10.
-     */
-    public native void parse(String recur);
-
-    public void setStartDate(Time date) {
-        startDate = date;
-    }
-    
     public static final int SECONDLY = 1;
     public static final int MINUTELY = 2;
     public static final int HOURLY = 3;
@@ -64,13 +46,15 @@
     public static final int FR = 0x00200000;
     public static final int SA = 0x00400000;
 
-    public Time      startDate;
-    public int       freq;
+    public Time      startDate;     // set by setStartDate(), not parse()
+
+    public int       freq;          // SECONDLY, MINUTELY, etc.
     public String    until;
     public int       count;
     public int       interval;
-    public int       wkst;
+    public int       wkst;          // SU, MO, TU, etc.
 
+    /* lists with zero entries may be null references */
     public int[]     bysecond;
     public int       bysecondCount;
     public int[]     byminute;
@@ -79,7 +63,7 @@
     public int       byhourCount;
     public int[]     byday;
     public int[]     bydayNum;
-    public int       bydayCount;   
+    public int       bydayCount;
     public int[]     bymonthday;
     public int       bymonthdayCount;
     public int[]     byyearday;
@@ -91,6 +75,134 @@
     public int[]     bysetpos;
     public int       bysetposCount;
 
+    /** maps a part string to a parser object */
+    private static HashMap<String,PartParser> sParsePartMap;
+    static {
+        sParsePartMap = new HashMap<String,PartParser>();
+        sParsePartMap.put("FREQ", new ParseFreq());
+        sParsePartMap.put("UNTIL", new ParseUntil());
+        sParsePartMap.put("COUNT", new ParseCount());
+        sParsePartMap.put("INTERVAL", new ParseInterval());
+        sParsePartMap.put("BYSECOND", new ParseBySecond());
+        sParsePartMap.put("BYMINUTE", new ParseByMinute());
+        sParsePartMap.put("BYHOUR", new ParseByHour());
+        sParsePartMap.put("BYDAY", new ParseByDay());
+        sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay());
+        sParsePartMap.put("BYYEARDAY", new ParseByYearDay());
+        sParsePartMap.put("BYWEEKNO", new ParseByWeekNo());
+        sParsePartMap.put("BYMONTH", new ParseByMonth());
+        sParsePartMap.put("BYSETPOS", new ParseBySetPos());
+        sParsePartMap.put("WKST", new ParseWkst());
+    }
+
+    /* values for bit vector that keeps track of what we have already seen */
+    private static final int PARSED_FREQ = 1 << 0;
+    private static final int PARSED_UNTIL = 1 << 1;
+    private static final int PARSED_COUNT = 1 << 2;
+    private static final int PARSED_INTERVAL = 1 << 3;
+    private static final int PARSED_BYSECOND = 1 << 4;
+    private static final int PARSED_BYMINUTE = 1 << 5;
+    private static final int PARSED_BYHOUR = 1 << 6;
+    private static final int PARSED_BYDAY = 1 << 7;
+    private static final int PARSED_BYMONTHDAY = 1 << 8;
+    private static final int PARSED_BYYEARDAY = 1 << 9;
+    private static final int PARSED_BYWEEKNO = 1 << 10;
+    private static final int PARSED_BYMONTH = 1 << 11;
+    private static final int PARSED_BYSETPOS = 1 << 12;
+    private static final int PARSED_WKST = 1 << 13;
+
+    /** maps a FREQ value to an integer constant */
+    private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>();
+    static {
+        sParseFreqMap.put("SECONDLY", SECONDLY);
+        sParseFreqMap.put("MINUTELY", MINUTELY);
+        sParseFreqMap.put("HOURLY", HOURLY);
+        sParseFreqMap.put("DAILY", DAILY);
+        sParseFreqMap.put("WEEKLY", WEEKLY);
+        sParseFreqMap.put("MONTHLY", MONTHLY);
+        sParseFreqMap.put("YEARLY", YEARLY);
+    }
+
+    /** maps a two-character weekday string to an integer constant */
+    private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>();
+    static {
+        sParseWeekdayMap.put("SU", SU);
+        sParseWeekdayMap.put("MO", MO);
+        sParseWeekdayMap.put("TU", TU);
+        sParseWeekdayMap.put("WE", WE);
+        sParseWeekdayMap.put("TH", TH);
+        sParseWeekdayMap.put("FR", FR);
+        sParseWeekdayMap.put("SA", SA);
+    }
+
+    /** If set, allow lower-case recurrence rule strings.  Minor performance impact. */
+    private static final boolean ALLOW_LOWER_CASE = false;
+
+    /** If set, validate the value of UNTIL parts.  Minor performance impact. */
+    private static final boolean VALIDATE_UNTIL = false;
+
+    /** If set, require that only one of {UNTIL,COUNT} is present.  Breaks compat w/ old parser. */
+    private static final boolean ONLY_ONE_UNTIL_COUNT = false;
+
+
+    /**
+     * Thrown when a recurrence string provided can not be parsed according
+     * to RFC2445.
+     */
+    public static class InvalidFormatException extends RuntimeException {
+        InvalidFormatException(String s) {
+            super(s);
+        }
+    }
+
+    /**
+     * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10.  The string is
+     * parsed twice, by the old and new parsers, and the results are compared.
+     * <p>
+     * TODO: this will go away, and what is now parse2() will simply become parse().
+     */
+    public void parse(String recur) {
+        InvalidFormatException newExcep = null;
+        try {
+            parse2(recur);
+        } catch (InvalidFormatException ife) {
+            newExcep = ife;
+        }
+
+        boolean oldThrew = false;
+        try {
+            EventRecurrence check = new EventRecurrence();
+            check.parseNative(recur);
+            if (newExcep == null) {
+                // Neither threw, check to see if results match.
+                if (!equals(check)) {
+                    throw new InvalidFormatException("Recurrence rule parse does not match [" +
+                            recur + "]");
+                }
+            }
+        } catch (InvalidFormatException ife) {
+            oldThrew = true;
+            if (newExcep == null) {
+                // Old threw, but new didn't.  Log a warning, but don't throw.
+                Log.d(TAG, "NOTE: old parser rejected [" + recur + "]: " + ife.getMessage());
+            }
+        }
+
+        if (newExcep != null) {
+            if (!oldThrew) {
+                // New threw, but old didn't.  Log a warning and throw the exception.
+                Log.d(TAG, "NOTE: new parser rejected [" + recur + "]: " + newExcep.getMessage());
+            }
+            throw newExcep;
+        }
+    }
+
+    native void parseNative(String recur);
+
+    public void setStartDate(Time date) {
+        startDate = date;
+    }
+
     /**
      * Converts one of the Calendar.SUNDAY constants to the SU, MO, etc.
      * constants.  btw, I think we should switch to those here too, to
@@ -118,7 +230,7 @@
                 throw new RuntimeException("bad day of week: " + day);
         }
     }
-    
+
     public static int timeDay2Day(int day)
     {
         switch (day)
@@ -191,16 +303,16 @@
                 throw new RuntimeException("bad day of week: " + day);
         }
     }
-    
+
     /**
      * Converts one of the internal day constants (SU, MO, etc.) to the
      * two-letter string representing that constant.
-     * 
-     * @throws IllegalArgumentException Thrown if the day argument is not one of
-     * the defined day constants.
-     * 
+     *
      * @param day one the internal constants SU, MO, etc.
      * @return the two-letter string for the day ("SU", "MO", etc.)
+     *
+     * @throws IllegalArgumentException Thrown if the day argument is not one of
+     * the defined day constants.
      */
     private static String day2String(int day) {
         switch (day) {
@@ -283,7 +395,7 @@
             s.append(";UNTIL=");
             s.append(until);
         }
-        
+
         if (this.count != 0) {
             s.append(";COUNT=");
             s.append(this.count);
@@ -323,36 +435,484 @@
 
         return s.toString();
     }
-    
+
     public boolean repeatsOnEveryWeekDay() {
         if (this.freq != WEEKLY) {
-            return false; 
+            return false;
         }
-        
+
         int count = this.bydayCount;
         if (count != 5) {
             return false;
         }
-        
+
         for (int i = 0 ; i < count ; i++) {
             int day = byday[i];
             if (day == SU || day == SA) {
                 return false;
             }
         }
-        
+
         return true;
     }
-    
+
     public boolean repeatsMonthlyOnDayCount() {
         if (this.freq != MONTHLY) {
             return false;
         }
-        
+
         if (bydayCount != 1 || bymonthdayCount != 0) {
             return false;
         }
-        
+
         return true;
     }
+
+    /**
+     * Determines whether two integer arrays contain identical elements.
+     * <p>
+     * The native implementation over-allocated the arrays (and may have stuff left over from
+     * a previous run), so we can't just check the arrays -- the separately-maintained count
+     * field also matters.  We assume that a null array will have a count of zero, and that the
+     * array can hold as many elements as the associated count indicates.
+     * <p>
+     * TODO: replace this with Arrays.equals() when the old parser goes away.
+     */
+    private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) {
+        if (count1 != count2) {
+            return false;
+        }
+
+        for (int i = 0; i < count1; i++) {
+            if (array1[i] != array2[i])
+                return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof EventRecurrence)) {
+            return false;
+        }
+
+        EventRecurrence er = (EventRecurrence) obj;
+        return  (startDate == null ?
+                        er.startDate == null : Time.compare(startDate, er.startDate) == 0) &&
+                freq == er.freq &&
+                (until == null ? er.until == null : until.equals(er.until)) &&
+                count == er.count &&
+                interval == er.interval &&
+                wkst == er.wkst &&
+                arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) &&
+                arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) &&
+                arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) &&
+                arraysEqual(byday, bydayCount, er.byday, er.bydayCount) &&
+                arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) &&
+                arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) &&
+                arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) &&
+                arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) &&
+                arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) &&
+                arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount);
+    }
+
+    @Override public int hashCode() {
+        // We overrode equals, so we must override hashCode().  Nobody seems to need this though.
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Resets parser-modified fields to their initial state.  Does not alter startDate.
+     * <p>
+     * The original parser always set all of the "count" fields, "wkst", and "until",
+     * essentially allowing the same object to be used multiple times by calling parse().
+     * It's unclear whether this behavior was intentional.  For now, be paranoid and
+     * preserve the existing behavior by resetting the fields.
+     * <p>
+     * We don't need to touch the integer arrays; they will either be ignored or
+     * overwritten.  The "startDate" field is not set by the parser, so we ignore it here.
+     */
+    private void resetFields() {
+        until = null;
+        freq = count = interval = bysecondCount = byminuteCount = byhourCount =
+            bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount =
+            bysetposCount = 0;
+    }
+
+    /**
+     * Parses an rfc2445 recurrence rule string into its component pieces.  Attempting to parse
+     * malformed input will result in an EventRecurrence.InvalidFormatException.
+     *
+     * @param recur The recurrence rule to parse (in un-folded form).
+     */
+    void parse2(String recur) {
+        /*
+         * From RFC 2445 section 4.3.10:
+         *
+         * recur = "FREQ"=freq *(
+         *       ; either UNTIL or COUNT may appear in a 'recur',
+         *       ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
+         *
+         *       ( ";" "UNTIL" "=" enddate ) /
+         *       ( ";" "COUNT" "=" 1*DIGIT ) /
+         *
+         *       ; the rest of these keywords are optional,
+         *       ; but MUST NOT occur more than once
+         *
+         *       ( ";" "INTERVAL" "=" 1*DIGIT )          /
+         *       ( ";" "BYSECOND" "=" byseclist )        /
+         *       ( ";" "BYMINUTE" "=" byminlist )        /
+         *       ( ";" "BYHOUR" "=" byhrlist )           /
+         *       ( ";" "BYDAY" "=" bywdaylist )          /
+         *       ( ";" "BYMONTHDAY" "=" bymodaylist )    /
+         *       ( ";" "BYYEARDAY" "=" byyrdaylist )     /
+         *       ( ";" "BYWEEKNO" "=" bywknolist )       /
+         *       ( ";" "BYMONTH" "=" bymolist )          /
+         *       ( ";" "BYSETPOS" "=" bysplist )         /
+         *       ( ";" "WKST" "=" weekday )              /
+         *       ( ";" x-name "=" text )
+         *       )
+         *
+         * Examples:
+         *   FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+         *   FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
+         *
+         * Strategy:
+         * (1) Split the string at ';' boundaries to get an array of rule "parts".
+         * (2) For each part, find substrings for left/right sides of '=' (name/value).
+         * (3) Call a <name>-specific parsing function to parse the <value> into an
+         *     output field.
+         *
+         * By keeping track of which names we've seen in a bit vector, we can verify the
+         * constraints indicated above (FREQ appears first, none of them appear more than once --
+         * though x-[name] would require special treatment), and we have either UNTIL or COUNT
+         * but not both.
+         *
+         * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must
+         * be handled in a case-insensitive fashion, but case may be significant for other
+         * properties.  We don't have any case-sensitive values in RRULE, except possibly
+         * for the custom "X-" properties, but we ignore those anyway.  Thus, we can trivially
+         * convert the entire string to upper case and then use simple comparisons.
+         *
+         * Differences from previous version:
+         * - allows lower-case property and enumeration values [optional]
+         * - enforces that FREQ appears first
+         * - enforces that only one of UNTIL and COUNT may be specified
+         * - allows (but ignores) X-* parts
+         * - improved validation on various values (e.g. UNTIL timestamps)
+         * - error messages are more specific
+         */
+
+        /* TODO: replace with "if (freq != 0) throw" if nothing requires this */
+        resetFields();
+
+        int parseFlags = 0;
+        String[] parts;
+        if (ALLOW_LOWER_CASE) {
+            parts = recur.toUpperCase().split(";");
+        } else {
+            parts = recur.split(";");
+        }
+        for (String part : parts) {
+            int equalIndex = part.indexOf('=');
+            if (equalIndex <= 0) {
+                /* no '=' or no LHS */
+                throw new InvalidFormatException("Missing LHS in " + part);
+            }
+
+            String lhs = part.substring(0, equalIndex);
+            String rhs = part.substring(equalIndex + 1);
+            if (rhs.length() == 0) {
+                throw new InvalidFormatException("Missing RHS in " + part);
+            }
+
+            /*
+             * In lieu of a "switch" statement that allows string arguments, we use a
+             * map from strings to parsing functions.
+             */
+            PartParser parser = sParsePartMap.get(lhs);
+            if (parser == null) {
+                if (lhs.startsWith("X-")) {
+                    //Log.d(TAG, "Ignoring custom part " + lhs);
+                    continue;
+                }
+                throw new InvalidFormatException("Couldn't find parser for " + lhs);
+            } else {
+                int flag = parser.parsePart(rhs, this);
+                if ((parseFlags & flag) != 0) {
+                    throw new InvalidFormatException("Part " + lhs + " was specified twice");
+                }
+                if (parseFlags == 0 && flag != PARSED_FREQ) {
+                    throw new InvalidFormatException("FREQ must be specified first");
+                }
+                parseFlags |= flag;
+            }
+        }
+
+        // If not specified, week starts on Monday.
+        if ((parseFlags & PARSED_WKST) == 0) {
+            wkst = MO;
+        }
+
+        // FREQ is mandatory.
+        if ((parseFlags & PARSED_FREQ) == 0) {
+            throw new InvalidFormatException("Must specify a FREQ value");
+        }
+
+        // Can't have both UNTIL and COUNT.
+        if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) {
+            if (ONLY_ONE_UNTIL_COUNT) {
+                throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur);
+            } else {
+                Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur);
+            }
+        }
+    }
+
+    /**
+     * Base class for the RRULE part parsers.
+     */
+    abstract static class PartParser {
+        /**
+         * Parses a single part.
+         *
+         * @param value The right-hand-side of the part.
+         * @param er The EventRecurrence into which the result is stored.
+         * @return A bit value indicating which part was parsed.
+         */
+        public abstract int parsePart(String value, EventRecurrence er);
+
+        /**
+         * Parses an integer, with range-checking.
+         *
+         * @param str The string to parse.
+         * @param minVal Minimum allowed value.
+         * @param maxVal Maximum allowed value.
+         * @param allowZero Is 0 allowed?
+         * @return The parsed value.
+         */
+        public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) {
+            try {
+                if (str.charAt(0) == '+') {
+                    // Integer.parseInt does not allow a leading '+', so skip it manually.
+                    str = str.substring(1);
+                }
+                int val = Integer.parseInt(str);
+                if (val < minVal || val > maxVal || (val == 0 && !allowZero)) {
+                    throw new InvalidFormatException("Integer value out of range: " + str);
+                }
+                return val;
+            } catch (NumberFormatException nfe) {
+                throw new InvalidFormatException("Invalid integer value: " + str);
+            }
+        }
+
+        /**
+         * Parses a comma-separated list of integers, with range-checking.
+         *
+         * @param listStr The string to parse.
+         * @param minVal Minimum allowed value.
+         * @param maxVal Maximum allowed value.
+         * @param allowZero Is 0 allowed?
+         * @return A new array with values, sized to hold the exact number of elements.
+         */
+        public static int[] parseNumberList(String listStr, int minVal, int maxVal,
+                boolean allowZero) {
+            int[] values;
+
+            if (listStr.indexOf(",") < 0) {
+                // Common case: only one entry, skip split() overhead.
+                values = new int[1];
+                values[0] = parseIntRange(listStr, minVal, maxVal, allowZero);
+            } else {
+                String[] valueStrs = listStr.split(",");
+                int len = valueStrs.length;
+                values = new int[len];
+                for (int i = 0; i < len; i++) {
+                    values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero);
+                }
+            }
+            return values;
+        }
+   }
+
+    /** parses FREQ={SECONDLY,MINUTELY,...} */
+    private static class ParseFreq extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            Integer freq = sParseFreqMap.get(value);
+            if (freq == null) {
+                throw new InvalidFormatException("Invalid FREQ value: " + value);
+            }
+            er.freq = freq;
+            return PARSED_FREQ;
+        }
+    }
+    /** parses UNTIL=enddate, e.g. "19970829T021400" */
+    private static class ParseUntil extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            if (VALIDATE_UNTIL) {
+                try {
+                    // Parse the time to validate it.  The result isn't retained.
+                    Time until = new Time();
+                    until.parse(value);
+                } catch (TimeFormatException tfe) {
+                    throw new InvalidFormatException("Invalid UNTIL value: " + value);
+                }
+            }
+            er.until = value;
+            return PARSED_UNTIL;
+        }
+    }
+    /** parses COUNT=[non-negative-integer] */
+    private static class ParseCount extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            er.count = parseIntRange(value, 0, Integer.MAX_VALUE, true);
+            return PARSED_COUNT;
+        }
+    }
+    /** parses INTERVAL=[non-negative-integer] */
+    private static class ParseInterval extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            er.interval = parseIntRange(value, 1, Integer.MAX_VALUE, false);
+            return PARSED_INTERVAL;
+        }
+    }
+    /** parses BYSECOND=byseclist */
+    private static class ParseBySecond extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] bysecond = parseNumberList(value, 0, 59, true);
+            er.bysecond = bysecond;
+            er.bysecondCount = bysecond.length;
+            return PARSED_BYSECOND;
+        }
+    }
+    /** parses BYMINUTE=byminlist */
+    private static class ParseByMinute extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] byminute = parseNumberList(value, 0, 59, true);
+            er.byminute = byminute;
+            er.byminuteCount = byminute.length;
+            return PARSED_BYMINUTE;
+        }
+    }
+    /** parses BYHOUR=byhrlist */
+    private static class ParseByHour extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] byhour = parseNumberList(value, 0, 23, true);
+            er.byhour = byhour;
+            er.byhourCount = byhour.length;
+            return PARSED_BYHOUR;
+        }
+    }
+    /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */
+    private static class ParseByDay extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] byday;
+            int[] bydayNum;
+            int bydayCount;
+
+            if (value.indexOf(",") < 0) {
+                /* only one entry, skip split() overhead */
+                bydayCount = 1;
+                byday = new int[1];
+                bydayNum = new int[1];
+                parseWday(value, byday, bydayNum, 0);
+            } else {
+                String[] wdays = value.split(",");
+                int len = wdays.length;
+                bydayCount = len;
+                byday = new int[len];
+                bydayNum = new int[len];
+                for (int i = 0; i < len; i++) {
+                    parseWday(wdays[i], byday, bydayNum, i);
+                }
+            }
+            er.byday = byday;
+            er.bydayNum = bydayNum;
+            er.bydayCount = bydayCount;
+            return PARSED_BYDAY;
+        }
+
+        /** parses [int]weekday, putting the pieces into parallel array entries */
+        private static void parseWday(String str, int[] byday, int[] bydayNum, int index) {
+            int wdayStrStart = str.length() - 2;
+            String wdayStr;
+
+            if (wdayStrStart > 0) {
+                /* number is included; parse it out and advance to weekday */
+                String numPart = str.substring(0, wdayStrStart);
+                int num = parseIntRange(numPart, -53, 53, false);
+                bydayNum[index] = num;
+                wdayStr = str.substring(wdayStrStart);
+            } else {
+                /* just the weekday string */
+                wdayStr = str;
+            }
+            Integer wday = sParseWeekdayMap.get(wdayStr);
+            if (wday == null) {
+                throw new InvalidFormatException("Invalid BYDAY value: " + str);
+            }
+            byday[index] = wday;
+        }
+    }
+    /** parses BYMONTHDAY=bymodaylist */
+    private static class ParseByMonthDay extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] bymonthday = parseNumberList(value, -31, 31, false);
+            er.bymonthday = bymonthday;
+            er.bymonthdayCount = bymonthday.length;
+            return PARSED_BYMONTHDAY;
+        }
+    }
+    /** parses BYYEARDAY=byyrdaylist */
+    private static class ParseByYearDay extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] byyearday = parseNumberList(value, -366, 366, false);
+            er.byyearday = byyearday;
+            er.byyeardayCount = byyearday.length;
+            return PARSED_BYYEARDAY;
+        }
+    }
+    /** parses BYWEEKNO=bywknolist */
+    private static class ParseByWeekNo extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] byweekno = parseNumberList(value, -53, 53, false);
+            er.byweekno = byweekno;
+            er.byweeknoCount = byweekno.length;
+            return PARSED_BYWEEKNO;
+        }
+    }
+    /** parses BYMONTH=bymolist */
+    private static class ParseByMonth extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] bymonth = parseNumberList(value, 1, 12, false);
+            er.bymonth = bymonth;
+            er.bymonthCount = bymonth.length;
+            return PARSED_BYMONTH;
+        }
+    }
+    /** parses BYSETPOS=bysplist */
+    private static class ParseBySetPos extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true);
+            er.bysetpos = bysetpos;
+            er.bysetposCount = bysetpos.length;
+            return PARSED_BYSETPOS;
+        }
+    }
+    /** parses WKST={SU,MO,...} */
+    private static class ParseWkst extends PartParser {
+        @Override public int parsePart(String value, EventRecurrence er) {
+            Integer wkst = sParseWeekdayMap.get(value);
+            if (wkst == null) {
+                throw new InvalidFormatException("Invalid WKST value: " + value);
+            }
+            er.wkst = wkst;
+            return PARSED_WKST;
+        }
+    }
 }
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 14e7bed..c90de17 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -16,9 +16,6 @@
 
 package android.preference;
 
-import com.android.internal.util.XmlUtils;
-
-import android.app.ActionBar;
 import android.app.Fragment;
 import android.app.FragmentBreadCrumbs;
 import android.app.FragmentManager;
@@ -26,7 +23,6 @@
 import android.app.ListActivity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -41,23 +37,26 @@
 import android.util.Xml;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This is the base class for an activity to show a hierarchy of preferences
  * to the user.  Prior to {@link android.os.Build.VERSION_CODES#HONEYCOMB}
@@ -66,7 +65,7 @@
  * class.  If you are using PreferenceActivity in its old mode, the documentation
  * there applies to the deprecated APIs here.
  *
- * <p>This activity shows one or more headers of preferences, each of with
+ * <p>This activity shows one or more headers of preferences, each of which
  * is associated with a {@link PreferenceFragment} to display the preferences
  * of that header.  The actual layout and display of these associations can
  * however vary; currently there are two major approaches it may take:
@@ -117,7 +116,6 @@
 public abstract class PreferenceActivity extends ListActivity implements
         PreferenceManager.OnPreferenceTreeClickListener,
         PreferenceFragment.OnPreferenceStartFragmentCallback {
-    private static final String TAG = "PreferenceActivity";
 
     // Constants for state save/restore
     private static final String HEADERS_TAG = ":android:headers";
@@ -182,8 +180,6 @@
 
     private final ArrayList<Header> mHeaders = new ArrayList<Header>();
 
-    private HeaderAdapter mAdapter;
-
     private FrameLayout mListFooter;
 
     private ViewGroup mPrefsContainer;
@@ -222,8 +218,8 @@
                     ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
                     mHeaders.clear();
                     onBuildHeaders(mHeaders);
-                    if (mAdapter != null) {
-                        mAdapter.notifyDataSetChanged();
+                    if (mAdapter instanceof BaseAdapter) {
+                        ((BaseAdapter) mAdapter).notifyDataSetChanged();
                     }
                     Header header = onGetNewHeader();
                     if (header != null && header.fragment != null) {
@@ -387,6 +383,7 @@
         public Bundle extras;
 
         public Header() {
+            // Empty
         }
 
         /**
@@ -521,7 +518,7 @@
             if (headers != null) {
                 mHeaders.addAll(headers);
                 int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
-                        (int)HEADER_ID_UNDEFINED);
+                        (int) HEADER_ID_UNDEFINED);
                 if (curHeader >= 0 && curHeader < mHeaders.size()) {
                     setSelectedHeader(mHeaders.get(curHeader));
                 }
@@ -567,8 +564,7 @@
             findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
             mPrefsContainer.setVisibility(View.VISIBLE);
         } else if (mHeaders.size() > 0) {
-            mAdapter = new HeaderAdapter(this, mHeaders);
-            setListAdapter(mAdapter);
+            setListAdapter(new HeaderAdapter(this, mHeaders));
             if (!mSinglePane) {
                 // Multi-pane.
                 getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
@@ -715,6 +711,7 @@
      * @param target The list in which to place the headers.
      */
     public void onBuildHeaders(List<Header> target) {
+        // Should be overloaded by subclasses
     }
 
     /**
@@ -743,6 +740,7 @@
             int type;
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                     && type != XmlPullParser.START_TAG) {
+                // Parse next until start tag is found
             }
 
             String nodeName = parser.getName();
@@ -951,7 +949,8 @@
         super.onListItemClick(l, v, position, id);
 
         if (mAdapter != null) {
-            onHeaderClick(mHeaders.get(position), position);
+            Object item = mAdapter.getItem(position);
+            if (item instanceof Header) onHeaderClick((Header) item, position);
         }
     }
 
@@ -993,7 +992,7 @@
      * @param fragmentName The name of the fragment to display.
      * @param args Optional arguments to supply to the fragment.
      * @param titleRes Optional resource ID of title to show for this item.
-     * @param titleRes Optional resource ID of short title to show for this item.
+     * @param shortTitleRes Optional resource ID of short title to show for this item.
      * @return Returns an Intent that can be launched to display the given
      * fragment.
      */
@@ -1032,7 +1031,7 @@
      * code in which to report the result.
      * @param titleRes Resource ID of string to display for the title of
      * this set of preferences.
-     * @param titleRes Resource ID of string to display for the short title of
+     * @param shortTitleRes Resource ID of string to display for the short title of
      * this set of preferences.
      */
     public void startWithFragment(String fragmentName, Bundle args,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 603edf0..19e9a67 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -300,6 +300,21 @@
             "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
 
     /**
+     * Activity Action: Show a dialog to select input method.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_INPUT_METHOD_PICKER =
+            "android.settings.SHOW_INPUT_METHOD_PICKER";
+
+    /**
      * Activity Action: Show settings to manage the user input dictionary.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/server/BluetoothBondState.java b/core/java/android/server/BluetoothBondState.java
index a36cd24..39c3c88 100644
--- a/core/java/android/server/BluetoothBondState.java
+++ b/core/java/android/server/BluetoothBondState.java
@@ -79,6 +79,7 @@
         mService = service;
         mBluetoothInputProfileHandler =
             BluetoothInputProfileHandler.getInstance(mContext, mService);
+        getProfileProxy();
     }
 
     synchronized void setPendingOutgoingBonding(String address) {
@@ -131,10 +132,6 @@
 
         if (state == BluetoothDevice.BOND_BONDED) {
             mService.addProfileState(address);
-        } else if (state == BluetoothDevice.BOND_BONDING) {
-            if (mA2dpProxy == null || mHeadsetProxy == null) {
-                getProfileProxy();
-            }
         } else if (state == BluetoothDevice.BOND_NONE) {
             mService.removeProfileState(address);
         }
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 62792f4..a4588ae 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1247,18 +1247,9 @@
             return true;
         }
 
-        boolean ret;
-        // Just do the SDP if the device is already  created and UUIDs are not
-        // NULL, else create the device and then do SDP.
-        if (mDeviceProperties.isInCache(address) && getRemoteUuids(address) != null) {
-            String path = getObjectPathFromAddress(address);
-            if (path == null) return false;
-
-            // Use an empty string for the UUID pattern
-            ret = discoverServicesNative(path, "");
-        } else {
-            ret = createDeviceNative(address);
-        }
+        // If the device is already created, we will
+        // do the SDP on the callback of createDeviceNative.
+        boolean ret= createDeviceNative(address);
 
         mUuidIntentTracker.add(address);
         if (uuid != null) {
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index e14b975..49f3bbe 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -32,6 +32,11 @@
 public final class InputEventConsistencyVerifier {
     private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
 
+    private static final String EVENT_TYPE_KEY = "KeyEvent";
+    private static final String EVENT_TYPE_TRACKBALL = "TrackballEvent";
+    private static final String EVENT_TYPE_TOUCH = "TouchEvent";
+    private static final String EVENT_TYPE_GENERIC_MOTION = "GenericMotionEvent";
+
     // The number of recent events to log when a problem is detected.
     // Can be set to 0 to disable logging recent events but the runtime overhead of
     // this feature is negligible on current hardware.
@@ -54,6 +59,7 @@
     // It does not make sense to examine the contents of the last event since it may have
     // been recycled.
     private InputEvent mLastEvent;
+    private String mLastEventType;
     private int mLastNestingLevel;
 
     // Copy of the most recent events.
@@ -185,7 +191,7 @@
      * and both dispatching methods call into the consistency verifier.
      */
     public void onKeyEvent(KeyEvent event, int nestingLevel) {
-        if (!startEvent(event, nestingLevel, "KeyEvent")) {
+        if (!startEvent(event, nestingLevel, EVENT_TYPE_KEY)) {
             return;
         }
 
@@ -247,7 +253,7 @@
      * and both dispatching methods call into the consistency verifier.
      */
     public void onTrackballEvent(MotionEvent event, int nestingLevel) {
-        if (!startEvent(event, nestingLevel, "TrackballEvent")) {
+        if (!startEvent(event, nestingLevel, EVENT_TYPE_TRACKBALL)) {
             return;
         }
 
@@ -310,23 +316,19 @@
      * and both dispatching methods call into the consistency verifier.
      */
     public void onTouchEvent(MotionEvent event, int nestingLevel) {
-        if (!startEvent(event, nestingLevel, "TouchEvent")) {
+        if (!startEvent(event, nestingLevel, EVENT_TYPE_TOUCH)) {
             return;
         }
 
         final int action = event.getAction();
         final boolean newStream = action == MotionEvent.ACTION_DOWN
                 || action == MotionEvent.ACTION_CANCEL;
-        if (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled) {
-            if (newStream) {
-                mTouchEventStreamIsTainted = false;
-                mTouchEventStreamUnhandled = false;
-                mTouchEventStreamPointers = 0;
-            } else {
-                finishEvent(mTouchEventStreamIsTainted);
-                return;
-            }
+        if (newStream && (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled)) {
+            mTouchEventStreamIsTainted = false;
+            mTouchEventStreamUnhandled = false;
+            mTouchEventStreamPointers = 0;
         }
+        final boolean wasTainted = mTouchEventStreamIsTainted;
 
         try {
             ensureMetaStateIsNormalized(event.getMetaState());
@@ -439,7 +441,7 @@
                 problem("Source was not SOURCE_CLASS_POINTER.");
             }
         } finally {
-            finishEvent(false);
+            finishEvent(wasTainted);
         }
     }
 
@@ -453,7 +455,7 @@
      * and both dispatching methods call into the consistency verifier.
      */
     public void onGenericMotionEvent(MotionEvent event, int nestingLevel) {
-        if (!startEvent(event, nestingLevel, "GenericMotionEvent")) {
+        if (!startEvent(event, nestingLevel, EVENT_TYPE_GENERIC_MOTION)) {
             return;
         }
 
@@ -568,21 +570,19 @@
     }
 
     private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
-        // Ignore the event if it is already tainted.
-        if (event.isTainted()) {
-            return false;
-        }
-
         // Ignore the event if we already checked it at a higher nesting level.
-        if (event == mLastEvent && nestingLevel < mLastNestingLevel) {
+        if (event == mLastEvent && nestingLevel < mLastNestingLevel
+                && eventType == mLastEventType) {
             return false;
         }
 
         if (nestingLevel > 0) {
             mLastEvent = event;
+            mLastEventType = eventType;
             mLastNestingLevel = nestingLevel;
         } else {
             mLastEvent = null;
+            mLastEventType = null;
             mLastNestingLevel = 0;
         }
 
@@ -593,27 +593,30 @@
 
     private void finishEvent(boolean tainted) {
         if (mViolationMessage != null && mViolationMessage.length() != 0) {
-            mViolationMessage.append("\n  in ").append(mCaller);
-            mViolationMessage.append("\n  ");
-            appendEvent(mViolationMessage, 0, mCurrentEvent, false);
+            if (!tainted) {
+                // Write a log message only if the event was not already tainted.
+                mViolationMessage.append("\n  in ").append(mCaller);
+                mViolationMessage.append("\n  ");
+                appendEvent(mViolationMessage, 0, mCurrentEvent, false);
 
-            if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
-                mViolationMessage.append("\n  -- recent events --");
-                for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
-                    final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
-                            % RECENT_EVENTS_TO_LOG;
-                    final InputEvent event = mRecentEvents[index];
-                    if (event == null) {
-                        break;
+                if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
+                    mViolationMessage.append("\n  -- recent events --");
+                    for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
+                        final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
+                                % RECENT_EVENTS_TO_LOG;
+                        final InputEvent event = mRecentEvents[index];
+                        if (event == null) {
+                            break;
+                        }
+                        mViolationMessage.append("\n  ");
+                        appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]);
                     }
-                    mViolationMessage.append("\n  ");
-                    appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]);
                 }
-            }
 
-            Log.d(mLogTag, mViolationMessage.toString());
+                Log.d(mLogTag, mViolationMessage.toString());
+                tainted = true;
+            }
             mViolationMessage.setLength(0);
-            tainted = true;
         }
 
         if (tainted) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4403591..f70ca90 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2262,6 +2262,8 @@
 
     private OnTouchListener mOnTouchListener;
 
+    private OnHoverListener mOnHoverListener;
+
     private OnGenericMotionListener mOnGenericMotionListener;
 
     private OnDragListener mOnDragListener;
@@ -2484,6 +2486,12 @@
     Rect mLocalDirtyRect;
 
     /**
+     * Set to true when the view is sending hover accessibility events because it
+     * is the innermost hovered view.
+     */
+    private boolean mSendingHoverAccessibilityEvents;
+
+    /**
      * Consistency verifier for debugging purposes.
      * @hide
      */
@@ -2503,6 +2511,9 @@
         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
+        mUserPaddingStart = -1;
+        mUserPaddingEnd = -1;
+        mUserPaddingRelative = false;
     }
 
     /**
@@ -2864,13 +2875,16 @@
 
         mUserPaddingRelative = (startPadding >= 0 || endPadding >= 0);
 
+        // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
+        // layout direction). Those cached values will be used later during padding resolution.
+        mUserPaddingStart = startPadding;
+        mUserPaddingEnd = endPadding;
+
         if (padding >= 0) {
             leftPadding = padding;
             topPadding = padding;
             rightPadding = padding;
             bottomPadding = padding;
-            startPadding = padding;
-            endPadding = padding;
         }
 
         // If the user specified the padding (either with android:padding or
@@ -2882,11 +2896,6 @@
                 rightPadding >= 0 ? rightPadding : mPaddingRight,
                 bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
 
-        // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
-        // layout direction). Those cached values will be used later during padding resolution.
-        mUserPaddingStart = startPadding;
-        mUserPaddingEnd = endPadding;
-
         if (viewFlagMasks != 0) {
             setFlags(viewFlagValues, viewFlagMasks);
         }
@@ -5117,9 +5126,6 @@
             return true;
         }
 
-        if (mInputEventConsistencyVerifier != null) {
-            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
-        }
         return false;
     }
 
@@ -5147,6 +5153,12 @@
                     || action == MotionEvent.ACTION_HOVER_MOVE
                     || action == MotionEvent.ACTION_HOVER_EXIT) {
                 if (dispatchHoverEvent(event)) {
+                    // For compatibility with existing applications that handled HOVER_MOVE
+                    // events in onGenericMotionEvent, dispatch the event there.  The
+                    // onHoverEvent method did not exist at the time.
+                    if (action == MotionEvent.ACTION_HOVER_MOVE) {
+                        dispatchGenericMotionEventInternal(event);
+                    }
                     return true;
                 }
             } else if (dispatchGenericPointerEvent(event)) {
@@ -5156,6 +5168,17 @@
             return true;
         }
 
+        if (dispatchGenericMotionEventInternal(event)) {
+            return true;
+        }
+
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+        }
+        return false;
+    }
+
+    private boolean dispatchGenericMotionEventInternal(MotionEvent event) {
         //noinspection SimplifiableIfStatement
         if (mOnGenericMotionListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnGenericMotionListener.onGenericMotion(this, event)) {
@@ -5181,13 +5204,42 @@
      *
      * @param event The motion event to be dispatched.
      * @return True if the event was handled by the view, false otherwise.
-     * @hide
      */
     protected boolean dispatchHoverEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_ENTER:
+                if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = true;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (mSendingHoverAccessibilityEvents) {
+                    mSendingHoverAccessibilityEvents = false;
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                }
+                break;
+        }
+
+        if (mOnHoverListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                && mOnHoverListener.onHover(this, event)) {
+            return true;
+        }
+
         return onHoverEvent(event);
     }
 
     /**
+     * Returns true if the view has a child to which it has recently sent
+     * {@link MotionEvent#ACTION_HOVER_ENTER}.  If this view is hovered and
+     * it does not have a hovered child, then it must be the innermost hovered view.
+     * @hide
+     */
+    protected boolean hasHoveredChild() {
+        return false;
+    }
+
+    /**
      * Dispatch a generic motion event to the view under the first pointer.
      * <p>
      * Do not call this method directly.
@@ -5196,7 +5248,6 @@
      *
      * @param event The motion event to be dispatched.
      * @return True if the event was handled by the view, false otherwise.
-     * @hide
      */
     protected boolean dispatchGenericPointerEvent(MotionEvent event) {
         return false;
@@ -5211,7 +5262,6 @@
      *
      * @param event The motion event to be dispatched.
      * @return True if the event was handled by the view, false otherwise.
-     * @hide
      */
     protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
         return false;
@@ -5788,71 +5838,129 @@
     /**
      * Implement this method to handle hover events.
      * <p>
-     * Hover events are pointer events with action {@link MotionEvent#ACTION_HOVER_ENTER},
-     * {@link MotionEvent#ACTION_HOVER_MOVE}, or {@link MotionEvent#ACTION_HOVER_EXIT}.
+     * This method is called whenever a pointer is hovering into, over, or out of the
+     * bounds of a view and the view is not currently being touched.
+     * Hover events are represented as pointer events with action
+     * {@link MotionEvent#ACTION_HOVER_ENTER}, {@link MotionEvent#ACTION_HOVER_MOVE},
+     * or {@link MotionEvent#ACTION_HOVER_EXIT}.
+     * </p>
+     * <ul>
+     * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_ENTER}
+     * when the pointer enters the bounds of the view.</li>
+     * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_MOVE}
+     * when the pointer has already entered the bounds of the view and has moved.</li>
+     * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_EXIT}
+     * when the pointer has exited the bounds of the view or when the pointer is
+     * about to go down due to a button click, tap, or similar user action that
+     * causes the view to be touched.</li>
+     * </ul>
+     * <p>
+     * The view should implement this method to return true to indicate that it is
+     * handling the hover event, such as by changing its drawable state.
      * </p><p>
-     * The view receives hover enter as the pointer enters the bounds of the view and hover
-     * exit as the pointer exits the bound of the view or just before the pointer goes down
-     * (which implies that {@link #onTouchEvent(MotionEvent)} will be called soon).
-     * </p><p>
-     * If the view would like to handle the hover event itself and prevent its children
-     * from receiving hover, it should return true from this method.  If this method returns
-     * true and a child has already received a hover enter event, the child will
-     * automatically receive a hover exit event.
-     * </p><p>
-     * The default implementation sets the hovered state of the view if the view is
-     * clickable.
+     * The default implementation calls {@link #setHovered} to update the hovered state
+     * of the view when a hover enter or hover exit event is received, if the view
+     * is enabled and is clickable.
      * </p>
      *
      * @param event The motion event that describes the hover.
-     * @return True if this view handled the hover event and does not want its children
-     * to receive the hover event.
+     * @return True if the view handled the hover event.
+     *
+     * @see #isHovered
+     * @see #setHovered
+     * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_HOVER_ENTER:
-                setHovered(true);
-                break;
+        if (isHoverable()) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_HOVER_ENTER:
+                    setHovered(true);
+                    break;
+                case MotionEvent.ACTION_HOVER_EXIT:
+                    setHovered(false);
+                    break;
+            }
+            return true;
+        }
+        return false;
+    }
 
-            case MotionEvent.ACTION_HOVER_EXIT:
-                setHovered(false);
-                break;
+    /**
+     * Returns true if the view should handle {@link #onHoverEvent}
+     * by calling {@link #setHovered} to change its hovered state.
+     *
+     * @return True if the view is hoverable.
+     */
+    private boolean isHoverable() {
+        final int viewFlags = mViewFlags;
+        if ((viewFlags & ENABLED_MASK) == DISABLED) {
+            return false;
         }
 
-        return false;
+        return (viewFlags & CLICKABLE) == CLICKABLE
+                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
     }
 
     /**
      * Returns true if the view is currently hovered.
      *
      * @return True if the view is currently hovered.
+     *
+     * @see #setHovered
+     * @see #onHoverChanged
      */
+    @ViewDebug.ExportedProperty
     public boolean isHovered() {
         return (mPrivateFlags & HOVERED) != 0;
     }
 
     /**
      * Sets whether the view is currently hovered.
+     * <p>
+     * Calling this method also changes the drawable state of the view.  This
+     * enables the view to react to hover by using different drawable resources
+     * to change its appearance.
+     * </p><p>
+     * The {@link #onHoverChanged} method is called when the hovered state changes.
+     * </p>
      *
      * @param hovered True if the view is hovered.
+     *
+     * @see #isHovered
+     * @see #onHoverChanged
      */
     public void setHovered(boolean hovered) {
         if (hovered) {
             if ((mPrivateFlags & HOVERED) == 0) {
                 mPrivateFlags |= HOVERED;
                 refreshDrawableState();
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                onHoverChanged(true);
             }
         } else {
             if ((mPrivateFlags & HOVERED) != 0) {
                 mPrivateFlags &= ~HOVERED;
                 refreshDrawableState();
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                onHoverChanged(false);
             }
         }
     }
 
     /**
+     * Implement this method to handle hover state changes.
+     * <p>
+     * This method is called whenever the hover state changes as a result of a
+     * call to {@link #setHovered}.
+     * </p>
+     *
+     * @param hovered The current hover state, as returned by {@link #isHovered}.
+     *
+     * @see #isHovered
+     * @see #setHovered
+     */
+    public void onHoverChanged(boolean hovered) {
+    }
+
+    /**
      * Implement this method to handle touch screen motion events.
      *
      * @param event The motion event.
@@ -8860,12 +8968,12 @@
                 // Start user padding override Left user padding. Otherwise, if Left user
                 // padding is not defined, use the default left padding. If Left user padding
                 // is defined, just use it.
-                if (mUserPaddingStart >= 0) {
+                if (mUserPaddingStart > 0) {
                     mUserPaddingLeft = mUserPaddingStart;
                 } else if (mUserPaddingLeft < 0) {
                     mUserPaddingLeft = mPaddingLeft;
                 }
-                if (mUserPaddingEnd >= 0) {
+                if (mUserPaddingEnd > 0) {
                     mUserPaddingRight = mUserPaddingEnd;
                 } else if (mUserPaddingRight < 0) {
                     mUserPaddingRight = mPaddingRight;
@@ -11026,6 +11134,10 @@
      */
     public void setPaddingRelative(int start, int top, int end, int bottom) {
         mUserPaddingRelative = true;
+
+        mUserPaddingStart = start;
+        mUserPaddingEnd = end;
+
         switch(getResolvedLayoutDirection()) {
             case LAYOUT_DIRECTION_RTL:
                 setPadding(end, top, start, bottom);
@@ -13097,6 +13209,24 @@
     }
 
     /**
+     * Interface definition for a callback to be invoked when a hover event is
+     * dispatched to this view. The callback will be invoked before the hover
+     * event is given to the view.
+     */
+    public interface OnHoverListener {
+        /**
+         * Called when a hover event is dispatched to a view. This allows listeners to
+         * get a chance to respond before the target view.
+         *
+         * @param v The view the hover event has been dispatched to.
+         * @param event The MotionEvent object containing full information about
+         *        the event.
+         * @return True if the listener has consumed the event, false otherwise.
+         */
+        boolean onHover(View v, MotionEvent event);
+    }
+
+    /**
      * Interface definition for a callback to be invoked when a generic motion event is
      * dispatched to this view. The callback will be invoked before the generic motion
      * event is given to the view.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a6bce75..e928f80 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -143,8 +143,16 @@
     @ViewDebug.ExportedProperty(category = "events")
     private float mLastTouchDownY;
 
-    // Child which last received ACTION_HOVER_ENTER and ACTION_HOVER_MOVE.
-    private View mHoveredChild;
+    // First hover target in the linked list of hover targets.
+    // The hover targets are children which have received ACTION_HOVER_ENTER.
+    // They might not have actually handled the hover event, but we will
+    // continue sending hover events to them as long as the pointer remains over
+    // their bounds and the view group does not intercept hover.
+    private HoverTarget mFirstHoverTarget;
+
+    // True if the view group itself received a hover event.
+    // It might not have actually handled the hover event.
+    private boolean mHoveredSelf;
 
     /**
      * Internal flags.
@@ -1222,56 +1230,31 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     protected boolean dispatchHoverEvent(MotionEvent event) {
-        // Send the hover enter or hover move event to the view group first.
-        // If it handles the event then a hovered child should receive hover exit.
-        boolean handled = false;
-        final boolean interceptHover;
         final int action = event.getAction();
-        if (action == MotionEvent.ACTION_HOVER_EXIT) {
-            interceptHover = true;
-        } else {
-            handled = super.dispatchHoverEvent(event);
-            interceptHover = handled;
-        }
 
-        // Send successive hover events to the hovered child as long as the pointer
-        // remains within the child's bounds.
+        // First check whether the view group wants to intercept the hover event.
+        final boolean interceptHover = onInterceptHoverEvent(event);
+        event.setAction(action); // restore action in case it was changed
+
         MotionEvent eventNoHistory = event;
-        if (mHoveredChild != null) {
+        boolean handled = false;
+
+        // Send events to the hovered children and build a new list of hover targets until
+        // one is found that handles the event.
+        HoverTarget firstOldHoverTarget = mFirstHoverTarget;
+        mFirstHoverTarget = null;
+        if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
             final float x = event.getX();
             final float y = event.getY();
-
-            if (interceptHover
-                    || !isTransformedTouchPointInView(x, y, mHoveredChild, null)) {
-                // Pointer exited the child.
-                // Send it a hover exit with only the most recent coordinates.  We could
-                // try to find the exact point in history when the pointer left the view
-                // but it is not worth the effort.
-                eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
-                eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
-                handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, mHoveredChild);
-                eventNoHistory.setAction(action);
-                mHoveredChild = null;
-            } else {
-                // Pointer is still within the child.
-                //noinspection ConstantConditions
-                handled |= dispatchTransformedGenericPointerEvent(event, mHoveredChild);
-            }
-        }
-
-        // Find a new hovered child if needed.
-        if (!interceptHover && mHoveredChild == null
-                && (action == MotionEvent.ACTION_HOVER_ENTER
-                        || action == MotionEvent.ACTION_HOVER_MOVE)) {
             final int childrenCount = mChildrenCount;
             if (childrenCount != 0) {
                 final View[] children = mChildren;
-                final float x = event.getX();
-                final float y = event.getY();
-
+                HoverTarget lastHoverTarget = null;
                 for (int i = childrenCount - 1; i >= 0; i--) {
                     final View child = children[i];
                     if (!canViewReceivePointerEvents(child)
@@ -1279,24 +1262,140 @@
                         continue;
                     }
 
-                    // Found the hovered child.
-                    mHoveredChild = child;
-                    if (action == MotionEvent.ACTION_HOVER_MOVE) {
-                        // Pointer was moving within the view group and entered the child.
-                        // Send it a hover enter and hover move with only the most recent
-                        // coordinates.  We could try to find the exact point in history when
-                        // the pointer entered the view but it is not worth the effort.
-                        eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
-                        eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
-                        handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
-                        eventNoHistory.setAction(action);
+                    // Obtain a hover target for this child.  Dequeue it from the
+                    // old hover target list if the child was previously hovered.
+                    HoverTarget hoverTarget = firstOldHoverTarget;
+                    final boolean wasHovered;
+                    for (HoverTarget predecessor = null; ;) {
+                        if (hoverTarget == null) {
+                            hoverTarget = HoverTarget.obtain(child);
+                            wasHovered = false;
+                            break;
+                        }
 
-                        handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
-                    } else { /* must be ACTION_HOVER_ENTER */
-                        // Pointer entered the child.
-                        handled |= dispatchTransformedGenericPointerEvent(event, child);
+                        if (hoverTarget.child == child) {
+                            if (predecessor != null) {
+                                predecessor.next = hoverTarget.next;
+                            } else {
+                                firstOldHoverTarget = hoverTarget.next;
+                            }
+                            hoverTarget.next = null;
+                            wasHovered = true;
+                            break;
+                        }
+
+                        predecessor = hoverTarget;
+                        hoverTarget = hoverTarget.next;
                     }
-                    break;
+
+                    // Enqueue the hover target onto the new hover target list.
+                    if (lastHoverTarget != null) {
+                        lastHoverTarget.next = hoverTarget;
+                    } else {
+                        lastHoverTarget = hoverTarget;
+                        mFirstHoverTarget = hoverTarget;
+                    }
+
+                    // Dispatch the event to the child.
+                    if (action == MotionEvent.ACTION_HOVER_ENTER) {
+                        if (!wasHovered) {
+                            // Send the enter as is.
+                            handled |= dispatchTransformedGenericPointerEvent(
+                                    event, child); // enter
+                        }
+                    } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
+                        if (!wasHovered) {
+                            // Synthesize an enter from a move.
+                            eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+                            eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+                            handled |= dispatchTransformedGenericPointerEvent(
+                                    eventNoHistory, child); // enter
+                            eventNoHistory.setAction(action);
+
+                            handled |= dispatchTransformedGenericPointerEvent(
+                                    eventNoHistory, child); // move
+                        } else {
+                            // Send the move as is.
+                            handled |= dispatchTransformedGenericPointerEvent(event, child);
+                        }
+                    }
+                    if (handled) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Send exit events to all previously hovered children that are no longer hovered.
+        while (firstOldHoverTarget != null) {
+            final View child = firstOldHoverTarget.child;
+
+            // Exit the old hovered child.
+            if (action == MotionEvent.ACTION_HOVER_EXIT) {
+                // Send the exit as is.
+                handled |= dispatchTransformedGenericPointerEvent(
+                        event, child); // exit
+            } else {
+                // Synthesize an exit from a move or enter.
+                // Ignore the result because hover focus has moved to a different view.
+                if (action == MotionEvent.ACTION_HOVER_MOVE) {
+                    dispatchTransformedGenericPointerEvent(
+                            event, child); // move
+                }
+                eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+                eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+                dispatchTransformedGenericPointerEvent(
+                        eventNoHistory, child); // exit
+                eventNoHistory.setAction(action);
+            }
+
+            final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
+            firstOldHoverTarget.recycle();
+            firstOldHoverTarget = nextOldHoverTarget;
+        }
+
+        // Send events to the view group itself if no children have handled it.
+        boolean newHoveredSelf = !handled;
+        if (newHoveredSelf == mHoveredSelf) {
+            if (newHoveredSelf) {
+                // Send event to the view group as before.
+                handled |= super.dispatchHoverEvent(event);
+            }
+        } else {
+            if (mHoveredSelf) {
+                // Exit the view group.
+                if (action == MotionEvent.ACTION_HOVER_EXIT) {
+                    // Send the exit as is.
+                    handled |= super.dispatchHoverEvent(event); // exit
+                } else {
+                    // Synthesize an exit from a move or enter.
+                    // Ignore the result because hover focus is moving to a different view.
+                    if (action == MotionEvent.ACTION_HOVER_MOVE) {
+                        super.dispatchHoverEvent(event); // move
+                    }
+                    eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+                    eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+                    super.dispatchHoverEvent(eventNoHistory); // exit
+                    eventNoHistory.setAction(action);
+                }
+                mHoveredSelf = false;
+            }
+
+            if (newHoveredSelf) {
+                // Enter the view group.
+                if (action == MotionEvent.ACTION_HOVER_ENTER) {
+                    // Send the enter as is.
+                    handled |= super.dispatchHoverEvent(event); // enter
+                    mHoveredSelf = true;
+                } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
+                    // Synthesize an enter from a move.
+                    eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+                    eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+                    handled |= super.dispatchHoverEvent(eventNoHistory); // enter
+                    eventNoHistory.setAction(action);
+
+                    handled |= super.dispatchHoverEvent(eventNoHistory); // move
+                    mHoveredSelf = true;
                 }
             }
         }
@@ -1306,25 +1405,55 @@
             eventNoHistory.recycle();
         }
 
-        // Send hover exit to the view group.  If there was a child, we will already have
-        // sent the hover exit to it.
-        if (action == MotionEvent.ACTION_HOVER_EXIT) {
-            handled |= super.dispatchHoverEvent(event);
-        }
-
         // Done.
         return handled;
     }
 
+    /** @hide */
     @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        // Handle the event only if leaf. This guarantees that
-        // the leafs (or any custom class that returns true from
-        // this method) will get a change to process the hover.
-        //noinspection SimplifiableIfStatement
-        if (getChildCount() == 0) {
-            return super.onHoverEvent(event);
-        }
+    protected boolean hasHoveredChild() {
+        return mFirstHoverTarget != null;
+    }
+
+    /**
+     * Implement this method to intercept hover events before they are handled
+     * by child views.
+     * <p>
+     * This method is called before dispatching a hover event to a child of
+     * the view group or to the view group's own {@link #onHoverEvent} to allow
+     * the view group a chance to intercept the hover event.
+     * This method can also be used to watch all pointer motions that occur within
+     * the bounds of the view group even when the pointer is hovering over
+     * a child of the view group rather than over the view group itself.
+     * </p><p>
+     * The view group can prevent its children from receiving hover events by
+     * implementing this method and returning <code>true</code> to indicate
+     * that it would like to intercept hover events.  The view group must
+     * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
+     * for as long as it wishes to continue intercepting hover events from
+     * its children.
+     * </p><p>
+     * Interception preserves the invariant that at most one view can be
+     * hovered at a time by transferring hover focus from the currently hovered
+     * child to the view group or vice-versa as needed.
+     * </p><p>
+     * If this method returns <code>true</code> and a child is already hovered, then the
+     * child view will first receive a hover exit event and then the view group
+     * itself will receive a hover enter event in {@link #onHoverEvent}.
+     * Likewise, if this method had previously returned <code>true</code> to intercept hover
+     * events and instead returns <code>false</code> while the pointer is hovering
+     * within the bounds of one of a child, then the view group will first receive a
+     * hover exit event in {@link #onHoverEvent} and then the hovered child will
+     * receive a hover enter event.
+     * </p><p>
+     * The default implementation always returns false.
+     * </p>
+     *
+     * @param event The motion event that describes the hover.
+     * @return True if the view group would like to intercept the hover event
+     * and prevent its children from receiving it.
+     */
+    public boolean onInterceptHoverEvent(MotionEvent event) {
         return false;
     }
 
@@ -1335,7 +1464,9 @@
         return MotionEvent.obtainNoHistory(event);
     }
 
-    /** @hide */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     protected boolean dispatchGenericPointerEvent(MotionEvent event) {
         // Send the event to the child under the pointer.
@@ -1362,7 +1493,9 @@
         return super.dispatchGenericPointerEvent(event);
     }
 
-    /** @hide */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
         // Send the event to the focused child or to this view group if it has focus.
@@ -3337,10 +3470,6 @@
             mTransition.removeChild(this, view);
         }
 
-        if (view == mHoveredChild) {
-            mHoveredChild = null;
-        }
-
         boolean clearChildFocus = false;
         if (view == mFocused) {
             view.clearFocusForRemoval();
@@ -3404,7 +3533,6 @@
         final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
         final boolean notifyListener = onHierarchyChangeListener != null;
         final View focused = mFocused;
-        final View hoveredChild = mHoveredChild;
         final boolean detach = mAttachInfo != null;
         View clearChildFocus = null;
 
@@ -3418,10 +3546,6 @@
                 mTransition.removeChild(this, view);
             }
 
-            if (view == hoveredChild) {
-                mHoveredChild = null;
-            }
-
             if (view == focused) {
                 view.clearFocusForRemoval();
                 clearChildFocus = view;
@@ -3479,7 +3603,6 @@
         final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
         final boolean notify = listener != null;
         final View focused = mFocused;
-        final View hoveredChild = mHoveredChild;
         final boolean detach = mAttachInfo != null;
         View clearChildFocus = null;
 
@@ -3492,10 +3615,6 @@
                 mTransition.removeChild(this, view);
             }
 
-            if (view == hoveredChild) {
-                mHoveredChild = null;
-            }
-
             if (view == focused) {
                 view.clearFocusForRemoval();
                 clearChildFocus = view;
@@ -5242,4 +5361,50 @@
             }
         }
     }
+
+    /* Describes a hovered view. */
+    private static final class HoverTarget {
+        private static final int MAX_RECYCLED = 32;
+        private static final Object sRecycleLock = new Object();
+        private static HoverTarget sRecycleBin;
+        private static int sRecycledCount;
+
+        // The hovered child view.
+        public View child;
+
+        // The next target in the target list.
+        public HoverTarget next;
+
+        private HoverTarget() {
+        }
+
+        public static HoverTarget obtain(View child) {
+            final HoverTarget target;
+            synchronized (sRecycleLock) {
+                if (sRecycleBin == null) {
+                    target = new HoverTarget();
+                } else {
+                    target = sRecycleBin;
+                    sRecycleBin = target.next;
+                     sRecycledCount--;
+                    target.next = null;
+                }
+            }
+            target.child = child;
+            return target;
+        }
+
+        public void recycle() {
+            synchronized (sRecycleLock) {
+                if (sRecycledCount < MAX_RECYCLED) {
+                    next = sRecycleBin;
+                    sRecycleBin = this;
+                    sRecycledCount += 1;
+                } else {
+                    next = null;
+                }
+                child = null;
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 18ef38a..555667b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -252,7 +252,8 @@
 
     /**
      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
-     * insensitive containment.
+     * insensitive containment. The search is relative to this info i.e.
+     * this info is the root of the traversed tree.
      *
      * @param text The searched text.
      * @return A list of node info.
@@ -260,7 +261,7 @@
     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
         enforceSealed();
         if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
-            return null;
+            return Collections.emptyList();
         }
         try {
             return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId,
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c56e6db..9f632d1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5707,8 +5707,8 @@
             return false;
         }
         WebViewCore.CursorData data = cursorDataNoPosition();
-        data.mX = viewToContentX((int) event.getX());
-        data.mY = viewToContentY((int) event.getY());
+        data.mX = viewToContentX((int) event.getX() + mScrollX);
+        data.mY = viewToContentY((int) event.getY() + mScrollY);
         mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
         return true;
     }
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index 82770ad..17512d8 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.SubMenuBuilder;
 
 import android.content.Context;
@@ -32,12 +33,25 @@
  * If the IME is visible the popup will not overlap it until it is touched. Touching outside
  * of the popup will dismiss it.
  */
-public class PopupMenu implements MenuBuilder.Callback {
+public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
     private Context mContext;
     private MenuBuilder mMenu;
     private View mAnchor;
     private MenuPopupHelper mPopup;
     private OnMenuItemClickListener mMenuItemClickListener;
+    private OnDismissListener mDismissListener;
+
+    /**
+     * Callback interface used to notify the application that the menu has closed.
+     */
+    public interface OnDismissListener {
+        /**
+         * Called when the associated menu has been dismissed.
+         *
+         * @param menu The PopupMenu that was dismissed.
+         */
+        public void onDismiss(PopupMenu menu);
+    }
 
     /**
      * Construct a new PopupMenu.
@@ -53,6 +67,7 @@
         mMenu.setCallback(this);
         mAnchor = anchor;
         mPopup = new MenuPopupHelper(context, mMenu, anchor);
+        mPopup.setCallback(this);
     }
 
     /**
@@ -77,6 +92,15 @@
     }
 
     /**
+     * Inflate a menu resource into this PopupMenu. This is equivalent to calling
+     * popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
+     * @param menuRes Menu resource to inflate
+     */
+    public void inflate(int menuRes) {
+        getMenuInflater().inflate(menuRes, mMenu);
+    }
+
+    /**
      * Show the menu popup anchored to the view specified during construction.
      * @see #dismiss()
      */
@@ -92,11 +116,25 @@
         mPopup.dismiss();
     }
 
+    /**
+     * Set a listener that will be notified when the user selects an item from the menu.
+     *
+     * @param listener Listener to notify
+     */
     public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
         mMenuItemClickListener = listener;
     }
 
     /**
+     * Set a listener that will be notified when this menu is dismissed.
+     *
+     * @param listener Listener to notify
+     */
+    public void setOnDismissListener(OnDismissListener listener) {
+        mDismissListener = listener;
+    }
+
+    /**
      * @hide
      */
     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
@@ -110,12 +148,15 @@
      * @hide
      */
     public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+        if (mDismissListener != null) {
+            mDismissListener.onDismiss(this);
+        }
     }
 
     /**
      * @hide
      */
-    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+    public boolean onOpenSubMenu(MenuBuilder subMenu) {
         if (!subMenu.hasVisibleItems()) {
             return true;
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 02c2b8f..77df7c8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7658,12 +7658,17 @@
     }
 
     @Override
-    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence searched) {
+        if (TextUtils.isEmpty(searched)) {
+            return;
+        }
         CharSequence thisText = getText();
         if (TextUtils.isEmpty(thisText)) {
             return;
         }
-        if (thisText.toString().toLowerCase().contains(text)) {
+        String searchedLowerCase = searched.toString().toLowerCase();
+        String thisTextLowerCase = thisText.toString().toLowerCase();
+        if (thisTextLowerCase.contains(searched)) {
             outViews.add(this);
         }
     }
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 98c2747..322a854 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -336,6 +336,7 @@
                 if (groupId != 0) {
                     seenGroups.put(groupId, true);
                 }
+                item.setIsActionButton(true);
             } else if (item.requestsActionButton()) {
                 // Items in a group with other items that already have an action slot
                 // can break the max actions rule, but not the width limit.
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index cc09927..27e4191 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -184,12 +184,12 @@
 
     private class MenuAdapter extends BaseAdapter {
         public int getCount() {
-            ArrayList<MenuItemImpl> items = mMenu.getVisibleItems();
+            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
             return items.size() - mItemIndexOffset;
         }
 
         public MenuItemImpl getItem(int position) {
-            ArrayList<MenuItemImpl> items = mMenu.getVisibleItems();
+            ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
             return items.get(position + mItemIndexOffset);
         }
 
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 1a6cc54..253511c 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -507,7 +507,7 @@
     }
     
     public boolean isActionButton() {
-        return (mFlags & IS_ACTION) == IS_ACTION || requiresActionButton();
+        return (mFlags & IS_ACTION) == IS_ACTION;
     }
     
     public boolean requestsActionButton() {
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 8db7e3c..cffbb4e 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -126,6 +126,7 @@
 
     public void onDismiss() {
         mPopup = null;
+        mMenu.close();
         if (mTreeObserver != null) {
             if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
             mTreeObserver.removeGlobalOnLayoutListener(this);
diff --git a/core/jni/android_pim_EventRecurrence.cpp b/core/jni/android_pim_EventRecurrence.cpp
index 44e898d..3e11569 100644
--- a/core/jni/android_pim_EventRecurrence.cpp
+++ b/core/jni/android_pim_EventRecurrence.cpp
@@ -147,7 +147,7 @@
  */

 static JNINativeMethod METHODS[] = {

     /* name, signature, funcPtr */

-    { "parse", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }

+    { "parseNative", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }

 };

 

 static const char*const CLASS_NAME = "android/pim/EventRecurrence";

diff --git a/core/res/res/drawable-hdpi/ic_notification_ime_default.png b/core/res/res/drawable-hdpi/ic_notification_ime_default.png
new file mode 100644
index 0000000..1a9d88c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_notification_ime_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notification_ime_default.png b/core/res/res/drawable-mdpi/ic_notification_ime_default.png
new file mode 100644
index 0000000..1a9d88c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_notification_ime_default.png
Binary files differ
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 734031f..d73ff99 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -16,4 +16,5 @@
 
 <resources>
     <bool name="preferences_prefer_dual_pane">true</bool>
+    <bool name="show_ongoing_ime_switcher">false</bool>
 </resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 9d6309d..9647bb7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -19,4 +19,5 @@
     <bool name="action_bar_embed_tabs">false</bool>
     <bool name="split_action_bar_is_narrow">true</bool>
     <bool name="preferences_prefer_dual_pane">false</bool>
+    <bool name="show_ongoing_ime_switcher">true</bool>
 </resources>
diff --git a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
new file mode 100644
index 0000000..05000f1
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.pim;
+
+import android.pim.EventRecurrence.InvalidFormatException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+/**
+ * Test android.pim.EventRecurrence.
+ *
+ * adb shell am instrument -w -e class android.pim.EventRecurrenceTest \
+ *   com.android.frameworks.coretests/android.test.InstrumentationTestRunner
+ */
+public class EventRecurrenceTest extends TestCase {
+
+    @SmallTest
+    public void test0() throws Exception {
+        verifyRecurType("FREQ=SECONDLY",
+                /* int freq */         EventRecurrence.SECONDLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test1() throws Exception {
+        verifyRecurType("FREQ=MINUTELY",
+                /* int freq */         EventRecurrence.MINUTELY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test2() throws Exception {
+        verifyRecurType("FREQ=HOURLY",
+                /* int freq */         EventRecurrence.HOURLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test3() throws Exception {
+        verifyRecurType("FREQ=DAILY",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test4() throws Exception {
+        verifyRecurType("FREQ=WEEKLY",
+                /* int freq */         EventRecurrence.WEEKLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test5() throws Exception {
+        verifyRecurType("FREQ=MONTHLY",
+                /* int freq */         EventRecurrence.MONTHLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test6() throws Exception {
+        verifyRecurType("FREQ=YEARLY",
+                /* int freq */         EventRecurrence.YEARLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test7() throws Exception {
+        // with an until
+        verifyRecurType("FREQ=DAILY;UNTIL=112233T223344Z",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     "112233T223344Z",
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test8() throws Exception {
+        // with a count
+        verifyRecurType("FREQ=DAILY;COUNT=334",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        334,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test9() throws Exception {
+        // with a count
+        verifyRecurType("FREQ=DAILY;INTERVAL=5000",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     5000,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @SmallTest
+    public void test10() throws Exception {
+        // verifyRecurType all of the BY* ones with one element
+        verifyRecurType("FREQ=DAILY"
+                + ";BYSECOND=0"
+                + ";BYMINUTE=1"
+                + ";BYHOUR=2"
+                + ";BYMONTHDAY=30"
+                + ";BYYEARDAY=300"
+                + ";BYWEEKNO=53"
+                + ";BYMONTH=12"
+                + ";BYSETPOS=-15"
+                + ";WKST=SU",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   new int[]{0},
+                /* int[] byminute */   new int[]{1},
+                /* int[] byhour */     new int[]{2},
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ new int[]{30},
+                /* int[] byyearday */  new int[]{300},
+                /* int[] byweekno */   new int[]{53},
+                /* int[] bymonth */    new int[]{12},
+                /* int[] bysetpos */   new int[]{-15},
+                /* int wkst */         EventRecurrence.SU
+        );
+    }
+
+    @SmallTest
+    public void test11() throws Exception {
+        // verifyRecurType all of the BY* ones with one element
+        verifyRecurType("FREQ=DAILY"
+                + ";BYSECOND=0,30,59"
+                + ";BYMINUTE=0,41,59"
+                + ";BYHOUR=0,4,23"
+                + ";BYMONTHDAY=-31,-1,1,31"
+                + ";BYYEARDAY=-366,-1,1,366"
+                + ";BYWEEKNO=-53,-1,1,53"
+                + ";BYMONTH=1,12"
+                + ";BYSETPOS=1,2,3,4,500,10000"
+                + ";WKST=SU",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   new int[]{0, 30, 59},
+                /* int[] byminute */   new int[]{0, 41, 59},
+                /* int[] byhour */     new int[]{0, 4, 23},
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ new int[]{-31, -1, 1, 31},
+                /* int[] byyearday */  new int[]{-366, -1, 1, 366},
+                /* int[] byweekno */   new int[]{-53, -1, 1, 53},
+                /* int[] bymonth */    new int[]{1, 12},
+                /* int[] bysetpos */   new int[]{1, 2, 3, 4, 500, 10000},
+                /* int wkst */         EventRecurrence.SU
+        );
+    }
+
+    private static class Check {
+        Check(String k, int... v) {
+            key = k;
+            values = v;
+        }
+
+        String key;
+        int[] values;
+    }
+
+    // this is a negative verifyRecurType case to verifyRecurType the range of the numbers accepted
+    @SmallTest
+    public void test12() throws Exception {
+        Check[] checks = new Check[]{
+                new Check("BYSECOND", -100, -1, 60, 100),
+                new Check("BYMINUTE", -100, -1, 60, 100),
+                new Check("BYHOUR", -100, -1, 24, 100),
+                new Check("BYMONTHDAY", -100, -32, 0, 32, 100),
+                new Check("BYYEARDAY", -400, -367, 0, 367, 400),
+                new Check("BYWEEKNO", -100, -54, 0, 54, 100),
+                new Check("BYMONTH", -100, -5, 0, 13, 100)
+        };
+
+        for (Check ck : checks) {
+            for (int n : ck.values) {
+                String recur = "FREQ=DAILY;" + ck.key + "=" + n;
+                try {
+                    EventRecurrence er = new EventRecurrence();
+                    er.parse(recur);
+                    fail("Negative verifyRecurType failed. "
+                            + " parse failed to throw an exception for '"
+                            + recur + "'");
+                } catch (EventRecurrence.InvalidFormatException e) {
+                    // expected
+                }
+            }
+        }
+    }
+
+    // verifyRecurType BYDAY
+    @SmallTest
+    public void test13() throws Exception {
+        verifyRecurType("FREQ=DAILY;BYDAY=1SU,-2MO,+33TU,WE,TH,FR,SA",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      new int[] {
+                        EventRecurrence.SU,
+                        EventRecurrence.MO,
+                        EventRecurrence.TU,
+                        EventRecurrence.WE,
+                        EventRecurrence.TH,
+                        EventRecurrence.FR,
+                        EventRecurrence.SA
+                },
+                /* int[] bydayNum */   new int[]{1, -2, 33, 0, 0, 0, 0},
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    @Suppress
+    // Repro bug #2331761 - this should fail because of the last comma into BYDAY
+    public void test14() throws Exception {
+        verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;BYDAY=MO,TU,WE,",
+                /* int freq */         EventRecurrence.WEEKLY,
+                /* String until */     "20100129T130000Z",
+                /* int count */        0,
+                /* int interval */     1,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      new int[] {
+                        EventRecurrence.MO,
+                        EventRecurrence.TU,
+                        EventRecurrence.WE,
+                },
+                /* int[] bydayNum */   new int[]{0, 0, 0},
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    // This test should pass
+    public void test15() throws Exception {
+        verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;"
+                + "BYDAY=MO,TU,WE,TH,FR,SA,SU",
+                /* int freq */         EventRecurrence.WEEKLY,
+                /* String until */     "20100129T130000Z",
+                /* int count */        0,
+                /* int interval */     1,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      new int[] {
+                        EventRecurrence.MO,
+                        EventRecurrence.TU,
+                        EventRecurrence.WE,
+                        EventRecurrence.TH,
+                        EventRecurrence.FR,
+                        EventRecurrence.SA,
+                        EventRecurrence.SU
+                },
+                /* int[] bydayNum */   new int[]{0, 0, 0, 0, 0, 0, 0},
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    // Sample coming from RFC2445
+    public void test16() throws Exception {
+        verifyRecurType("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1",
+                /* int freq */         EventRecurrence.MONTHLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      new int[] {
+                        EventRecurrence.MO,
+                        EventRecurrence.TU,
+                        EventRecurrence.WE,
+                        EventRecurrence.TH,
+                        EventRecurrence.FR
+                },
+                /* int[] bydayNum */   new int[] {0, 0, 0, 0, 0},
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   new int[] { -1 },
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    // Sample coming from RFC2445
+    public void test17() throws Exception {
+        verifyRecurType("FREQ=DAILY;COUNT=10;INTERVAL=2",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        10,
+                /* int interval */     2,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    // Sample coming from RFC2445
+    public void test18() throws Exception {
+        verifyRecurType("FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
+                /* int freq */         EventRecurrence.YEARLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      new int[] {
+                        EventRecurrence.SU
+                },
+                /* int[] bydayNum */   new int[] { -1 },
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    new int[] { 10 },
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    // Sample coming from bug #1640517
+    public void test19() throws Exception {
+        verifyRecurType("FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
+                /* int freq */         EventRecurrence.YEARLY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      new int[] {
+                        EventRecurrence.TH
+                },
+                /* int[] bydayNum */   new int[] { 0 },
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    new int[] { 3 },
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    // for your copying pleasure
+    public void fakeTestXX() throws Exception {
+        verifyRecurType("FREQ=DAILY;",
+                /* int freq */         EventRecurrence.DAILY,
+                /* String until */     null,
+                /* int count */        0,
+                /* int interval */     0,
+                /* int[] bysecond */   null,
+                /* int[] byminute */   null,
+                /* int[] byhour */     null,
+                /* int[] byday */      null,
+                /* int[] bydayNum */   null,
+                /* int[] bymonthday */ null,
+                /* int[] byyearday */  null,
+                /* int[] byweekno */   null,
+                /* int[] bymonth */    null,
+                /* int[] bysetpos */   null,
+                /* int wkst */         EventRecurrence.MO
+        );
+    }
+
+    private static void cmp(int vlen, int[] v, int[] correct, String name) {
+        if ((correct == null && v != null)
+                || (correct != null && v == null)) {
+            throw new RuntimeException("One is null, one isn't for " + name
+                    + ": correct=" + Arrays.toString(correct)
+                    + " actual=" + Arrays.toString(v));
+        }
+        if ((correct == null && vlen != 0)
+                || (vlen != (correct == null ? 0 : correct.length))) {
+            throw new RuntimeException("Reported length mismatch for " + name
+                    + ": correct=" + ((correct == null) ? "null" : correct.length)
+                    + " actual=" + vlen);
+        }
+        if (correct == null) {
+            return;
+        }
+        if (v.length < correct.length) {
+            throw new RuntimeException("Array length mismatch for " + name
+                    + ": correct=" + Arrays.toString(correct)
+                    + " actual=" + Arrays.toString(v));
+        }
+        for (int i = 0; i < correct.length; i++) {
+            if (v[i] != correct[i]) {
+                throw new RuntimeException("Array value mismatch for " + name
+                        + ": correct=" + Arrays.toString(correct)
+                        + " actual=" + Arrays.toString(v));
+            }
+        }
+    }
+
+    private static boolean eq(String a, String b) {
+        if ((a == null && b != null) || (a != null && b == null)) {
+            return false;
+        } else {
+            return a == b || a.equals(b);
+        }
+    }
+
+    private static void verifyRecurType(String recur,
+            int freq, String until, int count, int interval,
+            int[] bysecond, int[] byminute, int[] byhour,
+            int[] byday, int[] bydayNum, int[] bymonthday,
+            int[] byyearday, int[] byweekno, int[] bymonth,
+            int[] bysetpos, int wkst) {
+        EventRecurrence eventRecurrence = new EventRecurrence();
+        eventRecurrence.parse(recur);
+        if (eventRecurrence.freq != freq
+                || !eq(eventRecurrence.until, until)
+                || eventRecurrence.count != count
+                || eventRecurrence.interval != interval
+                || eventRecurrence.wkst != wkst) {
+            System.out.println("Error... got:");
+            print(eventRecurrence);
+            System.out.println("expected:");
+            System.out.println("{");
+            System.out.println("    freq=" + freq);
+            System.out.println("    until=" + until);
+            System.out.println("    count=" + count);
+            System.out.println("    interval=" + interval);
+            System.out.println("    wkst=" + wkst);
+            System.out.println("    bysecond=" + Arrays.toString(bysecond));
+            System.out.println("    byminute=" + Arrays.toString(byminute));
+            System.out.println("    byhour=" + Arrays.toString(byhour));
+            System.out.println("    byday=" + Arrays.toString(byday));
+            System.out.println("    bydayNum=" + Arrays.toString(bydayNum));
+            System.out.println("    bymonthday=" + Arrays.toString(bymonthday));
+            System.out.println("    byyearday=" + Arrays.toString(byyearday));
+            System.out.println("    byweekno=" + Arrays.toString(byweekno));
+            System.out.println("    bymonth=" + Arrays.toString(bymonth));
+            System.out.println("    bysetpos=" + Arrays.toString(bysetpos));
+            System.out.println("}");
+            throw new RuntimeException("Mismatch in fields");
+        }
+        cmp(eventRecurrence.bysecondCount, eventRecurrence.bysecond, bysecond, "bysecond");
+        cmp(eventRecurrence.byminuteCount, eventRecurrence.byminute, byminute, "byminute");
+        cmp(eventRecurrence.byhourCount, eventRecurrence.byhour, byhour, "byhour");
+        cmp(eventRecurrence.bydayCount, eventRecurrence.byday, byday, "byday");
+        cmp(eventRecurrence.bydayCount, eventRecurrence.bydayNum, bydayNum, "bydayNum");
+        cmp(eventRecurrence.bymonthdayCount, eventRecurrence.bymonthday, bymonthday, "bymonthday");
+        cmp(eventRecurrence.byyeardayCount, eventRecurrence.byyearday, byyearday, "byyearday");
+        cmp(eventRecurrence.byweeknoCount, eventRecurrence.byweekno, byweekno, "byweekno");
+        cmp(eventRecurrence.bymonthCount, eventRecurrence.bymonth, bymonth, "bymonth");
+        cmp(eventRecurrence.bysetposCount, eventRecurrence.bysetpos, bysetpos, "bysetpos");
+    }
+
+    private static void print(EventRecurrence er) {
+        System.out.println("{");
+        System.out.println("    freq=" + er.freq);
+        System.out.println("    until=" + er.until);
+        System.out.println("    count=" + er.count);
+        System.out.println("    interval=" + er.interval);
+        System.out.println("    wkst=" + er.wkst);
+        System.out.println("    bysecond=" + Arrays.toString(er.bysecond));
+        System.out.println("    bysecondCount=" + er.bysecondCount);
+        System.out.println("    byminute=" + Arrays.toString(er.byminute));
+        System.out.println("    byminuteCount=" + er.byminuteCount);
+        System.out.println("    byhour=" + Arrays.toString(er.byhour));
+        System.out.println("    byhourCount=" + er.byhourCount);
+        System.out.println("    byday=" + Arrays.toString(er.byday));
+        System.out.println("    bydayNum=" + Arrays.toString(er.bydayNum));
+        System.out.println("    bydayCount=" + er.bydayCount);
+        System.out.println("    bymonthday=" + Arrays.toString(er.bymonthday));
+        System.out.println("    bymonthdayCount=" + er.bymonthdayCount);
+        System.out.println("    byyearday=" + Arrays.toString(er.byyearday));
+        System.out.println("    byyeardayCount=" + er.byyeardayCount);
+        System.out.println("    byweekno=" + Arrays.toString(er.byweekno));
+        System.out.println("    byweeknoCount=" + er.byweeknoCount);
+        System.out.println("    bymonth=" + Arrays.toString(er.bymonth));
+        System.out.println("    bymonthCount=" + er.bymonthCount);
+        System.out.println("    bysetpos=" + Arrays.toString(er.bysetpos));
+        System.out.println("    bysetposCount=" + er.bysetposCount);
+        System.out.println("}");
+    }
+
+
+    /** A list of valid rules.  The parser must accept these. */
+    private static final String[] GOOD_RRULES = {
+        /* extracted wholesale from from RFC 2445 section 4.8.5.4 */
+        "FREQ=DAILY;COUNT=10",
+        "FREQ=DAILY;UNTIL=19971224T000000Z",
+        "FREQ=DAILY;INTERVAL=2",
+        "FREQ=DAILY;INTERVAL=10;COUNT=5",
+        "FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA",
+        "FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1",
+        "FREQ=WEEKLY;COUNT=10",
+        "FREQ=WEEKLY;UNTIL=19971224T000000Z",
+        "FREQ=WEEKLY;INTERVAL=2;WKST=SU",
+        "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH",
+        "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH",
+        "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR",
+        "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH",
+        "FREQ=MONTHLY;COUNT=10;BYDAY=1FR",
+        "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR",
+        "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU",
+        "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO",
+        "FREQ=MONTHLY;BYMONTHDAY=-3",
+        "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15",
+        "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
+        "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15",
+        "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU",
+        "FREQ=YEARLY;COUNT=10;BYMONTH=6,7",
+        "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3",
+        "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200",
+        "FREQ=YEARLY;BYDAY=20MO",
+        "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO",
+        "FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
+        "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8",
+        "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13",
+        "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13",
+        "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8",
+        "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
+        "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2",
+        "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z",
+        "FREQ=MINUTELY;INTERVAL=15;COUNT=6",
+        "FREQ=MINUTELY;INTERVAL=90;COUNT=4",
+        "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40",
+        "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16",
+        "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO",
+        "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU",
+        /* a few more */
+        "FREQ=SECONDLY;BYSECOND=0,15,59",
+        "FREQ=MINUTELY;BYMINUTE=0,15,59",
+        "FREQ=HOURLY;BYHOUR=+0,+15,+23",
+        "FREQ=DAILY;X-WHATEVER=blah",                       // fails on old parser
+        //"freq=daily;wkst=su",                               // fails on old parser
+    };
+
+    /** The parser must reject these. */
+    private static final String[] BAD_RRULES = {
+        "INTERVAL=4;FREQ=YEARLY",                           // FREQ must come first
+        "FREQ=MONTHLY;FREQ=MONTHLY",                        // can't specify twice
+        "FREQ=MONTHLY;COUNT=1;COUNT=1",                     // can't specify twice
+        "FREQ=SECONDLY;BYSECOND=60",                        // range
+        "FREQ=MINUTELY;BYMINUTE=-1",                        // range
+        "FREQ=HOURLY;BYHOUR=24",                            // range
+        "FREQ=YEARLY;BYMONTHDAY=0",                         // zero not valid
+        //"FREQ=YEARLY;COUNT=1;UNTIL=12345",                  // can't have both COUNT and UNTIL
+        //"FREQ=DAILY;UNTIL=19970829T021400e",                // invalid date
+    };
+
+    /**
+     * Simple test of good/bad rules.
+     */
+    @SmallTest
+    public void testBasicParse() {
+        for (String rule : GOOD_RRULES) {
+            EventRecurrence recur = new EventRecurrence();
+            recur.parse(rule);
+        }
+
+        for (String rule : BAD_RRULES) {
+            EventRecurrence recur = new EventRecurrence();
+            boolean didThrow = false;
+
+            try {
+                recur.parse(rule);
+            } catch (InvalidFormatException ife) {
+                didThrow = true;
+            }
+
+            assertTrue("Expected throw on " + rule, didThrow);
+        }
+    }
+}
diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd
index e4e1215..0e35637 100644
--- a/docs/html/guide/topics/usb/adk.jd
+++ b/docs/html/guide/topics/usb/adk.jd
@@ -55,7 +55,6 @@
       </ol>
 
 
-
       <h2>See also</h2>
 
       <ol>
@@ -66,6 +65,12 @@
       <h2>Where to buy</h2>
 
       <ol>
+      <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">
+        Modern Device</a></li>
+
+      <li><a href="http://www.seeedstudio.com/depot/seeeduino-adk-main-board-p-846.html">
+        Seeed Studio</a></li>
+
         <li><a href=
         "http://www.rt-net.jp/shop/index.php?main_page=product_info&cPath=3_4&products_id=1">
         RT Corp</a></li>
@@ -77,8 +82,6 @@
         <li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone">
         DIY Drones</a></li>
 
-    <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">
-        Modern Device</a></li>
       </ol>
     </div>
   </div>
@@ -106,15 +109,22 @@
   development boards:</p>
 
   <ul>
+    <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">Modern
+    Device</a> provides an Arduino-compatible board that supports the ADK firmware.</li>
+
+    <li><a href="http://www.seeedstudio.com/depot/seeeduino-adk-main-board-p-846.html">
+    Seeed Studio</a> provides an Arduino-compatible board that supports the ADK firmware.</li>
+
     <li><a href="http://www.rt-net.jp/shop/index.php?main_page=product_info&cPath=3_4&products_id=1">
     RT Corp</a> provides an Arduino-compatible board based on the Android ADK board design.</li>
+
     <li><a href="http://www.microchip.com/android">Microchip</a> provides a A PIC based USB
     microcontroller board.</li>
+
     <li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone">DIY
     Drones</a> provides an Arduino-compatible board geared towards RC (radio controlled) and UAV
     (unmanned aerial vehicle) enthusiasts.</li>
-    <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">Modern
-        Device</a> provides an Arduino-compatible board that supports the ADK firmware.</li>
+
   </ul>
 
   <p>We expect more hardware distributers to create a variety of kits, so please stay tuned for
@@ -125,7 +135,7 @@
   accessory that is based on the <a href="http://www.arduino.cc/">Arduino open source electronics
   prototyping platform</a>, the accessory's hardware design files, code that implements the
   accessory's firmware, and the Android application that interacts with the accessory. The hardware
-  design files and firmware code are contained in the <a href=
+  design files and firmware code are contained in the <a href=ctive 
   "https://dl-ssl.google.com/android/adk/adk_release_0512.zip">ADK package download</a>.</p>
   <p>The main hardware and software components of the ADK include:</p>
 
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 5750c81..d02c13d 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -175,7 +175,8 @@
       <span style="display:none" class="zh-TW"></span>
     </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5c</a>
+      <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5c <span
+       class="new">new!</span></a>
         </li>
       <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
     </ul>
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 07b3b47..31c3c14 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -512,6 +512,19 @@
         }
     }
 
+    if (NULL != decryptHandle) {
+        if (NULL != decryptHandle->decryptInfo) {
+            delete decryptHandle->decryptInfo;
+            decryptHandle->decryptInfo = NULL;
+        }
+
+        decryptHandle->copyControlVector.clear();
+        decryptHandle->extendedData.clear();
+
+        delete decryptHandle;
+        decryptHandle = NULL;
+    }
+
     LOGV("FwdLockEngine::onCloseDecryptSession Exit");
     return result;
 }
diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java
index 61f2e1f..77dd86a 100644
--- a/graphics/java/android/renderscript/AllocationAdapter.java
+++ b/graphics/java/android/renderscript/AllocationAdapter.java
@@ -27,8 +27,14 @@
  *
  **/
 public class AllocationAdapter extends Allocation {
+    private boolean mConstrainedLOD;
+    private boolean mConstrainedFace;
+    private boolean mConstrainedY;
+    private boolean mConstrainedZ;
+
     private int mSelectedDimX;
     private int mSelectedDimY;
+    private int mSelectedDimZ;
     private int mSelectedCount;
     private Allocation mAlloc;
 
@@ -37,13 +43,14 @@
 
     AllocationAdapter(int id, RenderScript rs, Allocation alloc) {
         super(id, rs, null, alloc.mUsage);
-        Type t = alloc.getType();
-        mSelectedDimX = t.getX();
-        mSelectedDimY = t.getY();
-        mSelectedCount = t.getCount();
+        mAlloc = alloc;
     }
 
 
+    int getID() {
+        return mAlloc.getID();
+    }
+
     public void copyFrom(BaseObj[] d) {
         mRS.validate();
         if (d.length != mSelectedCount) {
@@ -54,7 +61,7 @@
         for (int ct=0; ct < d.length; ct++) {
             i[ct] = d[ct].getID();
         }
-        subData1D(0, mType.getCount(), i);
+        subData1D(0, mAlloc.mType.getCount(), i);
     }
 
     void validateBitmap(Bitmap b) {
@@ -93,7 +100,7 @@
 
 
     public void subData(int xoff, FieldPacker fp) {
-        int eSize = mType.mElement.getSizeBytes();
+        int eSize = mAlloc.mType.mElement.getSizeBytes();
         final byte[] data = fp.getData();
 
         int count = data.length / eSize;
@@ -107,7 +114,7 @@
 
 
     public void subElementData(int xoff, int component_number, FieldPacker fp) {
-        if (component_number >= mType.mElement.mElements.length) {
+        if (component_number >= mAlloc.mType.mElement.mElements.length) {
             throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
         }
         if(xoff < 0) {
@@ -115,7 +122,7 @@
         }
 
         final byte[] data = fp.getData();
-        int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+        int eSize = mAlloc.mType.mElement.mElements[component_number].getSizeBytes();
 
         if (data.length != eSize) {
             throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
@@ -133,12 +140,13 @@
         if(count < 1) {
             throw new RSIllegalArgumentException("Count must be >= 1.");
         }
-        if((off + count) > mSelectedDimX * mSelectedDimY) {
-            throw new RSIllegalArgumentException("Overflow, Available count " + mType.getCount() +
+        if((off + count) > mSelectedCount) {
+            throw new RSIllegalArgumentException("Overflow, Available count " + mAlloc.mType.getCount() +
                                                ", got " + count + " at offset " + off + ".");
         }
         if((len) < dataSize) {
-            throw new RSIllegalArgumentException("Array too small for allocation type.");
+            throw new RSIllegalArgumentException("Array too small for allocation type.  len = " +
+                                                 len + ", dataSize = " + dataSize);
         }
     }
 
@@ -223,8 +231,51 @@
         mRS.nAllocationRead(getID(), d);
     }
 
+    private void initLOD(int lod) {
+        if (lod < 0) {
+            throw new RSIllegalArgumentException("Attempting to set negative lod (" + lod + ").");
+        }
+
+        int tx = mAlloc.mType.getX();
+        int ty = mAlloc.mType.getY();
+        int tz = mAlloc.mType.getZ();
+
+        for (int ct=0; ct < lod; ct++) {
+            if ((tx==1) && (ty == 1) && (tz == 1)) {
+                throw new RSIllegalArgumentException("Attempting to set lod (" + lod + ") out of range.");
+            }
+
+            if (tx > 1) tx >>= 1;
+            if (ty > 1) ty >>= 1;
+            if (tz > 1) tz >>= 1;
+        }
+
+        mSelectedDimX = tx;
+        mSelectedDimY = ty;
+        mSelectedCount = tx;
+        if (ty > 1) {
+            mSelectedCount *= ty;
+        }
+        if (tz > 1) {
+            mSelectedCount *= tz;
+        }
+    }
+
+    /**
+     * Set the active LOD.  The LOD must be within the range for the
+     * type being adapted.
+     *
+     * @param lod The LOD to make active.
+     */
     public void setLOD(int lod) {
-        mSelectedLOD = lod;
+        if (!mAlloc.getType().hasMipmaps()) {
+            throw new RSInvalidStateException("Cannot set LOD when the allocation type does not include mipmaps.");
+        }
+        if (!mConstrainedLOD) {
+            throw new RSInvalidStateException("Cannot set LOD when the adapter includes mipmaps.");
+        }
+
+        initLOD(lod);
     }
 
     public void setFace(Type.CubemapFace cf) {
@@ -245,6 +296,11 @@
     static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
         rs.validate();
         AllocationAdapter aa = new AllocationAdapter(0, rs, a);
+        aa.mConstrainedLOD = true;
+        aa.mConstrainedFace = true;
+        aa.mConstrainedY = false;
+        aa.mConstrainedZ = true;
+        aa.initLOD(0);
         return aa;
     }
 
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index fac7144..2739a4b8 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -165,6 +165,22 @@
         addF32(v.w);
     }
 
+    public void addF64(Double2 v) {
+        addF64(v.x);
+        addF64(v.y);
+    }
+    public void addF64(Double3 v) {
+        addF64(v.x);
+        addF64(v.y);
+        addF64(v.z);
+    }
+    public void addF64(Double4 v) {
+        addF64(v.x);
+        addF64(v.y);
+        addF64(v.z);
+        addF64(v.w);
+    }
+
     public void addI8(Byte2 v) {
         addI8(v.x);
         addI8(v.y);
@@ -261,6 +277,38 @@
         addU32(v.w);
     }
 
+    public void addI64(Long2 v) {
+        addI64(v.x);
+        addI64(v.y);
+    }
+    public void addI64(Long3 v) {
+        addI64(v.x);
+        addI64(v.y);
+        addI64(v.z);
+    }
+    public void addI64(Long4 v) {
+        addI64(v.x);
+        addI64(v.y);
+        addI64(v.z);
+        addI64(v.w);
+    }
+
+    public void addU64(Long2 v) {
+        addU64(v.x);
+        addU64(v.y);
+    }
+    public void addU64(Long3 v) {
+        addU64(v.x);
+        addU64(v.y);
+        addU64(v.z);
+    }
+    public void addU64(Long4 v) {
+        addU64(v.x);
+        addU64(v.y);
+        addU64(v.z);
+        addU64(v.w);
+    }
+
     public void addMatrix(Matrix4f v) {
         for (int i=0; i < v.mMat.length; i++) {
             addF32(v.mMat[i]);
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index f6cefa6..f219639 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -173,11 +173,11 @@
     }
 
     virtual EGLint getSurfaceWidth() {
-        return 64;
+        return 512;
     }
 
     virtual EGLint getSurfaceHeight() {
-        return 64;
+        return 512;
     }
 
     void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
@@ -526,18 +526,19 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0, 255, 127, 255, 255));
     EXPECT_TRUE(checkPixel(63,  0,   0, 133,   0, 255));
-    EXPECT_TRUE(checkPixel(63, 63,   0, 133,   0, 255));
-    EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(63, 65,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255));
 
-    EXPECT_TRUE(checkPixel(22, 44, 247,  70, 255, 255));
-    EXPECT_TRUE(checkPixel(45, 52, 209,  32, 235, 255));
-    EXPECT_TRUE(checkPixel(52, 51, 100, 255,  73, 255));
+    EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(52, 51,  98, 255,  73, 255));
     EXPECT_TRUE(checkPixel( 7, 31, 155,   0, 118, 255));
-    EXPECT_TRUE(checkPixel(31,  9, 148,  71, 110, 255));
+    EXPECT_TRUE(checkPixel(31,  9, 107,  24,  87, 255));
     EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255));
     EXPECT_TRUE(checkPixel(36, 22, 155,  29,   0, 255));
 }
@@ -570,6 +571,7 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0,   0, 133,   0, 255));
@@ -628,6 +630,7 @@
         glClearColor(0.2, 0.2, 0.2, 0.2);
         glClear(GL_COLOR_BUFFER_BIT);
 
+        glViewport(0, 0, 64, 64);
         drawTexture();
 
         EXPECT_TRUE(checkPixel( 0,  0,  82, 255,  35, 255));
@@ -675,28 +678,29 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0,  35,  35,  35,  35));
     EXPECT_TRUE(checkPixel(63,  0, 231, 231, 231, 231));
-    EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231));
-    EXPECT_TRUE(checkPixel( 0, 63,  35,  35,  35,  35));
+    EXPECT_TRUE(checkPixel(63, 65, 231, 231, 231, 231));
+    EXPECT_TRUE(checkPixel( 0, 65,  35,  35,  35,  35));
 
     EXPECT_TRUE(checkPixel(15, 10,  35, 231, 231, 231));
-    EXPECT_TRUE(checkPixel(24, 63,  35, 231, 231,  35));
-    EXPECT_TRUE(checkPixel(19, 40,  87, 179,  35,  35));
+    EXPECT_TRUE(checkPixel(24, 63,  38, 228, 231,  35));
+    EXPECT_TRUE(checkPixel(19, 40,  35, 231,  35,  35));
     EXPECT_TRUE(checkPixel(38, 30, 231,  35,  35,  35));
     EXPECT_TRUE(checkPixel(42, 54,  35,  35,  35, 231));
-    EXPECT_TRUE(checkPixel(37, 33,  35, 231, 231, 231));
+    EXPECT_TRUE(checkPixel(37, 33, 228,  38,  38,  38));
     EXPECT_TRUE(checkPixel(31,  8, 231,  35,  35, 231));
-    EXPECT_TRUE(checkPixel(36, 47, 231,  35, 231, 231));
-    EXPECT_TRUE(checkPixel(24, 63,  35, 231, 231,  35));
-    EXPECT_TRUE(checkPixel(48,  3, 231, 231,  35,  35));
+    EXPECT_TRUE(checkPixel(36, 47, 228,  35, 231, 231));
+    EXPECT_TRUE(checkPixel(24, 63,  38, 228, 231,  35));
+    EXPECT_TRUE(checkPixel(48,  3, 228, 228,  38,  35));
     EXPECT_TRUE(checkPixel(54, 50,  35, 231, 231, 231));
-    EXPECT_TRUE(checkPixel(24, 25, 191, 191, 231, 231));
-    EXPECT_TRUE(checkPixel(10,  9,  93,  93, 231, 231));
+    EXPECT_TRUE(checkPixel(24, 25,  41,  41, 231, 231));
+    EXPECT_TRUE(checkPixel(10,  9,  38,  38, 231, 231));
     EXPECT_TRUE(checkPixel(29,  4,  35,  35,  35, 231));
-    EXPECT_TRUE(checkPixel(56, 31,  35, 231, 231,  35));
+    EXPECT_TRUE(checkPixel(56, 31,  38, 228, 231,  35));
     EXPECT_TRUE(checkPixel(58, 55,  35,  35, 231, 231));
 }
 
@@ -730,6 +734,7 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0, 231, 231, 231, 231));
@@ -803,6 +808,7 @@
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
 
+    glViewport(0, 0, texWidth, texHeight);
     drawTexture();
 
     EXPECT_TRUE(checkPixel( 0,  0, 153, 153, 153, 153));
diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp
index acb990d..d29da7e 100644
--- a/libs/rs/driver/rsdRuntimeMath.cpp
+++ b/libs/rs/driver/rsdRuntimeMath.cpp
@@ -268,6 +268,87 @@
 }
 
 
+static int32_t SC_AtomicCas(volatile int32_t *ptr, int32_t expectedValue, int32_t newValue) {
+    int32_t prev;
+
+    do {
+        int32_t ret = android_atomic_release_cas(expectedValue, newValue, ptr);
+        if (!ret) {
+            // The android cas return 0 if it wrote the value.  This means the
+            // previous value was the expected value and we can return.
+            return expectedValue;
+        }
+        // We didn't write the value and need to load the "previous" value.
+        prev = *ptr;
+
+        // A race condition exists where the expected value could appear after our cas failed
+        // above.  In this case loop until we have a legit previous value or the
+        // write passes.
+        } while (prev == expectedValue);
+    return prev;
+}
+
+
+static int32_t SC_AtomicInc(volatile int32_t *ptr) {
+    return android_atomic_inc(ptr);
+}
+
+static int32_t SC_AtomicDec(volatile int32_t *ptr) {
+    return android_atomic_dec(ptr);
+}
+
+static int32_t SC_AtomicAdd(volatile int32_t *ptr, int32_t value) {
+    return android_atomic_add(value, ptr);
+}
+
+static int32_t SC_AtomicSub(volatile int32_t *ptr, int32_t value) {
+    int32_t prev, status;
+    do {
+        prev = *ptr;
+        status = android_atomic_release_cas(prev, prev - value, ptr);
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+static int32_t SC_AtomicAnd(volatile int32_t *ptr, int32_t value) {
+    return android_atomic_and(value, ptr);
+}
+
+static int32_t SC_AtomicOr(volatile int32_t *ptr, int32_t value) {
+    return android_atomic_or(value, ptr);
+}
+
+static int32_t SC_AtomicXor(volatile int32_t *ptr, int32_t value) {
+    int32_t prev, status;
+    do {
+        prev = *ptr;
+        status = android_atomic_release_cas(prev, prev ^ value, ptr);
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+static int32_t SC_AtomicMin(volatile int32_t *ptr, int32_t value) {
+    int32_t prev, status;
+    do {
+        prev = *ptr;
+        int32_t n = rsMin(value, prev);
+        status = android_atomic_release_cas(prev, n, ptr);
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+static int32_t SC_AtomicMax(volatile int32_t *ptr, int32_t value) {
+    int32_t prev, status;
+    do {
+        prev = *ptr;
+        int32_t n = rsMax(value, prev);
+        status = android_atomic_release_cas(prev, n, ptr);
+    } while (__builtin_expect(status != 0, 0));
+    return prev;
+}
+
+
+
 //////////////////////////////////////////////////////////////////////////////
 // Class implementation
 //////////////////////////////////////////////////////////////////////////////
@@ -425,6 +506,28 @@
     { "_Z6rsRandff", (void *)&SC_randf2, true },
     { "_Z6rsFracf", (void *)&SC_frac, true },
 
+    // Atomics
+    { "_Z11rsAtomicIncPVi", (void *)&SC_AtomicInc, true },
+    { "_Z11rsAtomicIncPVj", (void *)&SC_AtomicInc, true },
+    { "_Z11rsAtomicDecPVi", (void *)&SC_AtomicDec, true },
+    { "_Z11rsAtomicDecPVj", (void *)&SC_AtomicDec, true },
+    { "_Z11rsAtomicAddPVii", (void *)&SC_AtomicAdd, true },
+    { "_Z11rsAtomicAddPVjj", (void *)&SC_AtomicAdd, true },
+    { "_Z11rsAtomicSubPVii", (void *)&SC_AtomicSub, true },
+    { "_Z11rsAtomicSubPVjj", (void *)&SC_AtomicSub, true },
+    { "_Z11rsAtomicAndPVii", (void *)&SC_AtomicAnd, true },
+    { "_Z11rsAtomicAndPVjj", (void *)&SC_AtomicAnd, true },
+    { "_Z10rsAtomicOrPVii", (void *)&SC_AtomicOr, true },
+    { "_Z10rsAtomicOrPVjj", (void *)&SC_AtomicOr, true },
+    { "_Z11rsAtomicXorPVii", (void *)&SC_AtomicXor, true },
+    { "_Z11rsAtomicXorPVjj", (void *)&SC_AtomicXor, true },
+    { "_Z11rsAtomicMinPVii", (void *)&SC_AtomicMin, true },
+    { "_Z11rsAtomicMinPVjj", (void *)&SC_AtomicMin, true },
+    { "_Z11rsAtomicMaxPVii", (void *)&SC_AtomicMax, true },
+    { "_Z11rsAtomicMaxPVjj", (void *)&SC_AtomicMax, true },
+    { "_Z11rsAtomicCasPViii", (void *)&SC_AtomicCas, true },
+    { "_Z11rsAtomicCasPVjjj", (void *)&SC_AtomicCas, true },
+
     { NULL, NULL, false }
 };
 
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 584317e..f38f72c 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -258,4 +258,226 @@
               rs_allocation output, const void * usrData,
               const rs_script_call_t *);
 
+
+
+/**
+ * Atomic add one to the value at addr.
+ * Equal to rsAtomicAdd(addr, 1)
+ *
+ * @param addr Address of value to increment
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicInc(volatile int32_t* addr);
+/**
+ * Atomic add one to the value at addr.
+ * Equal to rsAtomicAdd(addr, 1)
+ *
+ * @param addr Address of value to increment
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicInc(volatile uint32_t* addr);
+
+/**
+ * Atomic subtract one from the value at addr. Equal to rsAtomicSub(addr, 1)
+ *
+ * @param addr Address of value to decrement
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicDec(volatile int32_t* addr);
+/**
+ * Atomic subtract one from the value at addr. Equal to rsAtomicSub(addr, 1)
+ *
+ * @param addr Address of value to decrement
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicDec(volatile uint32_t* addr);
+
+/**
+ * Atomic add a value to the value at addr.  addr[0] += value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to add to the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicAdd(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic add a value to the value at addr.  addr[0] += value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to add to the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicAdd(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Subtract a value from the value at addr.  addr[0] -= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to subtract from the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicSub(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Subtract a value from the value at addr.  addr[0] -= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to subtract from the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicSub(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise and a value from the value at addr.  addr[0] &= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to and with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicAnd(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Bitwise and a value from the value at addr.  addr[0] &= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to and with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicAnd(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise or a value from the value at addr.  addr[0] |= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to or with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicOr(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Bitwise or a value from the value at addr.  addr[0] |= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to or with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicOr(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise xor a value from the value at addr.  addr[0] ^= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to xor with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicXor(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Bitwise xor a value from the value at addr.  addr[0] ^= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to xor with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicXor(volatile int32_t* addr, int32_t value);
+
+/**
+ * Atomic Set the value at addr to the min of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicMin(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Set the value at addr to the min of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicMin(volatile int32_t* addr, int32_t value);
+
+/**
+ * Atomic Set the value at addr to the max of addr and value
+ * addr[0] = rsMax(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicMax(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Set the value at addr to the max of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicMax(volatile int32_t* addr, int32_t value);
+
+/**
+ * Compare-and-set operation with a full memory barrier.
+ *
+ * If the value at addr matches compareValue then newValue is written.
+ *
+ * @param addr The address to compare and replace if the compare passes.
+ * @param compareValue The value to test addr[0] against.
+ * @param newValue The value to write if the test passes.
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+    rsAtomicCas(volatile int32_t* addr, int32_t compareValue, int32_t newValue);
+
+/**
+ * Compare-and-set operation with a full memory barrier.
+ *
+ * If the value at addr matches compareValue then newValue is written.
+ *
+ * @param addr The address to compare and replace if the compare passes.
+ * @param compareValue The value to test addr[0] against.
+ * @param newValue The value to write if the test passes.
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+    rsAtomicCas(volatile uint32_t* addr, int32_t compareValue, int32_t newValue);
+
+
 #endif
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 58f6699..ea9911c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -47,10 +47,6 @@
 static const uint8_t kNalUnitTypePicParamSet = 0x08;
 static const int64_t kInitialDelayTimeUs     = 700000LL;
 
-// Using longer adjustment period to suppress fluctuations in
-// the audio encoding paths
-static const int64_t kVideoMediaTimeAdjustPeriodTimeUs = 600000000LL;  // 10 minutes
-
 class MPEG4Writer::Track {
 public:
     Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
@@ -88,8 +84,6 @@
     int64_t mTrackDurationUs;
     int64_t mMaxChunkDurationUs;
 
-    // For realtime applications, we need to adjust the media clock
-    // for video track based on the audio media clock
     bool mIsRealTimeRecording;
     int64_t mMaxTimeStampUs;
     int64_t mEstimatedTrackSizeBytes;
@@ -175,28 +169,9 @@
     int64_t mPreviousTrackTimeUs;
     int64_t mTrackEveryTimeDurationUs;
 
-    // Has the media time adjustment for video started?
-    bool    mIsMediaTimeAdjustmentOn;
-    // The time stamp when previous media time adjustment period starts
-    int64_t mPrevMediaTimeAdjustTimestampUs;
-    // Number of vidoe frames whose time stamp may be adjusted
-    int64_t mMediaTimeAdjustNumFrames;
-    // The sample number when previous meida time adjustmnet period starts
-    int64_t mPrevMediaTimeAdjustSample;
-    // The total accumulated drift time within a period of
-    // kVideoMediaTimeAdjustPeriodTimeUs.
-    int64_t mTotalDriftTimeToAdjustUs;
-    // The total accumalated drift time since the start of the recording
-    // excluding the current time adjustment period
-    int64_t mPrevTotalAccumDriftTimeUs;
-
     // Update the audio track's drift information.
     void updateDriftTime(const sp<MetaData>& meta);
 
-    // Adjust the time stamp of the video track according to
-    // the drift time information from the audio track.
-    void adjustMediaTime(int64_t *timestampUs);
-
     static void *ThreadWrapper(void *me);
     status_t threadEntry();
 
@@ -1512,12 +1487,7 @@
     mNumSttsTableEntries = 0;
     mNumCttsTableEntries = 0;
     mMdatSizeBytes = 0;
-    mIsMediaTimeAdjustmentOn = false;
-    mPrevMediaTimeAdjustTimestampUs = 0;
-    mMediaTimeAdjustNumFrames = 0;
-    mPrevMediaTimeAdjustSample = 0;
-    mTotalDriftTimeToAdjustUs = 0;
-    mPrevTotalAccumDriftTimeUs = 0;
+
     mMaxChunkDurationUs = 0;
     mHasNegativeCttsDeltaDuration = false;
 
@@ -1816,128 +1786,6 @@
 }
 
 /*
-* The video track's media time adjustment for real-time applications
-* is described as follows:
-*
-* First, the media time adjustment is done for every period of
-* kVideoMediaTimeAdjustPeriodTimeUs. kVideoMediaTimeAdjustPeriodTimeUs
-* is currently a fixed value chosen heuristically. The value of
-* kVideoMediaTimeAdjustPeriodTimeUs should not be very large or very small
-* for two considerations: on one hand, a relatively large value
-* helps reduce large fluctuation of drift time in the audio encoding
-* path; while on the other hand, a relatively small value helps keep
-* restoring synchronization in audio/video more frequently. Note for the
-* very first period of kVideoMediaTimeAdjustPeriodTimeUs, there is
-* no media time adjustment for the video track.
-*
-* Second, the total accumulated audio track time drift found
-* in a period of kVideoMediaTimeAdjustPeriodTimeUs is distributed
-* over a stream of incoming video frames. The number of video frames
-* affected is determined based on the number of recorded video frames
-* within the past kVideoMediaTimeAdjustPeriodTimeUs period.
-* We choose to distribute the drift time over only a portion
-* (rather than all) of the total number of recorded video frames
-* in order to make sure that the video track media time adjustment is
-* completed for the current period before the next video track media
-* time adjustment period starts. Currently, the portion chosen is a
-* half (0.5).
-*
-* Last, various additional checks are performed to ensure that
-* the actual audio encoding path does not have too much drift.
-* In particular, 1) we want to limit the average incremental time
-* adjustment for each video frame to be less than a threshold
-* for a single period of kVideoMediaTimeAdjustPeriodTimeUs.
-* Currently, the threshold is set to 5 ms. If the average incremental
-* media time adjustment for a video frame is larger than the
-* threshold, the audio encoding path has too much time drift.
-* 2) We also want to limit the total time drift in the audio
-* encoding path to be less than a threshold for a period of
-* kVideoMediaTimeAdjustPeriodTimeUs. Currently, the threshold
-* is 0.5% of kVideoMediaTimeAdjustPeriodTimeUs. If the time drift of
-* the audio encoding path is larger than the threshold, the audio
-* encoding path has too much time drift. We treat the large time
-* drift of the audio encoding path as errors, since there is no
-* way to keep audio/video in synchronization for real-time
-* applications if the time drift is too large unless we drop some
-* video frames, which has its own problems that we don't want
-* to get into for the time being.
-*/
-void MPEG4Writer::Track::adjustMediaTime(int64_t *timestampUs) {
-    if (*timestampUs - mPrevMediaTimeAdjustTimestampUs >=
-        kVideoMediaTimeAdjustPeriodTimeUs) {
-
-        LOGV("New media time adjustment period at %lld us", *timestampUs);
-        mIsMediaTimeAdjustmentOn = true;
-        mMediaTimeAdjustNumFrames =
-                (mNumSamples - mPrevMediaTimeAdjustSample) >> 1;
-
-        mPrevMediaTimeAdjustTimestampUs = *timestampUs;
-        mPrevMediaTimeAdjustSample = mNumSamples;
-        int64_t totalAccumDriftTimeUs = mOwner->getDriftTimeUs();
-        mTotalDriftTimeToAdjustUs =
-                totalAccumDriftTimeUs - mPrevTotalAccumDriftTimeUs;
-
-        mPrevTotalAccumDriftTimeUs = totalAccumDriftTimeUs;
-
-        // Check on incremental adjusted time per frame
-        int64_t adjustTimePerFrameUs =
-                mTotalDriftTimeToAdjustUs / mMediaTimeAdjustNumFrames;
-
-        if (adjustTimePerFrameUs < 0) {
-            adjustTimePerFrameUs = -adjustTimePerFrameUs;
-        }
-        if (adjustTimePerFrameUs >= 5000) {
-            LOGE("Adjusted time per video frame is %lld us",
-                adjustTimePerFrameUs);
-            CHECK(!"Video frame time adjustment is too large!");
-        }
-
-        // Check on total accumulated time drift within a period of
-        // kVideoMediaTimeAdjustPeriodTimeUs.
-        int64_t driftPercentage = (mTotalDriftTimeToAdjustUs * 1000)
-                / kVideoMediaTimeAdjustPeriodTimeUs;
-
-        if (driftPercentage < 0) {
-            driftPercentage = -driftPercentage;
-        }
-        if (driftPercentage > 5) {
-            LOGE("Audio track has time drift %lld us over %lld us",
-                mTotalDriftTimeToAdjustUs,
-                kVideoMediaTimeAdjustPeriodTimeUs);
-
-            CHECK(!"The audio track media time drifts too much!");
-        }
-
-    }
-
-    if (mIsMediaTimeAdjustmentOn) {
-        if (mNumSamples - mPrevMediaTimeAdjustSample <=
-            mMediaTimeAdjustNumFrames) {
-
-            // Do media time incremental adjustment
-            int64_t incrementalAdjustTimeUs =
-                        (mTotalDriftTimeToAdjustUs *
-                            (mNumSamples - mPrevMediaTimeAdjustSample))
-                                / mMediaTimeAdjustNumFrames;
-
-            *timestampUs +=
-                (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs);
-
-            LOGV("Incremental video frame media time adjustment: %lld us",
-                (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs));
-        } else {
-            // Within the remaining adjustment period,
-            // no incremental adjustment is needed.
-            *timestampUs +=
-                (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs);
-
-            LOGV("Fixed video frame media time adjustment: %lld us",
-                (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs));
-        }
-    }
-}
-
-/*
  * Updates the drift time from the audio track so that
  * the video track can get the updated drift time information
  * from the file writer. The fluctuation of the drift time of the audio
@@ -2080,32 +1928,6 @@
 
         int32_t isSync = false;
         meta_data->findInt32(kKeyIsSyncFrame, &isSync);
-
-        /*
-         * The original timestamp found in the data buffer will be modified as below:
-         *
-         * There is a playback offset into this track if the track's start time
-         * is not the same as the movie start time, which will be recorded in edst
-         * box of the output file. The playback offset is to make sure that the
-         * starting time of the audio/video tracks are synchronized. Although the
-         * track's media timestamp may be subject to various modifications
-         * as outlined below, the track's playback offset time remains unchanged
-         * once the first data buffer of the track is received.
-         *
-         * The media time stamp will be calculated by subtracting the playback offset
-         * (and potential pause durations) from the original timestamp in the buffer.
-         *
-         * If this track is a video track for a real-time recording application with
-         * both audio and video tracks, its media timestamp will subject to further
-         * modification based on the media clock of the audio track. This modification
-         * is needed for the purpose of maintaining good audio/video synchronization.
-         *
-         * If the recording session is paused and resumed multiple times, the track
-         * media timestamp will be modified as if the  recording session had never been
-         * paused at all during playback of the recorded output file. In other words,
-         * the output file will have no memory of pause/resume durations.
-         *
-         */
         CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -2146,31 +1968,13 @@
                 timestampUs, cttsDeltaTimeUs);
         }
 
-        // Media time adjustment for real-time applications
         if (mIsRealTimeRecording) {
             if (mIsAudio) {
                 updateDriftTime(meta_data);
-            } else {
-                adjustMediaTime(&timestampUs);
             }
         }
 
         CHECK(timestampUs >= 0);
-        if (mNumSamples > 1) {
-            if (timestampUs <= lastTimestampUs) {
-                LOGW("Frame arrives too late!");
-                // Don't drop the late frame, since dropping a frame may cause
-                // problems later during playback
-
-                // The idea here is to avoid having two or more samples with the
-                // same timestamp in the output file.
-                if (mTimeScale >= 1000000LL) {
-                    timestampUs = lastTimestampUs + 1;
-                } else {
-                    timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
-                }
-            }
-        }
 
         LOGV("%s media time stamp: %lld and previous paused duration %lld",
                 mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index eb135ab..a8a094e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -416,12 +416,16 @@
         uint32_t delta = mTimeToSample[2 * i + 1];
 
         for (uint32_t j = 0; j < n; ++j) {
-            CHECK(sampleIndex < mNumSampleSizes);
+            if (sampleIndex < mNumSampleSizes) {
+                // Technically this should always be the case if the file
+                // is well-formed, but you know... there's (gasp) malformed
+                // content out there.
 
-            mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
+                mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
 
-            mSampleTimeEntries[sampleIndex].mCompositionTime =
-                sampleTime + getCompositionTimeOffset(sampleIndex);
+                mSampleTimeEntries[sampleIndex].mCompositionTime =
+                    sampleTime + getCompositionTimeOffset(sampleIndex);
+            }
 
             ++sampleIndex;
             sampleTime += delta;
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index e9ce719..7e83163 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -117,11 +117,27 @@
     addPort(def);
 }
 
+static int GetCPUCoreCount() {
+    int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK(cpuCoreCount >= 1);
+    LOGV("Number of CPU cores: %d", cpuCoreCount);
+    return cpuCoreCount;
+}
+
 status_t SoftVPX::initDecoder() {
     mCtx = new vpx_codec_ctx_t;
     vpx_codec_err_t vpx_err;
+    vpx_codec_dec_cfg_t cfg;
+    memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
+    cfg.threads = GetCPUCoreCount();
     if ((vpx_err = vpx_codec_dec_init(
-                (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) {
+                (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, &cfg, 0))) {
         LOGE("on2 decoder failed to initialize. (%d)", vpx_err);
         return UNKNOWN_ERROR;
     }
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 012d9ad..165683e 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -296,6 +296,8 @@
         new M3UParser(url, buffer->data(), buffer->size());
 
     if (playlist->initCheck() != OK) {
+        LOGE("failed to parse .m3u8 playlist");
+
         return NULL;
     }
 
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 2eb180a..765f795 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -179,7 +179,7 @@
                 if (mIsVariantPlaylist) {
                     return ERROR_MALFORMED;
                 }
-                err = parseMetaData(line, &itemMeta, "duration");
+                err = parseMetaDataDuration(line, &itemMeta, "durationUs");
             } else if (line.startsWith("#EXT-X-DISCONTINUITY")) {
                 if (mIsVariantPlaylist) {
                     return ERROR_MALFORMED;
@@ -203,9 +203,9 @@
 
         if (!line.startsWith("#")) {
             if (!mIsVariantPlaylist) {
-                int32_t durationSecs;
+                int64_t durationUs;
                 if (itemMeta == NULL
-                        || !itemMeta->findInt32("duration", &durationSecs)) {
+                        || !itemMeta->findInt64("durationUs", &durationUs)) {
                     return ERROR_MALFORMED;
                 }
             }
@@ -252,6 +252,30 @@
 }
 
 // static
+status_t M3UParser::parseMetaDataDuration(
+        const AString &line, sp<AMessage> *meta, const char *key) {
+    ssize_t colonPos = line.find(":");
+
+    if (colonPos < 0) {
+        return ERROR_MALFORMED;
+    }
+
+    double x;
+    status_t err = ParseDouble(line.c_str() + colonPos + 1, &x);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (meta->get() == NULL) {
+        *meta = new AMessage;
+    }
+    (*meta)->setInt64(key, (int64_t)x * 1E6);
+
+    return OK;
+}
+
+// static
 status_t M3UParser::parseStreamInf(
         const AString &line, sp<AMessage> *meta) {
     ssize_t colonPos = line.find(":");
@@ -412,4 +436,18 @@
     return OK;
 }
 
+// static
+status_t M3UParser::ParseDouble(const char *s, double *x) {
+    char *end;
+    double dval = strtod(s, &end);
+
+    if (end == s || (*end != '\0' && *end != ',')) {
+        return ERROR_MALFORMED;
+    }
+
+    *x = dval;
+
+    return OK;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 63895b4..478582d 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -63,6 +63,9 @@
     static status_t parseMetaData(
             const AString &line, sp<AMessage> *meta, const char *key);
 
+    static status_t parseMetaDataDuration(
+            const AString &line, sp<AMessage> *meta, const char *key);
+
     static status_t parseStreamInf(
             const AString &line, sp<AMessage> *meta);
 
@@ -70,6 +73,7 @@
             const AString &line, sp<AMessage> *meta, const AString &baseURI);
 
     static status_t ParseInt32(const char *s, int32_t *x);
+    static status_t ParseDouble(const char *s, double *x);
 
     DISALLOW_EVIL_CONSTRUCTORS(M3UParser);
 };
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 545e6d4..504d470 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -86,7 +86,11 @@
 
         mPluginByComponentName.add(name8, plugin);
     }
-    CHECK_EQ(err, OMX_ErrorNoMore);
+
+    if (err != OMX_ErrorNoMore) {
+        LOGE("OMX plugin failed w/ error 0x%08x after registering %d "
+             "components", err, mPluginByComponentName.size());
+    }
 }
 
 void OMXMaster::clearPlugins() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 0b82123..f3c2623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1721,7 +1721,7 @@
                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
                 boolean excludeRecents = false;
                 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                    String reason = intent.getExtras().getString("reason");
+                    String reason = intent.getStringExtra("reason");
                     if (reason != null) {
                         excludeRecents = reason.equals("recentapps");
                     }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index fbde9d1..8037d7a 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -35,6 +35,8 @@
 
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -52,6 +54,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
+import android.graphics.BitmapFactory;
 import android.inputmethodservice.InputMethodService;
 import android.os.Binder;
 import android.os.Environment;
@@ -154,6 +157,13 @@
     private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
             new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
 
+    // Ongoing notification
+    private final NotificationManager mNotificationManager;
+    private final Notification mImeSwitcherNotification;
+    private final PendingIntent mImeSwitchPendingIntent;
+    private final boolean mShowOngoingImeSwitcherForPhones;
+    private boolean mNotificationShown;
+
     class SessionState {
         final ClientState client;
         final IInputMethod method;
@@ -508,6 +518,25 @@
                 handleMessage(msg);
             }
         });
+
+        mNotificationManager = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mImeSwitcherNotification = new Notification();
+        mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default;
+        mImeSwitcherNotification.when = 0;
+        mImeSwitcherNotification.flags = Notification.FLAG_ONGOING_EVENT;
+        mImeSwitcherNotification.tickerText = null;
+        mImeSwitcherNotification.defaults = 0; // please be quiet
+        mImeSwitcherNotification.sound = null;
+        mImeSwitcherNotification.vibrate = null;
+        Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mImeSwitchPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+        mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
+                com.android.internal.R.bool.show_ongoing_ime_switcher);
+
         synchronized (mMethodMap) {
             mFileManager = new InputMethodFileManager(mMethodMap);
         }
@@ -522,6 +551,7 @@
 
         mStatusBar = statusBar;
         statusBar.setIconVisibility("ime", false);
+        mNotificationShown = false;
 
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
@@ -1022,6 +1052,32 @@
         }
     }
 
+    private boolean needsToShowImeSwitchOngoingNotification() {
+        if (!mShowOngoingImeSwitcherForPhones) return false;
+        synchronized (mMethodMap) {
+            List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+            final int N = imis.size();
+            int count = 0;
+            for(int i = 0; i < N; ++i) {
+                final InputMethodInfo imi = imis.get(i);
+                final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeListLocked(
+                        imi, true);
+                final int subtypeCount = subtypes.size();
+                if (subtypeCount == 0) {
+                    ++count;
+                } else {
+                    for (int j = 0; j < subtypeCount; ++j) {
+                        if (!subtypes.get(j).isAuxiliary()) {
+                            ++count;
+                        }
+                    }
+                }
+                if (count > 1) return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
         int uid = Binder.getCallingUid();
@@ -1036,6 +1092,25 @@
                 mImeWindowVis = vis;
                 mBackDisposition = backDisposition;
                 mStatusBar.setImeWindowStatus(token, vis, backDisposition);
+                final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0;
+                if (iconVisibility && needsToShowImeSwitchOngoingNotification()) {
+                    final PackageManager pm = mContext.getPackageManager();
+                    final CharSequence label = mMethodMap.get(mCurMethodId).loadLabel(pm);
+                    final CharSequence title = mRes.getText(
+                            com.android.internal.R.string.select_input_method);
+                    mImeSwitcherNotification.setLatestEventInfo(
+                            mContext, title, label, mImeSwitchPendingIntent);
+                    mNotificationManager.notify(
+                            com.android.internal.R.string.select_input_method,
+                            mImeSwitcherNotification);
+                    mNotificationShown = true;
+                } else {
+                    if (mNotificationShown) {
+                        mNotificationManager.cancel(
+                                com.android.internal.R.string.select_input_method);
+                        mNotificationShown = false;
+                    }
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
index 541bf22..e77998e 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -65,6 +65,7 @@
         unitTests = new ArrayList<UnitTest>();
 
         unitTests.add(new UT_primitives(this, mRes, mCtx));
+        unitTests.add(new UT_vector(this, mRes, mCtx));
         unitTests.add(new UT_rsdebug(this, mRes, mCtx));
         unitTests.add(new UT_rstime(this, mRes, mCtx));
         unitTests.add(new UT_rstypes(this, mRes, mCtx));
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java
new file mode 100644
index 0000000..748701d
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_vector extends UnitTest {
+    private Resources mRes;
+
+    protected UT_vector(RSTestCore rstc, Resources res, Context ctx) {
+        super(rstc, "Vector", ctx);
+        mRes = res;
+    }
+
+    private boolean initializeGlobals(ScriptC_vector s) {
+        Float2 F2 = s.get_f2();
+        if (F2.x != 1.0f || F2.y != 2.0f) {
+            return false;
+        }
+        F2.x = 2.99f;
+        F2.y = 3.99f;
+        s.set_f2(F2);
+
+        Float3 F3 = s.get_f3();
+        if (F3.x != 1.0f || F3.y != 2.0f || F3.z != 3.0f) {
+            return false;
+        }
+        F3.x = 2.99f;
+        F3.y = 3.99f;
+        F3.z = 4.99f;
+        s.set_f3(F3);
+
+        Float4 F4 = s.get_f4();
+        if (F4.x != 1.0f || F4.y != 2.0f || F4.z != 3.0f || F4.w != 4.0f) {
+            return false;
+        }
+        F4.x = 2.99f;
+        F4.y = 3.99f;
+        F4.z = 4.99f;
+        F4.w = 5.99f;
+        s.set_f4(F4);
+
+        Double2 D2 = s.get_d2();
+        if (D2.x != 1.0 || D2.y != 2.0) {
+            return false;
+        }
+        D2.x = 2.99;
+        D2.y = 3.99;
+        s.set_d2(D2);
+
+        Double3 D3 = s.get_d3();
+        if (D3.x != 1.0 || D3.y != 2.0 || D3.z != 3.0) {
+            return false;
+        }
+        D3.x = 2.99;
+        D3.y = 3.99;
+        D3.z = 4.99;
+        s.set_d3(D3);
+
+        Double4 D4 = s.get_d4();
+        if (D4.x != 1.0 || D4.y != 2.0 || D4.z != 3.0 || D4.w != 4.0) {
+            return false;
+        }
+        D4.x = 2.99;
+        D4.y = 3.99;
+        D4.z = 4.99;
+        D4.w = 5.99;
+        s.set_d4(D4);
+
+        Byte2 B2 = s.get_i8_2();
+        if (B2.x != 1 || B2.y != 2) {
+            return false;
+        }
+        B2.x = 2;
+        B2.y = 3;
+        s.set_i8_2(B2);
+
+        Byte3 B3 = s.get_i8_3();
+        if (B3.x != 1 || B3.y != 2 || B3.z != 3) {
+            return false;
+        }
+        B3.x = 2;
+        B3.y = 3;
+        B3.z = 4;
+        s.set_i8_3(B3);
+
+        Byte4 B4 = s.get_i8_4();
+        if (B4.x != 1 || B4.y != 2 || B4.z != 3 || B4.w != 4) {
+            return false;
+        }
+        B4.x = 2;
+        B4.y = 3;
+        B4.z = 4;
+        B4.w = 5;
+        s.set_i8_4(B4);
+
+        Short2 S2 = s.get_u8_2();
+        if (S2.x != 1 || S2.y != 2) {
+            return false;
+        }
+        S2.x = 2;
+        S2.y = 3;
+        s.set_u8_2(S2);
+
+        Short3 S3 = s.get_u8_3();
+        if (S3.x != 1 || S3.y != 2 || S3.z != 3) {
+            return false;
+        }
+        S3.x = 2;
+        S3.y = 3;
+        S3.z = 4;
+        s.set_u8_3(S3);
+
+        Short4 S4 = s.get_u8_4();
+        if (S4.x != 1 || S4.y != 2 || S4.z != 3 || S4.w != 4) {
+            return false;
+        }
+        S4.x = 2;
+        S4.y = 3;
+        S4.z = 4;
+        S4.w = 5;
+        s.set_u8_4(S4);
+
+        S2 = s.get_i16_2();
+        if (S2.x != 1 || S2.y != 2) {
+            return false;
+        }
+        S2.x = 2;
+        S2.y = 3;
+        s.set_i16_2(S2);
+
+        S3 = s.get_i16_3();
+        if (S3.x != 1 || S3.y != 2 || S3.z != 3) {
+            return false;
+        }
+        S3.x = 2;
+        S3.y = 3;
+        S3.z = 4;
+        s.set_i16_3(S3);
+
+        S4 = s.get_i16_4();
+        if (S4.x != 1 || S4.y != 2 || S4.z != 3 || S4.w != 4) {
+            return false;
+        }
+        S4.x = 2;
+        S4.y = 3;
+        S4.z = 4;
+        S4.w = 5;
+        s.set_i16_4(S4);
+
+        Int2 I2 = s.get_u16_2();
+        if (I2.x != 1 || I2.y != 2) {
+            return false;
+        }
+        I2.x = 2;
+        I2.y = 3;
+        s.set_u16_2(I2);
+
+        Int3 I3 = s.get_u16_3();
+        if (I3.x != 1 || I3.y != 2 || I3.z != 3) {
+            return false;
+        }
+        I3.x = 2;
+        I3.y = 3;
+        I3.z = 4;
+        s.set_u16_3(I3);
+
+        Int4 I4 = s.get_u16_4();
+        if (I4.x != 1 || I4.y != 2 || I4.z != 3 || I4.w != 4) {
+            return false;
+        }
+        I4.x = 2;
+        I4.y = 3;
+        I4.z = 4;
+        I4.w = 5;
+        s.set_u16_4(I4);
+
+        I2 = s.get_i32_2();
+        if (I2.x != 1 || I2.y != 2) {
+            return false;
+        }
+        I2.x = 2;
+        I2.y = 3;
+        s.set_i32_2(I2);
+
+        I3 = s.get_i32_3();
+        if (I3.x != 1 || I3.y != 2 || I3.z != 3) {
+            return false;
+        }
+        I3.x = 2;
+        I3.y = 3;
+        I3.z = 4;
+        s.set_i32_3(I3);
+
+        I4 = s.get_i32_4();
+        if (I4.x != 1 || I4.y != 2 || I4.z != 3 || I4.w != 4) {
+            return false;
+        }
+        I4.x = 2;
+        I4.y = 3;
+        I4.z = 4;
+        I4.w = 5;
+        s.set_i32_4(I4);
+
+        Long2 L2 = s.get_u32_2();
+        if (L2.x != 1 || L2.y != 2) {
+            return false;
+        }
+        L2.x = 2;
+        L2.y = 3;
+        s.set_u32_2(L2);
+
+        Long3 L3 = s.get_u32_3();
+        if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+            return false;
+        }
+        L3.x = 2;
+        L3.y = 3;
+        L3.z = 4;
+        s.set_u32_3(L3);
+
+        Long4 L4 = s.get_u32_4();
+        if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+            return false;
+        }
+        L4.x = 2;
+        L4.y = 3;
+        L4.z = 4;
+        L4.w = 5;
+        s.set_u32_4(L4);
+
+        L2 = s.get_i64_2();
+        if (L2.x != 1 || L2.y != 2) {
+            return false;
+        }
+        L2.x = 2;
+        L2.y = 3;
+        s.set_i64_2(L2);
+
+        L3 = s.get_i64_3();
+        if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+            return false;
+        }
+        L3.x = 2;
+        L3.y = 3;
+        L3.z = 4;
+        s.set_i64_3(L3);
+
+        L4 = s.get_i64_4();
+        if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+            return false;
+        }
+        L4.x = 2;
+        L4.y = 3;
+        L4.z = 4;
+        L4.w = 5;
+        s.set_i64_4(L4);
+
+        L2 = s.get_u64_2();
+        if (L2.x != 1 || L2.y != 2) {
+            return false;
+        }
+        L2.x = 2;
+        L2.y = 3;
+        s.set_u64_2(L2);
+
+        L3 = s.get_u64_3();
+        if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+            return false;
+        }
+        L3.x = 2;
+        L3.y = 3;
+        L3.z = 4;
+        s.set_u64_3(L3);
+
+        L4 = s.get_u64_4();
+        if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+            return false;
+        }
+        L4.x = 2;
+        L4.y = 3;
+        L4.z = 4;
+        L4.w = 5;
+        s.set_u64_4(L4);
+
+        return true;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create(mCtx);
+        ScriptC_vector s = new ScriptC_vector(pRS, mRes, R.raw.vector);
+        pRS.setMessageHandler(mRsMessage);
+        if (!initializeGlobals(s)) {
+            result = -1;
+        } else {
+            s.invoke_vector_test();
+            pRS.finish();
+            waitForMessage();
+        }
+        pRS.destroy();
+    }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs
new file mode 100644
index 0000000..0430a2f
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs
@@ -0,0 +1,198 @@
+#include "shared.rsh"
+
+// Testing vector types
+float2 f2 = { 1.0f, 2.0f };
+float3 f3 = { 1.0f, 2.0f, 3.0f };
+float4 f4 = { 1.0f, 2.0f, 3.0f, 4.0f };
+
+double2 d2 = { 1.0, 2.0 };
+double3 d3 = { 1.0, 2.0, 3.0 };
+double4 d4 = { 1.0, 2.0, 3.0, 4.0 };
+
+char2 i8_2 = { 1, 2 };
+char3 i8_3 = { 1, 2, 3 };
+char4 i8_4 = { 1, 2, 3, 4 };
+
+uchar2 u8_2 = { 1, 2 };
+uchar3 u8_3 = { 1, 2, 3 };
+uchar4 u8_4 = { 1, 2, 3, 4 };
+
+short2 i16_2 = { 1, 2 };
+short3 i16_3 = { 1, 2, 3 };
+short4 i16_4 = { 1, 2, 3, 4 };
+
+ushort2 u16_2 = { 1, 2 };
+ushort3 u16_3 = { 1, 2, 3 };
+ushort4 u16_4 = { 1, 2, 3, 4 };
+
+int2 i32_2 = { 1, 2 };
+int3 i32_3 = { 1, 2, 3 };
+int4 i32_4 = { 1, 2, 3, 4 };
+
+uint2 u32_2 = { 1, 2 };
+uint3 u32_3 = { 1, 2, 3 };
+uint4 u32_4 = { 1, 2, 3, 4 };
+
+long2 i64_2 = { 1, 2 };
+long3 i64_3 = { 1, 2, 3 };
+long4 i64_4 = { 1, 2, 3, 4 };
+
+ulong2 u64_2 = { 1, 2 };
+ulong3 u64_3 = { 1, 2, 3 };
+ulong4 u64_4 = { 1, 2, 3, 4 };
+
+static bool test_vector_types() {
+    bool failed = false;
+
+    rsDebug("Testing F32", 0);
+    _RS_ASSERT(f2.x == 2.99f);
+    _RS_ASSERT(f2.y == 3.99f);
+
+    _RS_ASSERT(f3.x == 2.99f);
+    _RS_ASSERT(f3.y == 3.99f);
+    _RS_ASSERT(f3.z == 4.99f);
+
+    _RS_ASSERT(f4.x == 2.99f);
+    _RS_ASSERT(f4.y == 3.99f);
+    _RS_ASSERT(f4.z == 4.99f);
+    _RS_ASSERT(f4.w == 5.99f);
+
+    rsDebug("Testing F64", 0);
+    _RS_ASSERT(d2.x == 2.99);
+    _RS_ASSERT(d2.y == 3.99);
+
+    _RS_ASSERT(d3.x == 2.99);
+    _RS_ASSERT(d3.y == 3.99);
+    _RS_ASSERT(d3.z == 4.99);
+
+    _RS_ASSERT(d4.x == 2.99);
+    _RS_ASSERT(d4.y == 3.99);
+    _RS_ASSERT(d4.z == 4.99);
+    _RS_ASSERT(d4.w == 5.99);
+
+    rsDebug("Testing I8", 0);
+    _RS_ASSERT(i8_2.x == 2);
+    _RS_ASSERT(i8_2.y == 3);
+
+    _RS_ASSERT(i8_3.x == 2);
+    _RS_ASSERT(i8_3.y == 3);
+    _RS_ASSERT(i8_3.z == 4);
+
+    _RS_ASSERT(i8_4.x == 2);
+    _RS_ASSERT(i8_4.y == 3);
+    _RS_ASSERT(i8_4.z == 4);
+    _RS_ASSERT(i8_4.w == 5);
+
+    rsDebug("Testing U8", 0);
+    _RS_ASSERT(u8_2.x == 2);
+    _RS_ASSERT(u8_2.y == 3);
+
+    _RS_ASSERT(u8_3.x == 2);
+    _RS_ASSERT(u8_3.y == 3);
+    _RS_ASSERT(u8_3.z == 4);
+
+    _RS_ASSERT(u8_4.x == 2);
+    _RS_ASSERT(u8_4.y == 3);
+    _RS_ASSERT(u8_4.z == 4);
+    _RS_ASSERT(u8_4.w == 5);
+
+    rsDebug("Testing I16", 0);
+    _RS_ASSERT(i16_2.x == 2);
+    _RS_ASSERT(i16_2.y == 3);
+
+    _RS_ASSERT(i16_3.x == 2);
+    _RS_ASSERT(i16_3.y == 3);
+    _RS_ASSERT(i16_3.z == 4);
+
+    _RS_ASSERT(i16_4.x == 2);
+    _RS_ASSERT(i16_4.y == 3);
+    _RS_ASSERT(i16_4.z == 4);
+    _RS_ASSERT(i16_4.w == 5);
+
+    rsDebug("Testing U16", 0);
+    _RS_ASSERT(u16_2.x == 2);
+    _RS_ASSERT(u16_2.y == 3);
+
+    _RS_ASSERT(u16_3.x == 2);
+    _RS_ASSERT(u16_3.y == 3);
+    _RS_ASSERT(u16_3.z == 4);
+
+    _RS_ASSERT(u16_4.x == 2);
+    _RS_ASSERT(u16_4.y == 3);
+    _RS_ASSERT(u16_4.z == 4);
+    _RS_ASSERT(u16_4.w == 5);
+
+    rsDebug("Testing I32", 0);
+    _RS_ASSERT(i32_2.x == 2);
+    _RS_ASSERT(i32_2.y == 3);
+
+    _RS_ASSERT(i32_3.x == 2);
+    _RS_ASSERT(i32_3.y == 3);
+    _RS_ASSERT(i32_3.z == 4);
+
+    _RS_ASSERT(i32_4.x == 2);
+    _RS_ASSERT(i32_4.y == 3);
+    _RS_ASSERT(i32_4.z == 4);
+    _RS_ASSERT(i32_4.w == 5);
+
+    rsDebug("Testing U32", 0);
+    _RS_ASSERT(u32_2.x == 2);
+    _RS_ASSERT(u32_2.y == 3);
+
+    _RS_ASSERT(u32_3.x == 2);
+    _RS_ASSERT(u32_3.y == 3);
+    _RS_ASSERT(u32_3.z == 4);
+
+    _RS_ASSERT(u32_4.x == 2);
+    _RS_ASSERT(u32_4.y == 3);
+    _RS_ASSERT(u32_4.z == 4);
+    _RS_ASSERT(u32_4.w == 5);
+
+    rsDebug("Testing I64", 0);
+    _RS_ASSERT(i64_2.x == 2);
+    _RS_ASSERT(i64_2.y == 3);
+
+    _RS_ASSERT(i64_3.x == 2);
+    _RS_ASSERT(i64_3.y == 3);
+    _RS_ASSERT(i64_3.z == 4);
+
+    _RS_ASSERT(i64_4.x == 2);
+    _RS_ASSERT(i64_4.y == 3);
+    _RS_ASSERT(i64_4.z == 4);
+    _RS_ASSERT(i64_4.w == 5);
+
+    rsDebug("Testing U64", 0);
+    _RS_ASSERT(u64_2.x == 2);
+    _RS_ASSERT(u64_2.y == 3);
+
+    _RS_ASSERT(u64_3.x == 2);
+    _RS_ASSERT(u64_3.y == 3);
+    _RS_ASSERT(u64_3.z == 4);
+
+    _RS_ASSERT(u64_4.x == 2);
+    _RS_ASSERT(u64_4.y == 3);
+    _RS_ASSERT(u64_4.z == 4);
+    _RS_ASSERT(u64_4.w == 5);
+
+    if (failed) {
+        rsDebug("test_vector FAILED", 0);
+    }
+    else {
+        rsDebug("test_vector PASSED", 0);
+    }
+
+    return failed;
+}
+
+void vector_test() {
+    bool failed = false;
+    failed |= test_vector_types();
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index 98f8529..a45e879 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -138,6 +138,10 @@
         return "";
     }
 
+    @LayoutlibDelegate
+    /*package*/ static String getScript(String locale) {
+        return "";
+    }
 
     @LayoutlibDelegate
     /*package*/ static String[] getISOLanguagesNative() {
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
index e303638..98cade9 100644
--- a/tools/layoutlib/bridge/tests/Android.mk
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -19,6 +19,8 @@
 # Only compile source java files in this lib.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_JAVA_RESOURCE_DIRS := res
+
 LOCAL_MODULE := layoutlib-tests
 LOCAL_MODULE_TAGS := optional
 
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/testdata/layout1.xml b/tools/layoutlib/bridge/tests/res/com/android/layoutlib/testdata/layout1.xml
similarity index 100%
rename from tools/layoutlib/bridge/tests/src/com/android/layoutlib/testdata/layout1.xml
rename to tools/layoutlib/bridge/tests/res/com/android/layoutlib/testdata/layout1.xml
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 96436fe..865a008 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -38,7 +38,7 @@
     public void testXmlBlockParser() throws Exception {
 
         XmlPullParser parser = ParserFactory.create(
-                getClass().getResourceAsStream("com/android/layoutlib/testdata/layout1.xml"),
+                getClass().getResourceAsStream("/com/android/layoutlib/testdata/layout1.xml"),
                         "layout1.xml");
 
         parser = new BridgeXmlBlockParser(parser, null, false /* platformResourceFlag */);