Merge "Add ability to parse HTTP-format moratorium times (since pretty much every user wants this)."
diff --git a/api/current.xml b/api/current.xml
index 5e34322..0893e4e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -24873,17 +24873,6 @@
  visibility="public"
 >
 </field>
-<field name="INTENT_ACTION_SELECT_SEARCH_SOURCE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.SELECT_SEARCH_SOURCE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="INTENT_ACTION_WEB_SEARCH_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -55617,7 +55606,7 @@
 >
 </field>
 </class>
-<class name="GestureUtilities"
+<class name="GestureUtils"
  extends="java.lang.Object"
  abstract="false"
  static="false"
@@ -80725,6 +80714,21 @@
 <parameter name="tag" type="java.lang.String">
 </parameter>
 </method>
+<method name="getAttributeDouble"
+ return="double"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="double">
+</parameter>
+</method>
 <method name="getAttributeInt"
  return="int"
  abstract="false"
@@ -80924,6 +80928,17 @@
  visibility="public"
 >
 </field>
+<field name="TAG_FOCAL_LENGTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;FocalLength&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAG_GPS_DATESTAMP"
  type="java.lang.String"
  transient="false"
@@ -86759,7 +86774,7 @@
  visibility="public"
 >
 </method>
-<method name="getMobileRxPkts"
+<method name="getMobileRxPackets"
  return="long"
  abstract="false"
  native="false"
@@ -86781,7 +86796,7 @@
  visibility="public"
 >
 </method>
-<method name="getMobileTxPkts"
+<method name="getMobileTxPackets"
  return="long"
  abstract="false"
  native="false"
@@ -86803,7 +86818,7 @@
  visibility="public"
 >
 </method>
-<method name="getTotalRxPkts"
+<method name="getTotalRxPackets"
  return="long"
  abstract="false"
  native="false"
@@ -86825,7 +86840,7 @@
  visibility="public"
 >
 </method>
-<method name="getTotalTxPkts"
+<method name="getTotalTxPackets"
  return="long"
  abstract="false"
  native="false"
@@ -159338,6 +159353,21 @@
 </parameter>
 </method>
 <method name="tokenize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="out" type="java.util.Collection&lt;android.text.util.Rfc822Token&gt;">
+</parameter>
+</method>
+<method name="tokenize"
  return="android.text.util.Rfc822Token[]"
  abstract="false"
  native="false"
@@ -186327,6 +186357,108 @@
 </parameter>
 </method>
 </class>
+<class name="ConsoleMessage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ConsoleMessage"
+ type="android.webkit.ConsoleMessage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="sourceId" type="java.lang.String">
+</parameter>
+<parameter name="lineNumber" type="int">
+</parameter>
+<parameter name="msgLevel" type="android.webkit.ConsoleMessage.MessageLevel">
+</parameter>
+</constructor>
+<method name="lineNumber"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="message"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="messageLevel"
+ return="android.webkit.ConsoleMessage.MessageLevel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="sourceId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="ConsoleMessage.MessageLevel"
+ extends="java.lang.Enum"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="valueOf"
+ return="android.webkit.ConsoleMessage.MessageLevel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="values"
+ return="android.webkit.ConsoleMessage.MessageLevel[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="CookieManager"
  extends="java.lang.Object"
  abstract="false"
@@ -187815,7 +187947,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="message" type="java.lang.String">
@@ -187825,6 +187957,19 @@
 <parameter name="sourceID" type="java.lang.String">
 </parameter>
 </method>
+<method name="onConsoleMessage"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="consoleMessage" type="android.webkit.ConsoleMessage">
+</parameter>
+</method>
 <method name="onCreateWindow"
  return="boolean"
  abstract="false"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 8c15d0b..acfbb07 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -315,7 +315,7 @@
                 for (RestoreSet s : sets) {
                     if (s.token == token) {
                         System.out.println("Scheduling restore: " + s.name);
-                        didRestore = (mRestore.performRestore(token, observer) == 0);
+                        didRestore = (mRestore.restoreAll(token, observer) == 0);
                         break;
                     }
                 }
diff --git a/common/java/com/android/common/Base64.java b/common/java/com/android/common/Base64.java
new file mode 100644
index 0000000..d108f44
--- /dev/null
+++ b/common/java/com/android/common/Base64.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+/**
+ * Utilities for encoding and decoding the Base64 encoding.  See RFCs
+ * 2045 and 3548.
+ */
+public class Base64 {
+    /**
+     * Encoder flag bit to indicate you want the padding '='
+     * characters at the end (if any) to be omitted.
+     */
+    public static final int NO_PADDING = 1;
+
+    /**
+     * Encoder flag bit to indicate you want all line terminators to
+     * be omitted (ie, the output will be on one long line).
+     */
+    public static final int NO_WRAP = 2;
+
+    /**
+     * Encoder flag bit to indicate you want lines to be ended with
+     * CRLF instead of just LF.
+     */
+    public static final int CRLF = 4;
+
+    /**
+     * Encoder/decoder flag bit to indicate using the "web safe"
+     * variant of Base64 (see RFC 3548 section 4) where '-' and '_'
+     * are used in place of '+' and '/'.
+     */
+    public static final int WEB_SAFE = 8;
+
+    /**
+     * Lookup table for turning bytes into their position in the
+     * Base64 alphabet.
+     */
+    private static final int DECODE[] = {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    };
+
+    /**
+     * Decode lookup table for the "web safe" variant (RFC 3548
+     * sec. 4) where - and _ replace + and /.
+     */
+    private static final int DECODE_WEBSAFE[] = {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
+        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    };
+
+    /** Non-data values in the DECODE arrays. */
+    private static final int SKIP = -1;
+    private static final int EQUALS = -2;
+
+    /**
+     * Decode the Base64-encoded data in input and return the data in
+     * a new byte array.
+     *
+     * The padding '=' characters at the end are considered optional, but
+     * if any are present, there must be the correct number of them.
+     *
+     * @param input the input String to decode, which is converted to
+     *               bytes using the default charset
+     * @param flags  controls certain features of the decoded output.
+     *               Passing 0 to decode standard Base64.
+     *
+     * @throws IllegalArgumentException if the input contains
+     * incorrect padding
+     */
+    public static byte[] decode(String str, int flags) {
+        return decode(str.getBytes(), flags);
+    }
+
+    /**
+     * Decode the Base64-encoded data in input and return the data in
+     * a new byte array.
+     *
+     * The padding '=' characters at the end are considered optional, but
+     * if any are present, there must be the correct number of them.
+     *
+     * @param input the input array to decode
+     * @param flags  controls certain features of the decoded output.
+     *               Passing 0 to decode standard Base64.
+     *
+     * @throws IllegalArgumentException if the input contains
+     * incorrect padding
+     */
+    public static byte[] decode(byte[] input, int flags) {
+        return decode(input, 0, input.length, flags);
+    }
+
+    /**
+     * Decode the Base64-encoded data in input and return the data in
+     * a new byte array.
+     *
+     * The padding '=' characters at the end are considered optional, but
+     * if any are present, there must be the correct number of them.
+     *
+     * @param input  the data to decode
+     * @param offset the position within the input array at which to start
+     * @param len    the number of bytes of input to decode
+     * @param flags  controls certain features of the decoded output.
+     *               Passing 0 to decode standard Base64.
+     *
+     * @throws IllegalArgumentException if the input contains
+     * incorrect padding
+     */
+    public static byte[] decode(byte[] input, int offset, int len, int flags) {
+        int p = offset;
+        // Allocate space for the most data the input could represent.
+        // (It could contain less if it contains whitespace, etc.)
+        byte[] output = new byte[len*3/4];
+        len += offset;
+        int op = 0;
+
+        final int[] decode = ((flags & WEB_SAFE) == 0) ?
+            DECODE : DECODE_WEBSAFE;
+
+        int state = 0;
+        int value = 0;
+
+        while (p < len) {
+
+            // Try the fast path:  we're starting a new tuple and the
+            // next four bytes of the input stream are all data
+            // bytes.  This corresponds to going through states
+            // 0-1-2-3-0.  We expect to use this method for most of
+            // the data.
+            //
+            // If any of the next four bytes of input are non-data
+            // (whitespace, etc.), value will end up negative.  (All
+            // the non-data values in decode are small negative
+            // numbers, so shifting any of them up and or'ing them
+            // together will result in a value with its top bit set.)
+            //
+            // You can remove this whole block and the output should
+            // be the same, just slower.
+            if (state == 0 && p+4 <= len &&
+                (value = ((decode[input[p] & 0xff] << 18) |
+                          (decode[input[p+1] & 0xff] << 12) |
+                          (decode[input[p+2] & 0xff] << 6) |
+                          (decode[input[p+3] & 0xff]))) >= 0) {
+                output[op+2] = (byte) value;
+                output[op+1] = (byte) (value >> 8);
+                output[op] = (byte) (value >> 16);
+                op += 3;
+                p += 4;
+                continue;
+            }
+
+            // The fast path isn't available -- either we've read a
+            // partial tuple, or the next four input bytes aren't all
+            // data, or whatever.  Fall back to the slower state
+            // machine implementation.
+            //
+            // States 0-3 are reading through the next input tuple.
+            // State 4 is having read one '=' and expecting exactly
+            // one more.
+            // State 5 is expecting no more data or padding characters
+            // in the input.
+
+            int d = decode[input[p++] & 0xff];
+
+            switch (state) {
+                case 0:
+                    if (d >= 0) {
+                        value = d;
+                        ++state;
+                    } else if (d != SKIP) {
+                        throw new IllegalArgumentException("bad base-64");
+                    }
+                    break;
+
+                case 1:
+                    if (d >= 0) {
+                        value = (value << 6) | d;
+                        ++state;
+                    } else if (d != SKIP) {
+                        throw new IllegalArgumentException("bad base-64");
+                    }
+                    break;
+
+                case 2:
+                    if (d >= 0) {
+                        value = (value << 6) | d;
+                        ++state;
+                    } else if (d == EQUALS) {
+                        // Emit the last (partial) output tuple;
+                        // expect exactly one more padding character.
+                        output[op++] = (byte) (value >> 4);
+                        state = 4;
+                    } else if (d != SKIP) {
+                        throw new IllegalArgumentException("bad base-64");
+                    }
+                    break;
+
+                case 3:
+                    if (d >= 0) {
+                        // Emit the output triple and return to state 0.
+                        value = (value << 6) | d;
+                        output[op+2] = (byte) value;
+                        output[op+1] = (byte) (value >> 8);
+                        output[op] = (byte) (value >> 16);
+                        op += 3;
+                        state = 0;
+                    } else if (d == EQUALS) {
+                        // Emit the last (partial) output tuple;
+                        // expect no further data or padding characters.
+                        output[op+1] = (byte) (value >> 2);
+                        output[op] = (byte) (value >> 10);
+                        op += 2;
+                        state = 5;
+                    } else if (d != SKIP) {
+                        throw new IllegalArgumentException("bad base-64");
+                    }
+                    break;
+
+                case 4:
+                    if (d == EQUALS) {
+                        ++state;
+                    } else if (d != SKIP) {
+                        throw new IllegalArgumentException("bad base-64");
+                    }
+                    break;
+
+                case 5:
+                    if (d != SKIP) {
+                        throw new IllegalArgumentException("bad base-64");
+                    }
+                    break;
+            }
+        }
+
+        // Done reading input.  Now figure out where we are left in
+        // the state machine and finish up.
+
+        switch (state) {
+            case 0:
+                // Output length is a multiple of three.  Fine.
+                break;
+            case 1:
+                // Read one extra input byte, which isn't enough to
+                // make another output byte.  Illegal.
+                throw new IllegalArgumentException("bad base-64");
+            case 2:
+                // Read two extra input bytes, enough to emit 1 more
+                // output byte.  Fine.
+                output[op++] = (byte) (value >> 4);
+                break;
+            case 3:
+                // Read three extra input bytes, enough to emit 2 more
+                // output bytes.  Fine.
+                output[op+1] = (byte) (value >> 2);
+                output[op] = (byte) (value >> 10);
+                op += 2;
+                break;
+            case 4:
+                // Read one padding '=' when we expected 2.  Illegal.
+                throw new IllegalArgumentException("bad base-64");
+            case 5:
+                // Read all the padding '='s we expected and no more.
+                // Fine.
+                break;
+        }
+
+        // Maybe we got lucky and allocated exactly enough output space.
+        if (op == output.length) {
+            return output;
+        }
+
+        // Need to shorten the array, so allocate a new one of the
+        // right size and copy.
+        byte[] temp = new byte[op];
+        System.arraycopy(output, 0, temp, 0, op);
+        return temp;
+    }
+
+    /**
+     * Emit a new line every this many output tuples.  Corresponds to
+     * a 76-character line length (the maximum allowable according to
+     * RFC 2045).
+     */
+    private static final int LINE_GROUPS = 19;
+
+    /**
+     * Lookup table for turning Base64 alphabet positions (6 bits)
+     * into output bytes.
+     */
+    private static final byte ENCODE[] = {
+        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+        'w', 'x', 'y', 'z', '0', '1', '2', '3',
+        '4', '5', '6', '7', '8', '9', '+', '/',
+    };
+
+    /**
+     * Lookup table for turning Base64 alphabet positions (6 bits)
+     * into output bytes.
+     */
+    private static final byte ENCODE_WEBSAFE[] = {
+        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+        'w', 'x', 'y', 'z', '0', '1', '2', '3',
+        '4', '5', '6', '7', '8', '9', '-', '_',
+    };
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * String with the result.
+     *
+     * @param input  the data to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing 0 results in output that adheres to RFC
+     *               2045.
+     */
+    public static String encodeString(byte[] input, int flags) {
+        return new String(encode(input, flags));
+    }
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * String with the result.
+     *
+     * @param input  the data to encode
+     * @param offset the position within the input array at which to
+     *               start
+     * @param len    the number of bytes of input to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing 0 results in output that adheres to RFC
+     *               2045.
+     */
+    public static String encodeString(byte[] input, int offset, int len, int flags) {
+        return new String(encode(input, offset, len, flags));
+    }
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * byte[] with the result.
+     *
+     * @param input  the data to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing 0 results in output that adheres to RFC
+     *               2045.
+     */
+    public static byte[] encode(byte[] input, int flags) {
+        return encode(input, 0, input.length, flags);
+    }
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * byte[] with the result.
+     *
+     * @param input  the data to encode
+     * @param offset the position within the input array at which to
+     *               start
+     * @param len    the number of bytes of input to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing 0 results in output that adheres to RFC
+     *               2045.
+     */
+    public static byte[] encode(byte[] input, int offset, int len, int flags) {
+        final boolean do_padding = (flags & NO_PADDING) == 0;
+        final boolean do_newline = (flags & NO_WRAP) == 0;
+        final boolean do_cr = (flags & CRLF) != 0;
+
+        final byte[] encode = ((flags & WEB_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
+
+        // Compute the exact length of the array we will produce.
+        int output_len = len / 3 * 4;
+
+        // Account for the tail of the data and the padding bytes, if any.
+        if (do_padding) {
+            if (len % 3 > 0) {
+                output_len += 4;
+            }
+        } else {
+            switch (len % 3) {
+                case 0: break;
+                case 1: output_len += 2; break;
+                case 2: output_len += 3; break;
+            }
+        }
+
+        // Account for the newlines, if any.
+        if (do_newline && len > 0) {
+            output_len += (((len-1) / (3 * LINE_GROUPS)) + 1) * (do_cr ? 2 : 1);
+        }
+
+        int op = 0;
+        byte[] output = new byte[output_len];
+
+        // The main loop, turning 3 input bytes into 4 output bytes on
+        // each iteration.
+        int count = do_newline ? LINE_GROUPS : -1;
+        int p = offset;
+        len += offset;
+        while (p+3 <= len) {
+            int v = ((input[p++] & 0xff) << 16) |
+                ((input[p++] & 0xff) << 8) |
+                (input[p++] & 0xff);
+            output[op++] = encode[(v >> 18) & 0x3f];
+            output[op++] = encode[(v >> 12) & 0x3f];
+            output[op++] = encode[(v >> 6) & 0x3f];
+            output[op++] = encode[v & 0x3f];
+            if (--count == 0) {
+                if (do_cr) output[op++] = '\r';
+                output[op++] = '\n';
+                count = LINE_GROUPS;
+            }
+        }
+
+        // Finish up the tail of the input.
+        if (p == len-1) {
+            int v = (input[p] & 0xff) << 4;
+            output[op++] = encode[(v >> 6) & 0x3f];
+            output[op++] = encode[v & 0x3f];
+            if (do_padding) {
+                output[op++] = '=';
+                output[op++] = '=';
+            }
+            if (do_newline) {
+                if (do_cr) output[op++] = '\r';
+                output[op++] = '\n';
+            }
+        } else if (p == len-2) {
+            int v = ((input[p] & 0xff) << 10) | ((input[p+1] & 0xff) << 2);
+            output[op++] = encode[(v >> 12) & 0x3f];
+            output[op++] = encode[(v >> 6) & 0x3f];
+            output[op++] = encode[v & 0x3f];
+            if (do_padding) {
+                output[op++] = '=';
+            }
+            if (do_newline) {
+                if (do_cr) output[op++] = '\r';
+                output[op++] = '\n';
+            }
+        } else if (do_newline && op > 0 && count != LINE_GROUPS) {
+            if (do_cr) output[op++] = '\r';
+            output[op++] = '\n';
+        }
+
+        assert op == output.length;
+        return output;
+    }
+}
diff --git a/common/tests/src/com/android/common/Base64Test.java b/common/tests/src/com/android/common/Base64Test.java
new file mode 100644
index 0000000..39c4b20
--- /dev/null
+++ b/common/tests/src/com/android/common/Base64Test.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.common;
+
+import junit.framework.TestCase;
+
+public class Base64Test extends TestCase {
+    private static final String TAG = "B64Test";
+
+    /** Decodes a string, returning a string. */
+    private String decodeString(String in) throws Exception {
+        byte[] out = Base64.decode(in, 0);
+        return new String(out);
+    }
+
+    /**
+     * Encodes the string 'in' using 'flags'.  Asserts that decoding
+     * gives the same string.  Returns the encoded string.
+     */
+    private String encodeString(String in, int flags) throws Exception {
+        String b64 = Base64.encodeString(in.getBytes(), flags);
+        String dec = decodeString(b64);
+        assertEquals(in, dec);
+        return b64;
+    }
+
+    /** Assert that decoding 'in' throws IllegalArgumentException. */
+    private void assertBad(String in) throws Exception {
+        try {
+            byte[] out = Base64.decode(in, 0);
+            fail("should have failed to decode");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+
+    /** Assert that actual equals the first len bytes of expected. */
+    private void assertEquals(byte[] expected, int len, byte[] actual) {
+        assertEquals(len, actual.length);
+        for (int i = 0; i < len; ++i) {
+            assertEquals(expected[i], actual[i]);
+        }
+    }
+
+    public void testDecodeExtraChars() throws Exception {
+        // padding 0
+        assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
+        assertBad("aGVsbG8sIHdvcmxk=");
+        assertBad("aGVsbG8sIHdvcmxk==");
+        assertBad("aGVsbG8sIHdvcmxk =");
+        assertBad("aGVsbG8sIHdvcmxk = = ");
+        assertEquals("hello, world", decodeString(" aGVs bG8s IHdv cmxk  "));
+        assertEquals("hello, world", decodeString(" aGV sbG8 sIHd vcmx k "));
+        assertEquals("hello, world", decodeString(" aG VsbG 8sIH dvcm xk "));
+        assertEquals("hello, world", decodeString(" a GVsb G8sI Hdvc mxk "));
+        assertEquals("hello, world", decodeString(" a G V s b G 8 s I H d v c m x k "));
+        assertEquals("hello, world", decodeString("_a*G_V*s_b*G_8*s_I*H_d*v_c*m_x*k_"));
+        assertEquals("hello, world", decodeString("aGVsbG8sIHdvcmxk"));
+
+        // padding 1
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPyE="));
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPyE"));
+        assertBad("aGVsbG8sIHdvcmxkPyE==");
+        assertBad("aGVsbG8sIHdvcmxkPyE ==");
+        assertBad("aGVsbG8sIHdvcmxkPyE = = ");
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E="));
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E"));
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E ="));
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E "));
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E = "));
+        assertEquals("hello, world?!", decodeString("aGVsbG8sIHdvcmxkPy E   "));
+
+        // padding 2
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkLg=="));
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkLg"));
+        assertBad("aGVsbG8sIHdvcmxkLg=");
+        assertBad("aGVsbG8sIHdvcmxkLg =");
+        assertBad("aGVsbG8sIHdvcmxkLg = ");
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g=="));
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g"));
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g =="));
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g "));
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g = = "));
+        assertEquals("hello, world.", decodeString("aGVsbG8sIHdvcmxkL g   "));
+    }
+
+    private static final byte[] BYTES = { (byte) 0xff, (byte) 0xee, (byte) 0xdd,
+                                          (byte) 0xcc, (byte) 0xbb, (byte) 0xaa,
+                                          (byte) 0x99, (byte) 0x88, (byte) 0x77 };
+
+    public void testBinaryDecode() throws Exception {
+        assertEquals(BYTES, 0, Base64.decode("", 0));
+        assertEquals(BYTES, 1, Base64.decode("/w==", 0));
+        assertEquals(BYTES, 2, Base64.decode("/+4=", 0));
+        assertEquals(BYTES, 3, Base64.decode("/+7d", 0));
+        assertEquals(BYTES, 4, Base64.decode("/+7dzA==", 0));
+        assertEquals(BYTES, 5, Base64.decode("/+7dzLs=", 0));
+        assertEquals(BYTES, 6, Base64.decode("/+7dzLuq", 0));
+        assertEquals(BYTES, 7, Base64.decode("/+7dzLuqmQ==", 0));
+        assertEquals(BYTES, 8, Base64.decode("/+7dzLuqmYg=", 0));
+    }
+
+    public void testWebSafe() throws Exception {
+        assertEquals(BYTES, 0, Base64.decode("", Base64.WEB_SAFE));
+        assertEquals(BYTES, 1, Base64.decode("_w==", Base64.WEB_SAFE));
+        assertEquals(BYTES, 2, Base64.decode("_-4=", Base64.WEB_SAFE));
+        assertEquals(BYTES, 3, Base64.decode("_-7d", Base64.WEB_SAFE));
+        assertEquals(BYTES, 4, Base64.decode("_-7dzA==", Base64.WEB_SAFE));
+        assertEquals(BYTES, 5, Base64.decode("_-7dzLs=", Base64.WEB_SAFE));
+        assertEquals(BYTES, 6, Base64.decode("_-7dzLuq", Base64.WEB_SAFE));
+        assertEquals(BYTES, 7, Base64.decode("_-7dzLuqmQ==", Base64.WEB_SAFE));
+        assertEquals(BYTES, 8, Base64.decode("_-7dzLuqmYg=", Base64.WEB_SAFE));
+
+        assertEquals("", Base64.encodeString(BYTES, 0, 0, Base64.WEB_SAFE));
+        assertEquals("_w==\n", Base64.encodeString(BYTES, 0, 1, Base64.WEB_SAFE));
+        assertEquals("_-4=\n", Base64.encodeString(BYTES, 0, 2, Base64.WEB_SAFE));
+        assertEquals("_-7d\n", Base64.encodeString(BYTES, 0, 3, Base64.WEB_SAFE));
+        assertEquals("_-7dzA==\n", Base64.encodeString(BYTES, 0, 4, Base64.WEB_SAFE));
+        assertEquals("_-7dzLs=\n", Base64.encodeString(BYTES, 0, 5, Base64.WEB_SAFE));
+        assertEquals("_-7dzLuq\n", Base64.encodeString(BYTES, 0, 6, Base64.WEB_SAFE));
+        assertEquals("_-7dzLuqmQ==\n", Base64.encodeString(BYTES, 0, 7, Base64.WEB_SAFE));
+        assertEquals("_-7dzLuqmYg=\n", Base64.encodeString(BYTES, 0, 8, Base64.WEB_SAFE));
+    }
+
+    public void testFlags() throws Exception {
+        assertEquals("YQ==\n",       encodeString("a", 0));
+        assertEquals("YQ==",         encodeString("a", Base64.NO_WRAP));
+        assertEquals("YQ\n",         encodeString("a", Base64.NO_PADDING));
+        assertEquals("YQ",           encodeString("a", Base64.NO_PADDING | Base64.NO_WRAP));
+        assertEquals("YQ==\r\n",     encodeString("a", Base64.CRLF));
+        assertEquals("YQ\r\n",       encodeString("a", Base64.CRLF | Base64.NO_PADDING));
+
+        assertEquals("YWI=\n",       encodeString("ab", 0));
+        assertEquals("YWI=",         encodeString("ab", Base64.NO_WRAP));
+        assertEquals("YWI\n",        encodeString("ab", Base64.NO_PADDING));
+        assertEquals("YWI",          encodeString("ab", Base64.NO_PADDING | Base64.NO_WRAP));
+        assertEquals("YWI=\r\n",     encodeString("ab", Base64.CRLF));
+        assertEquals("YWI\r\n",      encodeString("ab", Base64.CRLF | Base64.NO_PADDING));
+
+        assertEquals("YWJj\n",       encodeString("abc", 0));
+        assertEquals("YWJj",         encodeString("abc", Base64.NO_WRAP));
+        assertEquals("YWJj\n",       encodeString("abc", Base64.NO_PADDING));
+        assertEquals("YWJj",         encodeString("abc", Base64.NO_PADDING | Base64.NO_WRAP));
+        assertEquals("YWJj\r\n",     encodeString("abc", Base64.CRLF));
+        assertEquals("YWJj\r\n",     encodeString("abc", Base64.CRLF | Base64.NO_PADDING));
+
+        assertEquals("YWJjZA==\n",   encodeString("abcd", 0));
+        assertEquals("YWJjZA==",     encodeString("abcd", Base64.NO_WRAP));
+        assertEquals("YWJjZA\n",     encodeString("abcd", Base64.NO_PADDING));
+        assertEquals("YWJjZA",       encodeString("abcd", Base64.NO_PADDING | Base64.NO_WRAP));
+        assertEquals("YWJjZA==\r\n", encodeString("abcd", Base64.CRLF));
+        assertEquals("YWJjZA\r\n",   encodeString("abcd", Base64.CRLF | Base64.NO_PADDING));
+    }
+
+    public void testLineLength() throws Exception {
+        String in_56 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd";
+        String in_57 = in_56 + "e";
+        String in_58 = in_56 + "ef";
+        String in_59 = in_56 + "efg";
+        String in_60 = in_56 + "efgh";
+        String in_61 = in_56 + "efghi";
+
+        String prefix = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5emFi";
+        String out_56 = prefix + "Y2Q=\n";
+        String out_57 = prefix + "Y2Rl\n";
+        String out_58 = prefix + "Y2Rl\nZg==\n";
+        String out_59 = prefix + "Y2Rl\nZmc=\n";
+        String out_60 = prefix + "Y2Rl\nZmdo\n";
+        String out_61 = prefix + "Y2Rl\nZmdoaQ==\n";
+
+        // no newline for an empty input array.
+        assertEquals("", encodeString("", 0));
+
+        assertEquals(out_56, encodeString(in_56, 0));
+        assertEquals(out_57, encodeString(in_57, 0));
+        assertEquals(out_58, encodeString(in_58, 0));
+        assertEquals(out_59, encodeString(in_59, 0));
+        assertEquals(out_60, encodeString(in_60, 0));
+        assertEquals(out_61, encodeString(in_61, 0));
+
+        assertEquals(out_56.replaceAll("=", ""), encodeString(in_56, Base64.NO_PADDING));
+        assertEquals(out_57.replaceAll("=", ""), encodeString(in_57, Base64.NO_PADDING));
+        assertEquals(out_58.replaceAll("=", ""), encodeString(in_58, Base64.NO_PADDING));
+        assertEquals(out_59.replaceAll("=", ""), encodeString(in_59, Base64.NO_PADDING));
+        assertEquals(out_60.replaceAll("=", ""), encodeString(in_60, Base64.NO_PADDING));
+        assertEquals(out_61.replaceAll("=", ""), encodeString(in_61, Base64.NO_PADDING));
+
+        assertEquals(out_56.replaceAll("\n", ""), encodeString(in_56, Base64.NO_WRAP));
+        assertEquals(out_57.replaceAll("\n", ""), encodeString(in_57, Base64.NO_WRAP));
+        assertEquals(out_58.replaceAll("\n", ""), encodeString(in_58, Base64.NO_WRAP));
+        assertEquals(out_59.replaceAll("\n", ""), encodeString(in_59, Base64.NO_WRAP));
+        assertEquals(out_60.replaceAll("\n", ""), encodeString(in_60, Base64.NO_WRAP));
+        assertEquals(out_61.replaceAll("\n", ""), encodeString(in_61, Base64.NO_WRAP));
+    }
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10fef0d..56e44c8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -61,6 +61,7 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewManager;
+import android.view.ViewRoot;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -291,7 +292,7 @@
             if (mAppDir == null) {
                 if (mSystemContext == null) {
                     mSystemContext =
-                        ApplicationContext.createSystemContext(mainThread);
+                        ContextImpl.createSystemContext(mainThread);
                     mSystemContext.getResources().updateConfiguration(
                              mainThread.getConfiguration(),
                              mainThread.getDisplayMetricsLocked(false));
@@ -512,7 +513,7 @@
 
             try {
                 java.lang.ClassLoader cl = getClassLoader();
-                ApplicationContext appContext = new ApplicationContext();
+                ContextImpl appContext = new ContextImpl();
                 appContext.init(this, null, mActivityThread);
                 app = mActivityThread.mInstrumentation.newApplication(
                         cl, appClass, appContext);
@@ -1144,7 +1145,7 @@
         }
     }
 
-    private static ApplicationContext mSystemContext = null;
+    private static ContextImpl mSystemContext = null;
 
     private static final class ActivityRecord {
         IBinder token;
@@ -1307,7 +1308,7 @@
     }
 
     private static final class ContextCleanupInfo {
-        ApplicationContext context;
+        ContextImpl context;
         String what;
         String who;
     }
@@ -1628,7 +1629,7 @@
             long dalvikAllocated = dalvikMax - dalvikFree;
             long viewInstanceCount = ViewDebug.getViewInstanceCount();
             long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
-            long appContextInstanceCount = ApplicationContext.getInstanceCount();
+            long appContextInstanceCount = ContextImpl.getInstanceCount();
             long activityInstanceCount = Activity.getInstanceCount();
             int globalAssetCount = AssetManager.getGlobalAssetCount();
             int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
@@ -1813,6 +1814,7 @@
         public static final int DESTROY_BACKUP_AGENT    = 129;
         public static final int SUICIDE                 = 130;
         public static final int REMOVE_PROVIDER         = 131;
+        public static final int ENABLE_JIT              = 132;
         String codeToString(int code) {
             if (localLOGV) {
                 switch (code) {
@@ -1848,6 +1850,7 @@
                     case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
                     case SUICIDE: return "SUICIDE";
                     case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
+                    case ENABLE_JIT: return "ENABLE_JIT";
                 }
             }
             return "(unknown)";
@@ -1965,6 +1968,9 @@
                 case REMOVE_PROVIDER:
                     completeRemoveProvider((IContentProvider)msg.obj);
                     break;
+                case ENABLE_JIT:
+                    ensureJitEnabled();
+                    break;
             }
         }
 
@@ -2000,6 +2006,7 @@
                     prev.nextIdle = null;
                 } while (a != null);
             }
+            ensureJitEnabled();
             return false;
         }
     }
@@ -2064,6 +2071,7 @@
     String mInstrumentationAppPackage = null;
     String mInstrumentedAppDir = null;
     boolean mSystemThread = false;
+    boolean mJitEnabled = false;
 
     /**
      * Activities that are enqueued to be relaunched.  This list is accessed
@@ -2245,11 +2253,11 @@
         return mBoundApplication.processName;
     }
 
-    public ApplicationContext getSystemContext() {
+    public ContextImpl getSystemContext() {
         synchronized (this) {
             if (mSystemContext == null) {
-                ApplicationContext context =
-                    ApplicationContext.createSystemContext(this);
+                ContextImpl context =
+                    ContextImpl.createSystemContext(this);
                 PackageInfo info = new PackageInfo(this, "android", context, null);
                 context.init(info, null, this);
                 context.getResources().updateConfiguration(
@@ -2264,11 +2272,18 @@
 
     public void installSystemApplicationInfo(ApplicationInfo info) {
         synchronized (this) {
-            ApplicationContext context = getSystemContext();
+            ContextImpl context = getSystemContext();
             context.init(new PackageInfo(this, "android", context, info), null, this);
         }
     }
 
+    void ensureJitEnabled() {
+        if (!mJitEnabled) {
+            mJitEnabled = true;
+            dalvik.system.VMRuntime.getRuntime().startJitCompilation();
+        }
+    }
+    
     void scheduleGcIdler() {
         if (!mGcIdlerScheduled) {
             mGcIdlerScheduled = true;
@@ -2372,7 +2387,7 @@
         }
     }
 
-    final void scheduleContextCleanup(ApplicationContext context, String who,
+    final void scheduleContextCleanup(ContextImpl context, String who,
             String what) {
         ContextCleanupInfo cci = new ContextCleanupInfo();
         cci.context = context;
@@ -2431,7 +2446,7 @@
                     + ", dir=" + r.packageInfo.getAppDir());
 
             if (activity != null) {
-                ApplicationContext appContext = new ApplicationContext();
+                ContextImpl appContext = new ContextImpl();
                 appContext.init(r.packageInfo, r.token, this);
                 appContext.setOuterContext(activity);
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2628,7 +2643,7 @@
                 + ", comp=" + data.intent.getComponent().toShortString()
                 + ", dir=" + packageInfo.getAppDir());
 
-            ApplicationContext context = (ApplicationContext)app.getBaseContext();
+            ContextImpl context = (ContextImpl)app.getBaseContext();
             receiver.setOrderedHint(true);
             receiver.setResult(data.resultCode, data.resultData,
                 data.resultExtras);
@@ -2697,7 +2712,7 @@
                 if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent "
                         + data.appInfo.backupAgentName);
 
-                ApplicationContext context = new ApplicationContext();
+                ContextImpl context = new ContextImpl();
                 context.init(packageInfo, null, this);
                 context.setOuterContext(agent);
                 agent.attach(context);
@@ -2769,7 +2784,7 @@
         try {
             if (localLOGV) Log.v(TAG, "Creating service " + data.info.name);
 
-            ApplicationContext context = new ApplicationContext();
+            ContextImpl context = new ContextImpl();
             context.init(packageInfo, null, this);
 
             Application app = packageInfo.makeApplication(false, mInstrumentation);
@@ -2808,6 +2823,7 @@
                         ActivityManagerNative.getDefault().serviceDoneExecuting(
                                 data.token, 0, 0, 0);
                     }
+                    ensureJitEnabled();
                 } catch (RemoteException ex) {
                 }
             } catch (Exception e) {
@@ -2876,6 +2892,7 @@
                 } catch (RemoteException e) {
                     // nothing to do.
                 }
+                ensureJitEnabled();
             } catch (Exception e) {
                 if (!mInstrumentation.onException(s, e)) {
                     throw new RuntimeException(
@@ -2893,9 +2910,9 @@
                 if (localLOGV) Log.v(TAG, "Destroying service " + s);
                 s.onDestroy();
                 Context context = s.getBaseContext();
-                if (context instanceof ApplicationContext) {
+                if (context instanceof ContextImpl) {
                     final String who = s.getClassName();
-                    ((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
+                    ((ContextImpl) context).scheduleFinalCleanup(who, "Service");
                 }
                 try {
                     ActivityManagerNative.getDefault().serviceDoneExecuting(
@@ -3510,8 +3527,8 @@
             // ApplicationContext we need to have it tear down things
             // cleanly.
             Context c = r.activity.getBaseContext();
-            if (c instanceof ApplicationContext) {
-                ((ApplicationContext) c).scheduleFinalCleanup(
+            if (c instanceof ContextImpl) {
+                ((ContextImpl) c).scheduleFinalCleanup(
                         r.activity.getClass().getName(), "Activity");
             }
         }
@@ -3773,7 +3790,7 @@
 
             Resources.updateSystemConfiguration(config, dm);
 
-            ApplicationContext.ApplicationPackageManager.configurationChanged();
+            ContextImpl.ApplicationPackageManager.configurationChanged();
             //Log.i(TAG, "Configuration changed in " + currentPackageName());
             {
                 Iterator<WeakReference<Resources>> it =
@@ -3864,10 +3881,6 @@
         mBoundApplication = data;
         mConfiguration = new Configuration(data.config);
 
-        // We now rely on this being set by zygote.
-        //Process.setGid(data.appInfo.gid);
-        //Process.setUid(data.appInfo.uid);
-
         // send up app name; do this *before* waiting for debugger
         Process.setArgV0(data.processName);
         android.ddm.DdmHandleAppName.setAppName(data.processName);
@@ -3929,7 +3942,7 @@
         }
 
         if (data.instrumentationName != null) {
-            ApplicationContext appContext = new ApplicationContext();
+            ContextImpl appContext = new ContextImpl();
             appContext.init(data.info, null, this);
             InstrumentationInfo ii = null;
             try {
@@ -3954,7 +3967,7 @@
             instrApp.dataDir = ii.dataDir;
             PackageInfo pi = getPackageInfo(instrApp,
                     appContext.getClassLoader(), false, true);
-            ApplicationContext instrContext = new ApplicationContext();
+            ContextImpl instrContext = new ContextImpl();
             instrContext.init(pi, null, this);
 
             try {
@@ -3998,6 +4011,9 @@
         List<ProviderInfo> providers = data.providers;
         if (providers != null) {
             installContentProviders(app, providers);
+            // For process that contain content providers, we want to
+            // ensure that the JIT is enabled "at some point".
+            mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
         }
 
         try {
@@ -4303,6 +4319,11 @@
         sThreadLocal.set(this);
         mSystemThread = system;
         if (!system) {
+            ViewRoot.addFirstDrawHandler(new Runnable() {
+                public void run() {
+                    ensureJitEnabled();
+                }
+            });
             android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
             RuntimeInit.setApplicationObject(mAppThread.asBinder());
             IActivityManager mgr = ActivityManagerNative.getDefault();
@@ -4316,7 +4337,7 @@
             android.ddm.DdmHandleAppName.setAppName("system_process");
             try {
                 mInstrumentation = new Instrumentation();
-                ApplicationContext context = new ApplicationContext();
+                ContextImpl context = new ContextImpl();
                 context.init(getSystemContext().mPackageInfo, null, this);
                 Application app = Instrumentation.newApplication(Application.class, context);
                 mAllApplications.add(app);
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ContextImpl.java
similarity index 99%
rename from core/java/android/app/ApplicationContext.java
rename to core/java/android/app/ContextImpl.java
index cf6e0e7..5f89496 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ContextImpl.java
@@ -148,10 +148,10 @@
 }
 
 /**
- * Common implementation of Context API, which Activity and other application
- * classes inherit.
+ * Common implementation of Context API, which provides the base
+ * context object for Activity and other application components.
  */
-class ApplicationContext extends Context {
+class ContextImpl extends Context {
     private final static String TAG = "ApplicationContext";
     private final static boolean DEBUG = false;
     private final static boolean DEBUG_ICONS = false;
@@ -1328,13 +1328,13 @@
     public Context createPackageContext(String packageName, int flags)
         throws PackageManager.NameNotFoundException {
         if (packageName.equals("system") || packageName.equals("android")) {
-            return new ApplicationContext(mMainThread.getSystemContext());
+            return new ContextImpl(mMainThread.getSystemContext());
         }
 
         ActivityThread.PackageInfo pi =
             mMainThread.getPackageInfo(packageName, flags);
         if (pi != null) {
-            ApplicationContext c = new ApplicationContext();
+            ContextImpl c = new ContextImpl();
             c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
             c.init(pi, null, mMainThread, mResources);
             if (c.mResources != null) {
@@ -1371,13 +1371,13 @@
         return file;
     }
 
-    static ApplicationContext createSystemContext(ActivityThread mainThread) {
-        ApplicationContext context = new ApplicationContext();
+    static ContextImpl createSystemContext(ActivityThread mainThread) {
+        ContextImpl context = new ContextImpl();
         context.init(Resources.getSystem(), mainThread);
         return context;
     }
 
-    ApplicationContext() {
+    ContextImpl() {
         ++sInstanceCount;
         mOuterContext = this;
     }
@@ -1388,7 +1388,7 @@
      *
      * @param context Existing application context.
      */
-    public ApplicationContext(ApplicationContext context) {
+    public ContextImpl(ContextImpl context) {
         ++sInstanceCount;
         mPackageInfo = context.mPackageInfo;
         mResources = context.mResources;
@@ -2124,7 +2124,7 @@
             }
         }
 
-        ApplicationPackageManager(ApplicationContext context,
+        ApplicationPackageManager(ContextImpl context,
                 IPackageManager pm) {
             mContext = context;
             mPM = pm;
@@ -2656,7 +2656,7 @@
             }
         }
 
-        private final ApplicationContext mContext;
+        private final ContextImpl mContext;
         private final IPackageManager mPM;
 
         private static final Object sSync = new Object();
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index ec9f3b4..7fa5b08 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -60,6 +60,7 @@
 import android.widget.AutoCompleteTextView;
 import android.widget.Button;
 import android.widget.ImageButton;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
@@ -105,7 +106,7 @@
 
     // views & widgets
     private TextView mBadgeLabel;
-    private SearchSourceSelector mSourceSelector;
+    private ImageView mAppIcon;
     private SearchAutoComplete mSearchAutoComplete;
     private Button mGoButton;
     private ImageButton mVoiceButton;
@@ -209,8 +210,7 @@
         mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
         mSearchAutoComplete = (SearchAutoComplete)
                 findViewById(com.android.internal.R.id.search_src_text);
-        mSourceSelector = new SearchSourceSelector(
-                findViewById(com.android.internal.R.id.search_source_selector));
+        mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon);
         mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
         mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
         mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
@@ -609,16 +609,13 @@
     }
     
     private void updateSearchAppIcon() {
-        mSourceSelector.setSource(mSearchable.getSearchActivity());
-        mSourceSelector.setAppSearchData(mAppSearchData);
-
         // In Donut, we special-case the case of the browser to hide the app icon as if it were
         // global search, for extra space for url entry.
         //
         // TODO: Remove this special case once the issue has been reconciled in Eclair. 
         if (mGlobalSearchMode || isBrowserSearch()) {
-            mSourceSelector.setSourceIcon(null);
-            mSourceSelector.setVisibility(View.GONE);
+            mAppIcon.setImageResource(0);
+            mAppIcon.setVisibility(View.GONE);
             mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
                     mSearchPlate.getPaddingTop(),
                     mSearchPlate.getPaddingRight(),
@@ -634,8 +631,8 @@
                 icon = pm.getDefaultActivityIcon();
                 Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
             }
-            mSourceSelector.setSourceIcon(icon);
-            mSourceSelector.setVisibility(View.VISIBLE);
+            mAppIcon.setImageDrawable(icon);
+            mAppIcon.setVisibility(View.VISIBLE);
             mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL,
                     mSearchPlate.getPaddingTop(),
                     mSearchPlate.getPaddingRight(),
@@ -818,7 +815,6 @@
             if (!mSearchAutoComplete.isPerformingCompletion()) {
                 // The user changed the query, remember it.
                 mUserQuery = s == null ? "" : s.toString();
-                mSourceSelector.setQuery(mUserQuery);
             }
         }
 
@@ -1932,7 +1928,6 @@
              query = "";
          }
          mUserQuery = query;
-         mSourceSelector.setQuery(query);
          mSearchAutoComplete.setText(query);
          mSearchAutoComplete.setSelection(query.length());
      }
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 12a4347..3046a2c 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1604,15 +1604,6 @@
     public final static String SUGGEST_PARAMETER_LIMIT = "limit";
 
     /**
-     * Intent action for opening the search source selection activity.
-     * The intent may include these extra values:
-     * {@link #QUERY},
-     * {@link #APP_DATA}.
-     */
-    public static final String INTENT_ACTION_SELECT_SEARCH_SOURCE
-            = "android.intent.action.SELECT_SEARCH_SOURCE";
-
-    /**
      * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
      * the search dialog will switch to a different suggestion source when the
      * suggestion is clicked. 
diff --git a/core/java/android/app/SearchSourceSelector.java b/core/java/android/app/SearchSourceSelector.java
deleted file mode 100644
index fabf858..0000000
--- a/core/java/android/app/SearchSourceSelector.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import com.android.internal.R;
-
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageButton;
-
-import java.util.List;
-
-/**
- * Utilities for setting up the search source selector.
- *
- * This class has two copies:
- * android.app.SearchSourceSelector
- * com.android.quicksearchbox.ui.SearchSourceSelector
- *
- * They should keep the same look and feel as much as possible,
- * but only the intent details must absolutely stay in sync.
- *
- * @hide
- */
-public class SearchSourceSelector implements View.OnClickListener {
-
-    private static final String TAG = "SearchSourceSelector";
-
-    // TODO: This should be defined in android.provider.Applications,
-    // and have a less made-up value.
-    private static final String APPLICATION_TYPE = "application/vnd.android.application";
-
-    public static final int ICON_VIEW_ID = R.id.search_source_selector_icon;
-
-    private final View mView;
-
-    private final ImageButton mIconView;
-
-    private ComponentName mSource;
-
-    private Bundle mAppSearchData;
-
-    private String mQuery;
-
-    public SearchSourceSelector(View view) {
-        mView = view;
-        mIconView = (ImageButton) view.findViewById(ICON_VIEW_ID);
-        mIconView.setOnClickListener(this);
-    }
-
-    /**
-     * Sets the icon displayed in the search source selector.
-     */
-    public void setSourceIcon(Drawable icon) {
-        mIconView.setImageDrawable(icon);
-    }
-
-    /**
-     * Sets the current search source.
-     */
-    public void setSource(ComponentName source) {
-        mSource = source;
-    }
-
-    /**
-     * Sets the app-specific data that will be passed to the search activity if
-     * the user opens the source selector and chooses a source.
-     */
-    public void setAppSearchData(Bundle appSearchData) {
-        mAppSearchData = appSearchData;
-    }
-
-     /**
-      * Sets the initial query that will be passed to the search activity if
-      * the user opens the source selector and chooses a source.
-      */
-    public void setQuery(String query) {
-        mQuery = query;
-    }
-
-    public void setVisibility(int visibility) {
-        mView.setVisibility(visibility);
-    }
-
-    /**
-     * Creates an intent for opening the search source selector activity.
-     *
-     * @param source The current search source.
-     * @param query The initial query that will be passed to the search activity if
-     *        the user opens the source selector and chooses a source.
-     * @param appSearchData The app-specific data that will be passed to the search
-     *        activity if the user opens the source selector and chooses a source.
-     */
-    public static Intent createIntent(ComponentName source, String query, Bundle appSearchData) {
-        Intent intent = new Intent(SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_CLEAR_TOP
-                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-        Uri sourceUri = componentNameToUri(source);
-        if (sourceUri != null) {
-            intent.setDataAndType(sourceUri, APPLICATION_TYPE);
-        }
-        if (query != null) {
-            intent.putExtra(SearchManager.QUERY, query);
-        }
-        if (query != null) {
-            intent.putExtra(SearchManager.APP_DATA, appSearchData);
-        }
-        return intent;
-    }
-
-    /**
-     * Gets the search source from which the given
-     * {@link SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE} intent was sent.
-     */
-    public static ComponentName getSource(Intent intent) {
-        return uriToComponentName(intent.getData());
-    }
-
-    private static Uri componentNameToUri(ComponentName name) {
-        if (name == null) return null;
-        // TODO: This URI format is specificed in android.provider.Applications which is @hidden
-        return new Uri.Builder()
-                .scheme(ContentResolver.SCHEME_CONTENT)
-                .authority("applications")
-                .appendEncodedPath("applications")
-                .appendPath(name.getPackageName())
-                .appendPath(name.getClassName())
-                .build();
-    }
-
-    private static ComponentName uriToComponentName(Uri uri) {
-        if (uri == null) return null;
-        List<String> path = uri.getPathSegments();
-        if (path == null || path.size() != 3) return null;
-        String pkg = path.get(1);
-        String cls = path.get(2);
-        if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(cls)) return null;
-        return new ComponentName(pkg, cls);
-    }
-
-    public void onClick(View v) {
-        trigger();
-    }
-
-    private void trigger() {
-        try {
-            Intent intent = createIntent(mSource, mQuery, mAppSearchData);
-            intent.setSourceBounds(getOnScreenRect(mIconView));
-            mIconView.getContext().startActivity(intent);
-        } catch (ActivityNotFoundException ex) {
-            Log.e(TAG, "No source selector activity found", ex);
-        }
-    }
-
-    // TODO: This code is replicated in lots of places:
-    // - android.provider.ContactsContract.QuickContact.showQuickContact()
-    // - android.widget.RemoteViews.setOnClickPendingIntent()
-    // - com.android.launcher2.Launcher.onClick()
-    // - com.android.launcher.Launcher.onClick()
-    // - com.android.server.status.StatusBarService.Launcher.onClick()
-    private static Rect getOnScreenRect(View v) {
-        final float appScale = v.getResources().getCompatibilityInfo().applicationScale;
-        final int[] pos = new int[2];
-        v.getLocationOnScreen(pos);
-        final Rect rect = new Rect();
-        rect.left = (int) (pos[0] * appScale + 0.5f);
-        rect.top = (int) (pos[1] * appScale + 0.5f);
-        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
-        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
-        return rect;
-    }
-
-}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 51d7393..72ec616 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -31,12 +31,12 @@
 public class StatusBarManager {
     /**
      * Flag for {@link #disable} to make the status bar not expandable.  Unless you also
-     * set {@link #DISABLE_NOTIFICATIONS}, new notifications will continue to show.
+     * set {@link #DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show.
      */
     public static final int DISABLE_EXPAND = 0x00000001;
 
     /**
-     * Flag for {@link #disable} to hide notification icons and ticker text.
+     * Flag for {@link #disable} to hide notification icons and scrolling ticker text.
      */
     public static final int DISABLE_NOTIFICATION_ICONS = 0x00000002;
 
@@ -47,6 +47,12 @@
     public static final int DISABLE_NOTIFICATION_ALERTS = 0x00000004;
 
     /**
+     * Flag for {@link #disable} to hide only the scrolling ticker.  Note that
+     * {@link #DISABLE_NOTIFICATION_ICONS} implies {@link #DISABLE_NOTIFICATION_TICKER}.
+     */
+    public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008;
+
+    /**
      * Re-enable all of the status bar features that you've disabled.
      */
     public static final int DISABLE_NONE = 0x00000000;
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index fd40d98..bead395 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -40,6 +40,8 @@
      * Restore the given set onto the device, replacing the current data of any app
      * contained in the restore set with the data previously backed up.
      *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
      * @return Zero on success; nonzero on error.  The observer will only receive
      *   progress callbacks if this method returned zero.
      * @param token The token from {@link getAvailableRestoreSets()} corresponding to
@@ -47,7 +49,24 @@
      * @param observer If non-null, this binder points to an object that will receive
      *   progress callbacks during the restore operation.
      */
-    int performRestore(long token, IRestoreObserver observer);
+    int restoreAll(long token, IRestoreObserver observer);
+
+    /**
+     * Restore a single application from backup.  The data will be restored from the
+     * current backup dataset if the given package has stored data there, or from
+     * the dataset used during the last full device setup operation if the current
+     * backup dataset has no matching data.  If no backup data exists for this package
+     * in either source, a nonzero value will be returned.
+     *
+     * @return Zero on success; nonzero on error.  The observer will only receive
+     *   progress callbacks if this method returned zero.
+     * @param packageName The name of the package whose data to restore.  If this is
+     *   not the name of the caller's own package, then the android.permission.BACKUP
+     *   permission must be held.
+     * @param observer If non-null, this binder points to an object that will receive
+     *   progress callbacks during the restore operation.
+     */
+    int restorePackage(in String packageName, IRestoreObserver observer);
 
     /**
      * End this restore session.  After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/RestoreSession.java b/core/java/android/backup/RestoreSession.java
index 6b35fe8..d10831e 100644
--- a/core/java/android/backup/RestoreSession.java
+++ b/core/java/android/backup/RestoreSession.java
@@ -58,25 +58,56 @@
      * Restore the given set onto the device, replacing the current data of any app
      * contained in the restore set with the data previously backed up.
      *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
      * @return Zero on success; nonzero on error.  The observer will only receive
      *   progress callbacks if this method returned zero.
-     * @param token The token from {@link #getAvailableRestoreSets()} corresponding to
+     * @param token The token from {@link getAvailableRestoreSets()} corresponding to
      *   the restore set that should be used.
-     * @param observer If non-null, this argument points to an object that will receive
-     *   progress callbacks during the restore operation. These callbacks will occur
-     *   on the main thread of the application.
+     * @param observer If non-null, this binder points to an object that will receive
+     *   progress callbacks during the restore operation.
      */
-    public int performRestore(long token, RestoreObserver observer) {
+    public int restoreAll(long token, RestoreObserver observer) {
         int err = -1;
         if (mObserver != null) {
-            Log.d(TAG, "performRestore() called during active restore");
+            Log.d(TAG, "restoreAll() called during active restore");
             return -1;
         }
         mObserver = new RestoreObserverWrapper(mContext, observer);
         try {
-            err = mBinder.performRestore(token, mObserver);
+            err = mBinder.restoreAll(token, mObserver);
         } catch (RemoteException e) {
-            Log.d(TAG, "Can't contact server to perform restore");
+            Log.d(TAG, "Can't contact server to restore");
+        }
+        return err;
+    }
+
+    /**
+     * Restore a single application from backup.  The data will be restored from the
+     * current backup dataset if the given package has stored data there, or from
+     * the dataset used during the last full device setup operation if the current
+     * backup dataset has no matching data.  If no backup data exists for this package
+     * in either source, a nonzero value will be returned.
+     *
+     * @return Zero on success; nonzero on error.  The observer will only receive
+     *   progress callbacks if this method returned zero.
+     * @param packageName The name of the package whose data to restore.  If this is
+     *   not the name of the caller's own package, then the android.permission.BACKUP
+     *   permission must be held.
+     * @param observer If non-null, this binder points to an object that will receive
+     *   progress callbacks during the restore operation.
+     */
+    public int restorePackage(String packageName, RestoreObserver observer) {
+        int err = -1;
+        if (mObserver != null) {
+            Log.d(TAG, "restorePackage() called during active restore");
+            return -1;
+        }
+        mObserver = new RestoreObserverWrapper(mContext, observer);
+        try {
+            err = mBinder.restorePackage(packageName, mObserver);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Can't contact server to restore package");
         }
         return err;
     }
@@ -87,7 +118,7 @@
      *
      * <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
      *   even if {@link #getAvailableRestoreSets()} or
-     *   {@link #performRestore(long, RestoreObserver)} failed.
+     *   {@link #restorePackage(long, String, RestoreObserver)} failed.
      */
     public void endRestoreSession() {
         try {
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index d71344c..300cd28 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -293,7 +293,7 @@
             } catch (IOException e) {
                 Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e);
             } finally {
-                GestureUtilities.closeStream(inStream);
+                GestureUtils.closeStream(inStream);
             }
 
             if (gesture != null) {
@@ -322,8 +322,8 @@
         } catch (IOException e) {
             Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e);
         } finally {
-            GestureUtilities.closeStream(outStream);
-            GestureUtilities.closeStream(byteStream);
+            GestureUtils.closeStream(outStream);
+            GestureUtils.closeStream(byteStream);
         }
 
         if (result) {
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index 30ecf5a..b6c260f 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -638,7 +638,7 @@
 
                 if (mTotalLength > mGestureStrokeLengthThreshold) {
                     final OrientedBoundingBox box =
-                            GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer);
+                            GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);
 
                     float angle = Math.abs(box.orientation);
                     if (angle > 90) {
diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java
index 11a94d1..11b5044 100644
--- a/core/java/android/gesture/GestureStore.java
+++ b/core/java/android/gesture/GestureStore.java
@@ -264,7 +264,7 @@
 
             mChanged = false;
         } finally {
-            if (closeStream) GestureUtilities.closeStream(out);
+            if (closeStream) GestureUtils.closeStream(out);
         }
     }
 
@@ -299,7 +299,7 @@
                 Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms");
             }
         } finally {
-            if (closeStream) GestureUtilities.closeStream(in);
+            if (closeStream) GestureUtils.closeStream(in);
         }
     }
 
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index c3ddb28..1d0f0fe 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -159,15 +159,15 @@
      * @return the path
      */
     public Path toPath(float width, float height, int numSample) {
-        final float[] pts = GestureUtilities.temporalSampling(this, numSample);
+        final float[] pts = GestureUtils.temporalSampling(this, numSample);
         final RectF rect = boundingBox;
 
-        GestureUtilities.translate(pts, -rect.left, -rect.top);
+        GestureUtils.translate(pts, -rect.left, -rect.top);
         
         float sx = width / rect.width();
         float sy = height / rect.height();
         float scale = sx > sy ? sy : sx;
-        GestureUtilities.scale(pts, scale, scale);
+        GestureUtils.scale(pts, scale, scale);
 
         float mX = 0;
         float mY = 0;
@@ -241,6 +241,6 @@
      * @return OrientedBoundingBox
      */
     public OrientedBoundingBox computeOrientedBoundingBox() {
-        return GestureUtilities.computeOrientedBoundingBox(points);
+        return GestureUtils.computeOrientedBoundingBox(points);
     }
 }
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtils.java
similarity index 99%
rename from core/java/android/gesture/GestureUtilities.java
rename to core/java/android/gesture/GestureUtils.java
index 9d95ce4..dd221fc 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtils.java
@@ -36,12 +36,12 @@
  * distances between two gestures).
  * </ul>
  */
-public final class GestureUtilities {
+public final class GestureUtils {
   
     private static final float SCALING_THRESHOLD = 0.26f;
     private static final float NONUNIFORM_SCALE = (float) Math.sqrt(2);
     
-    private GestureUtilities() {
+    private GestureUtils() {
     }
 
     /**
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index bb0b340..02a6519 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -84,13 +84,13 @@
     }
 
     private static float[] spatialSampler(Gesture gesture) {
-        return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
+        return GestureUtils.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
     }
 
     private static float[] temporalSampler(int orientationType, Gesture gesture) {
-        float[] pts = GestureUtilities.temporalSampling(gesture.getStrokes().get(0),
+        float[] pts = GestureUtils.temporalSampling(gesture.getStrokes().get(0),
                 SEQUENCE_SAMPLE_SIZE);
-        float[] center = GestureUtilities.computeCentroid(pts);
+        float[] center = GestureUtils.computeCentroid(pts);
         float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
 
         float adjustment = -orientation;
@@ -104,8 +104,8 @@
             }
         }
 
-        GestureUtilities.translate(pts, -center[0], -center[1]);
-        GestureUtilities.rotate(pts, adjustment);
+        GestureUtils.translate(pts, -center[0], -center[1]);
+        GestureUtils.rotate(pts, adjustment);
 
         return pts;
     }
diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java
index 9987e69..7224ded 100644
--- a/core/java/android/gesture/InstanceLearner.java
+++ b/core/java/android/gesture/InstanceLearner.java
@@ -53,9 +53,9 @@
             }
             double distance;
             if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
-                distance = GestureUtilities.minimumCosineDistance(sample.vector, vector, orientationType);
+                distance = GestureUtils.minimumCosineDistance(sample.vector, vector, orientationType);
             } else {
-                distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
+                distance = GestureUtils.squaredEuclideanDistance(sample.vector, vector);
             }
             double weight;
             if (distance == 0) {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0c6bb1a..8eed9f7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -287,6 +287,7 @@
     static private class SensorThread {
 
         Thread mThread;
+        boolean mSensorsReady;
 
         SensorThread() {
             // this gets to the sensor module. We can have only one per process.
@@ -299,17 +300,28 @@
         }
 
         // must be called with sListeners lock
-        void startLocked(ISensorService service) {
+        boolean startLocked(ISensorService service) {
             try {
                 if (mThread == null) {
                     Bundle dataChannel = service.getDataChannel();
-                    mThread = new Thread(new SensorThreadRunnable(dataChannel),
-                            SensorThread.class.getName());
-                    mThread.start();
+                    if (dataChannel != null) {
+                        mSensorsReady = false;
+                        SensorThreadRunnable runnable = new SensorThreadRunnable(dataChannel);
+                        Thread thread = new Thread(runnable, SensorThread.class.getName());
+                        thread.start();
+                        synchronized (runnable) {
+                            while (mSensorsReady == false) {
+                                runnable.wait();
+                            }
+                        }
+                        mThread = thread;
+                    }
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException in startLocked: ", e);
+            } catch (InterruptedException e) {
             }
+            return mThread == null ? false : true;
         }
 
         private class SensorThreadRunnable implements Runnable {
@@ -319,13 +331,9 @@
             }
 
             private boolean open() {
-                if (mDataChannel == null) {
-                    Log.e(TAG, "mDataChannel == NULL, exiting");
-                    synchronized (sListeners) {
-                        mThread = null;
-                    }
-                    return false;
-                }
+                // NOTE: this cannot synchronize on sListeners, since
+                // it's held in the main thread at least until we
+                // return from here.
 
                 // this thread is guaranteed to be unique
                 Parcelable[] pfds = mDataChannel.getParcelableArray("fds");
@@ -370,6 +378,12 @@
                     return;
                 }
 
+                synchronized (this) {
+                    // we've open the driver, we're ready to open the sensors
+                    mSensorsReady = true;
+                    this.notify();
+                }
+
                 while (true) {
                     // wait for an event
                     final int sensor = sensors_data_poll(values, status, timestamp);
@@ -907,14 +921,18 @@
                 String name = sensor.getName();
                 int handle = sensor.getHandle();
                 if (l == null) {
+                    result = false;
                     l = new ListenerDelegate(listener, sensor, handler);
-                    result = mSensorService.enableSensor(l, name, handle, delay);
-                    if (result) {
-                        sListeners.add(l);
-                        sListeners.notify();
-                    }
+                    sListeners.add(l);
                     if (!sListeners.isEmpty()) {
-                        sSensorThread.startLocked(mSensorService);
+                        result = sSensorThread.startLocked(mSensorService);
+                        if (result) {
+                            result = mSensorService.enableSensor(l, name, handle, delay);
+                            if (!result) {
+                                // there was an error, remove the listeners
+                                sListeners.remove(l);
+                            }
+                        }
                     }
                 } else {
                     result = mSensorService.enableSensor(l, name, handle, delay);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30799ec..d435df5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -116,6 +116,24 @@
             "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
 
     /**
+     * Broadcast Action: A tetherable connection has come or gone
+     * TODO - finish the doc
+     * @hide
+     */
+    public static final String ACTION_TETHER_STATE_CHANGED =
+            "android.net.conn.TETHER_STATE_CHANGED";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_AVAILABLE_TETHER_COUNT = "availableCount";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_ACTIVE_TETHER_COUNT = "activeCount";
+
+    /**
      * The Default Mobile data connection.  When active, all data traffic
      * will use this connection by default.  Should not coexist with other
      * default connections.
@@ -338,4 +356,48 @@
         }
         mService = service;
     }
+
+    /**
+     * {@hide}
+     */
+    public String[] getTetherableIfaces() {
+        try {
+            return mService.getTetherableIfaces();
+        } catch (RemoteException e) {
+            return new String[0];
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public String[] getTetheredIfaces() {
+        try {
+            return mService.getTetheredIfaces();
+        } catch (RemoteException e) {
+            return new String[0];
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public boolean tether(String iface) {
+        try {
+            return mService.tether(iface);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public boolean untether(String iface) {
+        try {
+            return mService.untether(iface);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 9f59cce..caa3f2b 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -50,4 +50,12 @@
     boolean getBackgroundDataSetting();
 
     void setBackgroundDataSetting(boolean allowBackgroundData);
+
+    boolean tether(String iface);
+
+    boolean untether(String iface);
+
+    String[] getTetherableIfaces();
+
+    String[] getTetheredIfaces();
 }
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 62e9f1f..ad8e2bf 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -54,7 +54,7 @@
      * @return number of packets.  If the statistics are not supported by this device,
      * {@link #UNSUPPORTED} will be returned.
      */
-    public static long getMobileTxPkts() {
+    public static long getMobileTxPackets() {
         return getMobileStat(MOBILE_TX_PACKETS);
     }
 
@@ -64,7 +64,7 @@
      * @return number of packets.  If the statistics are not supported by this device,
      * {@link #UNSUPPORTED} will be returned.
      */
-    public static long getMobileRxPkts() {
+    public static long getMobileRxPackets() {
         return getMobileStat(MOBILE_RX_PACKETS);
     }
 
@@ -94,7 +94,7 @@
      * @return the number of packets.  If the statistics are not supported by this device,
      * {@link #UNSUPPORTED} will be returned.
      */
-    public static long getTotalTxPkts() {
+    public static long getTotalTxPackets() {
         return getTotalStat("tx_packets");
     }
 
@@ -104,7 +104,7 @@
      * @return number of packets.  If the statistics are not supported by this device,
      * {@link #UNSUPPORTED} will be returned.
      */
-    public static long getTotalRxPkts() {
+    public static long getTotalRxPackets() {
         return getTotalStat("rx_packets");
     }
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e4ec098..f48f45f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -140,7 +140,8 @@
      * Attaches a PPP server daemon to the specified TTY with the specified
      * local/remote addresses.
      */
-    void attachPppd(String tty, String localAddr, String remoteAddr);
+    void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
+            String dns2Addr);
 
     /**
      * Detaches a PPP server daemon from the specified TTY.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7128005..bacaf43 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2944,6 +2944,13 @@
         public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
 
         /**
+         * Whether or not a notification is displayed when a Tetherable interface is detected.
+         * (0 = false, 1 = true)
+         * @hide
+         */
+        public static final String TETHER_NOTIFY = "tether_notify";
+
+        /**
          * If nonzero, ANRs in invisible background processes bring up a dialog.
          * Otherwise, the process will be silently killed.
          * @hide
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index cb39f7d..9d8bfd9 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -41,7 +41,6 @@
      * It will try to be tolerant of broken syntax instead of
      * returning an error.
      *
-     * @hide
      */
     public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
         StringBuilder name = new StringBuilder();
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 094b7dd..07b2d1c 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -98,6 +98,9 @@
 
     static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
 
+    static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
+    static boolean sFirstDrawComplete = false;
+    
     private static int sDrawTime;
 
     long mLastTrackballTime = 0;
@@ -254,6 +257,14 @@
         return sInstanceCount;
     }
 
+    public static void addFirstDrawHandler(Runnable callback) {
+        synchronized (sFirstDrawHandlers) {
+            if (!sFirstDrawComplete) {
+                sFirstDrawHandlers.add(callback);
+            }
+        }
+    }
+    
     // FIXME for perf testing only
     private boolean mProfile = false;
 
@@ -1189,6 +1200,15 @@
             return;
         }
 
+        if (!sFirstDrawComplete) {
+            synchronized (sFirstDrawHandlers) {
+                sFirstDrawComplete = true;
+                for (int i=0; i<sFirstDrawHandlers.size(); i++) {
+                    post(sFirstDrawHandlers.get(i));
+                }
+            }
+        }
+        
         scrollToRectOrFocus(null, false);
 
         if (mAttachInfo.mViewScrollChanged) {
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
index de8f888..aeb537c 100644
--- a/core/java/android/webkit/CacheLoader.java
+++ b/core/java/android/webkit/CacheLoader.java
@@ -43,7 +43,7 @@
     protected boolean setupStreamAndSendStatus() {
         mDataStream = mCacheResult.inStream;
         mContentLength = mCacheResult.contentLength;
-        mHandler.status(1, 1, mCacheResult.httpStatusCode, "OK");
+        mLoadListener.status(1, 1, mCacheResult.httpStatusCode, "OK");
         return true;
     }
 
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 6790c5d..61a2d2ef 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -651,7 +651,42 @@
                 String message = msg.getData().getString("message");
                 String sourceID = msg.getData().getString("sourceID");
                 int lineNumber = msg.getData().getInt("lineNumber");
-                mWebChromeClient.onConsoleMessage(message, lineNumber, sourceID);
+                int msgLevel = msg.getData().getInt("msgLevel");
+                int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length;
+                // Sanity bounds check as we'll index an array with msgLevel
+                if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) {
+                    msgLevel = 0;
+                }
+
+                ConsoleMessage.MessageLevel messageLevel =
+                        ConsoleMessage.MessageLevel.values()[msgLevel];
+
+                if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
+                        lineNumber, messageLevel))) {
+                    // If false was returned the user did not provide their own console function so
+                    //  we should output some default messages to the system log.
+                    String logTag = "Web Console";
+                    String logMessage = message + " at " + sourceID + ":" + lineNumber;
+
+                    switch (messageLevel) {
+                        case TIP:
+                            Log.v(logTag, logMessage);
+                            break;
+                        case LOG:
+                            Log.i(logTag, logMessage);
+                            break;
+                        case WARNING:
+                            Log.w(logTag, logMessage);
+                            break;
+                        case ERROR:
+                            Log.e(logTag, logMessage);
+                            break;
+                        case DEBUG:
+                            Log.d(logTag, logMessage);
+                            break;
+                    }
+                }
+
                 break;
 
             case GET_VISITED_HISTORY:
@@ -1286,8 +1321,10 @@
      *     occurred.
      * @param sourceID The filename of the source file in which the error
      *     occurred.
+     * @param msgLevel The message level, corresponding to the MessageLevel enum in
+     *     WebCore/page/Console.h
      */
-    public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+    public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) {
         if (mWebChromeClient == null) {
             return;
         }
@@ -1296,6 +1333,7 @@
         msg.getData().putString("message", message);
         msg.getData().putString("sourceID", sourceID);
         msg.getData().putInt("lineNumber", lineNumber);
+        msg.getData().putInt("msgLevel", msgLevel);
         sendMessage(msg);
     }
 
diff --git a/core/java/android/webkit/ConsoleMessage.java b/core/java/android/webkit/ConsoleMessage.java
new file mode 100644
index 0000000..a9c351a
--- /dev/null
+++ b/core/java/android/webkit/ConsoleMessage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * Public class representing a JavaScript console message from WebCore. This could be a issued
+ * by a call to one of the <code>console</code> logging functions (e.g.
+ * <code>console.log('...')</code>) or a JavaScript error on the  page. To receive notifications
+ * of these messages, override the
+ * {@link WebChromeClient#onConsoleMessage(ConsoleMessage)} function.
+ */
+public class ConsoleMessage {
+
+    // This must be kept in sync with the WebCore enum in WebCore/page/Console.h
+    public enum MessageLevel {
+        TIP,
+        LOG,
+        WARNING,
+        ERROR,
+        DEBUG
+    };
+
+    private MessageLevel mLevel;
+    private String mMessage;
+    private String mSourceId;
+    private int mLineNumber;
+
+    public ConsoleMessage(String message, String sourceId, int lineNumber, MessageLevel msgLevel) {
+        mMessage = message;
+        mSourceId = sourceId;
+        mLineNumber = lineNumber;
+        mLevel = msgLevel;
+    }
+
+    public MessageLevel messageLevel() {
+        return mLevel;
+    }
+
+    public String message() {
+        return mMessage;
+    }
+
+    public String sourceId() {
+        return mSourceId;
+    }
+
+    public int lineNumber() {
+        return mLineNumber;
+    }
+};
diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java
index 5eb54b0..d13210aa 100644
--- a/core/java/android/webkit/ContentLoader.java
+++ b/core/java/android/webkit/ContentLoader.java
@@ -16,14 +16,10 @@
 
 package android.webkit;
 
-import android.content.Context;
 import android.net.http.EventHandler;
 import android.net.http.Headers;
 import android.net.Uri;
 
-import java.io.File;
-import java.io.FileInputStream;
-
 /**
  * This class is a concrete implementation of StreamLoader that loads
  * "content:" URIs
@@ -68,7 +64,7 @@
     protected boolean setupStreamAndSendStatus() {
         Uri uri = Uri.parse(mUrl);
         if (uri == null) {
-            mHandler.error(
+            mLoadListener.error(
                     EventHandler.FILE_NOT_FOUND_ERROR,
                     mContext.getString(
                             com.android.internal.R.string.httpErrorBadUrl) +
@@ -78,18 +74,14 @@
 
         try {
             mDataStream = mContext.getContentResolver().openInputStream(uri);
-            mHandler.status(1, 1, 200, "OK");
+            mLoadListener.status(1, 1, 200, "OK");
         } catch (java.io.FileNotFoundException ex) {
-            mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
-            return false;
-
-        } catch (java.io.IOException ex) {
-            mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+            mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
             return false;
         } catch (RuntimeException ex) {
             // readExceptionWithFileNotFoundExceptionFromParcel in DatabaseUtils
             // can throw a serial of RuntimeException. Catch them all here.
-            mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+            mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
             return false;
         }
         return true;
@@ -103,16 +95,4 @@
         // content can change, we don't want WebKit to cache it
         headers.setCacheControl("no-store, no-cache");
     }
-
-    /**
-     * Construct a ContentLoader and instruct it to start loading.
-     *
-     * @param url "content:" url pointing to content to be loaded
-     * @param loadListener LoadListener to pass the content to
-     */
-    public static void requestUrl(String url, LoadListener loadListener) {
-        ContentLoader loader = new ContentLoader(url, loadListener);
-        loader.load();
-    }
-
 }
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
index 2a68a5d..235dc5be 100644
--- a/core/java/android/webkit/DataLoader.java
+++ b/core/java/android/webkit/DataLoader.java
@@ -62,10 +62,10 @@
     @Override
     protected boolean setupStreamAndSendStatus() {
         if (mDataStream != null) {
-            mHandler.status(1, 1, 200, "OK");
+            mLoadListener.status(1, 1, 200, "OK");
             return true;
         } else {
-            mHandler.error(EventHandler.ERROR,
+            mLoadListener.error(EventHandler.ERROR,
                     mContext.getString(R.string.httpError));
             return false;
         }
@@ -74,16 +74,4 @@
     @Override
     protected void buildHeaders(android.net.http.Headers h) {
     }
-
-    /**
-     * Construct a DataLoader and instruct it to start loading.
-     *
-     * @param url data: URL string optionally containing a mimetype
-     * @param loadListener LoadListener to pass the content to
-     */
-    public static void requestUrl(String url, LoadListener loadListener) {
-        DataLoader loader = new DataLoader(url, loadListener);
-        loader.load();
-    }
-
 }
diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java
index e856cde..e21e9ef 100644
--- a/core/java/android/webkit/FileLoader.java
+++ b/core/java/android/webkit/FileLoader.java
@@ -18,11 +18,9 @@
 
 import com.android.internal.R;
 
-import android.content.Context;
 import android.content.res.AssetManager;
 import android.net.http.EventHandler;
 import android.net.http.Headers;
-import android.os.Environment;
 import android.util.Log;
 import android.util.TypedValue;
 
@@ -111,7 +109,7 @@
                 // "<package>.R$drawable"
                 if (mPath == null || mPath.length() == 0) {
                     Log.e(LOGTAG, "Need a path to resolve the res file");
-                    mHandler.error(EventHandler.FILE_ERROR, mContext
+                    mLoadListener.error(EventHandler.FILE_ERROR, mContext
                             .getString(R.string.httpErrorFileNotFound));
                     return false;
 
@@ -120,7 +118,7 @@
                 int dot = mPath.indexOf('.', slash);
                 if (slash == -1 || dot == -1) {
                     Log.e(LOGTAG, "Incorrect res path: " + mPath);
-                    mHandler.error(EventHandler.FILE_ERROR, mContext
+                    mLoadListener.error(EventHandler.FILE_ERROR, mContext
                             .getString(R.string.httpErrorFileNotFound));
                     return false;
                 }
@@ -157,13 +155,13 @@
                     errorMsg = "Caught IllegalAccessException: " + e;
                 }
                 if (errorMsg != null) {
-                    mHandler.error(EventHandler.FILE_ERROR, mContext
+                    mLoadListener.error(EventHandler.FILE_ERROR, mContext
                             .getString(R.string.httpErrorFileNotFound));
                     return false;
                 }
             } else {
                 if (!mAllowFileAccess) {
-                    mHandler.error(EventHandler.FILE_ERROR,
+                    mLoadListener.error(EventHandler.FILE_ERROR,
                             mContext.getString(R.string.httpErrorFileNotFound));
                     return false;
                 }
@@ -171,14 +169,14 @@
                 mDataStream = new FileInputStream(mPath);
                 mContentLength = (new File(mPath)).length();
             }
-            mHandler.status(1, 1, 200, "OK");
+            mLoadListener.status(1, 1, 200, "OK");
 
         } catch (java.io.FileNotFoundException ex) {
-            mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
+            mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
             return false;
 
         } catch (java.io.IOException ex) {
-            mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+            mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
             return false;
         }
         return true;
@@ -188,22 +186,4 @@
     protected void buildHeaders(Headers headers) {
         // do nothing.
     }
-
-
-    /**
-     * Construct a FileLoader and instruct it to start loading.
-     *
-     * @param url Full file url pointing to content to be loaded
-     * @param loadListener LoadListener to pass the content to
-     * @param asset true if url points to an asset.
-     * @param allowFileAccess true if this FileLoader can load files from the
-     *                        file system.
-     */
-    public static void requestUrl(String url, LoadListener loadListener,
-            int type, boolean allowFileAccess) {
-        FileLoader loader = new FileLoader(url, loadListener, type,
-                allowFileAccess);
-        loader.load();
-    }
-
 }
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 58eca38..b13c405 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -141,24 +141,29 @@
             return true;
         }
         if (URLUtil.isAssetUrl(url)) {
-            FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_ASSET,
-                    true);
+            // load asset in a separate thread as it involves IO
+            new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, true)
+                    .enqueue();
             return true;
         } else if (URLUtil.isResourceUrl(url)) {
-            FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_RES,
-                    true);
+            // load resource in a separate thread as it involves IO
+            new FileLoader(url, loadListener, FileLoader.TYPE_RES, true)
+                    .enqueue();
             return true;
         } else if (URLUtil.isFileUrl(url)) {
-            FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_FILE,
-                    settings.getAllowFileAccess());
+            // load file in a separate thread as it involves IO
+            new FileLoader(url, loadListener, FileLoader.TYPE_FILE, settings
+                    .getAllowFileAccess()).enqueue();
             return true;
         } else if (URLUtil.isContentUrl(url)) {
             // Send the raw url to the ContentLoader because it will do a
-            // permission check and the url has to match..
-            ContentLoader.requestUrl(loadListener.url(), loadListener);
+            // permission check and the url has to match.
+            // load content in a separate thread as it involves IO
+            new ContentLoader(loadListener.url(), loadListener).enqueue();
             return true;
         } else if (URLUtil.isDataUrl(url)) {
-            DataLoader.requestUrl(url, loadListener);
+            // load data in the current thread to reduce the latency
+            new DataLoader(url, loadListener).load();
             return true;
         } else if (URLUtil.isAboutUrl(url)) {
             loadListener.data(mAboutBlank.getBytes(), mAboutBlank.length());
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index ce26268..4c32997 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -20,12 +20,13 @@
 import android.net.http.EventHandler;
 import android.net.http.Headers;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Message;
 
 import java.io.IOException;
 import java.io.InputStream;
 
-
 /**
  * This abstract class is used for all content loaders that rely on streaming
  * content into the rendering engine loading framework.
@@ -44,9 +45,7 @@
  * that indicates the content should not be cached.
  *
  */
-abstract class StreamLoader extends Handler {
-
-    public static final String NO_STORE       = "no-store";
+abstract class StreamLoader implements Handler.Callback {
 
     private static final int MSG_STATUS = 100;  // Send status to loader
     private static final int MSG_HEADERS = 101; // Send headers to loader
@@ -54,11 +53,19 @@
     private static final int MSG_END = 103;  // Send endData to loader
 
     protected final Context mContext;
-    protected final LoadListener mHandler; // loader class
+    protected final LoadListener mLoadListener; // loader class
     protected InputStream mDataStream; // stream to read data from
     protected long mContentLength; // content length of data
     private byte [] mData; // buffer to pass data to loader with.
 
+    // Handler which will be initialized in the thread where load() is called.
+    private Handler mHandler;
+
+    // Handler which will be used to load StreamLoader in a separate thread
+    private static StreamQueueHandler sStreamQueueHandler;
+
+    private static final Object sStreamQueueLock = new Object();
+
     /**
      * Constructor. Although this class calls the LoadListener, it only calls
      * the EventHandler Interface methods. LoadListener concrete class is used
@@ -67,13 +74,13 @@
      * @param loadlistener The LoadListener to call with the data.
      */
     StreamLoader(LoadListener loadlistener) {
-        mHandler = loadlistener;
+        mLoadListener = loadlistener;
         mContext = loadlistener.getContext();
     }
 
     /**
      * This method is called when the derived class should setup mDataStream,
-     * and call mHandler.status() to indicate that the load can occur. If it
+     * and call mLoadListener.status() to indicate that the load can occur. If it
      * fails to setup, it should still call status() with the error code.
      *
      * @return true if stream was successfully setup
@@ -89,15 +96,40 @@
      */
     abstract protected void buildHeaders(Headers headers);
 
+    /**
+     * Calling this method to load this StreamLoader in a separate
+     * "StreamLoadingThread".
+     */
+    final void enqueue() {
+        synchronized (sStreamQueueLock) {
+            if (sStreamQueueHandler == null) {
+                HandlerThread thread = new HandlerThread(
+                        StreamQueueHandler.THREAD_NAME,
+                        android.os.Process.THREAD_PRIORITY_DEFAULT +
+                        android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+                thread.start();
+                sStreamQueueHandler = new StreamQueueHandler(thread.getLooper());
+            }
+        }
+
+        sStreamQueueHandler.obtainMessage(StreamQueueHandler.MSG_ADD_LOADER,
+                this).sendToTarget();
+    }
 
     /**
      * Calling this method starts the load of the content for this StreamLoader.
-     * This method simply posts a message to send the status and returns
-     * immediately.
+     * This method simply creates a Handler in the current thread and posts a
+     * message to send the status and returns immediately.
      */
-    public void load() {
-        if (!mHandler.isSynchronous()) {
-            sendMessage(obtainMessage(MSG_STATUS));
+    final void load() {
+        synchronized (this) {
+            if (mHandler == null) {
+                mHandler = new Handler(this);
+            }
+        }
+
+        if (!mLoadListener.isSynchronous()) {
+            mHandler.sendEmptyMessage(MSG_STATUS);
         } else {
             // Load the stream synchronously.
             if (setupStreamAndSendStatus()) {
@@ -105,23 +137,20 @@
                 // to pass data to the loader
                 mData = new byte[8192];
                 sendHeaders();
-                while (!sendData() && !mHandler.cancelled());
+                while (!sendData() && !mLoadListener.cancelled());
                 closeStreamAndSendEndData();
-                mHandler.loadSynchronousMessages();
+                mLoadListener.loadSynchronousMessages();
             }
         }
     }
 
-    /* (non-Javadoc)
-     * @see android.os.Handler#handleMessage(android.os.Message)
-     */
-    public void handleMessage(Message msg) {
-        if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
+    public boolean handleMessage(Message msg) {
+        if (mLoadListener.isSynchronous()) {
             throw new AssertionError();
         }
-        if (mHandler.cancelled()) {
+        if (mLoadListener.cancelled()) {
             closeStreamAndSendEndData();
-            return;
+            return true;
         }
         switch(msg.what) {
             case MSG_STATUS:
@@ -129,27 +158,27 @@
                     // We were able to open the stream, create the array
                     // to pass data to the loader
                     mData = new byte[8192];
-                    sendMessage(obtainMessage(MSG_HEADERS));
+                    mHandler.sendEmptyMessage(MSG_HEADERS);
                 }
                 break;
             case MSG_HEADERS:
                 sendHeaders();
-                sendMessage(obtainMessage(MSG_DATA));
+                mHandler.sendEmptyMessage(MSG_DATA);
                 break;
             case MSG_DATA:
                 if (sendData()) {
-                    sendMessage(obtainMessage(MSG_END));
+                    mHandler.sendEmptyMessage(MSG_END);
                 } else {
-                    sendMessage(obtainMessage(MSG_DATA));
+                    mHandler.sendEmptyMessage(MSG_DATA);
                 }
                 break;
             case MSG_END:
                 closeStreamAndSendEndData();
                 break;
             default:
-                super.handleMessage(msg);
-                break;
+                return false;
         }
+        return true;
     }
 
     /**
@@ -161,7 +190,7 @@
             headers.setContentLength(mContentLength);
         }
         buildHeaders(headers);
-        mHandler.headers(headers);
+        mLoadListener.headers(headers);
     }
 
     /**
@@ -176,12 +205,11 @@
             try {
                 int amount = mDataStream.read(mData);
                 if (amount > 0) {
-                    mHandler.data(mData, amount);
+                    mLoadListener.data(mData, amount);
                     return false;
                 }
             } catch (IOException ex) {
-                mHandler.error(EventHandler.FILE_ERROR,
-                               ex.getMessage());
+                mLoadListener.error(EventHandler.FILE_ERROR, ex.getMessage());
             }
         }
         return true;
@@ -198,7 +226,24 @@
                 // ignore.
             }
         }
-        mHandler.endData();
+        mLoadListener.endData();
     }
 
+    private static class StreamQueueHandler extends Handler {
+        private static final String THREAD_NAME = "StreamLoadingThread";
+
+        private static final int MSG_ADD_LOADER = 101;
+
+        StreamQueueHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_ADD_LOADER) {
+                StreamLoader loader = (StreamLoader) msg.obj;
+                loader.load();
+            }
+        }
+    }
 }
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index f40b55c..1d5aac7 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -262,8 +262,24 @@
      * @param message The error message to report.
      * @param lineNumber The line number of the error.
      * @param sourceID The name of the source file that caused the error.
+     * @deprecated Use {@link #onConsoleMessage(ConsoleMessage) onConsoleMessage(ConsoleMessage)}
+     *      instead.
      */
-    public void onConsoleMessage(String message, int lineNumber, String sourceID) {}
+    @Deprecated
+    public void onConsoleMessage(String message, int lineNumber, String sourceID) { }
+
+    /**
+     * Report a JavaScript console message to the host application. The ChromeClient
+     * should override this to process the log message as they see fit.
+     * @param consoleMessage Object containing details of the console message.
+     * @return true if the message is handled by the client.
+     */
+    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+        // Call the old version of this function for backwards compatability.
+        onConsoleMessage(consoleMessage.message(), consoleMessage.lineNumber(),
+                consoleMessage.sourceId());
+        return false;
+    }
 
     /**
      * When not playing, video elements are represented by a 'poster' image. The
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 6e45e39..9c91919 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -260,9 +260,12 @@
      * @param message The message to add
      * @param lineNumber the line on which the error occurred
      * @param sourceID the filename of the source that caused the error.
+     * @param msgLevel the log level of this message. This is a value casted to int
+     *     from WebCore::MessageLevel in WebCore/page/Console.h.
      */
-    protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
-        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+    protected void addMessageToConsole(String message, int lineNumber, String sourceID,
+            int msgLevel) {
+        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel);
     }
 
     /**
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
new file mode 100644
index 0000000..2b93dbc
--- /dev/null
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IMountService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.widget.Toast;
+import android.util.Log;
+
+/**
+ * This activity is shown to the user for him/her to connect/disconnect a Tether
+ * connection.  It will display notification when a suitable connection is made
+ * to allow the tether to be setup.  A second notification will be show when a
+ * tether is active, allowing the user to manage tethered connections.
+ */
+public class TetherActivity extends AlertActivity implements
+        DialogInterface.OnClickListener {
+
+    private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
+
+    /* Used to detect when the USB cable is unplugged, so we can call finish() */
+    private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction() == ConnectivityManager.ACTION_TETHER_STATE_CHANGED) {
+                handleTetherStateChanged(intent);
+            }
+        }
+    };
+
+    private boolean mWantTethering;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // determine if we advertise tethering or untethering
+        ConnectivityManager cm =
+                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm.getTetheredIfaces().length > 0) {
+            mWantTethering = false;
+        } else if (cm.getTetherableIfaces().length > 0) {
+            mWantTethering = true;
+        } else {
+            finish();
+            return;
+        }
+
+        // Set up the "dialog"
+        if (mWantTethering == true) {
+            mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+            mAlertParams.mTitle = getString(com.android.internal.R.string.tether_title);
+            mAlertParams.mMessage = getString(com.android.internal.R.string.tether_message);
+            mAlertParams.mPositiveButtonText =
+                    getString(com.android.internal.R.string.tether_button);
+            mAlertParams.mPositiveButtonListener = this;
+            mAlertParams.mNegativeButtonText =
+                    getString(com.android.internal.R.string.tether_button_cancel);
+            mAlertParams.mNegativeButtonListener = this;
+        } else {
+            mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+            mAlertParams.mTitle = getString(com.android.internal.R.string.tether_stop_title);
+            mAlertParams.mMessage = getString(com.android.internal.R.string.tether_stop_message);
+            mAlertParams.mPositiveButtonText =
+                    getString(com.android.internal.R.string.tether_stop_button);
+            mAlertParams.mPositiveButtonListener = this;
+            mAlertParams.mNegativeButtonText =
+                    getString(com.android.internal.R.string.tether_stop_button_cancel);
+            mAlertParams.mNegativeButtonListener = this;
+        }
+        setupAlert();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        registerReceiver(mTetherReceiver, new IntentFilter(
+                ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        unregisterReceiver(mTetherReceiver);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void onClick(DialogInterface dialog, int which) {
+
+        if (which == POSITIVE_BUTTON) {
+            ConnectivityManager connManager =
+                    (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+            // start/stop tethering
+            if (mWantTethering) {
+                if (!connManager.tether("ppp0")) {
+                    showTetheringError();
+                }
+            } else {
+                if (!connManager.untether("ppp0")) {
+                    showUnTetheringError();
+                }
+            }
+        }
+        // No matter what, finish the activity
+        finish();
+    }
+
+    private void handleTetherStateChanged(Intent intent) {
+        finish();
+    }
+
+    private void showTetheringError() {
+        Toast.makeText(this, com.android.internal.R.string.tether_error_message,
+                Toast.LENGTH_LONG).show();
+    }
+
+    private void showUnTetheringError() {
+        Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
+                Toast.LENGTH_LONG).show();
+    }
+
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 665088a..1406b66 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1239,6 +1239,10 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+        <activity android:name="com.android.internal.app.TetherActivity"
+                android:theme="@style/Theme.Dialog.Alert"
+                android:excludeFromRecents="true">
+        </activity>
         <activity android:name="com.android.internal.app.UsbStorageActivity"
                 android:excludeFromRecents="true">
         </activity>
diff --git a/core/res/res/drawable-hdpi/search_source_selector_indicator.png b/core/res/res/drawable-hdpi/search_source_selector_indicator.png
deleted file mode 100644
index b93a0c0..0000000
--- a/core/res/res/drawable-hdpi/search_source_selector_indicator.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_active.png b/core/res/res/drawable-hdpi/stat_sys_tether_active.png
new file mode 100755
index 0000000..4c14c07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_active.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
new file mode 100755
index 0000000..4c14c07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android.png b/core/res/res/drawable-hdpi/usb_android.png
index 8153ec4..f6f899a 100644
--- a/core/res/res/drawable-hdpi/usb_android.png
+++ b/core/res/res/drawable-hdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android_connected.png b/core/res/res/drawable-hdpi/usb_android_connected.png
index 6449b7c..583ca00 100644
--- a/core/res/res/drawable-hdpi/usb_android_connected.png
+++ b/core/res/res/drawable-hdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/search_source_selector_indicator.png b/core/res/res/drawable-mdpi/search_source_selector_indicator.png
deleted file mode 100644
index 26bf18a..0000000
--- a/core/res/res/drawable-mdpi/search_source_selector_indicator.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_active.png b/core/res/res/drawable-mdpi/stat_sys_tether_active.png
new file mode 100644
index 0000000..2d0da4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_active.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
new file mode 100644
index 0000000..2d0da4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android.png b/core/res/res/drawable-mdpi/usb_android.png
new file mode 100644
index 0000000..df1afbb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android_connected.png b/core/res/res/drawable-mdpi/usb_android_connected.png
new file mode 100644
index 0000000..fca77a7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable/search_source_selector_background.xml b/core/res/res/drawable/search_source_selector_background.xml
deleted file mode 100644
index fcacd89..0000000
--- a/core/res/res/drawable/search_source_selector_background.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- TODO: Need focused and pressed backgrounds -->
-
-</selector>
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index 12285fd..cf246ba 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -55,11 +55,14 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal">
-
-            <include android:id="@+id/search_source_selector"
-                layout="@layout/search_source_selector"
+            
+            <ImageView
+                android:id="@+id/search_app_icon"
+                android:layout_height="36dip"
+                android:layout_width="36dip"
                 android:layout_marginRight="7dip"
-                android:layout_gravity="center_vertical" />
+                android:layout_gravity="center_vertical"
+            />
 
             <view class="android.app.SearchDialog$SearchAutoComplete"
                 android:id="@+id/search_src_text"
diff --git a/core/res/res/layout/search_source_selector.xml b/core/res/res/layout/search_source_selector.xml
deleted file mode 100644
index c69dfc0..0000000
--- a/core/res/res/layout/search_source_selector.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="48dip"
-    android:layout_height="match_parent"
-    android:foreground="@drawable/search_source_selector_indicator"
-    android:foregroundGravity="bottom|right"
-    >
-
-    <ImageButton
-        android:id="@+id/search_source_selector_icon"
-        android:background="@drawable/search_source_selector_background"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:focusable="true"
-        android:clickable="true"
-    />
-
-</FrameLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2b8ddc4..46d6352 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -237,4 +237,8 @@
 
     <!-- Component name of the service providing geocoder API support. -->
     <string name="config_geocodeProvider">@null</string>
+
+    <!-- Flag indicating whether headset events are used by kernel to indicate
+    TTY mode changes. -->
+    <bool name="tty_mode_uses_headset_events">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 30d0da7..d1bfc68 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1078,7 +1078,13 @@
     <string name="permlab_changeNetworkState">change network connectivity</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_changeNetworkState">Allows an application to change
-      the state network connectivity.</string>
+      the state of network connectivity.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_changeTetherState">change tethered connectivity</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the applicaiton to do this. -->
+    <string name="permdesc_changeTetherState">Allows an application to change
+      the state of tethered network connectivity.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_changeBackgroundDataSetting">change background data usage setting</string>
@@ -2200,4 +2206,40 @@
          Used by AccessibilityService to announce the purpose of the view.
     -->
     <string name="description_star">favorite</string>
+
+
+    <!-- Strings for Tethering dialogs -->
+    <!-- This is the label for the activity, and should never be visible to the user. -->
+    <!-- See TETHERING.  TETHERING_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to Tether.  This is the title. -->
+    <string name="tether_title">USB tethering available</string>
+    <!-- See TETHER.    This is the message. -->
+    <string name="tether_message">Select \"Tether\" if you want to share your phone\'s data connection with your computer.</string>
+    <!-- See TETHER.    This is the button text to Tether the computer with the phone. -->
+    <string name="tether_button">Tether</string>
+    <!-- See TETHER.   This is the button text to ignore the plugging in of the phone.. -->
+    <string name="tether_button_cancel">Cancel</string>
+    <!-- See TETHER.  If there was an error mounting, this is the text. -->
+    <string name="tether_error_message">There is a problem tethering.</string>
+    <!-- TETHER: When the user connects the phone to a computer, we show a notification asking if he wants to share his cellular network connection.  This is the title -->
+    <string name="tether_available_notification_title">USB tethering available</string>
+    <!-- See USB_STORAGE. This is the message. -->
+    <string name="tether_available_notification_message">Select to tether your computer to your phone.</string>
+    <!-- TETHER_STOP: While TETHER is enabled, we show a notification dialog asking if he wants to stop. This is the title -->
+    <string name="tether_stop_notification_title">Untether</string>
+    <!-- See TETHER. This is the message. -->
+    <string name="tether_stop_notification_message">Select to untether your computer.</string>
+
+    <!-- TETHER stop dialog strings -->
+    <!-- This is the label for the activity, and should never be visible to the user. -->
+    <!-- See TETHER_STOP.  TETHER_STOP_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to stop tethering.  This is the title. -->
+    <string name="tether_stop_title">Disconnect tethering</string>
+    <!-- See TETHER_STOP.    This is the message. -->
+    <string name="tether_stop_message">You have been sharing your phone\'s cellular data connection with your computer. Select \"Disconnect\" to disconnect USB tethering.</string>
+    <!-- See TETHER_STOP.    This is the button text to disconnect tethering. -->
+    <string name="tether_stop_button">Disconnect</string>
+    <!-- See TETHER_STOP.   This is the button text to cancel disconnecting the tether. -->
+    <string name="tether_stop_button_cancel">Cancel</string>
+    <!-- See TETHER_STOP_DIALOG.  If there was an error disconnect, this is the text. -->
+    <string name="tether_stop_error_message">We\'ve encountered a problem turning off Tethering. Please try again.</string>
+
 </resources>
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index be928ec..3f253f9 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -53,8 +53,8 @@
      *
      * Supported param name are:
      * file.format - output file format. see mediarecorder.h for details
-     * codec.vid - video encoder. see mediarecorder.h for details.
-     * codec.aud - audio encoder. see mediarecorder.h for details.
+     * vid.codec - video encoder. see mediarecorder.h for details.
+     * aud.codec - audio encoder. see mediarecorder.h for details.
      * vid.width - video frame width
      * vid.height - video frame height
      * vid.fps - video frame rate
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
new file mode 100644
index 0000000..ce56443
--- /dev/null
+++ b/media/java/android/media/CamcorderProfile.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The CamcorderProfile class is used to retrieve the
+ * predefined camcorder profile settings for camcorder applications.
+ * The compressed output from a recording session with a given
+ * CamcorderProfile contains two tracks: one for auido and one for video.
+ *
+ * <p>Each profile specifies the following set of parameters:
+ * <ul>
+ * <li> The file output format, @see android.media.MediaRecorder.OutputFormat
+ * <li> Video codec format, @see android.media.MediaRecorder.VideoEncoder
+ * <li> Video bit rate in bits per second
+ * <li> Video frame rate in frames per second
+ * <li> Video frame width and height,
+ * <li> Audio codec format, @see android.media.MediaRecorder.AudioEncoder
+ * <li> Audio bit rate in bits per second,
+ * <li> Audio sample rate
+ * <li> Number of audio channels for recording.
+ * </ul>
+ * {@hide}
+ */
+public class CamcorderProfile
+{
+
+    /**
+     * The Quality class represents the quality level of each CamcorderProfile.
+     *
+     * The output from recording sessions with high quality level usually may have
+     * larger output bit rate, better video and/or audio recording quality, and
+     * laerger video frame resolution and higher audio sampling rate, etc, than those
+     * with low quality level.
+     */
+    public enum Quality {
+       /* Do not change these values/ordinals without updating their counterpart
+        * in include/media/MediaProfiles.h!
+        */
+        HIGH,
+        LOW
+    };
+
+    /**
+     * The quality level of the camcorder profile
+     * @see android.media.CamcorderProfile.Quality
+     */
+    public final Quality mQuality;
+
+    /**
+     * The file output format of the camcorder profile
+     * @see android.media.MediaRecorder.OutputFormat
+     */
+    public final int mFileFormat;
+
+    /**
+     * The video encoder being used for the video track
+     * @see android.media.MediaRecorder.VideoEncoder
+     */
+    public final int mVideoCodec;
+
+    /**
+     * The target video output bit rate in bits per second
+     */
+    public final int mVideoBitRate;
+
+    /**
+     * The target video frame rate in frames per second
+     */
+    public final int mVideoFrameRate;
+
+    /**
+     * The target video frame width in pixels
+     */
+    public final int mVideoFrameWidth;
+
+    /**
+     * The target video frame height in pixels
+     */
+    public final int mVideoFrameHeight;
+
+    /**
+     * The audio encoder being used for the audio track.
+     * @see android.media.MediaRecorder.AudioEncoder
+     */
+    public final int mAudioCodec;
+
+    /**
+     * The target audio output bit rate in bits per second
+     */
+    public final int mAudioBitRate;
+
+    /**
+     * The audio sampling rate used for the audio track
+     */
+    public final int mAudioSampleRate;
+
+    /**
+     * The number of audio channels used for the audio track
+     */
+    public final int mAudioChannels;
+
+    /**
+     * Returns the camcorder profile for the given quality.
+     * @param quality the target quality level for the camcorder profile
+     * @see android.media.CamcorderProfile.Quality
+     */
+    public static CamcorderProfile get(Quality quality) {
+        return native_get_camcorder_profile(quality.ordinal());
+    }
+
+    static {
+        System.loadLibrary("media_jni");
+        native_init();
+    }
+
+    // Private constructor called by JNI
+    private CamcorderProfile(int quality,
+                             int fileFormat,
+                             int videoCodec,
+                             int videoBitRate,
+                             int videoFrameRate,
+                             int videoWidth,
+                             int videoHeight,
+                             int audioCodec,
+                             int audioBitRate,
+                             int audioSampleRate,
+                             int audioChannels) {
+
+        mQuality          = Quality.values()[quality];
+        mFileFormat       = fileFormat;
+        mVideoCodec       = videoCodec;
+        mVideoBitRate     = videoBitRate;
+        mVideoFrameRate   = videoFrameRate;
+        mVideoFrameWidth  = videoWidth;
+        mVideoFrameHeight = videoHeight;
+        mAudioCodec       = audioCodec;
+        mAudioBitRate     = audioBitRate;
+        mAudioSampleRate  = audioSampleRate;
+        mAudioChannels    = audioChannels;
+    }
+
+    // Methods implemented by JNI
+    private static native final void native_init();
+    private static native final CamcorderProfile native_get_camcorder_profile(int quality);
+}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index e9d3372..4292754 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -28,22 +28,37 @@
  * This is a class for reading and writing Exif tags in a JPEG file.
  */
 public class ExifInterface {
-
     // The Exif tag names
+    /** Type is int. */
     public static final String TAG_ORIENTATION = "Orientation";
+    /** Type is String. */
     public static final String TAG_DATETIME = "DateTime";
+    /** Type is String. */
     public static final String TAG_MAKE = "Make";
+    /** Type is String. */
     public static final String TAG_MODEL = "Model";
+    /** Type is int. */
     public static final String TAG_FLASH = "Flash";
+    /** Type is int. */
     public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+    /** Type is int. */
     public static final String TAG_IMAGE_LENGTH = "ImageLength";
+    /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
     public static final String TAG_GPS_LATITUDE = "GPSLatitude";
+    /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
     public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+    /** Type is String. */
     public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    /** Type is String. */
     public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    /** Type is String. */
     public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    /** Type is String. */
     public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    /** Type is int. */
     public static final String TAG_WHITE_BALANCE = "WhiteBalance";
+    /** Type is rational. */
+    public static final String TAG_FOCAL_LENGTH = "FocalLength";
 
     // Constants used for the Orientation Exif tag.
     public static final int ORIENTATION_UNDEFINED = 0;
@@ -114,6 +129,29 @@
     }
 
     /**
+     * Returns the double value of the specified rational tag. If there is no
+     * such tag in the JPEG file or the value cannot be parsed as double, return
+     * <var>defaultValue</var>.
+     *
+     * @param tag the name of the tag.
+     * @param defaultValue the value to return if the tag is not available.
+     */
+    public double getAttributeDouble(String tag, double defaultValue) {
+        String value = mAttributes.get(tag);
+        if (value == null) return defaultValue;
+        try {
+            int index = value.indexOf("/");
+            if (index == -1) return defaultValue;
+            double denom = Double.parseDouble(value.substring(index + 1));
+            if (denom == 0) return defaultValue;
+            double num = Double.parseDouble(value.substring(0, index));
+            return num / denom;
+        } catch (NumberFormatException ex) {
+            return defaultValue;
+        }
+    }
+
+    /**
      * Set the value of the specified tag.
      *
      * @param tag the name of the tag.
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index cd3ad88..50380c1 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -162,7 +162,54 @@
     return cap;
 }
 
-static JNINativeMethod gMethods[] = {
+static jobject
+android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint quality)
+{
+    LOGV("native_get_camcorder_profile: %d", quality);
+    if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) {
+        jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality");
+        return NULL;
+    }
+
+    camcorder_quality q = static_cast<camcorder_quality>(quality);
+    int fileFormat       = sProfiles->getCamcorderProfileParamByName("file.format", q);
+    int videoCodec       = sProfiles->getCamcorderProfileParamByName("vid.codec",   q);
+    int videoBitRate     = sProfiles->getCamcorderProfileParamByName("vid.bps",     q);
+    int videoFrameRate   = sProfiles->getCamcorderProfileParamByName("vid.fps",     q);
+    int videoFrameWidth  = sProfiles->getCamcorderProfileParamByName("vid.width",   q);
+    int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height",  q);
+    int audioCodec       = sProfiles->getCamcorderProfileParamByName("aud.codec",   q);
+    int audioBitRate     = sProfiles->getCamcorderProfileParamByName("aud.bps",     q);
+    int audioSampleRate  = sProfiles->getCamcorderProfileParamByName("aud.hz",      q);
+    int audioChannels    = sProfiles->getCamcorderProfileParamByName("aud.ch",      q);
+
+    // Check on the values retrieved
+    if (fileFormat == -1 || videoCodec == -1 || audioCodec == -1 ||
+        videoBitRate == -1 || videoFrameRate == -1 || videoFrameWidth == -1 || videoFrameHeight == -1 ||
+        audioBitRate == -1 || audioSampleRate == -1 || audioChannels == -1) {
+
+        jniThrowException(env, "java/lang/RuntimeException", "Error retrieving camcorder profile params");
+        return NULL;
+    }
+
+    jclass camcorderProfileClazz = env->FindClass("android/media/CamcorderProfile");
+    jmethodID camcorderProfileConstructorMethodID = env->GetMethodID(camcorderProfileClazz, "<init>", "(IIIIIIIIIII)V");
+    return env->NewObject(camcorderProfileClazz,
+                          camcorderProfileConstructorMethodID,
+                          quality,
+                          fileFormat,
+                          videoCodec,
+                          videoBitRate,
+                          videoFrameRate,
+                          videoFrameWidth,
+                          videoFrameHeight,
+                          audioCodec,
+                          audioBitRate,
+                          audioSampleRate,
+                          audioChannels);
+}
+
+static JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = {
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_num_file_formats",            "()I",                    (void *)android_media_MediaProfiles_native_get_num_file_formats},
     {"native_get_file_format",                 "(I)I",                   (void *)android_media_MediaProfiles_native_get_file_format},
@@ -176,12 +223,29 @@
                                                                          (void *)android_media_MediaProfiles_native_get_audio_encoder_cap},
 };
 
-static const char* const kClassPathName = "android/media/MediaProfiles";
+static JNINativeMethod gMethodsForCamcorderProfileClass[] = {
+    {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
+    {"native_get_camcorder_profile",           "(I)Landroid/media/CamcorderProfile;",
+                                                                         (void *)android_media_MediaProfiles_native_get_camcorder_profile},
+};
+
+static const char* const kEncoderCapabilitiesClassPathName = "android/media/EncoderCapabilities";
+static const char* const kCamcorderProfileClassPathName = "android/media/CamcorderProfile";
 
 // This function only registers the native methods, and is called from
 // JNI_OnLoad in android_media_MediaPlayer.cpp
 int register_android_media_MediaProfiles(JNIEnv *env)
 {
-    return AndroidRuntime::registerNativeMethods(env,
-                "android/media/EncoderCapabilities", gMethods, NELEM(gMethods));
+    int ret1 = AndroidRuntime::registerNativeMethods(env,
+               kEncoderCapabilitiesClassPathName,
+               gMethodsForEncoderCapabilitiesClass,
+               NELEM(gMethodsForEncoderCapabilitiesClass));
+
+    int ret2 = AndroidRuntime::registerNativeMethods(env,
+               kCamcorderProfileClassPathName,
+               gMethodsForCamcorderProfileClass,
+               NELEM(gMethodsForCamcorderProfileClass));
+
+    // Success if ret1 == 0 && ret2 == 0
+    return (ret1 || ret2);
 }
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index dbb52c6..b25ce67 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -60,8 +60,6 @@
         libsonivox        \
         libvorbisidec
 
-ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_aacdec \
         libstagefright_amrnbdec \
@@ -69,12 +67,18 @@
         libstagefright_amrwbdec \
         libstagefright_avcdec \
         libstagefright_m4vh263dec \
-        libstagefright_mp3dec \
-        libstagefright_id3
+        libstagefright_mp3dec
 
 LOCAL_SHARED_LIBRARIES += \
         libstagefright_amrnb_common \
-        libstagefright_avc_common \
+        libstagefright_avc_common
+
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
+LOCAL_STATIC_LIBRARIES += \
+        libstagefright_id3
+
+LOCAL_SHARED_LIBRARIES += \
         libstagefright_color_conversion
 
 endif
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 90bbdfe..0355a82 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,7 +18,6 @@
 #define LOG_TAG "OMXCodec"
 #include <utils/Log.h>
 
-#if BUILD_WITH_FULL_STAGEFRIGHT
 #include "include/AACDecoder.h"
 #include "include/AMRNBDecoder.h"
 #include "include/AMRNBEncoder.h"
@@ -26,7 +25,6 @@
 #include "include/AVCDecoder.h"
 #include "include/M4vH263Decoder.h"
 #include "include/MP3Decoder.h"
-#endif
 
 #include "include/ESDS.h"
 
@@ -56,9 +54,6 @@
     const char *codec;
 };
 
-#if BUILD_WITH_FULL_STAGEFRIGHT
-#define OPTIONAL(x,y) { x, y },
-
 #define FACTORY_CREATE(name) \
 static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
     return new name(source); \
@@ -103,42 +98,30 @@
 #undef FACTORY_REF
 #undef FACTORY_CREATE
 
-#else
-#define OPTIONAL(x,y)
-#endif
-
 static const CodecInfo kDecoderInfo[] = {
     { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
     { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
-    OPTIONAL(MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder")
-    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
+    { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
-    OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder")
-    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
+    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
-    OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder")
-    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
+    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
-    OPTIONAL(MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder")
-    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+    { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
-    OPTIONAL(MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder")
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+    { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.Decoder" },
-    OPTIONAL(MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder")
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+    { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
-    OPTIONAL(MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder")
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
 };
 
 static const CodecInfo kEncoderInfo[] = {
     { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
-    OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder")
-    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrencnb" },
+    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
diff --git a/media/libstagefright/codecs/Android.mk b/media/libstagefright/codecs/Android.mk
index 1fa7e93..2e431205 100644
--- a/media/libstagefright/codecs/Android.mk
+++ b/media/libstagefright/codecs/Android.mk
@@ -1,8 +1,4 @@
-ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 3c47e2e..add8f3c 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -1,3 +1,5 @@
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -25,3 +27,4 @@
 
 include $(BUILD_EXECUTABLE)
 
+endif
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
new file mode 100644
index 0000000..b7ae4a9
--- /dev/null
+++ b/native/graphics/jni/Android.mk
@@ -0,0 +1,36 @@
+BASE_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PRELINK_MODULE := false
+
+# setup for skia optimizations
+#
+ifneq ($(ARCH_ARM_HAVE_VFP),true)
+	LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
+endif
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+	LOCAL_CFLAGS += -D__ARM_HAVE_NEON
+endif
+
+# our source files
+#
+LOCAL_SRC_FILES:= \
+	bitmap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libandroid_runtime \
+    libskia
+
+LOCAL_C_INCLUDES += \
+	external/skia/include/core \
+	frameworks/base/native/include \
+	frameworks/base/core/jni/android/graphics \
+	dalvik/libnativehelper/include/nativehelper
+
+LOCAL_MODULE:= libjnigraphics
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
new file mode 100644
index 0000000..fd73430
--- /dev/null
+++ b/native/graphics/jni/bitmap.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/bitmap.h>
+#include <GraphicsJNI.h>
+
+int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
+                          AndroidBitmapInfo* info) {
+    if (NULL == env || NULL == jbitmap) {
+        return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+    if (NULL == bm) {
+        return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+    }
+
+    if (info) {
+        info->width     = bm->width();
+        info->height    = bm->height();
+        info->stride    = bm->rowBytes();
+        info->flags     = 0;
+
+        switch (bm->config()) {
+            case SkBitmap::kARGB_8888_Config:
+                info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
+                break;
+            case SkBitmap::kRGB_565_Config:
+                info->format = ANDROID_BITMAP_FORMAT_RGB_565;
+                break;
+            case SkBitmap::kARGB_4444_Config:
+                info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
+                break;
+            case SkBitmap::kA8_Config:
+                info->format = ANDROID_BITMAP_FORMAT_A_8;
+                break;
+            default:
+                info->format = ANDROID_BITMAP_FORMAT_NONE;
+                break;
+        }
+    }
+    return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
+int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
+    if (NULL == env || NULL == jbitmap) {
+        return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+    if (NULL == bm) {
+        return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+    }
+
+    bm->lockPixels();
+    void* addr = bm->getPixels();
+    if (NULL == addr) {
+        bm->unlockPixels();
+        return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
+    }
+
+    if (addrPtr) {
+        *addrPtr = addr;
+    }
+    return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
+int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) {
+    if (NULL == env || NULL == jbitmap) {
+        return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+    }
+
+    SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+    if (NULL == bm) {
+        return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+    }
+
+    bm->unlockPixels();
+    return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
diff --git a/native/include/android/bitmap.h b/native/include/android/bitmap.h
new file mode 100644
index 0000000..5078277
--- /dev/null
+++ b/native/include/android/bitmap.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BITMAP_H
+#define ANDROID_BITMAP_H
+
+#include <stdint.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANDROID_BITMAP_RESUT_SUCCESS            0
+#define ANDROID_BITMAP_RESULT_BAD_PARAMETER     -1
+#define ANDROID_BITMAP_RESULT_JNI_EXCEPTION     -2
+#define ANDROID_BITMAP_RESULT_ALLOCATION_FAILED -3
+
+enum AndroidBitmapFormat {
+    ANDROID_BITMAP_FORMAT_NONE      = 0,
+    ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
+    ANDROID_BITMAP_FORMAT_RGB_565   = 4,
+    ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
+    ANDROID_BITMAP_FORMAT_A_8       = 8,
+};
+
+typedef struct {
+    uint32_t    width;
+    uint32_t    height;
+    uint32_t    stride;
+    int32_t     format;
+    uint32_t    flags;      // 0 for now
+} AndroidBitmapInfo;
+
+/**
+ * Given a java bitmap object, fill out the AndroidBitmap struct for it.
+ * If the call fails, the info parameter will be ignored
+ */
+int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
+                          AndroidBitmapInfo* info);
+
+/**
+ * Given a java bitmap object, attempt to lock the pixel address.
+ * Locking will ensure that the memory for the pixels will not move
+ * until the unlockPixels call, and ensure that, if the pixels had been
+ * previously purged, they will have been restored.
+ *
+ * If this call succeeds, it must be balanced by a call to
+ * AndroidBitmap_unlockPixels, after which time the address of the pixels should
+ * no longer be used.
+ *
+ * If this succeeds, *addrPtr will be set to the pixel address. If the call
+ * fails, addrPtr will be ignored.
+ */
+int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
+
+/**
+ * Call this to balanace a successful call to AndroidBitmap_lockPixels
+ */
+int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/preloaded-classes b/preloaded-classes
index 3e2eb13..092b539 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -22,14 +22,14 @@
 android.app.ActivityThread$ProviderRefCount
 android.app.AlertDialog
 android.app.Application
-android.app.ApplicationContext
-android.app.ApplicationContext$ApplicationContentResolver
-android.app.ApplicationContext$ApplicationPackageManager
-android.app.ApplicationContext$ApplicationPackageManager$PackageRemovedReceiver
-android.app.ApplicationContext$ApplicationPackageManager$ResourceName
-android.app.ApplicationContext$SharedPreferencesImpl
 android.app.ApplicationLoaders
 android.app.ApplicationThreadNative
+android.app.ContextImpl
+android.app.ContextImpl$ApplicationContentResolver
+android.app.ContextImpl$ApplicationPackageManager
+android.app.ContextImpl$ApplicationPackageManager$PackageRemovedReceiver
+android.app.ContextImpl$ApplicationPackageManager$ResourceName
+android.app.ContextImpl$SharedPreferencesImpl
 android.app.Dialog
 android.app.ExpandableListActivity
 android.app.IActivityManager
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 0c1e0602..6795bdd 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -176,11 +176,21 @@
         public IBackupTransport transport;
         public IRestoreObserver observer;
         public long token;
+        public PackageInfo pkgInfo;
+
+        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
+                long _token, PackageInfo _pkg) {
+            transport = _transport;
+            observer = _obs;
+            token = _token;
+            pkgInfo = _pkg;
+        }
 
         RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
             transport = _transport;
             observer = _obs;
             token = _token;
+            pkgInfo = null;
         }
     }
 
@@ -210,10 +220,16 @@
     File mJournalDir;
     File mJournal;
 
-    // Keep a log of all the apps we've ever backed up
+    // Keep a log of all the apps we've ever backed up, and what the
+    // dataset tokens are for both the current backup dataset and
+    // the ancestral dataset.
     private File mEverStored;
     HashSet<String> mEverStoredApps = new HashSet<String>();
 
+    File mTokenFile;
+    long mAncestralToken = 0;
+    long mCurrentToken = 0;
+
     // Persistently track the need to do a full init
     static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
     HashSet<String> mPendingInits = new HashSet<String>();  // transport names
@@ -277,7 +293,7 @@
                 RestoreParams params = (RestoreParams)msg.obj;
                 Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
                 (new PerformRestoreTask(params.transport, params.observer,
-                        params.token)).run();
+                        params.token, params.pkgInfo)).run();
                 break;
             }
 
@@ -475,6 +491,16 @@
     private void initPackageTracking() {
         if (DEBUG) Log.v(TAG, "Initializing package tracking");
 
+        // Remember our ancestral dataset
+        mTokenFile = new File(mBaseStateDir, "ancestral");
+        try {
+            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
+            mAncestralToken = tf.readLong();
+            mCurrentToken = tf.readLong();
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to read token file", e);
+        }
+
         // Keep a log of what apps we've ever backed up.  Because we might have
         // rebooted in the middle of an operation that was removing something from
         // this log, we sanity-check its contents here and reconstruct it.
@@ -607,6 +633,9 @@
             mEverStoredApps.clear();
             mEverStored.delete();
 
+            mCurrentToken = 0;
+            writeRestoreTokens();
+
             // Remove all the state files
             for (File sf : stateFileDir.listFiles()) {
                 // ... but don't touch the needs-init sentinel
@@ -880,7 +909,7 @@
         addPackageParticipantsLockedInner(packageName, allApps);
     }
 
-    // Called from the backup thread: record that the given app has been successfully
+    // Called from the backup task: record that the given app has been successfully
     // backed up at least once
     void logBackupComplete(String packageName) {
         if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
@@ -938,6 +967,18 @@
         }
     }
 
+    // Record the current and ancestral backup tokens persistently
+    void writeRestoreTokens() {
+        try {
+            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
+            af.writeLong(mAncestralToken);
+            af.writeLong(mCurrentToken);
+            af.close();
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to write token file:", e);
+        }
+    }
+
     // Return the given transport
     private IBackupTransport getTransport(String transportName) {
         synchronized (mTransports) {
@@ -1154,6 +1195,16 @@
                 Log.e(TAG, "Error in backup thread", e);
                 status = BackupConstants.TRANSPORT_ERROR;
             } finally {
+                // If everything actually went through and this is the first time we've
+                // done a backup, we can now record what the current backup dataset token
+                // is.
+                if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) {
+                    try {
+                        mCurrentToken = mTransport.getCurrentRestoreSet();
+                    } catch (RemoteException e) { /* cannot happen */ }
+                    writeRestoreTokens();
+                }
+
                 // If things went wrong, we need to re-stage the apps we had expected
                 // to be backing up in this pass.  This journals the package names in
                 // the current active pending-backup file, not in the we are holding
@@ -1395,6 +1446,7 @@
         private IBackupTransport mTransport;
         private IRestoreObserver mObserver;
         private long mToken;
+        private PackageInfo mTargetPackage;
         private File mStateDir;
 
         class RestoreRequest {
@@ -1408,11 +1460,11 @@
         }
 
         PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
-                long restoreSetToken) {
+                long restoreSetToken, PackageInfo targetPackage) {
             mTransport = transport;
-            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
             mObserver = observer;
             mToken = restoreSetToken;
+            mTargetPackage = targetPackage;
 
             try {
                 mStateDir = new File(mBaseStateDir, transport.transportDirName());
@@ -1424,7 +1476,8 @@
         public void run() {
             long startRealtime = SystemClock.elapsedRealtime();
             if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
-                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
+                    + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
+                    + " mTargetPackage=" + mTargetPackage);
             /**
              * Restore sequence:
              *
@@ -1441,7 +1494,7 @@
              *
              * On errors, we try our best to recover and move on to the next
              * application, but if necessary we abort the whole operation --
-             * the user is waiting, after al.
+             * the user is waiting, after all.
              */
 
             int error = -1; // assume error
@@ -1459,7 +1512,12 @@
                 restorePackages.add(omPackage);
 
                 List<PackageInfo> agentPackages = allAgentPackages();
-                restorePackages.addAll(agentPackages);
+                if (mTargetPackage == null) {
+                    restorePackages.addAll(agentPackages);
+                } else {
+                    // Just one package to attempt restore of
+                    restorePackages.add(mTargetPackage);
+                }
 
                 // let the observer know that we're running
                 if (mObserver != null) {
@@ -1641,6 +1699,13 @@
                     }
                 }
 
+                // If this was a restoreAll operation, record that this was our
+                // ancestral dataset
+                if (mTargetPackage == null) {
+                    mAncestralToken = mToken;
+                    writeRestoreTokens();
+                }
+
                 // done; we can finally release the wakelock
                 mWakelock.release();
             }
@@ -2219,7 +2284,7 @@
             }
         }
 
-        public synchronized int performRestore(long token, IRestoreObserver observer) {
+        public synchronized int restoreAll(long token, IRestoreObserver observer) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                     "performRestore");
 
@@ -2249,6 +2314,55 @@
             return -1;
         }
 
+        public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
+            if (DEBUG) Log.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
+
+            PackageInfo app = null;
+            try {
+                app = mPackageManager.getPackageInfo(packageName, 0);
+            } catch (NameNotFoundException nnf) {
+                Log.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+                return -1;
+            }
+
+            // If the caller is not privileged and is not coming from the target
+            // app's uid, throw a permission exception back to the caller.
+            int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
+                    Binder.getCallingPid(), Binder.getCallingUid());
+            if ((perm == PackageManager.PERMISSION_DENIED) &&
+                    (app.applicationInfo.uid != Binder.getCallingUid())) {
+                Log.w(TAG, "restorePackage: bad packageName=" + packageName
+                        + " or calling uid=" + Binder.getCallingUid());
+                throw new SecurityException("No permission to restore other packages");
+            }
+
+            // So far so good; we're allowed to try to restore this package.  Now
+            // check whether there is data for it in the current dataset, falling back
+            // to the ancestral dataset if not.
+            long token = mAncestralToken;
+            synchronized (mQueueLock) {
+                if (mEverStoredApps.contains(packageName)) {
+                    token = mCurrentToken;
+                }
+            }
+
+            // If we didn't come up with a place to look -- no ancestral dataset and
+            // the app has never been backed up from this device -- there's nothing
+            // to do but return failure.
+            if (token == 0) {
+                return -1;
+            }
+
+            // Ready to go:  enqueue the restore request and claim success
+            long oldId = Binder.clearCallingIdentity();
+            mWakelock.acquire();
+            Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+            msg.obj = new RestoreParams(mRestoreTransport, observer, token, app);
+            mBackupHandler.sendMessage(msg);
+            Binder.restoreCallingIdentity(oldId);
+            return 0;
+        }
+
         public synchronized void endRestoreSession() {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                     "endRestoreSession");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index aa4956f..4259016 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -43,6 +43,8 @@
 
 import com.android.internal.telephony.Phone;
 
+import com.android.server.connectivity.Tethering;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -62,6 +64,9 @@
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
+
+    private Tethering mTethering;
+
     /**
      * Sometimes we want to refer to the individual network state
      * trackers separately, and sometimes we just want to treat them
@@ -308,6 +313,8 @@
                 continue;
             }
         }
+
+        mTethering = new Tethering(mContext);
     }
 
 
@@ -784,6 +791,13 @@
                 "ConnectivityService");
     }
 
+    // TODO Make this a special check when it goes public
+    private void enforceTetherChangePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE,
+                "ConnectivityService");
+    }
+
     /**
      * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
      * network, we ignore it. If it is for the active network, we send out a
@@ -1368,4 +1382,28 @@
             }
         }
     }
+
+    // javadoc from interface
+    public boolean tether(String iface) {
+        enforceTetherChangePermission();
+        return mTethering.tether(iface);
+    }
+
+    // javadoc from interface
+    public boolean untether(String iface) {
+        enforceTetherChangePermission();
+        return mTethering.untether(iface);
+    }
+
+    // TODO - move iface listing, queries, etc to new module
+    // javadoc from interface
+    public String[] getTetherableIfaces() {
+        enforceAccessPermission();
+        return mTethering.getTetherableIfaces();
+    }
+
+    public String[] getTetheredIfaces() {
+        enforceAccessPermission();
+        return mTethering.getTetheredIfaces();
+    }
 }
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 9d69564..c94450b 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -53,6 +53,19 @@
     private final Context mContext;
     private final WakeLock mWakeLock;  // held while there is a pending route change
 
+    private boolean mHandleTTY;
+    private int mTTYState;
+    private AudioManager mAudioManager = null;
+
+    // special use of bits in headset state received from kernel made by some
+    // platforms to indicate changes in TTY mode.
+    private static final int BIT_TTY_OFF = 0;
+    private static final int BIT_TTY_FULL = (1 << 2);
+    private static final int BIT_TTY_VCO = (1 << 5);
+    private static final int BIT_TTY_HCO = (1 << 6);
+    private static final int TTY_BITS_MASK = (BIT_TTY_FULL | BIT_TTY_VCO | BIT_TTY_HCO);
+
+
     public HeadsetObserver(Context context) {
         mContext = context;
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -61,6 +74,11 @@
 
         startObserving(HEADSET_UEVENT_MATCH);
 
+        // read settings for TTY mode indication method
+        mHandleTTY = context.getResources().getBoolean(
+                com.android.internal.R.bool.tty_mode_uses_headset_events);
+        mTTYState = BIT_TTY_OFF;
+
         init();  // set initial status
     }
 
@@ -100,6 +118,39 @@
     }
 
     private synchronized final void update(String newName, int newState) {
+        // handle TTY state change first
+        if (mHandleTTY) {
+            int ttyState = newState  & TTY_BITS_MASK;
+            if (ttyState != mTTYState) {
+                String ttyMode;
+
+                switch (ttyState) {
+                case BIT_TTY_FULL:
+                    ttyMode = "tty_full";
+                    break;
+                case BIT_TTY_VCO:
+                    ttyMode = "tty_vco";
+                    break;
+                case BIT_TTY_HCO:
+                    ttyMode = "tty_hco";
+                    break;
+                case BIT_TTY_OFF:
+                    ttyMode = "tty_off";
+                    break;
+                default:
+                    ttyMode = "tty_invalid";
+                    break;
+
+                }
+                if (ttyMode != "tty_invalid") {
+                    mTTYState = ttyState;
+                    if (mAudioManager == null) {
+                        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+                    }
+                    mAudioManager.setParameters("tty_mode="+ttyMode);
+                }
+            }
+        }
         // Retain only relevant bits
         int headsetState = newState & SUPPORTED_HEADSETS;
         int newOrOld = headsetState | mHeadsetState;
diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java
index 0834405..ac3445e 100644
--- a/services/java/com/android/server/NetStatService.java
+++ b/services/java/com/android/server/NetStatService.java
@@ -27,11 +27,11 @@
     }
 
     public long getMobileTxPackets() {
-        return TrafficStats.getMobileTxPkts();
+        return TrafficStats.getMobileTxPackets();
     }
 
     public long getMobileRxPackets() {
-        return TrafficStats.getMobileRxPkts();
+        return TrafficStats.getMobileRxPackets();
     }
 
     public long getMobileTxBytes() {
@@ -43,11 +43,11 @@
     }
 
     public long getTotalTxPackets() {
-        return TrafficStats.getTotalTxPkts();
+        return TrafficStats.getTotalTxPackets();
     }
 
     public long getTotalRxPackets() {
-        return TrafficStats.getTotalRxPkts();
+        return TrafficStats.getTotalRxPackets();
     }
 
     public long getTotalTxBytes() {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index b34b50a..d41aacf 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -334,9 +334,9 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
         try {
-            String cmd = "tether dns set ";
+            String cmd = "tether dns set";
             for (String s : dns) {
-                cmd += InetAddress.getByName(s).toString() + " ";
+                cmd += " " + InetAddress.getByName(s).getHostAddress();
             }
             mConnector.doCommand(cmd);
         } catch (UnknownHostException e) {
@@ -373,14 +373,16 @@
         return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
     }
 
-    public void attachPppd(String tty, String localAddr, String remoteAddr)
-            throws IllegalStateException {
+    public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
+            String dns2Addr) throws IllegalStateException {
         try {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
-            mConnector.doCommand(String.format("pppd attach %s %s %s", tty,
-                    InetAddress.getByName(localAddr).toString(),
-                    InetAddress.getByName(localAddr).toString()));
+            mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
+                    InetAddress.getByName(localAddr).getHostAddress(),
+                    InetAddress.getByName(remoteAddr).getHostAddress(),
+                    InetAddress.getByName(dns1Addr).getHostAddress(),
+                    InetAddress.getByName(dns2Addr).getHostAddress()));
         } catch (UnknownHostException e) {
             throw new IllegalStateException("Error resolving addr", e);
         }
@@ -392,4 +394,3 @@
         mConnector.doCommand(String.format("pppd detach %s", tty));
     }
 }
-
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 22447ed..ee1cc8d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -529,7 +529,8 @@
         // The system server has to run all of the time, so it needs to be
         // as efficient as possible with its memory usage.
         VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-
+        VMRuntime.getRuntime().startJitCompilation();
+        
         System.loadLibrary("android_servers");
         init1(args);
     }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
new file mode 100644
index 0000000..f685383
--- /dev/null
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.INetworkManagementEventObserver;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * @hide
+ */
+public class Tethering extends INetworkManagementEventObserver.Stub {
+
+    private Notification mTetheringNotification;
+    private Context mContext;
+    private final String TAG = "Tethering";
+
+    private boolean mPlaySounds = false;
+
+    private ArrayList<String> mAvailableIfaces;
+    private ArrayList<String> mActiveIfaces;
+
+    private ArrayList<String> mActiveTtys;
+
+    private BroadcastReceiver mStateReceiver;
+
+    public Tethering(Context context) {
+        Log.d(TAG, "Tethering starting");
+        mContext = context;
+
+        // register for notifications from NetworkManagement Service
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        try {
+            service.registerObserver(this);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error registering observer :" + e);
+        }
+
+        mAvailableIfaces = new ArrayList<String>();
+        mActiveIfaces = new ArrayList<String>();
+        mActiveTtys = new ArrayList<String>();
+
+        // TODO - remove this hack after real USB connections are detected.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
+        filter.addAction(Intent.ACTION_UMS_CONNECTED);
+        mStateReceiver = new UMSStateReceiver();
+        mContext.registerReceiver(mStateReceiver, filter);
+    }
+
+    public synchronized void interfaceLinkStatusChanged(String iface, boolean link) {
+        Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
+    }
+
+    public synchronized void interfaceAdded(String iface) {
+        if (mActiveIfaces.contains(iface)) {
+            Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
+            return;
+        }
+        if (mAvailableIfaces.contains(iface)) {
+            Log.e(TAG, "available iface (" + iface + ") readded, ignoring");
+            return;
+        }
+        mAvailableIfaces.add(iface);
+        Log.d(TAG, "interfaceAdded :" + iface);
+        sendTetherStateChangedBroadcast();
+    }
+
+    public synchronized void interfaceRemoved(String iface) {
+        if (mActiveIfaces.contains(iface)) {
+            Log.d(TAG, "removed an active iface (" + iface + ")");
+            untether(iface);
+        }
+        if (mAvailableIfaces.contains(iface)) {
+            mAvailableIfaces.remove(iface);
+            Log.d(TAG, "interfaceRemoved " + iface);
+            sendTetherStateChangedBroadcast();
+        }
+    }
+
+    public synchronized boolean tether(String iface) {
+        Log.d(TAG, "Tethering " + iface);
+
+        if (!mAvailableIfaces.contains(iface)) {
+            Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
+            return false;
+        }
+        if (mActiveIfaces.contains(iface)) {
+            Log.e(TAG, "Tried to Tether an already Tethered iface :" + iface + ", ignoring");
+            return false;
+        }
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        if (mActiveIfaces.size() == 0) {
+            try {
+                service.setIpForwardingEnabled(true);
+            } catch (Exception e) {
+                Log.e(TAG, "Error in setIpForwardingEnabled(true) :" + e);
+                return false;
+            }
+
+            try {
+                // TODO - don't hardcode this - though with non-conf values (un-routable)
+                // maybe it's not a big deal
+                service.startTethering("169.254.2.1", "169.254.2.64");
+            } catch (Exception e) {
+                Log.e(TAG, "Error in startTethering :" + e);
+                try {
+                    service.setIpForwardingEnabled(false);
+                } catch (Exception ee) {}
+                return false;
+            }
+
+            try {
+                // TODO - maybe use the current connection's dns servers for this
+                String[] dns = new String[2];
+                dns[0] = new String("8.8.8.8");
+                dns[1] = new String("4.2.2.2");
+                service.setDnsForwarders(dns);
+            } catch (Exception e) {
+                Log.e(TAG, "Error in setDnsForwarders :" + e);
+                try {
+                    service.stopTethering();
+                } catch (Exception ee) {}
+                try {
+                    service.setIpForwardingEnabled(false);
+                } catch (Exception ee) {}
+            }
+        }
+
+        try {
+            service.tetherInterface(iface);
+        } catch (Exception e) {
+            Log.e(TAG, "Error in tetherInterface :" + e);
+            if (mActiveIfaces.size() == 0) {
+                try {
+                    service.stopTethering();
+                } catch (Exception ee) {}
+                try {
+                    service.setIpForwardingEnabled(false);
+                } catch (Exception ee) {}
+            }
+            return false;
+        }
+
+        try {
+            // TODO - use the currently active external iface
+            service.enableNat (iface, "rmnet0");
+        } catch (Exception e) {
+            Log.e(TAG, "Error in enableNat :" + e);
+            try {
+                service.untetherInterface(iface);
+            } catch (Exception ee) {}
+            if (mActiveIfaces.size() == 0) {
+                try {
+                    service.stopTethering();
+                } catch (Exception ee) {}
+                try {
+                    service.setIpForwardingEnabled(false);
+                } catch (Exception ee) {}
+            }
+            return false;
+        }
+        mAvailableIfaces.remove(iface);
+        mActiveIfaces.add(iface);
+        Log.d(TAG, "Tethered " + iface);
+        sendTetherStateChangedBroadcast();
+        return true;
+    }
+
+    public synchronized boolean untether(String iface) {
+        Log.d(TAG, "Untethering " + iface);
+
+        if (mAvailableIfaces.contains(iface)) {
+            Log.e(TAG, "Tried to Untether an available iface :" + iface);
+            return false;
+        }
+        if (!mActiveIfaces.contains(iface)) {
+            Log.e(TAG, "Tried to Untether an inactive iface :" + iface);
+            return false;
+        }
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        // none of these errors are recoverable - ie, multiple calls won't help
+        // and the user can't do anything.  Basically a reboot is required and probably
+        // the device is misconfigured or something bad has happend.
+        // Because of this, we should try to unroll as much state as we can.
+        try {
+            service.disableNat(iface, "rmnet0");
+        } catch (Exception e) {
+            Log.e(TAG, "Error in disableNat :" + e);
+        }
+        try {
+            service.untetherInterface(iface);
+        } catch (Exception e) {
+            Log.e(TAG, "Error untethering " + iface + ", :" + e);
+        }
+        mActiveIfaces.remove(iface);
+        mAvailableIfaces.add(iface);
+
+        if (mActiveIfaces.size() == 0) {
+            Log.d(TAG, "no active tethers - turning down dhcp/ipforward");
+            try {
+                service.stopTethering();
+            } catch (Exception e) {
+                Log.e(TAG, "Error in stopTethering :" + e);
+            }
+            try {
+                service.setIpForwardingEnabled(false);
+            } catch (Exception e) {
+                Log.e(TAG, "Error in setIpForwardingEnabled(false) :" + e);
+            }
+        }
+        sendTetherStateChangedBroadcast();
+        Log.d(TAG, "Untethered " + iface);
+        return true;
+    }
+
+    private void sendTetherStateChangedBroadcast() {
+        Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        broadcast.putExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER_COUNT,
+                mAvailableIfaces.size());
+        broadcast.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER_COUNT, mActiveIfaces.size());
+        mContext.sendBroadcast(broadcast);
+
+        // for USB we only have the one, so don't have to deal with additional
+        if (mAvailableIfaces.size() > 0) {
+            // Check if the user wants to be bothered
+            boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.TETHER_NOTIFY, 0) == 1);
+
+            if (tellUser) {
+                showTetherAvailableNotification();
+            }
+        } else if (mActiveIfaces.size() > 0) {
+            showTetheredNotification();
+        } else {
+            clearNotification();
+        }
+    }
+
+    private void showTetherAvailableNotification() {
+        NotificationManager notificationManager = (NotificationManager)mContext.
+                getSystemService(Context.NOTIFICATION_SERVICE);
+        if (notificationManager == null) {
+            return;
+        }
+
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
+
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        Resources r = Resources.getSystem();
+        CharSequence title = r.getText(com.android.internal.R.string.
+                tether_available_notification_title);
+        CharSequence message = r.getText(com.android.internal.R.string.
+                tether_available_notification_message);
+
+        if(mTetheringNotification == null) {
+            mTetheringNotification = new Notification();
+            mTetheringNotification.when = 0;
+        }
+        mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+
+        boolean playSounds = false;
+        //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+        if (playSounds) {
+            mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
+        } else {
+            mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
+        }
+
+        mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
+        mTetheringNotification.tickerText = title;
+        mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
+
+        notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
+
+    }
+
+    private void showTetheredNotification() {
+        NotificationManager notificationManager = (NotificationManager)mContext.
+                getSystemService(Context.NOTIFICATION_SERVICE);
+        if (notificationManager == null) {
+            return;
+        }
+
+        Intent intent = new Intent();
+        intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
+
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+        Resources r = Resources.getSystem();
+        CharSequence title = r.getText(com.android.internal.R.string.
+                tether_stop_notification_title);
+        CharSequence message = r.getText(com.android.internal.R.string.
+                tether_stop_notification_message);
+
+        if(mTetheringNotification == null) {
+            mTetheringNotification = new Notification();
+            mTetheringNotification.when = 0;
+        }
+        mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+
+        boolean playSounds = false;
+        //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+        if (playSounds) {
+            mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
+        } else {
+            mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
+        }
+
+        mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
+        mTetheringNotification.tickerText = title;
+        mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
+
+        notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
+    }
+
+    private void clearNotification() {
+        NotificationManager notificationManager = (NotificationManager)mContext.
+                getSystemService(Context.NOTIFICATION_SERVICE);
+        if (notificationManager != null && mTetheringNotification != null) {
+            notificationManager.cancel(mTetheringNotification.icon);
+            mTetheringNotification = null;
+        }
+    }
+
+
+
+
+// TODO - remove this hack after we get proper USB detection
+    private class UMSStateReceiver extends BroadcastReceiver {
+        public void onReceive(Context content, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_UMS_CONNECTED)) {
+                Tethering.this.handleTtyConnect();
+            } else if (intent.getAction().equals(Intent.ACTION_UMS_DISCONNECTED)) {
+                Tethering.this.handleTtyDisconnect();
+            }
+        }
+    }
+
+    private synchronized void handleTtyConnect() {
+        Log.d(TAG, "handleTtyConnect");
+        // for each of the available Tty not already supported by a ppp session,
+        // create a ppp session
+        // TODO - this should be data-driven rather than hard coded.
+        String[] allowedTtys = new String[1];
+        allowedTtys[0] = new String("ttyGS0");
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        String[] availableTtys;
+        try {
+            availableTtys = service.listTtys();
+        } catch (RemoteException e) {
+            Log.e(TAG, "error listing Ttys :" + e);
+            return;
+        }
+
+        for (String tty : availableTtys) {
+            for (String pattern : allowedTtys) {
+                if (tty.matches(pattern)) {
+                    synchronized (this) {
+                        if (!mActiveTtys.contains(tty)) {
+                            // TODO - don't hardcode this
+                            try {
+                                // local, remote, dns
+                                service.attachPppd(tty, "169.254.1.128", "169.254.1.1",
+                                        "169.254.1.128", "0.0.0.0");
+                            } catch (Exception e) {
+                                Log.e(TAG, "error calling attachPppd: " + e);
+                                return;
+                            }
+                            Log.d(TAG, "started Pppd on tty " + tty);
+                            mActiveTtys.add(tty);
+                            // TODO - remove this after we detect the new iface
+                            interfaceAdded("ppp0");
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private synchronized void handleTtyDisconnect() {
+        Log.d(TAG, "handleTtyDisconnect");
+
+        // TODO - this should be data-driven rather than hard coded.
+        String[] allowedTtys = new String[1];
+        allowedTtys[0] = new String("ttyGS0");
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+        String[] availableTtys;
+        try {
+            availableTtys = service.listTtys();
+        } catch (RemoteException e) {
+            Log.e(TAG, "error listing Ttys :" + e);
+            return;
+        }
+
+        for (String tty : availableTtys) {
+            for (String pattern : allowedTtys) {
+                if (tty.matches(pattern)) {
+                    synchronized (this) {
+                        if (mActiveTtys.contains(tty)) {
+                            try {
+                                service.detachPppd(tty);
+                            } catch (Exception e) {
+                                Log.e(TAG, "error calling detachPppd on " + tty + " :" + e);
+                            }
+                            mActiveTtys.remove(tty);
+                            // TODO - remove this after we detect the new iface
+                            interfaceRemoved("ppp0");
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public synchronized String[] getTetheredIfaces() {
+        int size = mActiveIfaces.size();
+        String[] result = new String[size];
+        size -= 1;
+        for (int i=0; i< size; i++) {
+            result[i] = mActiveIfaces.get(i);
+        }
+        return result;
+    }
+
+    public synchronized String[] getTetherableIfaces() {
+        int size = mAvailableIfaces.size();
+        String[] result = new String[size];
+        size -= 1;
+        for (int i=0; i< size; i++) {
+            result[i] = mActiveIfaces.get(i);
+        }
+        return result;
+    }
+}
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
index 0a3411a1..784b781 100644
--- a/services/java/com/android/server/status/NotificationData.java
+++ b/services/java/com/android/server/status/NotificationData.java
@@ -19,7 +19,7 @@
     public PendingIntent deleteIntent;
 
     public String toString() {
-        return "NotificationData(package=" + pkg + " tickerText=" + tickerText
+        return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
                 + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
                 + " deleteIntent=" + deleteIntent
                 + " clearable=" + clearable
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
index 6229292..8f1633f 100644
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ b/services/java/com/android/server/status/NotificationViewList.java
@@ -104,10 +104,25 @@
         return null;
     }
 
-    // gets the index of the notification in its expanded parent view
+    // gets the index of the notification's view in its expanded parent view
     int getExpandedIndex(StatusBarNotification notification) {
         ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
-        return list.size() - indexForKey(list, notification.key) - 1;
+        final IBinder key = notification.key;
+        int index = 0;
+        // (the view order is backwards from this list order)
+        for (int i=list.size()-1; i>=0; i--) {
+            StatusBarNotification item = list.get(i);
+            if (item.key == key) {
+                return index;
+            }
+            if (item.view != null) {
+                index++;
+            }
+        }
+        Log.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
+        Log.e(StatusBarService.TAG, "notification=" + notification);
+        dump(notification);
+        return 0;
     }
 
     void clearViews() {
@@ -156,6 +171,13 @@
         list.add(index, notification);
 
         if (StatusBarService.SPEW) {
+            Log.d(StatusBarService.TAG, "NotificationViewList index=" + index);
+            dump(notification);
+        }
+    }
+
+    void dump(StatusBarNotification notification) {
+        if (StatusBarService.SPEW) {
             String s = "";
             for (int i=0; i<mOngoing.size(); i++) {
                 StatusBarNotification that = mOngoing.get(i);
@@ -168,7 +190,7 @@
                 }
                 s += " ";
             }
-            Log.d(StatusBarService.TAG, "NotificationViewList ongoing index=" + index + ": " + s);
+            Log.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s);
 
             s = "";
             for (int i=0; i<mLatest.size(); i++) {
@@ -182,7 +204,7 @@
                 }
                 s += " ";
             }
-            Log.d(StatusBarService.TAG, "NotificationViewList latest  index=" + index + ": " + s);
+            Log.d(StatusBarService.TAG, "NotificationViewList latest:  " + s);
         }
     }
 
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 7b722a5..55041fb 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -687,7 +687,8 @@
                     && (oldData == null
                         || oldData.tickerText == null
                         || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
-                if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+                if (0 == (mDisabled & 
+                    (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
                     mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
                 }
             }
@@ -886,7 +887,8 @@
                 && n.contentView.getPackage() != null
                 && oldData.contentView.getPackage() != null
                 && oldData.contentView.getPackage().equals(n.contentView.getPackage())
-                && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()) {
+                && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
+                && notification.view != null) {
             mNotificationData.update(notification);
             try {
                 n.contentView.reapply(mContext, notification.contentView);
@@ -1697,6 +1699,10 @@
                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
                 }
             }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                mTicker.halt();
+            }
         }
     }
 
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index c2548b9..70d0e78 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -26,6 +26,8 @@
     <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
     <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
     <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
diff --git a/tests/AndroidTests/res/raw/install b/tests/AndroidTests/res/raw/install
new file mode 100644
index 0000000..2ee1f3c
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
new file mode 100755
index 0000000..163ddd5
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests;
+
+import android.net.Uri;
+import android.os.FileUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageStats;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StatFs;
+
+public class PackageManagerTests extends AndroidTestCase {
+    private static final boolean localLOGV = false;
+    public static final String TAG="PackageManagerTests";
+    public final long MAX_WAIT_TIME=60*1000;
+    public final long WAIT_TIME_INCR=10*1000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    private class PackageInstallObserver extends IPackageInstallObserver.Stub {
+        public int returnCode;
+        private boolean doneFlag = false;
+
+        public void packageInstalled(String packageName, int returnCode) {
+            synchronized(this) {
+                this.returnCode = returnCode;
+                doneFlag = true;
+                notifyAll();
+            }
+        }
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+    }
+
+    abstract class GenericReceiver extends BroadcastReceiver {
+        private boolean doneFlag = false;
+        boolean received = false;
+        Intent intent;
+        IntentFilter filter;
+        abstract boolean notifyNow(Intent intent);
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (notifyNow(intent)) {
+                synchronized (this) {
+                    received = true;
+                    doneFlag = true;
+                    this.intent = intent;
+                    notifyAll();
+                }
+            }
+        }
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+
+        public void setFilter(IntentFilter filter) {
+            this.filter = filter;
+        }
+    }
+
+    class InstallReceiver extends GenericReceiver {
+        String pkgName;
+
+        InstallReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addDataScheme("package");
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                return false;
+            }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName.equals(installedPkg)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    PackageManager getPm() {
+        return mContext.getPackageManager();
+    }
+
+    public boolean invokeInstallPackage(Uri packageURI, int flags,
+            final String pkgName, GenericReceiver receiver) throws Exception {
+        PackageInstallObserver observer = new PackageInstallObserver();
+        final boolean received = false;
+        mContext.registerReceiver(receiver, receiver.filter);
+        try {
+            // Wait on observer
+            synchronized(observer) {
+                    getPm().installPackage(packageURI, observer, flags, null);
+                    long waitTime = 0;
+                    while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        observer.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!observer.isDone()) {
+                        throw new Exception("Timed out waiting for packageInstalled callback");
+                    }
+                    if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+                        return false;
+                    }
+                    synchronized (receiver) {
+                    // Verify we received the broadcast
+                    waitTime = 0;
+                    while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        receiver.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!receiver.isDone()) {
+                        throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+                    }
+                    return receiver.received;
+                }
+            }
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    Uri getInstallablePackage(int fileResId, File outFile) {
+        Resources res = mContext.getResources();
+        InputStream is = null;
+        try {
+            is = res.openRawResource(fileResId);
+        } catch (NotFoundException e) {
+            failStr("Failed to load resource with id: " + fileResId);
+        }
+        FileUtils.setPermissions(outFile.getPath(),
+                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+                -1, -1);
+        assertTrue(FileUtils.copyToFile(is, outFile));
+        FileUtils.setPermissions(outFile.getPath(),
+                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+                -1, -1);
+        return Uri.fromFile(outFile);
+    }
+
+    private PackageParser.Package parsePackage(Uri packageURI) {
+        final String archiveFilePath = packageURI.getPath();
+        PackageParser packageParser = new PackageParser(archiveFilePath);
+        File sourceFile = new File(archiveFilePath);
+        DisplayMetrics metrics = new DisplayMetrics();
+        metrics.setToDefaults();
+        return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+    }
+
+    private void assertInstall(String pkgName, int flags) {
+        try {
+        ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+        assertNotNull(info);
+        assertEquals(pkgName, info.packageName);
+        File dataDir = Environment.getDataDirectory();
+        String appInstallPath = new File(dataDir, "app").getPath();
+        String drmInstallPath = new File(dataDir, "app-private").getPath();
+        File srcDir = new File(info.sourceDir);
+        String srcPath = srcDir.getParent();
+        File publicSrcDir = new File(info.publicSourceDir);
+        String publicSrcPath = publicSrcDir.getParent();
+
+        if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+            assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+            assertEquals(srcPath, drmInstallPath);
+            assertEquals(publicSrcPath, appInstallPath);
+        } else {
+            assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+            if ((flags & PackageManager.INSTALL_ON_SDCARD) != 0) {
+                assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+                // Hardcoded for now
+                assertTrue(srcPath.startsWith("/asec"));
+                assertTrue(publicSrcPath.startsWith("/asec"));
+            } else {
+                assertEquals(srcPath, appInstallPath);
+                assertEquals(publicSrcPath, appInstallPath);
+                assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+            }
+        }
+        } catch (NameNotFoundException e) {
+            failStr("failed with exception : " + e);
+        }
+    }
+
+    class InstallParams {
+        String outFileName;
+        Uri packageURI;
+        PackageParser.Package pkg;
+        InstallParams(PackageParser.Package pkg, String outFileName, Uri packageURI) {
+            this.outFileName = outFileName;
+            this.packageURI = packageURI;
+            this.pkg = pkg;
+        }
+    }
+
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install it.
+     */
+    public InstallParams installFromRawResource(int flags, boolean cleanUp) {
+        String outFileName = "install.apk";
+        File filesDir = mContext.getFilesDir();
+        File outFile = new File(filesDir, outFileName);
+        Uri packageURI = getInstallablePackage(R.raw.install, outFile);
+        PackageParser.Package pkg = parsePackage(packageURI);
+        assertNotNull(pkg);
+        InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+        try {
+            try {
+                assertTrue(invokeInstallPackage(packageURI, flags,
+                        pkg.packageName, receiver));
+            } catch (Exception e) {
+                failStr("Failed with exception : " + e);
+            }
+            // Verify installed information
+            assertInstall(pkg.packageName, flags);
+            return new InstallParams(pkg, outFileName, packageURI);
+        } finally {
+            if (cleanUp) {
+                getPm().deletePackage(pkg.packageName, null, 0);
+                if (outFile != null && outFile.exists()) {
+                    outFile.delete();
+                }
+            }
+        }
+    }
+
+    @MediumTest
+    public void testInstallNormalInternal() {
+        installFromRawResource(0, true);
+    }
+
+    @MediumTest
+    public void testInstallFwdLockedInternal() {
+        installFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true);
+    }
+
+    @MediumTest
+    public void testInstallSdcard() {
+        installFromRawResource(PackageManager.INSTALL_ON_SDCARD, true);
+    }
+
+    /* ------------------------- Test replacing packages --------------*/
+    class ReplaceReceiver extends GenericReceiver {
+        String pkgName;
+        boolean removed = false;
+
+        ReplaceReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+                    Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                Uri data = intent.getData();
+                String installedPkg = data.getEncodedSchemeSpecificPart();
+                if (pkgName.equals(installedPkg)) {
+                    if (removed) {
+                        return true;
+                    } else {
+                        removed = true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    /*
+     * Utility function that reads a apk bundled as a raw resource
+     * copies it into own data directory and invokes
+     * PackageManager api to install first and then replace it
+     * again.
+     */
+    public void replaceFromRawResource(int flags) {
+        InstallParams ip = installFromRawResource(flags, false);
+        boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+        GenericReceiver receiver;
+        if (result) {
+            receiver = new ReplaceReceiver(ip.pkg.packageName);
+        } else {
+            receiver = new InstallReceiver(ip.pkg.packageName);
+        }
+        try {
+            try {
+                assertEquals(invokeInstallPackage(ip.packageURI, flags,
+                        ip.pkg.packageName, receiver), result);
+                if (result) {
+                    assertInstall(ip.pkg.packageName, flags);
+                }
+            } catch (Exception e) {
+                failStr("Failed with exception : " + e);
+            }
+        } finally {
+            getPm().deletePackage(ip.pkg.packageName, null, 0);
+            File outFile = new File(ip.outFileName);
+            if (outFile != null && outFile.exists()) {
+                outFile.delete();
+            }
+        }
+    }
+
+    @MediumTest
+    public void testReplaceFailNormalInternal() {
+        replaceFromRawResource(0);
+    }
+
+    @MediumTest
+    public void testReplaceFailFwdLockedInternal() {
+        replaceFromRawResource(PackageManager.INSTALL_FORWARD_LOCK);
+    }
+
+    @MediumTest
+    public void testReplaceFailSdcard() {
+        replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
+    }
+
+    void failStr(String errMsg) {
+        Log.w(TAG, "errMsg="+errMsg);
+        fail(errMsg);
+    }
+    void failStr(Exception e) {
+        Log.w(TAG, "e.getMessage="+e.getMessage());
+        Log.w(TAG, "e="+e);
+    }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index e8a66c1..2667520 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -670,7 +670,12 @@
         public boolean onCreateWindow(WebView view, boolean dialog,
                 boolean userGesture, Message resultMsg) {
             if (!mCanOpenWindows) {
-                return false;
+                // We can't open windows, so just send null back.
+                WebView.WebViewTransport transport =
+                        (WebView.WebViewTransport) resultMsg.obj;
+                transport.setWebView(null);
+                resultMsg.sendToTarget();
+                return true;
             }
 
             // We never display the new window, just create the view and
@@ -688,6 +693,11 @@
             resultMsg.sendToTarget();
             return true;
         }
+
+        @Override
+        public void onCloseWindow(WebView view) {
+            view.destroy();
+        }
     };
 
     private static class NewWindowWebView extends WebView {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index ffc2cbc..f548145 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -121,6 +121,32 @@
             }
         },
 
+        new Test("Bad resource #2") {
+            public void run()
+            {
+                Notification n = new Notification(NotificationTestList.this,
+                            R.drawable.ic_statusbar_missedcall,
+                            null, System.currentTimeMillis()-(1000*60*60*24),
+                            "(453) 123-2328",
+                            "", null);
+                n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+                mNM.notify(2, n);
+            }
+        },
+
+        new Test("Bad resource #3") {
+            public void run()
+            {
+                Notification n = new Notification(NotificationTestList.this,
+                            R.drawable.ic_statusbar_missedcall,
+                            null, System.currentTimeMillis()-(1000*60*60*24),
+                            "(453) 123-2328",
+                            "", null);
+                n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+                mNM.notify(3, n);
+            }
+        },
+
         new Test("Times") {
             public void run()
             {
@@ -405,6 +431,16 @@
             }
         },
 
+        new Test("Persistent #3") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon2, "tock tock tock",
+                        System.currentTimeMillis());
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #3",
+                            "Notify me!!!", makeIntent());
+                mNM.notify(3, n);
+            }
+        },
+
         new Test("Persistent #2 Vibrate") {
             public void run() {
                 Notification n = new Notification(R.drawable.icon2, "tock tock tock",
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 275e5cb6..06506fb 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -62,6 +62,11 @@
                 mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_ALERTS);
             }
         },
+        new Test("Disable Ticker") {
+            public void run() {
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_TICKER);
+            }
+        },
         new Test("Disable Expand in 3 sec.") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
@@ -80,7 +85,7 @@
                     }, 3000);
             }
         },
-        new Test("Disable Both in 3 sec.") {
+        new Test("Disable Expand + Notifications in 3 sec.") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {
@@ -90,7 +95,12 @@
                     }, 3000);
             }
         },
-        new Test("Disable None in 3 sec.") {
+        new Test("Enable everything") {
+            public void run() {
+                mStatusBarManager.disable(0);
+            }
+        },
+        new Test("Enable everything in 3 sec.") {
             public void run() {
                 mHandler.postDelayed(new Runnable() {
                         public void run() {