Merge "Some code refactoring"
diff --git a/api/current.txt b/api/current.txt
index 6ded422..7c78c06 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8937,6 +8937,8 @@
     method public java.lang.String flatten();
     method public java.lang.String get(java.lang.String);
     method public java.lang.String getAntibanding();
+    method public boolean getAutoExposureLock();
+    method public boolean getAutoWhiteBalanceLock();
     method public java.lang.String getColorEffect();
     method public int getExposureCompensation();
     method public float getExposureCompensationStep();
@@ -8982,6 +8984,8 @@
     method public java.lang.String getWhiteBalance();
     method public int getZoom();
     method public java.util.List<java.lang.Integer> getZoomRatios();
+    method public boolean isAutoExposureLockSupported();
+    method public boolean isAutoWhiteBalanceLockSupported();
     method public boolean isSmoothZoomSupported();
     method public boolean isZoomSupported();
     method public void remove(java.lang.String);
@@ -8989,6 +8993,8 @@
     method public void set(java.lang.String, java.lang.String);
     method public void set(java.lang.String, int);
     method public void setAntibanding(java.lang.String);
+    method public void setAutoExposureLock(boolean);
+    method public void setAutoWhiteBalanceLock(boolean);
     method public void setColorEffect(java.lang.String);
     method public void setExposureCompensation(int);
     method public void setFlashMode(java.lang.String);
@@ -14029,6 +14035,7 @@
     method public void dispatchMessage(android.os.Message);
     method public final void dump(android.util.Printer, java.lang.String);
     method public final android.os.Looper getLooper();
+    method public java.lang.String getMessageName(android.os.Message);
     method public void handleMessage(android.os.Message);
     method public final boolean hasMessages(int);
     method public final boolean hasMessages(int, java.lang.Object);
@@ -14098,13 +14105,13 @@
 
   public class Looper {
     method public void dump(android.util.Printer, java.lang.String);
-    method public static final synchronized android.os.Looper getMainLooper();
+    method public static synchronized android.os.Looper getMainLooper();
     method public java.lang.Thread getThread();
-    method public static final void loop();
-    method public static final android.os.Looper myLooper();
-    method public static final android.os.MessageQueue myQueue();
-    method public static final void prepare();
-    method public static final void prepareMainLooper();
+    method public static void loop();
+    method public static android.os.Looper myLooper();
+    method public static android.os.MessageQueue myQueue();
+    method public static void prepare();
+    method public static void prepareMainLooper();
     method public void quit();
     method public void setMessageLogging(android.util.Printer);
   }
@@ -18018,7 +18025,6 @@
 
   public abstract interface SynthesisCallback {
     method public abstract int audioAvailable(byte[], int, int);
-    method public abstract int completeAudioAvailable(int, int, int, byte[], int, int);
     method public abstract int done();
     method public abstract void error();
     method public abstract int getMaxBufferSize();
@@ -22074,6 +22080,7 @@
     method public void buildDrawingCache();
     method public void buildDrawingCache(boolean);
     method public void buildLayer();
+    method protected boolean canResolveLayoutDirection();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
     method public void cancelLongPress();
@@ -22636,8 +22643,10 @@
     ctor public ViewDebug();
     method public static void dumpCapturedView(java.lang.String, java.lang.Object);
     method public static void startHierarchyTracing(java.lang.String, android.view.View);
+    method public static void startLooperProfiling(java.io.File);
     method public static void startRecyclerTracing(java.lang.String, android.view.View);
     method public static void stopHierarchyTracing();
+    method public static void stopLooperProfiling();
     method public static void stopRecyclerTracing();
     method public static void trace(android.view.View, android.view.ViewDebug.RecyclerTraceType, int...);
     method public static void trace(android.view.View, android.view.ViewDebug.HierarchyTraceType);
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index bbf6b14..e9ee95d 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -17,19 +17,135 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 
-#include <cutils/properties.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/route.h>
 
+#define LOG_TAG "ip-up-vpn"
+#include <cutils/log.h>
+
+#define DIR "/data/misc/vpn/"
+
+static const char *env(const char *name) {
+    const char *value = getenv(name);
+    return value ? value : "";
+}
+
+static int set_address(struct sockaddr *sa, const char *address) {
+    sa->sa_family = AF_INET;
+    return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr);
+}
+
+/*
+ * The primary goal is to create a file with VPN parameters. Currently they
+ * are interface, addresses, routes, DNS servers, and search domains. Each
+ * parameter occupies one line in the file, and it can be an empty string or
+ * space-separated values. The order and the format must be consistent with
+ * com.android.server.connectivity.Vpn. Here is an example.
+ *
+ *   ppp0
+ *   192.168.1.100/24
+ *   0.0.0.0/0
+ *   192.168.1.1 192.168.1.2
+ *   example.org
+ *
+ * The secondary goal is to unify the outcome of VPN. The current baseline
+ * is to have an interface configured with the given address and netmask
+ * and maybe add a host route to protect the tunnel. PPP-based VPN already
+ * does this, but others might not. Routes, DNS servers, and search domains
+ * are handled by the framework since they can be overridden by the users.
+ */
 int main(int argc, char **argv)
 {
-    if (argc > 1 && strlen(argv[1]) > 0) {
-        char dns[PROPERTY_VALUE_MAX];
-        char *dns1 = getenv("DNS1");
-        char *dns2 = getenv("DNS2");
+    FILE *state = fopen(DIR ".tmp", "wb");
+    if (!state) {
+        LOGE("Cannot create state: %s", strerror(errno));
+        return 1;
+    }
 
-        snprintf(dns, sizeof(dns), "%s %s", dns1 ? dns1 : "", dns2 ? dns2 : "");
-        property_set("vpn.dns", dns);
-        property_set("vpn.via", argv[1]);
+    if (argc >= 6) {
+        /* Invoked by pppd. */
+        fprintf(state, "%s\n", argv[1]);
+        fprintf(state, "%s/32\n", argv[4]);
+        fprintf(state, "0.0.0.0/0\n");
+        fprintf(state, "%s %s\n", env("DNS1"), env("DNS2"));
+        fprintf(state, "\n");
+    } else if (argc == 2) {
+        /* Invoked by racoon. */
+        const char *interface = env("INTERFACE");
+        const char *address = env("INTERNAL_ADDR4");
+        const char *routes = env("SPLIT_INCLUDE_CIDR");
+
+        int s = socket(AF_INET, SOCK_DGRAM, 0);
+        struct rtentry rt;
+        struct ifreq ifr;
+
+        memset(&rt, 0, sizeof(rt));
+        memset(&ifr, 0, sizeof(ifr));
+
+        /* Remove the old host route. There could be more than one. */
+        rt.rt_flags |= RTF_UP | RTF_HOST;
+        if (set_address(&rt.rt_dst, env("REMOTE_ADDR"))) {
+            while (!ioctl(s, SIOCDELRT, &rt));
+        }
+        if (errno != ESRCH) {
+            LOGE("Cannot remove host route: %s", strerror(errno));
+            return 1;
+        }
+
+        /* Create a new host route. */
+        rt.rt_flags |= RTF_GATEWAY;
+        if (!set_address(&rt.rt_gateway, argv[1]) ||
+                (ioctl(s, SIOCADDRT, &rt) && errno != EEXIST)) {
+            LOGE("Cannot create host route: %s", strerror(errno));
+            return 1;
+        }
+
+        /* Bring up the interface. */
+        ifr.ifr_flags = IFF_UP;
+        strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+        if (ioctl(s, SIOCSIFFLAGS, &ifr)) {
+            LOGE("Cannot bring up %s: %s", interface, strerror(errno));
+            return 1;
+        }
+
+        /* Set the address. */
+        if (!set_address(&ifr.ifr_addr, address) ||
+                ioctl(s, SIOCSIFADDR, &ifr)) {
+            LOGE("Cannot set address: %s", strerror(errno));
+            return 1;
+        }
+
+        /* Set the netmask. */
+        if (!set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4")) ||
+                ioctl(s, SIOCSIFNETMASK, &ifr)) {
+            LOGE("Cannot set netmask: %s", strerror(errno));
+            return 1;
+        }
+
+        /* TODO: Send few packets to trigger phase 2? */
+
+        fprintf(state, "%s\n", interface);
+        fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4"));
+        fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0");
+        fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST"));
+        fprintf(state, "%s\n", env("DEFAULT_DOMAIN"));
+    } else {
+        LOGE("Cannot parse parameters");
+        return 1;
+    }
+
+    fclose(state);
+    if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) {
+        LOGE("Cannot write state: %s", strerror(errno));
+        return 1;
     }
     return 0;
 }
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index d25de97..12a4dbb 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -235,7 +235,7 @@
             PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
             PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
             PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
-            defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this,
+            defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder((Object)null,
                     pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
             defaultChangeIn.setDuration(DEFAULT_DURATION);
             defaultChangeIn.setStartDelay(mChangingAppearingDelay);
@@ -244,11 +244,11 @@
             defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
             defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
 
-            defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f);
+            defaultFadeIn = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
             defaultFadeIn.setDuration(DEFAULT_DURATION);
             defaultFadeIn.setStartDelay(mAppearingDelay);
             defaultFadeIn.setInterpolator(mAppearingInterpolator);
-            defaultFadeOut = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f);
+            defaultFadeOut = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
             defaultFadeOut.setDuration(DEFAULT_DURATION);
             defaultFadeOut.setStartDelay(mDisappearingDelay);
             defaultFadeOut.setInterpolator(mDisappearingInterpolator);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8a42693..7d67e11 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -720,8 +720,20 @@
          * onAutoFocus will be called immediately with a fake value of
          * <code>success</code> set to <code>true</code>.
          *
+         * The auto-focus routine may lock auto-exposure and auto-white balance
+         * after it completes. To check for the state of these locks, use the
+         * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} and
+         * {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()}
+         * methods. If such locking is undesirable, use
+         * {@link android.hardware.Camera.Parameters#setAutoExposureLock(boolean)}
+         * and
+         * {@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)}
+         * to release the locks.
+         *
          * @param success true if focus was successful, false if otherwise
          * @param camera  the Camera service object
+         * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean)
+         * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)
          */
         void onAutoFocus(boolean success, Camera camera);
     };
@@ -747,8 +759,21 @@
      * {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}, flash may be
      * fired during auto-focus, depending on the driver and camera hardware.<p>
      *
+     * The auto-focus routine may lock auto-exposure and auto-white balance
+     * after it completes. To check for the state of these locks, use the
+     * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} and
+     * {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()}
+     * methods after the {@link AutoFocusCallback#onAutoFocus(boolean, Camera)}
+     * callback is invoked. If such locking is undesirable, use
+     * {@link android.hardware.Camera.Parameters#setAutoExposureLock(boolean)}
+     * and
+     * {@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)}
+     * to release the locks.
+     *
      * @param cb the callback to run
      * @see #cancelAutoFocus()
+     * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean)
+     * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)
      */
     public final void autoFocus(AutoFocusCallback cb)
     {
@@ -763,7 +788,13 @@
      * this function will return the focus position to the default.
      * If the camera does not support auto-focus, this is a no-op.
      *
+     * Canceling auto-focus will return the auto-exposure lock and auto-white
+     * balance lock to their state before {@link #autoFocus(AutoFocusCallback)}
+     * was called.
+     *
      * @see #autoFocus(Camera.AutoFocusCallback)
+     * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean)
+     * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)
      */
     public final void cancelAutoFocus()
     {
@@ -2562,8 +2593,6 @@
          *        routine is free to run normally.
          *
          * @see #getAutoExposureLock()
-         *
-         * @hide
          */
         public void setAutoExposureLock(boolean toggle) {
             set(KEY_AUTO_EXPOSURE_LOCK, toggle ? TRUE : FALSE);
@@ -2583,7 +2612,6 @@
          *
          * @see #setAutoExposureLock(boolean)
          *
-         * @hide
          */
         public boolean getAutoExposureLock() {
             String str = get(KEY_AUTO_EXPOSURE_LOCK);
@@ -2598,7 +2626,6 @@
          * @return true if auto-exposure lock is supported.
          * @see #setAutoExposureLock(boolean)
          *
-         * @hide
          */
         public boolean isAutoExposureLockSupported() {
             String str = get(KEY_AUTO_EXPOSURE_LOCK_SUPPORTED);
@@ -2645,8 +2672,6 @@
          *        auto-white balance routine is free to run normally.
          *
          * @see #getAutoWhiteBalanceLock()
-         *
-         * @hide
          */
         public void setAutoWhiteBalanceLock(boolean toggle) {
             set(KEY_AUTO_WHITEBALANCE_LOCK, toggle ? TRUE : FALSE);
@@ -2668,7 +2693,6 @@
          *
          * @see #setAutoWhiteBalanceLock(boolean)
          *
-         * @hide
          */
         public boolean getAutoWhiteBalanceLock() {
             String str = get(KEY_AUTO_WHITEBALANCE_LOCK);
@@ -2683,7 +2707,6 @@
          * @return true if auto-white balance lock is supported.
          * @see #setAutoWhiteBalanceLock(boolean)
          *
-         * @hide
          */
         public boolean isAutoWhiteBalanceLockSupported() {
             String str = get(KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED);
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 3447e76c..44e7e52 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -98,11 +98,8 @@
     }
     
     @Override public boolean onTextContextMenuItem(int id) {
-        // Horrible hack: select word option has to be handled by original view to work.
-        if (mIME != null && id != android.R.id.startSelectingText) {
-            if (mIME.onExtractTextContextMenuItem(id)) {
-                return true;
-            }
+        if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
+            return true;
         }
         return super.onTextContextMenuItem(id);
     }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d6f5643..d95fc8d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -100,7 +100,7 @@
 
     void setDataDependency(int networkType, boolean met);
 
-    void protectVpn(in ParcelFileDescriptor socket);
+    boolean protectVpn(in ParcelFileDescriptor socket);
 
     boolean prepareVpn(String oldPackage, String newPackage);
 
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index f2f0e82..9826bec 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -57,16 +57,16 @@
     private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
     private ProxyProperties mHttpProxy;
 
-    public static class CompareAddressesResult {
-        public ArrayList<LinkAddress> removed = new ArrayList<LinkAddress>();
-        public ArrayList<LinkAddress> added = new ArrayList<LinkAddress>();
+    public static class CompareResult<T> {
+        public ArrayList<T> removed = new ArrayList<T>();
+        public ArrayList<T> added = new ArrayList<T>();
 
         @Override
         public String toString() {
-            String retVal = "removedAddresses=[";
-            for (LinkAddress addr : removed) retVal += addr.toString() + ",";
-            retVal += "] addedAddresses=[";
-            for (LinkAddress addr : added) retVal += addr.toString() + ",";
+            String retVal = "removed=[";
+            for (T addr : removed) retVal += addr.toString() + ",";
+            retVal += "] added=[";
+            for (T addr : added) retVal += addr.toString() + ",";
             retVal += "]";
             return retVal;
         }
@@ -263,10 +263,10 @@
      * mLinkAddress which would then result in target and mLinkAddresses
      * being the same list.
      *
-     * @param target is a new list of addresses
+     * @param target is a LinkProperties with the new list of addresses
      * @return the removed and added lists.
      */
-    public CompareAddressesResult compareAddresses(LinkProperties target) {
+    public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
         /*
          * Duplicate the LinkAddresses into removed, we will be removing
          * address which are common between mLinkAddresses and target
@@ -274,17 +274,81 @@
          * are in target but not in mLinkAddresses are placed in the
          * addedAddresses.
          */
-        CompareAddressesResult result = new CompareAddressesResult();
+        CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
         result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
         result.added.clear();
-        for (LinkAddress newAddress : target.getLinkAddresses()) {
-            if (! result.removed.remove(newAddress)) {
-                result.added.add(newAddress);
+        if (target != null) {
+            for (LinkAddress newAddress : target.getLinkAddresses()) {
+                if (! result.removed.remove(newAddress)) {
+                    result.added.add(newAddress);
+                }
             }
         }
         return result;
     }
 
+    /**
+     * Return two lists, a list of dns addresses that would be removed from
+     * mDnses and a list of addresses that would be added to
+     * mDnses which would then result in target and mDnses
+     * being the same list.
+     *
+     * @param target is a LinkProperties with the new list of dns addresses
+     * @return the removed and added lists.
+     */
+    public CompareResult<InetAddress> compareDnses(LinkProperties target) {
+        /*
+         * Duplicate the InetAddresses into removed, we will be removing
+         * dns address which are common between mDnses and target
+         * leaving the addresses that are different. And dns address which
+         * are in target but not in mDnses are placed in the
+         * addedAddresses.
+         */
+        CompareResult<InetAddress> result = new CompareResult<InetAddress>();
+
+        result.removed = new ArrayList<InetAddress>(mDnses);
+        result.added.clear();
+        if (target != null) {
+            for (InetAddress newAddress : target.getDnses()) {
+                if (! result.removed.remove(newAddress)) {
+                    result.added.add(newAddress);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Return two lists, a list of routes that would be removed from
+     * mRoutes and a list of routes that would be added to
+     * mRoutes which would then result in target and mRoutes
+     * being the same list.
+     *
+     * @param target is a LinkProperties with the new list of routes
+     * @return the removed and added lists.
+     */
+    public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
+        /*
+         * Duplicate the RouteInfos into removed, we will be removing
+         * routes which are common between mDnses and target
+         * leaving the routes that are different. And route address which
+         * are in target but not in mRoutes are placed in added.
+         */
+        CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
+
+        result.removed = new ArrayList<RouteInfo>(mRoutes);
+        result.added.clear();
+        if (target != null) {
+            for (RouteInfo r : target.getRoutes()) {
+                if (! result.removed.remove(r)) {
+                    result.added.add(r);
+                }
+            }
+        }
+        return result;
+    }
+
+
     @Override
     /**
      * generate hashcode based on significant fields
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 9381f1d..1ef0d9d 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -18,6 +18,7 @@
 
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -38,41 +39,69 @@
  */
 public class NetworkTemplate implements Parcelable {
 
+    /** {@hide} */
+    public static final int MATCH_MOBILE_ALL = 1;
+    /** {@hide} */
+    public static final int MATCH_MOBILE_3G_LOWER = 2;
+    /** {@hide} */
+    public static final int MATCH_MOBILE_4G = 3;
+    /** {@hide} */
+    public static final int MATCH_WIFI = 4;
+    /** {@hide} */
+    public static final int MATCH_ETHERNET = 5;
+
     /**
      * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
      * networks together. Only uses statistics for requested IMSI.
      */
-    public static final int MATCH_MOBILE_ALL = 1;
+    public static NetworkTemplate buildTemplateMobileAll(String subscriberId) {
+        return new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
+    }
 
     /**
      * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
      * networks together that roughly meet a "3G" definition, or lower. Only
      * uses statistics for requested IMSI.
      */
-    public static final int MATCH_MOBILE_3G_LOWER = 2;
+    public static NetworkTemplate buildTemplateMobile3gLower(String subscriberId) {
+        return new NetworkTemplate(MATCH_MOBILE_3G_LOWER, subscriberId);
+    }
 
     /**
      * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
      * networks together that meet a "4G" definition. Only uses statistics for
      * requested IMSI.
      */
-    public static final int MATCH_MOBILE_4G = 3;
+    public static NetworkTemplate buildTemplateMobile4g(String subscriberId) {
+        return new NetworkTemplate(MATCH_MOBILE_4G, subscriberId);
+    }
 
     /**
      * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
      * networks together.
      */
-    public static final int MATCH_WIFI = 4;
+    public static NetworkTemplate buildTemplateWifi() {
+        return new NetworkTemplate(MATCH_WIFI, null);
+    }
 
-    final int mMatchRule;
-    final String mSubscriberId;
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_ETHERNET} style
+     * networks together.
+     */
+    public static NetworkTemplate buildTemplateEthernet() {
+        return new NetworkTemplate(MATCH_ETHERNET, null);
+    }
 
+    private final int mMatchRule;
+    private final String mSubscriberId;
+
+    /** {@hide} */
     public NetworkTemplate(int matchRule, String subscriberId) {
         this.mMatchRule = matchRule;
         this.mSubscriberId = subscriberId;
     }
 
-    public NetworkTemplate(Parcel in) {
+    private NetworkTemplate(Parcel in) {
         mMatchRule = in.readInt();
         mSubscriberId = in.readString();
     }
@@ -110,10 +139,12 @@
         return false;
     }
 
+    /** {@hide} */
     public int getMatchRule() {
         return mMatchRule;
     }
 
+    /** {@hide} */
     public String getSubscriberId() {
         return mSubscriberId;
     }
@@ -131,6 +162,8 @@
                 return matchesMobile4g(ident);
             case MATCH_WIFI:
                 return matchesWifi(ident);
+            case MATCH_ETHERNET:
+                return matchesEthernet(ident);
             default:
                 throw new IllegalArgumentException("unknown network template");
         }
@@ -190,7 +223,17 @@
         return false;
     }
 
-    public static String getMatchRuleName(int matchRule) {
+    /**
+     * Check if matches Ethernet network template.
+     */
+    private boolean matchesEthernet(NetworkIdentity ident) {
+        if (ident.mType == TYPE_ETHERNET) {
+            return true;
+        }
+        return false;
+    }
+
+    private static String getMatchRuleName(int matchRule) {
         switch (matchRule) {
             case MATCH_MOBILE_3G_LOWER:
                 return "MOBILE_3G_LOWER";
@@ -200,6 +243,8 @@
                 return "MOBILE_ALL";
             case MATCH_WIFI:
                 return "WIFI";
+            case MATCH_ETHERNET:
+                return "ETHERNET";
             default:
                 return "UNKNOWN";
         }
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 8e5ddda..275f32a 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -43,6 +43,7 @@
     private final InetAddress mGateway;
 
     private final boolean mIsDefault;
+    private final boolean mIsHost;
 
     public RouteInfo(LinkAddress destination, InetAddress gateway) {
         if (destination == null) {
@@ -68,6 +69,7 @@
                 destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
         mGateway = gateway;
         mIsDefault = isDefault();
+        mIsHost = isHost();
     }
 
     public RouteInfo(InetAddress gateway) {
@@ -88,6 +90,10 @@
         }
     }
 
+    private boolean isHost() {
+        return (mGateway.equals(Inet4Address.ANY) || mGateway.equals(Inet6Address.ANY));
+    }
+
     private boolean isDefault() {
         boolean val = false;
         if (mGateway != null) {
@@ -100,6 +106,7 @@
         return val;
     }
 
+
     public LinkAddress getDestination() {
         return mDestination;
     }
@@ -112,6 +119,10 @@
         return mIsDefault;
     }
 
+    public boolean isHostRoute() {
+        return mIsHost;
+    }
+
     public String toString() {
         String val = "";
         if (mDestination != null) val = mDestination.toString();
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 165e438..cd39d5c 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -169,6 +169,21 @@
     }
 
     /**
+     * Returns a string representing the name of the specified message.
+     * The default implementation will either return the class name of the
+     * message callback if any, or the hexadecimal representation of the
+     * message "what" field.
+     *  
+     * @param message The message whose name is being queried 
+     */
+    public String getMessageName(Message message) {
+        if (message.callback != null) {
+            return message.callback.getClass().getName();
+        }
+        return "0x" + Integer.toHexString(message.what);
+    }
+
+    /**
      * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
      * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
      *  If you don't want that facility, just call Message.obtain() instead.
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index c0be664..720e802b 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -52,7 +52,6 @@
   */
 public class Looper {
     private static final String TAG = "Looper";
-    private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE);
 
     // sThreadLocal.get() will return null unless you've called prepare().
     private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@@ -70,7 +69,7 @@
       * {@link #loop()} after calling this method, and end it by calling
       * {@link #quit()}.
       */
-    public static final void prepare() {
+    public static void prepare() {
         if (sThreadLocal.get() != null) {
             throw new RuntimeException("Only one Looper may be created per thread");
         }
@@ -83,7 +82,7 @@
      * is created by the Android environment, so you should never need
      * to call this function yourself.  See also: {@link #prepare()}
      */
-    public static final void prepareMainLooper() {
+    public static void prepareMainLooper() {
         prepare();
         setMainLooper(myLooper());
         myLooper().mQueue.mQuitAllowed = false;
@@ -95,7 +94,7 @@
 
     /** Returns the application's main looper, which lives in the main thread of the application.
      */
-    public synchronized static final Looper getMainLooper() {
+    public synchronized static Looper getMainLooper() {
         return mMainLooper;
     }
 
@@ -103,7 +102,7 @@
      * Run the message queue in this thread. Be sure to call
      * {@link #quit()} to end the loop.
      */
-    public static final void loop() {
+    public static void loop() {
         Looper me = myLooper();
         if (me == null) {
             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
@@ -122,20 +121,36 @@
                     // No target is a magic identifier for the quit message.
                     return;
                 }
-                if (me.mLogging != null) me.mLogging.println(
-                        ">>>>> Dispatching to " + msg.target + " "
-                        + msg.callback + ": " + msg.what
-                        );
+
+                long wallStart = 0;
+                long threadStart = 0;
+
+                // This must be in a local variable, in case a UI event sets the logger
+                Printer logging = me.mLogging;
+                if (logging != null) {
+                    logging.println(">>>>> Dispatching to " + msg.target + " " +
+                            msg.callback + ": " + msg.what);
+                    wallStart = System.currentTimeMillis();
+                    threadStart = SystemClock.currentThreadTimeMillis();
+                }
+
                 msg.target.dispatchMessage(msg);
-                if (me.mLogging != null) me.mLogging.println(
-                        "<<<<< Finished to    " + msg.target + " "
-                        + msg.callback);
-                
+
+                if (logging != null) {
+                    long wallTime = System.currentTimeMillis() - wallStart;
+                    long threadTime = SystemClock.currentThreadTimeMillis() - threadStart;
+
+                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
+                    if (logging instanceof Profiler) {
+                        ((Profiler) logging).profile(msg, wallStart, wallTime, threadTime);
+                    }
+                }
+
                 // Make sure that during the course of dispatching the
                 // identity of the thread wasn't corrupted.
                 final long newIdent = Binder.clearCallingIdentity();
                 if (ident != newIdent) {
-                    Log.wtf("Looper", "Thread identity changed from 0x"
+                    Log.wtf(TAG, "Thread identity changed from 0x"
                             + Long.toHexString(ident) + " to 0x"
                             + Long.toHexString(newIdent) + " while dispatching to "
                             + msg.target.getClass().getName() + " "
@@ -151,7 +166,7 @@
      * Return the Looper object associated with the current thread.  Returns
      * null if the calling thread is not associated with a Looper.
      */
-    public static final Looper myLooper() {
+    public static Looper myLooper() {
         return sThreadLocal.get();
     }
 
@@ -173,7 +188,7 @@
      * thread.  This must be called from a thread running a Looper, or a
      * NullPointerException will be thrown.
      */
-    public static final MessageQueue myQueue() {
+    public static MessageQueue myQueue() {
         return myLooper().mQueue;
     }
 
@@ -225,23 +240,13 @@
     }
 
     public String toString() {
-        return "Looper{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + "}";
+        return "Looper{" + Integer.toHexString(System.identityHashCode(this)) + "}";
     }
 
-    static class HandlerException extends Exception {
-
-        HandlerException(Message message, Throwable cause) {
-            super(createMessage(cause), cause);
-        }
-
-        static String createMessage(Throwable cause) {
-            String causeMsg = cause.getMessage();
-            if (causeMsg == null) {
-                causeMsg = cause.toString();
-            }
-            return causeMsg;
-        }
+    /**
+     * @hide
+     */
+    public static interface Profiler {
+        void profile(Message message, long wallStart, long wallTime, long threadTime);
     }
 }
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index b8919c2..7133d3a 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -77,6 +77,11 @@
     }
 
     @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        return a.getInt(index, 0);
+    }
+
+    @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         if (event.getAction() != KeyEvent.ACTION_UP) {
             if (keyCode == KeyEvent.KEYCODE_PLUS
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index f799af3..f3bcedb 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -283,6 +283,13 @@
          */
         public static final String IS_DRM = "is_drm";
 
+        /**
+         * Used by the media scanner to suppress files from being processed as media files.
+         *
+         * <P>Type: INTEGER (boolean)</P>
+         * @hide
+         */
+        public static final String NO_MEDIA = "no_media";
      }
 
     /**
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index 1210941..dea708a 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -31,8 +31,7 @@
 
     private static final int SYNTHESIS_START = 1;
     private static final int SYNTHESIS_DATA_AVAILABLE = 2;
-    private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3;
-    private static final int SYNTHESIS_DONE = 4;
+    private static final int SYNTHESIS_DONE = 3;
 
     private static final int PLAY_AUDIO = 5;
     private static final int PLAY_SILENCE = 6;
@@ -120,10 +119,6 @@
         mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token));
     }
 
-    void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) {
-        mQueue.add(new ListEntry(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token));
-    }
-
     void enqueueSynthesisDone(SynthesisMessageParams token) {
         mQueue.add(new ListEntry(SYNTHESIS_DONE, token));
     }
@@ -280,8 +275,6 @@
             handleSynthesisDataAvailable(msg);
         } else if (entry.mWhat == SYNTHESIS_DONE) {
             handleSynthesisDone(msg);
-        } else if (entry.mWhat == SYNTHESIS_COMPLETE_DATA_AVAILABLE) {
-            handleSynthesisCompleteDataAvailable(msg);
         } else if (entry.mWhat == PLAY_AUDIO) {
             handleAudio(msg);
         } else if (entry.mWhat == PLAY_SILENCE) {
@@ -424,54 +417,11 @@
             return;
         }
 
-        final AudioTrack track = params.mAudioTrack;
+        final AudioTrack audioTrack = params.mAudioTrack;
         final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat);
         final int lengthInBytes = params.mBytesWritten;
+        final int lengthInFrames = lengthInBytes / bytesPerFrame;
 
-        blockUntilDone(track, bytesPerFrame, lengthInBytes);
-    }
-
-    private void handleSynthesisCompleteDataAvailable(MessageParams msg) {
-        final SynthesisMessageParams params = (SynthesisMessageParams) msg;
-        if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
-
-        params.mLogger.onPlaybackStart();
-
-        // Channel config and bytes per frame are checked before
-        // this message is sent.
-        int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount);
-        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat);
-
-        SynthesisMessageParams.ListEntry entry = params.getNextBuffer();
-
-        if (entry == null) {
-            Log.w(TAG, "completeDataAvailable : No buffers available to play.");
-            return;
-        }
-
-        final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz,
-                channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC);
-
-        // So that handleDone can access this correctly.
-        params.mAudioTrack = audioTrack;
-
-        try {
-            audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength);
-            setupVolume(audioTrack, params.mVolume, params.mPan);
-            audioTrack.play();
-            blockUntilDone(audioTrack, bytesPerFrame, entry.mLength);
-            if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength);
-        } catch (IllegalStateException ex) {
-            Log.e(TAG, "Playback error", ex);
-        } finally {
-            handleSynthesisDone(msg);
-        }
-    }
-
-
-    private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame,
-            int lengthInBytes) {
-        int lengthInFrames = lengthInBytes / bytesPerFrame;
         int currentPosition = 0;
         while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
             if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java
index 4f4b3fb..5808919 100644
--- a/core/java/android/speech/tts/FileSynthesisCallback.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -187,37 +187,6 @@
         }
     }
 
-    @Override
-    public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
-            byte[] buffer, int offset, int length) {
-        synchronized (mStateLock) {
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-        }
-        FileOutputStream out = null;
-        try {
-            out = new FileOutputStream(mFileName);
-            out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length));
-            out.write(buffer, offset, length);
-            mDone = true;
-            return TextToSpeech.SUCCESS;
-        } catch (IOException ex) {
-            Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
-            mFileName.delete();
-            return TextToSpeech.ERROR;
-        } finally {
-            try {
-                if (out != null) {
-                    out.close();
-                }
-            } catch (IOException ex) {
-                Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
-            }
-        }
-    }
-
     private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
             int dataLength) {
         // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 38030a6..04bd745 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -53,8 +53,7 @@
 
     // Handler associated with a thread that plays back audio requests.
     private final AudioPlaybackHandler mAudioTrackHandler;
-    // A request "token", which will be non null after start() or
-    // completeAudioAvailable() have been called.
+    // A request "token", which will be non null after start() has been called.
     private SynthesisMessageParams mToken = null;
     // Whether this request has been stopped. This is useful for keeping
     // track whether stop() has been called before start(). In all other cases,
@@ -206,35 +205,4 @@
         stop();
     }
 
-    @Override
-    public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
-            byte[] buffer, int offset, int length) {
-        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
-        if (channelConfig == 0) {
-            Log.e(TAG, "Unsupported number of channels :" + channelCount);
-            return TextToSpeech.ERROR;
-        }
-
-        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(audioFormat);
-        if (bytesPerFrame < 0) {
-            Log.e(TAG, "Unsupported audio format :" + audioFormat);
-            return TextToSpeech.ERROR;
-        }
-
-        synchronized (mStateLock) {
-            if (mStopped) {
-                return TextToSpeech.ERROR;
-            }
-            SynthesisMessageParams params = new SynthesisMessageParams(
-                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
-                    mDispatcher, mCallingApp, mLogger);
-            params.addBuffer(buffer, offset, length);
-
-            mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params);
-            mToken = params;
-        }
-
-        return TextToSpeech.SUCCESS;
-    }
-
 }
diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java
index 1b80e40..d70c371d 100644
--- a/core/java/android/speech/tts/SynthesisCallback.java
+++ b/core/java/android/speech/tts/SynthesisCallback.java
@@ -22,19 +22,16 @@
  * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
  * {@link #done}.
  *
- * Alternatively, the engine can provide all the audio at once, by using
- * {@link #completeAudioAvailable}.
  *
  * {@link #error} can be called at any stage in the synthesis process to
- * indicate that an error has occured, but if the call is made after a call
- * to {@link #done} or {@link #completeAudioAvailable} it might be discarded.
+ * indicate that an error has occurred, but if the call is made after a call
+ * to {@link #done}, it might be discarded.
  */
 public interface SynthesisCallback {
     /**
      * @return the maximum number of bytes that the TTS engine can pass in a single call of
-     *         {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
-     *         Calls to {@link #audioAvailable} with data lengths larger than this
-     *         value will not succeed.
+     *         {@link #audioAvailable}. Calls to {@link #audioAvailable} with data lengths
+     *         larger than this value will not succeed.
      */
     public int getMaxBufferSize();
 
@@ -69,23 +66,6 @@
     public int audioAvailable(byte[] buffer, int offset, int length);
 
     /**
-     * The service can call this method instead of using {@link #start}, {@link #audioAvailable}
-     * and {@link #done} if all the audio data is available in a single buffer.
-     *
-     * @param sampleRateInHz Sample rate in HZ of the generated audio.
-     * @param audioFormat Audio format of the generated audio. Must be one of
-     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
-     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
-     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
-     *         so the caller is free to modify it after this method returns.
-     * @param offset The offset into {@code buffer} where the audio data starts.
-     * @param length The number of bytes of audio data in {@code buffer}.
-     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
-     */
-    public int completeAudioAvailable(int sampleRateInHz, int audioFormat,
-            int channelCount, byte[] buffer, int offset, int length);
-
-    /**
      * The service should call this method when all the synthesized audio for a request has
      * been passed to {@link #audioAvailable}.
      *
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 757a8c3..5a244f1 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -226,7 +226,17 @@
      */
     public static Metrics isBoring(CharSequence text,
                                    TextPaint paint) {
-        return isBoring(text, paint, null);
+        return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
+    }
+
+    /**
+     * Returns null if not boring; the width, ascent, and descent if boring.
+     * @hide
+     */
+    public static Metrics isBoring(CharSequence text,
+                                   TextPaint paint,
+                                   TextDirectionHeuristic textDir) {
+        return isBoring(text, paint, textDir, null);
     }
 
     /**
@@ -235,6 +245,17 @@
      * if boring.
      */
     public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
+        return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
+    }
+
+    /**
+     * Returns null if not boring; the width, ascent, and descent in the
+     * provided Metrics object (or a new one if the provided one was null)
+     * if boring.
+     * @hide
+     */
+    public static Metrics isBoring(CharSequence text, TextPaint paint,
+            TextDirectionHeuristic textDir, Metrics metrics) {
         char[] temp = TextUtils.obtain(500);
         int length = text.length();
         boolean boring = true;
@@ -258,6 +279,11 @@
                     break outer;
                 }
             }
+
+            if (textDir.isRtl(temp, 0, n)) {
+               boring = false;
+               break outer;
+            }
         }
 
         TextUtils.recycle(temp);
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index f196b34..cb96969 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -75,12 +75,31 @@
                          float spacingmult, float spacingadd,
                          boolean includepad,
                          TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
-        super((ellipsize == null) 
-                ? display 
-                : (display instanceof Spanned) 
-                    ? new SpannedEllipsizer(display) 
+        this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth);
+    }
+
+    /**
+     * Make a layout for the transformed text (password transformation
+     * being the primary example of a transformation)
+     * that will be updated as the base text is changed.
+     * If ellipsize is non-null, the Layout will ellipsize the text
+     * down to ellipsizedWidth.
+     * *
+     * *@hide
+     */
+    public DynamicLayout(CharSequence base, CharSequence display,
+                         TextPaint paint,
+                         int width, Alignment align, TextDirectionHeuristic textDir,
+                         float spacingmult, float spacingadd,
+                         boolean includepad,
+                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+        super((ellipsize == null)
+                ? display
+                : (display instanceof Spanned)
+                    ? new SpannedEllipsizer(display)
                     : new Ellipsizer(display),
-              paint, width, align, spacingmult, spacingadd);
+              paint, width, align, textDir, spacingmult, spacingadd);
 
         mBase = base;
         mDisplay = display;
@@ -259,7 +278,7 @@
             reflowed = new StaticLayout(true);
 
         reflowed.generate(text, where, where + after,
-                getPaint(), getWidth(), getAlignment(),
+                getPaint(), getWidth(), getAlignment(), getTextDirectionHeuristic(),
                 getSpacingMultiplier(), getSpacingAdd(),
                 false, true, mEllipsizedWidth, mEllipsizeAt);
         int n = reflowed.getLineCount();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index aae9ccf..eabeef0 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,8 +16,6 @@
 
 package android.text;
 
-import com.android.internal.util.ArrayUtils;
-
 import android.emoji.EmojiFactory;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -32,6 +30,8 @@
 import android.text.style.ReplacementSpan;
 import android.text.style.TabStopSpan;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.util.Arrays;
 
 /**
@@ -113,6 +113,29 @@
     protected Layout(CharSequence text, TextPaint paint,
                      int width, Alignment align,
                      float spacingMult, float spacingAdd) {
+        this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
+                spacingMult, spacingAdd);
+    }
+
+    /**
+     * Subclasses of Layout use this constructor to set the display text,
+     * width, and other standard properties.
+     * @param text the text to render
+     * @param paint the default paint for the layout.  Styles can override
+     * various attributes of the paint.
+     * @param width the wrapping width for the text.
+     * @param align whether to left, right, or center the text.  Styles can
+     * override the alignment.
+     * @param spacingMult factor by which to scale the font size to get the
+     * default line spacing
+     * @param spacingAdd amount to add to the default line spacing
+     *
+     * @hide
+     */
+    protected Layout(CharSequence text, TextPaint paint,
+                     int width, Alignment align, TextDirectionHeuristic textDir,
+                     float spacingMult, float spacingAdd) {
+
         if (width < 0)
             throw new IllegalArgumentException("Layout: " + width + " < 0");
 
@@ -133,6 +156,7 @@
         mSpacingMult = spacingMult;
         mSpacingAdd = spacingAdd;
         mSpannedText = text instanceof Spanned;
+        mTextDir = textDir;
     }
 
     /**
@@ -531,6 +555,14 @@
     }
 
     /**
+     * Return the heuristic used to determine paragraph text direction.
+     * @hide
+     */
+    public final TextDirectionHeuristic getTextDirectionHeuristic() {
+        return mTextDir;
+    }
+
+    /**
      * Return the number of lines of text in this layout.
      */
     public abstract int getLineCount();
@@ -1419,7 +1451,7 @@
         MeasuredText mt = MeasuredText.obtain();
         TextLine tl = TextLine.obtain();
         try {
-            mt.setPara(text, start, end, DIR_REQUEST_LTR);
+            mt.setPara(text, start, end, TextDirectionHeuristics.LTR);
             Directions directions;
             int dir;
             if (mt.mEasy) {
@@ -1769,6 +1801,7 @@
     private float mSpacingAdd;
     private static final Rect sTempRect = new Rect();
     private boolean mSpannedText;
+    private TextDirectionHeuristic mTextDir;
 
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index a81be09..2920ac5 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -85,7 +85,7 @@
      * Analyzes text for bidirectional runs.  Allocates working buffers.
      */
     /* package */
-    void setPara(CharSequence text, int start, int end, int bidiRequest) {
+    void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
         mText = text;
         mTextStart = start;
 
@@ -115,13 +115,29 @@
             }
         }
 
-        if (TextUtils.doesNotNeedBidi(mChars, 0, len)) {
+        if ((textDir == TextDirectionHeuristics.LTR ||
+                textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
+                textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
+                TextUtils.doesNotNeedBidi(mChars, 0, len)) {
             mDir = Layout.DIR_LEFT_TO_RIGHT;
             mEasy = true;
         } else {
             if (mLevels == null || mLevels.length < len) {
                 mLevels = new byte[ArrayUtils.idealByteArraySize(len)];
             }
+            int bidiRequest;
+            if (textDir == TextDirectionHeuristics.LTR) {
+                bidiRequest = Layout.DIR_REQUEST_LTR;
+            } else if (textDir == TextDirectionHeuristics.RTL) {
+                bidiRequest = Layout.DIR_REQUEST_RTL;
+            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
+                bidiRequest = Layout.DIR_REQUEST_DEFAULT_LTR;
+            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
+                bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
+            } else {
+                boolean isRtl = textDir.isRtl(mChars, 0, len);
+                bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
+            }
             mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false);
             mEasy = false;
         }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 9e48eff..f7b9502 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,8 +16,6 @@
 
 package android.text;
 
-import com.android.internal.util.ArrayUtils;
-
 import android.graphics.Bitmap;
 import android.graphics.Paint;
 import android.text.style.LeadingMarginSpan;
@@ -26,6 +24,8 @@
 import android.text.style.MetricAffectingSpan;
 import android.text.style.TabStopSpan;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * StaticLayout is a Layout for text that will not be edited after it
  * is laid out.  Use {@link DynamicLayout} for text that may change.
@@ -46,6 +46,17 @@
              spacingmult, spacingadd, includepad);
     }
 
+    /**
+     * @hide
+     */
+    public StaticLayout(CharSequence source, TextPaint paint,
+            int width, Alignment align, TextDirectionHeuristic textDir,
+            float spacingmult, float spacingadd,
+            boolean includepad) {
+        this(source, 0, source.length(), paint, width, align, textDir,
+                spacingmult, spacingadd, includepad);
+    }
+
     public StaticLayout(CharSequence source, int bufstart, int bufend,
                         TextPaint paint, int outerwidth,
                         Alignment align,
@@ -55,9 +66,35 @@
              spacingmult, spacingadd, includepad, null, 0);
     }
 
+    /**
+     * @hide
+     */
+    public StaticLayout(CharSequence source, int bufstart, int bufend,
+            TextPaint paint, int outerwidth,
+            Alignment align, TextDirectionHeuristic textDir,
+            float spacingmult, float spacingadd,
+            boolean includepad) {
+        this(source, bufstart, bufend, paint, outerwidth, align, textDir,
+                spacingmult, spacingadd, includepad, null, 0);
+}
+
+    public StaticLayout(CharSequence source, int bufstart, int bufend,
+            TextPaint paint, int outerwidth,
+            Alignment align,
+            float spacingmult, float spacingadd,
+            boolean includepad,
+            TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
+        this(source, bufstart, bufend, paint, outerwidth, align,
+                TextDirectionHeuristics.FIRSTSTRONG_LTR,
+                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth);
+    }
+
+    /**
+     * @hide
+     */
     public StaticLayout(CharSequence source, int bufstart, int bufend,
                         TextPaint paint, int outerwidth,
-                        Alignment align,
+                        Alignment align, TextDirectionHeuristic textDir,
                         float spacingmult, float spacingadd,
                         boolean includepad,
                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
@@ -66,7 +103,7 @@
                 : (source instanceof Spanned)
                     ? new SpannedEllipsizer(source)
                     : new Ellipsizer(source),
-              paint, outerwidth, align, spacingmult, spacingadd);
+              paint, outerwidth, align, textDir, spacingmult, spacingadd);
 
         /*
          * This is annoying, but we can't refer to the layout until
@@ -96,7 +133,7 @@
 
         mMeasured = MeasuredText.obtain();
 
-        generate(source, bufstart, bufend, paint, outerwidth, align,
+        generate(source, bufstart, bufend, paint, outerwidth, align, textDir,
                  spacingmult, spacingadd, includepad, includepad,
                  ellipsizedWidth, ellipsize);
 
@@ -116,7 +153,7 @@
 
     /* package */ void generate(CharSequence source, int bufStart, int bufEnd,
                         TextPaint paint, int outerWidth,
-                        Alignment align,
+                        Alignment align, TextDirectionHeuristic textDir,
                         float spacingmult, float spacingadd,
                         boolean includepad, boolean trackpad,
                         float ellipsizedWidth, TextUtils.TruncateAt ellipsize) {
@@ -157,7 +194,7 @@
                     LeadingMarginSpan lms = sp[i];
                     firstWidth -= sp[i].getLeadingMargin(true);
                     restWidth -= sp[i].getLeadingMargin(false);
-                    
+
                     // LeadingMarginSpan2 is odd.  The count affects all
                     // leading margin spans, not just this particular one,
                     // and start from the top of the span, not the top of the
@@ -195,7 +232,7 @@
                 }
             }
 
-            measured.setPara(source, paraStart, paraEnd, DIR_REQUEST_DEFAULT_LTR);
+            measured.setPara(source, paraStart, paraEnd, textDir);
             char[] chs = measured.mChars;
             float[] widths = measured.mWidths;
             byte[] chdirs = measured.mLevels;
diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java
new file mode 100644
index 0000000..130f879
--- /dev/null
+++ b/core/java/android/text/TextDirectionHeuristic.java
@@ -0,0 +1,13 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.text;
+
+/**
+ * Interface for objects that guess at the paragraph direction by examining text.
+ *
+ * @hide
+ */
+public interface TextDirectionHeuristic {
+    /** @hide */ boolean isRtl(CharSequence text, int start, int end);
+    /** @hide */ boolean isRtl(char[] text, int start, int count);
+}
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
new file mode 100644
index 0000000..5f9ffc5
--- /dev/null
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -0,0 +1,310 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package android.text;
+
+
+/**
+ * Some objects that implement TextDirectionHeuristic.
+ * @hide
+ */
+public class TextDirectionHeuristics {
+
+    /** Always decides that the direction is left to right. */
+    public static final TextDirectionHeuristic LTR =
+        new TextDirectionHeuristicInternal(null /* no algorithm */, false);
+
+    /** Always decides that the direction is right to left. */
+    public static final TextDirectionHeuristic RTL =
+        new TextDirectionHeuristicInternal(null /* no algorithm */, true);
+
+    /**
+     * Determines the direction based on the first strong directional character,
+     * including bidi format chars, falling back to left to right if it
+     * finds none.  This is the default behavior of the Unicode Bidirectional
+     * Algorithm.
+     */
+    public static final TextDirectionHeuristic FIRSTSTRONG_LTR =
+        new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, false);
+
+    /**
+     * Determines the direction based on the first strong directional character,
+     * including bidi format chars, falling back to right to left if it
+     * finds none.  This is similar to the default behavior of the Unicode
+     * Bidirectional Algorithm, just with different fallback behavior.
+     */
+    public static final TextDirectionHeuristic FIRSTSTRONG_RTL =
+        new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
+
+    /**
+     * If the text contains any strong right to left non-format character, determines
+     * that the direction is right to left, falling back to left to right if it
+     * finds none.
+     */
+    public static final TextDirectionHeuristic ANYRTL_LTR =
+        new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
+
+    /**
+     * If the text contains any strong left to right non-format character, determines
+     * that the direction is left to right, falling back to right to left if it
+     * finds none.
+     */
+    public static final TextDirectionHeuristic ANYLTR_RTL =
+        new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, true);
+
+    /**
+     * Examines only the strong directional non-format characters, and if either
+     * left to right or right to left characters are 60% or more of this total,
+     * determines that the direction follows the majority of characters.  Falls
+     * back to left to right if neither direction meets this threshold.
+     */
+    public static final TextDirectionHeuristic CHARCOUNT_LTR =
+        new TextDirectionHeuristicInternal(CharCount.INSTANCE_DEFAULT, false);
+
+    /**
+     * Examines only the strong directional non-format characters, and if either
+     * left to right or right to left characters are 60% or more of this total,
+     * determines that the direction follows the majority of characters.  Falls
+     * back to right to left if neither direction meets this threshold.
+     */
+    public static final TextDirectionHeuristic CHARCOUNT_RTL =
+        new TextDirectionHeuristicInternal(CharCount.INSTANCE_DEFAULT, true);
+
+    private static enum TriState {
+        TRUE, FALSE, UNKNOWN;
+    }
+
+    /**
+     * Computes the text direction based on an algorithm.  Subclasses implement
+     * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
+     * direction from the text alone.
+     * @hide
+     */
+    public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
+        private final TextDirectionAlgorithm mAlgorithm;
+
+        public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
+            mAlgorithm = algorithm;
+        }
+
+        /**
+         * Return true if the default text direction is rtl.
+         */
+        abstract protected boolean defaultIsRtl();
+
+        @Override
+        public boolean isRtl(CharSequence text, int start, int end) {
+            if (text == null || start < 0 || end < start || text.length() < end) {
+                throw new IllegalArgumentException();
+            }
+            if (mAlgorithm == null) {
+                return defaultIsRtl();
+            }
+            text = text.subSequence(start, end);
+            char[] chars = text.toString().toCharArray();
+            return doCheck(chars, 0, chars.length);
+        }
+
+        @Override
+        public boolean isRtl(char[] chars, int start, int count) {
+            if (chars == null || start < 0 || count < 0 || chars.length - count < start) {
+                throw new IllegalArgumentException();
+            }
+            if (mAlgorithm == null) {
+                return defaultIsRtl();
+            }
+            return doCheck(chars, start, count);
+        }
+
+        private boolean doCheck(char[] chars, int start, int count) {
+            switch(mAlgorithm.checkRtl(chars, start, count)) {
+                case TRUE:
+                    return true;
+                case FALSE:
+                    return false;
+                default:
+                    return defaultIsRtl();
+            }
+        }
+    }
+
+    private static class TextDirectionHeuristicInternal extends TextDirectionHeuristicImpl {
+        private final boolean mDefaultIsRtl;
+
+        private TextDirectionHeuristicInternal(TextDirectionAlgorithm algorithm,
+                boolean defaultIsRtl) {
+            super(algorithm);
+            mDefaultIsRtl = defaultIsRtl;
+        }
+
+        @Override
+        protected boolean defaultIsRtl() {
+            return mDefaultIsRtl;
+        }
+    }
+
+    private static TriState isRtlText(int directionality) {
+        switch (directionality) {
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+                return TriState.FALSE;
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+                return TriState.TRUE;
+            default:
+                return TriState.UNKNOWN;
+        }
+    }
+
+    private static TriState isRtlTextOrFormat(int directionality) {
+        switch (directionality) {
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+                return TriState.FALSE;
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+                return TriState.TRUE;
+            default:
+                return TriState.UNKNOWN;
+        }
+    }
+
+    /**
+     * Interface for an algorithm to guess the direction of a paragraph of text.
+     *
+     * @hide
+     */
+    public static interface TextDirectionAlgorithm {
+        /**
+         * Returns whether the range of text is RTL according to the algorithm.
+         *
+         * @hide
+         */
+        TriState checkRtl(char[] text, int start, int count);
+    }
+
+    /**
+     * Algorithm that uses the first strong directional character to determine
+     * the paragraph direction.  This is the standard Unicode Bidirectional
+     * algorithm.
+     *
+     * @hide
+     */
+    public static class FirstStrong implements TextDirectionAlgorithm {
+        @Override
+        public TriState checkRtl(char[] text, int start, int count) {
+            TriState result = TriState.UNKNOWN;
+            for (int i = start, e = start + count; i < e && result == TriState.UNKNOWN; ++i) {
+                result = isRtlTextOrFormat(Character.getDirectionality(text[i]));
+            }
+            return result;
+        }
+
+        private FirstStrong() {
+        }
+
+        public static final FirstStrong INSTANCE = new FirstStrong();
+    }
+
+    /**
+     * Algorithm that uses the presence of any strong directional non-format
+     * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
+     * direction of text.
+     *
+     * @hide
+     */
+    public static class AnyStrong implements TextDirectionAlgorithm {
+        private final boolean mLookForRtl;
+
+        @Override
+        public TriState checkRtl(char[] text, int start, int count) {
+            boolean haveUnlookedFor = false;
+            for (int i = start, e = start + count; i < e; ++i) {
+                switch (isRtlText(Character.getDirectionality(text[i]))) {
+                    case TRUE:
+                        if (mLookForRtl) {
+                            return TriState.TRUE;
+                        }
+                        haveUnlookedFor = true;
+                        break;
+                    case FALSE:
+                        if (!mLookForRtl) {
+                            return TriState.FALSE;
+                        }
+                        haveUnlookedFor = true;
+                        break;
+                    default:
+                        break;
+                }
+            }
+            if (haveUnlookedFor) {
+                return mLookForRtl ? TriState.FALSE : TriState.TRUE;
+            }
+            return TriState.UNKNOWN;
+        }
+
+        private AnyStrong(boolean lookForRtl) {
+            this.mLookForRtl = lookForRtl;
+        }
+
+        public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
+        public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
+    }
+
+    /**
+     * Algorithm that uses the relative proportion of strong directional
+     * characters (excluding LRE, LRO, RLE, RLO) to determine the direction
+     * of the paragraph, if the proportion exceeds a given threshold.
+     *
+     * @hide
+     */
+    public static class CharCount implements TextDirectionAlgorithm {
+        private final float mThreshold;
+
+        @Override
+        public TriState checkRtl(char[] text, int start, int count) {
+            int countLtr = 0;
+            int countRtl = 0;
+            for(int i = start, e = start + count; i < e; ++i) {
+                switch (isRtlText(Character.getDirectionality(text[i]))) {
+                    case TRUE:
+                        ++countLtr;
+                        break;
+                    case FALSE:
+                        ++countRtl;
+                        break;
+                    default:
+                        break;
+                }
+            }
+            int limit = (int)((countLtr + countRtl) * mThreshold);
+            if (limit > 0) {
+                if (countLtr > limit) {
+                    return TriState.FALSE;
+                }
+                if (countRtl > limit) {
+                    return TriState.TRUE;
+                }
+            }
+            return TriState.UNKNOWN;
+        }
+
+        private CharCount(float threshold) {
+            mThreshold = threshold;
+        }
+
+        public static CharCount withThreshold(float threshold) {
+            if (threshold < 0 || threshold > 1) {
+                throw new IllegalArgumentException();
+            }
+            if (threshold == DEFAULT_THRESHOLD) {
+                return INSTANCE_DEFAULT;
+            }
+            return new CharCount(threshold);
+        }
+
+        public static final float DEFAULT_THRESHOLD = 0.6f;
+        public static final CharCount INSTANCE_DEFAULT = new CharCount(DEFAULT_THRESHOLD);
+    }
+}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6741059..29c9853 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -16,9 +16,6 @@
 
 package android.text;
 
-import com.android.internal.R;
-import com.android.internal.util.ArrayUtils;
-
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -45,6 +42,9 @@
 import android.text.style.UnderlineSpan;
 import android.util.Printer;
 
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
 import java.lang.reflect.Array;
 import java.util.Iterator;
 import java.util.regex.Pattern;
@@ -1001,13 +1001,37 @@
      * will be padded with zero-width spaces to preserve the original
      * length and offsets instead of truncating.
      * If <code>callback</code> is non-null, it will be called to
-     * report the start and end of the ellipsized range.
+     * report the start and end of the ellipsized range.  TextDirection
+     * is determined by the first strong directional character.
      */
     public static CharSequence ellipsize(CharSequence text,
                                          TextPaint paint,
                                          float avail, TruncateAt where,
                                          boolean preserveLength,
                                          EllipsizeCallback callback) {
+        return ellipsize(text, paint, avail, where, preserveLength, callback,
+                TextDirectionHeuristics.FIRSTSTRONG_LTR);
+    }
+
+    /**
+     * Returns the original text if it fits in the specified width
+     * given the properties of the specified Paint,
+     * or, if it does not fit, a copy with ellipsis character added
+     * at the specified edge or center.
+     * If <code>preserveLength</code> is specified, the returned copy
+     * will be padded with zero-width spaces to preserve the original
+     * length and offsets instead of truncating.
+     * If <code>callback</code> is non-null, it will be called to
+     * report the start and end of the ellipsized range.
+     *
+     * @hide
+     */
+    public static CharSequence ellipsize(CharSequence text,
+            TextPaint paint,
+            float avail, TruncateAt where,
+            boolean preserveLength,
+            EllipsizeCallback callback,
+            TextDirectionHeuristic textDir) {
         if (sEllipsis == null) {
             Resources r = Resources.getSystem();
             sEllipsis = r.getString(R.string.ellipsis);
@@ -1017,8 +1041,7 @@
 
         MeasuredText mt = MeasuredText.obtain();
         try {
-            float width = setPara(mt, paint, text, 0, text.length(),
-                    Layout.DIR_REQUEST_DEFAULT_LTR);
+            float width = setPara(mt, paint, text, 0, text.length(), textDir);
 
             if (width <= avail) {
                 if (callback != null) {
@@ -1108,11 +1131,20 @@
                                               TextPaint p, float avail,
                                               String oneMore,
                                               String more) {
+        return commaEllipsize(text, p, avail, oneMore, more,
+                TextDirectionHeuristics.FIRSTSTRONG_LTR);
+    }
+
+    /**
+     * @hide
+     */
+    public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
+         float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
 
         MeasuredText mt = MeasuredText.obtain();
         try {
             int len = text.length();
-            float width = setPara(mt, p, text, 0, len, Layout.DIR_REQUEST_DEFAULT_LTR);
+            float width = setPara(mt, p, text, 0, len, textDir);
             if (width <= avail) {
                 return text;
             }
@@ -1135,9 +1167,6 @@
             int count = 0;
             float[] widths = mt.mWidths;
 
-            int request = mt.mDir == 1 ? Layout.DIR_REQUEST_LTR :
-                Layout.DIR_REQUEST_RTL;
-
             MeasuredText tempMt = MeasuredText.obtain();
             for (int i = 0; i < len; i++) {
                 w += widths[i];
@@ -1155,7 +1184,7 @@
                     }
 
                     // XXX this is probably ok, but need to look at it more
-                    tempMt.setPara(format, 0, format.length(), request);
+                    tempMt.setPara(format, 0, format.length(), textDir);
                     float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null);
 
                     if (w + moreWid <= avail) {
@@ -1175,9 +1204,9 @@
     }
 
     private static float setPara(MeasuredText mt, TextPaint paint,
-            CharSequence text, int start, int end, int bidiRequest) {
+            CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
 
-        mt.setPara(text, start, end, bidiRequest);
+        mt.setPara(text, start, end, textDir);
 
         float width;
         Spanned sp = text instanceof Spanned ? (Spanned) text : null;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 74dc100..c8ff37a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,13 +16,6 @@
 
 package android.view;
 
-import android.util.FloatProperty;
-import android.util.LocaleUtil;
-import android.util.Property;
-import com.android.internal.R;
-import com.android.internal.util.Predicate;
-import com.android.internal.view.menu.MenuBuilder;
-
 import android.content.ClipData;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -53,11 +46,14 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.LocaleUtil;
 import android.util.Log;
 import android.util.Pool;
 import android.util.Poolable;
 import android.util.PoolableManager;
 import android.util.Pools;
+import android.util.Property;
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -72,6 +68,10 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.ScrollBarDrawable;
 
+import com.android.internal.R;
+import com.android.internal.util.Predicate;
+import com.android.internal.view.menu.MenuBuilder;
+
 import java.lang.ref.WeakReference;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -2493,12 +2493,6 @@
     private boolean mSendingHoverAccessibilityEvents;
 
     /**
-     * Undefined text direction (used by resolution algorithm).
-     * @hide
-     */
-    public static final int TEXT_DIRECTION_UNDEFINED = -1;
-
-    /**
      * Text direction is inherited thru {@link ViewGroup}
      * @hide
      */
@@ -2507,7 +2501,7 @@
     /**
      * Text direction is using "first strong algorithm". The first strong directional character
      * determines the paragraph direction. If there is no strong directional character, the
-     * paragraph direction is the view’s resolved ayout direction.
+     * paragraph direction is the view's resolved ayout direction.
      *
      * @hide
      */
@@ -2516,7 +2510,7 @@
     /**
      * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains
      * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
-     * If there are neither, the paragraph direction is the view’s resolved layout direction.
+     * If there are neither, the paragraph direction is the view's resolved layout direction.
      *
      * @hide
      */
@@ -2560,7 +2554,6 @@
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
-            @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
@@ -2568,21 +2561,25 @@
             @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
     })
-    protected int mTextDirection = DEFAULT_TEXT_DIRECTION;
+    private int mTextDirection = DEFAULT_TEXT_DIRECTION;
 
     /**
-     * The resolved text direction. If resolution has not yet been done or has been reset, it will
-     * be equal to {@link #TEXT_DIRECTION_UNDEFINED}. Otherwise it will be either {@link #TEXT_DIRECTION_LTR}
-     * or {@link #TEXT_DIRECTION_RTL}.
+     * The resolved text direction.  This needs resolution if the value is
+     * TEXT_DIRECTION_INHERIT.  The resolution matches mTextDirection if that is
+     * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent
+     * chain of the view.
      *
      * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
-            @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_CHAR_COUNT, to = "CHAR_COUNT"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
             @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
     })
-    protected int mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+    private int mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
 
     /**
      * Consistency verifier for debugging purposes.
@@ -9066,11 +9063,20 @@
         // Set resolved depending on layout direction
         switch (getLayoutDirection()) {
             case LAYOUT_DIRECTION_INHERIT:
+                // We cannot do the resolution if there is no parent
+                if (mParent == null) return;
+
                 // If this is root view, no need to look at parent's layout dir.
-                if (mParent != null &&
-                        mParent instanceof ViewGroup &&
-                        ((ViewGroup) mParent).getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
-                    mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
+                if (mParent instanceof ViewGroup) {
+                    ViewGroup viewGroup = ((ViewGroup) mParent);
+
+                    // Check if the parent view group can resolve
+                    if (! viewGroup.canResolveLayoutDirection()) {
+                        return;
+                    }
+                    if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+                        mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
+                    }
                 }
                 break;
             case LAYOUT_DIRECTION_RTL:
@@ -9132,6 +9138,15 @@
         recomputePadding();
     }
 
+    protected boolean canResolveLayoutDirection() {
+        switch (getLayoutDirection()) {
+            case LAYOUT_DIRECTION_INHERIT:
+                return (mParent != null);
+            default:
+                return true;
+        }
+    }
+
     /**
      * Reset the resolved layout direction.
      *
@@ -13048,43 +13063,41 @@
      *
      * @return the resolved text direction. Return one of:
      *
+     * {@link #TEXT_DIRECTION_FIRST_STRONG}
+     * {@link #TEXT_DIRECTION_ANY_RTL},
+     * {@link #TEXT_DIRECTION_CHAR_COUNT},
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      *
      * @hide
      */
     public int getResolvedTextDirection() {
-        if (!isResolvedTextDirection()) {
+        if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
             resolveTextDirection();
         }
         return mResolvedTextDirection;
     }
 
     /**
-     * Resolve the text direction. Classes that extend View and want to do a specific text direction
-     * resolution will need to implement this method and set the mResolvedTextDirection to
-     * either TEXT_DIRECTION_LTR if direction is LTR or TEXT_DIRECTION_RTL if
-     * direction is RTL.
+     * Resolve the text direction.
      */
     protected void resolveTextDirection() {
+        if (mTextDirection != TEXT_DIRECTION_INHERIT) {
+            mResolvedTextDirection = mTextDirection;
+            return;
+        }
+        if (mParent != null && mParent instanceof ViewGroup) {
+            mResolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
+            return;
+        }
+        mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
     }
 
     /**
-     * Return if the text direction has been resolved or not.
-     *
-     * @return true, if resolved and false if not resolved
-     *
-     * @hide
-     */
-    public boolean isResolvedTextDirection() {
-        return (mResolvedTextDirection != TEXT_DIRECTION_UNDEFINED);
-    }
-
-    /**
-     * Reset resolved text direction. Will be resolved during a call to getResolvedLayoutDirection().
+     * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection().
      */
     protected void resetResolvedTextDirection() {
-        mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+        mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
     }
 
     //
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 1dcbc26..ac73611 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -2209,6 +2209,62 @@
     public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1023;
 
     @Override
+    public String getMessageName(Message message) {
+        switch (message.what) {
+            case DO_TRAVERSAL:
+                return "DO_TRAVERSAL";
+            case DIE:
+                return "DIE";
+            case RESIZED:
+                return "RESIZED";
+            case RESIZED_REPORT:
+                return "RESIZED_REPORT";
+            case WINDOW_FOCUS_CHANGED:
+                return "WINDOW_FOCUS_CHANGED";
+            case DISPATCH_KEY:
+                return "DISPATCH_KEY";
+            case DISPATCH_POINTER:
+                return "DISPATCH_POINTER";
+            case DISPATCH_TRACKBALL:
+                return "DISPATCH_TRACKBALL";
+            case DISPATCH_APP_VISIBILITY:
+                return "DISPATCH_APP_VISIBILITY";
+            case DISPATCH_GET_NEW_SURFACE:
+                return "DISPATCH_GET_NEW_SURFACE";
+            case FINISHED_EVENT:
+                return "FINISHED_EVENT";
+            case DISPATCH_KEY_FROM_IME:
+                return "DISPATCH_KEY_FROM_IME";
+            case FINISH_INPUT_CONNECTION:
+                return "FINISH_INPUT_CONNECTION";
+            case CHECK_FOCUS:
+                return "CHECK_FOCUS";
+            case CLOSE_SYSTEM_DIALOGS:
+                return "CLOSE_SYSTEM_DIALOGS";
+            case DISPATCH_DRAG_EVENT:
+                return "DISPATCH_DRAG_EVENT";
+            case DISPATCH_DRAG_LOCATION_EVENT:
+                return "DISPATCH_DRAG_LOCATION_EVENT";
+            case DISPATCH_SYSTEM_UI_VISIBILITY:
+                return "DISPATCH_SYSTEM_UI_VISIBILITY";
+            case DISPATCH_GENERIC_MOTION:
+                return "DISPATCH_GENERIC_MOTION";
+            case UPDATE_CONFIGURATION:
+                return "UPDATE_CONFIGURATION";
+            case DO_PERFORM_ACCESSIBILITY_ACTION:
+                return "DO_PERFORM_ACCESSIBILITY_ACTION";
+            case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
+                return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
+            case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
+                return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
+            case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT:
+                return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT";
+                                                                                                                                                                                                                                    
+        }
+        return super.getMessageName(message);
+    }
+
+    @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
         case View.AttachInfo.INVALIDATE_MSG:
@@ -2634,8 +2690,9 @@
             mInputEventDeliverTimeNanos = System.nanoTime();
         }
 
+        final boolean isTouchEvent = event.isTouchEvent();
         if (mInputEventConsistencyVerifier != null) {
-            if (event.isTouchEvent()) {
+            if (isTouchEvent) {
                 mInputEventConsistencyVerifier.onTouchEvent(event, 0);
             } else {
                 mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
@@ -2653,9 +2710,9 @@
             mTranslator.translateEventInScreenToAppWindow(event);
         }
 
-        // Enter touch mode on the down.
-        boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
-        if (isDown) {
+        // Enter touch mode on down or scroll.
+        final int action = event.getAction();
+        if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
             ensureTouchMode(true);
         }
 
@@ -2668,8 +2725,10 @@
         }
 
         // Remember the touch position for possible drag-initiation.
-        mLastTouchPoint.x = event.getRawX();
-        mLastTouchPoint.y = event.getRawY();
+        if (isTouchEvent) {
+            mLastTouchPoint.x = event.getRawX();
+            mLastTouchPoint.y = event.getRawY();
+        }
 
         // Dispatch touch to view hierarchy.
         boolean handled = mView.dispatchPointerEvent(event);
@@ -2681,51 +2740,6 @@
             return;
         }
 
-        // Apply edge slop and try again, if appropriate.
-        final int edgeFlags = event.getEdgeFlags();
-        if (edgeFlags != 0 && mView instanceof ViewGroup) {
-            final int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
-            int direction = View.FOCUS_UP;
-            int x = (int)event.getX();
-            int y = (int)event.getY();
-            final int[] deltas = new int[2];
-
-            if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
-                direction = View.FOCUS_DOWN;
-                if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
-                    deltas[0] = edgeSlop;
-                    x += edgeSlop;
-                } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
-                    deltas[0] = -edgeSlop;
-                    x -= edgeSlop;
-                }
-            } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
-                direction = View.FOCUS_UP;
-                if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
-                    deltas[0] = edgeSlop;
-                    x += edgeSlop;
-                } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
-                    deltas[0] = -edgeSlop;
-                    x -= edgeSlop;
-                }
-            } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
-                direction = View.FOCUS_RIGHT;
-            } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
-                direction = View.FOCUS_LEFT;
-            }
-
-            View nearest = FocusFinder.getInstance().findNearestTouchable(
-                    ((ViewGroup) mView), x, y, direction, deltas);
-            if (nearest != null) {
-                event.offsetLocation(deltas[0], deltas[1]);
-                event.setEdgeFlags(0);
-                if (mView.dispatchPointerEvent(event)) {
-                    finishMotionEvent(event, sendDone, true);
-                    return;
-                }
-            }
-        }
-
         // Pointer event was unhandled.
         finishMotionEvent(event, sendDone, false);
     }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index f014070..f7f5a21 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -16,41 +16,45 @@
 
 package android.view;
 
-import android.util.Log;
-import android.util.DisplayMetrics;
-import android.content.res.Resources;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.os.Environment;
 import android.os.Debug;
+import android.os.Environment;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Printer;
 
-import java.io.ByteArrayOutputStream;
-import java.io.File;
+import java.io.BufferedOutputStream;
 import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.FileOutputStream;
-import java.io.DataOutputStream;
-import java.io.OutputStreamWriter;
-import java.io.BufferedOutputStream;
 import java.io.OutputStream;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.lang.annotation.Target;
+import java.io.OutputStreamWriter;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
+import java.lang.annotation.Target;
 import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Various debugging/tracing tools related to {@link View} and the view hierarchy.
@@ -106,13 +110,6 @@
     public static final boolean DEBUG_PROFILE_LAYOUT = false;
 
     /**
-     * Profiles real fps (times between draws) and displays the result.
-     *
-     * @hide
-     */
-    public static final boolean DEBUG_SHOW_FPS = false;
-
-    /**
      * Enables detailed logging of drag/drop operations.
      * @hide
      */
@@ -396,6 +393,9 @@
     private static List<RecyclerTrace> sRecyclerTraces;
     private static String sRecyclerTracePrefix;
 
+    private static final ThreadLocal<LooperProfiler> sLooperProfilerStorage =
+            new ThreadLocal<LooperProfiler>();
+
     /**
      * Returns the number of instanciated Views.
      *
@@ -419,6 +419,124 @@
     }
 
     /**
+     * Starts profiling the looper associated with the current thread.
+     * You must call {@link #stopLooperProfiling} to end profiling
+     * and obtain the traces. Both methods must be invoked on the
+     * same thread.
+     * 
+     * @param traceFile The path where to write the looper traces
+     * 
+     * @see #stopLooperProfiling() 
+     */
+    public static void startLooperProfiling(File traceFile) {
+        if (sLooperProfilerStorage.get() == null) {
+            LooperProfiler profiler = new LooperProfiler(traceFile);
+            sLooperProfilerStorage.set(profiler);
+            Looper.myLooper().setMessageLogging(profiler);
+        }
+    }
+
+    /**
+     * Stops profiling the looper associated with the current thread.
+     * 
+     * @see #startLooperProfiling(java.io.File) 
+     */
+    public static void stopLooperProfiling() {
+        LooperProfiler profiler = sLooperProfilerStorage.get();
+        if (profiler != null) {
+            sLooperProfilerStorage.remove();
+            Looper.myLooper().setMessageLogging(null);
+            profiler.save();
+        }
+    }
+
+    private static class LooperProfiler implements Looper.Profiler, Printer {
+        private static final int LOOPER_PROFILER_VERSION = 1;
+
+        private static final String LOG_TAG = "LooperProfiler";
+
+        private final ArrayList<Entry> mTraces = new ArrayList<Entry>(512);
+        private final File mTraceFile;
+
+        public LooperProfiler(File traceFile) {
+            mTraceFile = traceFile;
+        }
+
+        @Override
+        public void println(String x) {
+            // Ignore messages
+        }
+
+        @Override
+        public void profile(Message message, long wallStart, long wallTime, long threadTime) {
+            Entry entry = new Entry();
+            entry.messageId = message.what;
+            entry.name = message.getTarget().getMessageName(message);
+            entry.wallStart = wallStart;
+            entry.wallTime = wallTime;
+            entry.threadTime = threadTime;
+
+            mTraces.add(entry);
+        }
+
+        void save() {
+            // Don't block the UI thread
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    saveTraces();
+                }
+            }, "LooperProfiler[" + mTraceFile + "]").start();
+        }
+
+        private void saveTraces() {
+            FileOutputStream fos;
+            try {
+                fos = new FileOutputStream(mTraceFile);
+            } catch (FileNotFoundException e) {
+                Log.e(LOG_TAG, "Could not open trace file: " + mTraceFile);
+                return;
+            }
+
+            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
+
+            try {
+                out.writeInt(LOOPER_PROFILER_VERSION);
+                out.writeInt(mTraces.size());
+                for (Entry entry : mTraces) {
+                    saveTrace(entry, out);
+                }
+
+                Log.d(LOG_TAG, "Looper traces ready: " + mTraceFile);
+            } catch (IOException e) {
+                Log.e(LOG_TAG, "Could not write trace file: ", e);
+            } finally {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+
+        private void saveTrace(Entry entry, DataOutputStream out) throws IOException {
+            out.writeInt(entry.messageId);
+            out.writeUTF(entry.name);
+            out.writeLong(entry.wallStart);
+            out.writeLong(entry.wallTime);
+            out.writeLong(entry.threadTime);
+        }
+
+        static class Entry {
+            int messageId;
+            String name;
+            long wallStart;
+            long wallTime;
+            long threadTime;
+        }
+    }
+
+    /**
      * Outputs a trace to the currently opened recycler traces. The trace records the type of
      * recycler action performed on the supplied view as well as a number of parameters.
      *
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 752fd5a..cb3e9c6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -41,6 +41,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.LayoutAnimationController;
 import android.view.animation.Transformation;
+
 import com.android.internal.R;
 import com.android.internal.util.Predicate;
 
@@ -5012,37 +5013,6 @@
         }
     }
 
-    /**
-     * This method will be called during text direction resolution (text direction resolution
-     * inheritance)
-     */
-    @Override
-    protected void resolveTextDirection() {
-        int resolvedTextDirection;
-        switch(mTextDirection) {
-            default:
-            case TEXT_DIRECTION_INHERIT:
-                // Try to the text direction from the parent layout
-                if (mParent != null && mParent instanceof ViewGroup) {
-                    resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
-                } else {
-                    // We reached the top of the View hierarchy, so set the text direction
-                    // heuristic to "first strong"
-                    resolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG;
-                }
-                break;
-            // Pass down the hierarchy the following text direction values
-            case TEXT_DIRECTION_FIRST_STRONG:
-            case TEXT_DIRECTION_ANY_RTL:
-            case TEXT_DIRECTION_CHAR_COUNT:
-            case TEXT_DIRECTION_LTR:
-            case TEXT_DIRECTION_RTL:
-                resolvedTextDirection = mTextDirection;
-                break;
-        }
-        mResolvedTextDirection = resolvedTextDirection;
-    }
-
     @Override
     protected void resetResolvedTextDirection() {
         super.resetResolvedTextDirection();
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 761007f..d584acd 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1783,6 +1783,20 @@
     }
 
     /**
+     * @hide
+     */
+    public void setProperty(String key, String value) {
+        mWebView.nativeSetProperty(key, value);
+    }
+
+    /**
+     * @hide
+     */
+    public String getProperty(String key) {
+        return mWebView.nativeGetProperty(key);
+    }
+
+    /**
      * Transfer messages from the queue to the new WebCoreThread. Called from
      * WebCore thread.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3ae10fe..b22c57b0 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -9289,4 +9289,6 @@
      */
     private native boolean  nativeScrollLayer(int layer, int newX, int newY);
     private native int      nativeGetBackgroundColor();
+    native void     nativeSetProperty(String key, String value);
+    native String   nativeGetProperty(String key);
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1449b18..8f8c1d0 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -271,12 +271,6 @@
     Drawable mSelector;
 
     /**
-     * Set to true if we would like to have the selector showing itself.
-     * We still need to draw and position it even if this is false.
-     */
-    boolean mSelectorShowing;
-
-    /**
      * The current position of the selector in the list.
      */
     int mSelectorPosition = INVALID_POSITION;
@@ -1669,7 +1663,6 @@
         setSelectedPositionInt(INVALID_POSITION);
         setNextSelectedPositionInt(INVALID_POSITION);
         mSelectedTop = 0;
-        mSelectorShowing = false;
         mSelectorPosition = INVALID_POSITION;
         mSelectorRect.setEmpty();
         invalidate();
@@ -2025,7 +2018,7 @@
         final boolean isChildViewEnabled = mIsChildViewEnabled;
         if (sel.isEnabled() != isChildViewEnabled) {
             mIsChildViewEnabled = !isChildViewEnabled;
-            if (mSelectorShowing) {
+            if (getSelectedItemPosition() != INVALID_POSITION) {
                 refreshDrawableState();
             }
         }
@@ -2769,6 +2762,7 @@
                 // touch mode). Force an initial layout to get rid of the selection.
                 layoutChildren();
             }
+            updateSelectorState();
         } else {
             int touchMode = mTouchMode;
             if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
@@ -2847,14 +2841,6 @@
                         }
                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                     } else {
-                        if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
-                            // If we couldn't find a view to click on, but the down event
-                            // was touching the edge, we will bail out and try again.
-                            // This allows the edge correcting code in ViewAncestor to try to
-                            // find a nearby view to select
-                            return false;
-                        }
-
                         if (mTouchMode == TOUCH_MODE_FLING) {
                             // Stopped a fling. It is a scroll.
                             createScrollingCache();
@@ -2888,7 +2874,11 @@
         }
 
         case MotionEvent.ACTION_MOVE: {
-            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+            int pointerIndex = ev.findPointerIndex(mActivePointerId);
+            if (pointerIndex == -1) {
+                pointerIndex = 0;
+                mActivePointerId = ev.getPointerId(pointerIndex);
+            }
             final int y = (int) ev.getY(pointerIndex);
             deltaY = y - mMotionY;
             switch (mTouchMode) {
@@ -3464,7 +3454,11 @@
         case MotionEvent.ACTION_MOVE: {
             switch (mTouchMode) {
             case TOUCH_MODE_DOWN:
-                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == -1) {
+                    pointerIndex = 0;
+                    mActivePointerId = ev.getPointerId(pointerIndex);
+                }
                 final int y = (int) ev.getY(pointerIndex);
                 if (startScrollIfNeeded(y - mMotionY)) {
                     return true;
@@ -4521,7 +4515,6 @@
             setSelectedPositionInt(INVALID_POSITION);
             setNextSelectedPositionInt(INVALID_POSITION);
             mSelectedTop = 0;
-            mSelectorShowing = false;
         }
     }
 
@@ -4645,6 +4638,9 @@
                             childrenTop += getVerticalFadingEdgeLength();
                         }
                     }
+                    // Don't ever focus a disabled item.
+                    if (!mAdapter.isEnabled(i)) continue;
+
                     if (top >= childrenTop) {
                         // Found a view whose top is fully visisble
                         selectedPos = firstPosition + i;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 7c9be1e..b428301 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -498,13 +498,6 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-
-        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
-            // Don't handle edge touches immediately -- they may actually belong to one of our
-            // descendants.
-            return false;
-        }
-
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index e7a9e41..1f29b16 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3588,17 +3588,6 @@
         return null;
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        //noinspection SimplifiableIfStatement
-        if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
-            // Don't handle edge touches immediately -- they may actually belong to one of our
-            // descendants.
-            return false;
-        }
-        return super.onTouchEvent(ev);
-    }
-
     /**
      * Returns the set of checked items ids. The result is only valid if the
      * choice mode has not been set to {@link #CHOICE_MODE_NONE}.
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 4c47d37..867ebb4 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -53,6 +53,10 @@
     // This ensures that we don't stay continually bound to the service and that it can be destroyed
     // if we need the memory elsewhere in the system.
     private static final int sUnbindServiceDelay = 5000;
+
+    // Default height for the default loading view, in case we cannot get inflate the first view
+    private static final int sDefaultLoadingViewHeight = 50;
+
     // Type defs for controlling different messages across the main and worker message queues
     private static final int sDefaultMessageType = 0;
     private static final int sUnbindServiceMessageType = 1;
@@ -386,21 +390,39 @@
 
             // Create a new loading view
             synchronized (mCache) {
+                boolean customLoadingViewAvailable = false;
+
                 if (mUserLoadingView != null) {
-                    // A user-specified loading view
-                    View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
-                    loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0));
-                    layout.addView(loadingView);
-                } else {
+                    // Try to inflate user-specified loading view
+                    try {
+                        View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
+                        loadingView.setTagInternal(com.android.internal.R.id.rowTypeId,
+                                new Integer(0));
+                        layout.addView(loadingView);
+                        customLoadingViewAvailable = true;
+                    } catch (Exception e) {
+                        Log.w(TAG, "Error inflating custom loading view, using default loading" +
+                                "view instead", e);
+                    }
+                }
+                if (!customLoadingViewAvailable) {
                     // A default loading view
                     // Use the size of the first row as a guide for the size of the loading view
                     if (mFirstViewHeight < 0) {
-                        View firstView = mFirstView.apply(parent.getContext(), parent);
-                        firstView.measure(
-                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
-                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-                        mFirstViewHeight = firstView.getMeasuredHeight();
-                        mFirstView = null;
+                        try {
+                            View firstView = mFirstView.apply(parent.getContext(), parent);
+                            firstView.measure(
+                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+                            mFirstViewHeight = firstView.getMeasuredHeight();
+                            mFirstView = null;
+                        } catch (Exception e) {
+                            float density = mContext.getResources().getDisplayMetrics().density;
+                            mFirstViewHeight = (int)
+                                    Math.round(sDefaultLoadingViewHeight * density);
+                            mFirstView = null;
+                            Log.w(TAG, "Error inflating first RemoteViews" + e);
+                        }
                     }
 
                     // Compose the loading view text
@@ -937,24 +959,40 @@
                 indexMetaData.isRequested = true;
                 int typeId = indexMetaData.typeId;
 
-                // Reuse the convert view where possible
-                if (layout != null) {
-                    if (convertViewTypeId == typeId) {
-                        rv.reapply(context, convertViewChild);
-                        return layout;
+                try {
+                    // Reuse the convert view where possible
+                    if (layout != null) {
+                        if (convertViewTypeId == typeId) {
+                            rv.reapply(context, convertViewChild);
+                            return layout;
+                        }
+                        layout.removeAllViews();
+                    } else {
+                        layout = new RemoteViewsFrameLayout(context);
                     }
-                    layout.removeAllViews();
-                } else {
-                    layout = new RemoteViewsFrameLayout(context);
+
+                    // Otherwise, create a new view to be returned
+                    View newView = rv.apply(context, parent);
+                    newView.setTagInternal(com.android.internal.R.id.rowTypeId,
+                            new Integer(typeId));
+                    layout.addView(newView);
+                    return layout;
+
+                } catch (Exception e){
+                    // We have to make sure that we successfully inflated the RemoteViews, if not
+                    // we return the loading view instead.
+                    Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" +
+                            "loading view instead" + e);
+
+                    RemoteViewsFrameLayout loadingView = null;
+                    final RemoteViewsMetaData metaData = mCache.getMetaData();
+                    synchronized (metaData) {
+                        loadingView = metaData.createLoadingView(position, convertView, parent);
+                    }
+                    return loadingView;
+                } finally {
+                    if (hasNewItems) loadNextIndexInBackground();
                 }
-
-                // Otherwise, create a new view to be returned
-                View newView = rv.apply(context, parent);
-                newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId));
-                layout.addView(newView);
-                if (hasNewItems) loadNextIndexInBackground();
-
-                return layout;
             } else {
                 // If the cache does not have the RemoteViews at this position, then create a
                 // loading view and queue the actual position to be loaded in the background
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 12775a4..e59f731 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -65,7 +65,6 @@
 
     static final float MAX_SCROLL_FACTOR = 0.5f;
 
-
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
@@ -506,13 +505,6 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-
-        if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
-            // Don't handle edge touches immediately -- they may actually belong to one of our
-            // descendants.
-            return false;
-        }
-
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -1438,17 +1430,6 @@
 
             final boolean movingDown = velocityY > 0;
 
-            View currentFocused = findFocus();
-            View newFocused =
-                    findFocusableViewInMyBounds(movingDown, mScroller.getFinalY(), currentFocused);
-            if (newFocused == null) {
-                newFocused = this;
-            }
-
-            if (newFocused != currentFocused) {
-                    newFocused.requestFocus(movingDown ? View.FOCUS_DOWN : View.FOCUS_UP);
-            }
-
             if (mFlingStrictSpan == null) {
                 mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
             }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 51eee20..5a5fae4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,11 @@
 
 package android.widget;
 
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.R;
 import android.content.ClipData;
 import android.content.ClipData.Item;
@@ -59,6 +64,13 @@
 import android.text.Spanned;
 import android.text.SpannedString;
 import android.text.StaticLayout;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextDirectionHeuristics.AnyStrong;
+import android.text.TextDirectionHeuristics.CharCount;
+import android.text.TextDirectionHeuristics.FirstStrong;
+import android.text.TextDirectionHeuristics.TextDirectionAlgorithm;
+import android.text.TextDirectionHeuristics.TextDirectionHeuristicImpl;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -127,11 +139,6 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.RemoteViews.RemoteView;
 
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.text.BreakIterator;
@@ -5832,6 +5839,7 @@
         if (curs >= 0) {
             mHighlightPathBogus = true;
             makeBlink();
+            bringPointIntoView(curs);
         }
 
         checkForResize();
@@ -5992,14 +6000,17 @@
         Layout.Alignment alignment = getLayoutAlignment();
         boolean shouldEllipsize = mEllipsize != null && mInput == null;
 
+        if (mTextDir == null) {
+            resolveTextDirection();
+        }
         if (mText instanceof Spannable) {
             mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w,
-                    alignment, mSpacingMult,
+                    alignment, mTextDir, mSpacingMult,
                     mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null,
                     ellipsisWidth);
         } else {
             if (boring == UNKNOWN_BORING) {
-                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -6036,23 +6047,23 @@
                 } else if (shouldEllipsize) {
                     mLayout = new StaticLayout(mTransformed,
                                 0, mTransformed.length(),
-                                mTextPaint, w, alignment, mSpacingMult,
+                                mTextPaint, w, alignment, mTextDir, mSpacingMult,
                                 mSpacingAdd, mIncludePad, mEllipsize,
                                 ellipsisWidth);
                 } else {
                     mLayout = new StaticLayout(mTransformed, mTextPaint,
-                            w, alignment, mSpacingMult, mSpacingAdd,
+                            w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
                             mIncludePad);
                 }
             } else if (shouldEllipsize) {
                 mLayout = new StaticLayout(mTransformed,
                             0, mTransformed.length(),
-                            mTextPaint, w, alignment, mSpacingMult,
+                            mTextPaint, w, alignment, mTextDir, mSpacingMult,
                             mSpacingAdd, mIncludePad, mEllipsize,
                             ellipsisWidth);
             } else {
                 mLayout = new StaticLayout(mTransformed, mTextPaint,
-                        w, alignment, mSpacingMult, mSpacingAdd,
+                        w, alignment, mTextDir, mSpacingMult, mSpacingAdd,
                         mIncludePad);
             }
         }
@@ -6064,7 +6075,7 @@
             if (shouldEllipsize) hintWidth = w;
 
             if (hintBoring == UNKNOWN_BORING) {
-                hintBoring = BoringLayout.isBoring(mHint, mTextPaint,
+                hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
                                                    mHintBoring);
                 if (hintBoring != null) {
                     mHintBoring = hintBoring;
@@ -6102,23 +6113,23 @@
                 } else if (shouldEllipsize) {
                     mHintLayout = new StaticLayout(mHint,
                                 0, mHint.length(),
-                                mTextPaint, hintWidth, alignment, mSpacingMult,
+                                mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult,
                                 mSpacingAdd, mIncludePad, mEllipsize,
                                 ellipsisWidth);
                 } else {
                     mHintLayout = new StaticLayout(mHint, mTextPaint,
-                            hintWidth, alignment, mSpacingMult, mSpacingAdd,
+                            hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
                             mIncludePad);
                 }
             } else if (shouldEllipsize) {
                 mHintLayout = new StaticLayout(mHint,
                             0, mHint.length(),
-                            mTextPaint, hintWidth, alignment, mSpacingMult,
+                            mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult,
                             mSpacingAdd, mIncludePad, mEllipsize,
                             ellipsisWidth);
             } else {
                 mHintLayout = new StaticLayout(mHint, mTextPaint,
-                        hintWidth, alignment, mSpacingMult, mSpacingAdd,
+                        hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
                         mIncludePad);
             }
         }
@@ -6219,6 +6230,10 @@
         BoringLayout.Metrics boring = UNKNOWN_BORING;
         BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
 
+        if (mTextDir == null) {
+            resolveTextDirection();
+        }
+
         int des = -1;
         boolean fromexisting = false;
 
@@ -6231,7 +6246,7 @@
             }
 
             if (des < 0) {
-                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -9441,7 +9456,7 @@
         @Override
         public void onClick(View v) {
             if (canPaste()) {
-                paste(getSelectionStart(), getSelectionEnd());
+                onTextContextMenuItem(ID_PASTE);
             }
             hide();
         }
@@ -10439,11 +10454,21 @@
         return mInBatchEditControllers;
     }
 
+    private class TextViewDirectionHeuristic extends TextDirectionHeuristicImpl {
+        private TextViewDirectionHeuristic(TextDirectionAlgorithm algorithm) {
+            super(algorithm);
+        }
+        @Override
+        protected boolean defaultIsRtl() {
+            return getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        }
+    }
+
     /**
      * Resolve the text direction.
      *
      * Text direction of paragraphs in a TextView is determined using a heuristic. If the correct
-     * text direction cannot be determined by the heuristic, the view’s resolved layout direction
+     * text direction cannot be determined by the heuristic, the view's resolved layout direction
      * determines the direction.
      *
      * This heuristic and result is applied individually to each paragraph in a TextView, based on
@@ -10452,157 +10477,27 @@
      */
     @Override
     protected void resolveTextDirection() {
-        int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
-        switch(mTextDirection) {
+        super.resolveTextDirection();
+
+        int textDir = getResolvedTextDirection();
+        switch (textDir) {
             default:
-            case TEXT_DIRECTION_INHERIT:
-                // Try to the text direction from the parent layout. If not possible, then we will
-                // use the default layout direction to decide later
-                if (mParent != null && mParent instanceof ViewGroup) {
-                    resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
-                }
-                break;
             case TEXT_DIRECTION_FIRST_STRONG:
-                resolvedTextDirection = getTextDirectionFromFirstStrong(mText);
+                mTextDir = new TextViewDirectionHeuristic(FirstStrong.INSTANCE);
                 break;
             case TEXT_DIRECTION_ANY_RTL:
-                resolvedTextDirection = getTextDirectionFromAnyRtl(mText);
+                mTextDir = new TextViewDirectionHeuristic(AnyStrong.INSTANCE_RTL);
                 break;
             case TEXT_DIRECTION_CHAR_COUNT:
-                resolvedTextDirection = getTextDirectionFromCharCount(mText);
+                mTextDir = new TextViewDirectionHeuristic(CharCount.INSTANCE_DEFAULT);
                 break;
             case TEXT_DIRECTION_LTR:
-                resolvedTextDirection = TEXT_DIRECTION_LTR;
+                mTextDir = TextDirectionHeuristics.LTR;
                 break;
             case TEXT_DIRECTION_RTL:
-                resolvedTextDirection = TEXT_DIRECTION_RTL;
+                mTextDir = TextDirectionHeuristics.RTL;
                 break;
         }
-        // if we have been so far unable to get the text direction from the heuristics, then we are
-        // falling back using the layout direction
-        if (resolvedTextDirection == TEXT_DIRECTION_UNDEFINED) {
-            switch(getResolvedLayoutDirection()) {
-                default:
-                case LAYOUT_DIRECTION_LTR:
-                    resolvedTextDirection = TEXT_DIRECTION_LTR;
-                    break;
-                case LAYOUT_DIRECTION_RTL:
-                    resolvedTextDirection = TEXT_DIRECTION_RTL;
-                    break;
-            }
-        }
-        mResolvedTextDirection = resolvedTextDirection;
-    }
-
-    /**
-     * Get text direction following the "first strong" heuristic.
-     *
-     * @param cs the CharSequence used to get the text direction.
-     *
-     * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
-     * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
-     */
-    private static int getTextDirectionFromFirstStrong(final CharSequence cs) {
-        final int length = cs.length();
-        if (length == 0) {
-            return TEXT_DIRECTION_UNDEFINED;
-        }
-        for(int i = 0; i < length; i++) {
-            final char c = cs.charAt(i);
-            final byte dir = Character.getDirectionality(c);
-            if (isStrongLtrChar(dir)) {
-                return TEXT_DIRECTION_LTR;
-            } else if (isStrongRtlChar(dir)) {
-                return TEXT_DIRECTION_RTL;
-            }
-        }
-        return TEXT_DIRECTION_UNDEFINED;
-    }
-
-    /**
-     * Get text direction following the "any RTL" heuristic.
-     *
-     * @param cs the CharSequence used to get the text direction.
-     *
-     * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
-     * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
-     */
-    private static int getTextDirectionFromAnyRtl(final CharSequence cs) {
-        final int length = cs.length();
-        if (length == 0) {
-            return TEXT_DIRECTION_UNDEFINED;
-        }
-        boolean foundStrongLtr = false;
-        boolean foundStrongRtl = false;
-        for(int i = 0; i < length; i++) {
-            final char c = cs.charAt(i);
-            final byte dir = Character.getDirectionality(c);
-            if (isStrongLtrChar(dir)) {
-                foundStrongLtr = true;
-            } else if (isStrongRtlChar(dir)) {
-                foundStrongRtl = true;
-                break;
-            }
-        }
-        if (foundStrongRtl) {
-            return TEXT_DIRECTION_RTL;
-        }
-        if (foundStrongLtr) {
-            return TEXT_DIRECTION_LTR;
-        }
-        return TEXT_DIRECTION_UNDEFINED;
-    }
-
-    /**
-     * Get text direction following the "char count" heuristic.
-     *
-     * @param cs the CharSequence used to get the text direction.
-     *
-     * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
-     * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
-     */
-    private int getTextDirectionFromCharCount(CharSequence cs) {
-        final int length = cs.length();
-        if (length == 0) {
-            return TEXT_DIRECTION_UNDEFINED;
-        }
-        int countLtr = 0;
-        int countRtl = 0;
-        for(int i = 0; i < length; i++) {
-            final char c = cs.charAt(i);
-            final byte dir = Character.getDirectionality(c);
-            if (isStrongLtrChar(dir)) {
-                countLtr++;
-            } else if (isStrongRtlChar(dir)) {
-                countRtl++;
-            }
-        }
-        final float percentLtr = ((float) countLtr) / (countLtr + countRtl);
-        if (percentLtr > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) {
-            return TEXT_DIRECTION_LTR;
-        }
-        final float percentRtl = ((float) countRtl) / (countLtr + countRtl);
-        if (percentRtl > DEFAULT_TEXT_DIRECTION_CHAR_COUNT_THRESHOLD) {
-            return TEXT_DIRECTION_RTL;
-        }
-        return TEXT_DIRECTION_UNDEFINED;
-    }
-
-    /**
-     * Return true if the char direction is corresponding to a "strong RTL char" following the
-     * Unicode Bidirectional Algorithm (UBA).
-     */
-    private static boolean isStrongRtlChar(final byte dir) {
-        return (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
-                dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC);
-    }
-
-    /**
-     * Return true if the char direction is corresponding to a "strong LTR char" following the
-     * Unicode Bidirectional Algorithm (UBA).
-     */
-    private static boolean isStrongLtrChar(final byte dir) {
-        return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT);
     }
 
     /**
@@ -10768,6 +10663,8 @@
 
     private BoringLayout mSavedLayout, mSavedHintLayout;
 
+    private TextDirectionHeuristic mTextDir = null;
+
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
     private InputFilter[] mFilters = NO_FILTERS;
     private static final Spanned EMPTY_SPANNED = new SpannedString("");
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 243c605..cf5666c 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -500,7 +500,7 @@
 
     @Override
     public int getHeight() {
-        return mActionView.getHeight();
+        return mContainerView.getHeight();
     }
 
     @Override
diff --git a/services/java/com/android/server/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
similarity index 99%
rename from services/java/com/android/server/ProcessStats.java
rename to core/java/com/android/internal/os/ProcessStats.java
index f693ed1..ea5ce09 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.internal.os;
 
 import static android.os.Process.*;
 
@@ -182,7 +182,7 @@
 
         public String baseName;
         public String name;
-        int nameWidth;
+        public int nameWidth;
 
         public long base_uptime;
         public long rel_uptime;
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index b754d94..69b80d9 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -40,6 +40,7 @@
 
     /** Non system protocols */
     public static final int BASE_WIFI                                               = 0x00020000;
+    public static final int BASE_WIFI_WATCHDOG                                      = 0x00021000;
     public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 58e7c8d..06dc083 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -145,7 +145,6 @@
 	android_server_Watchdog.cpp \
 	android_ddm_DdmHandleNativeHeap.cpp \
 	com_android_internal_os_ZygoteInit.cpp \
-	com_android_internal_graphics_NativeUtils.cpp \
 	android_backup_BackupDataInput.cpp \
 	android_backup_BackupDataOutput.cpp \
 	android_backup_FileBackupHelperBase.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 23c6da7..08431cd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -115,7 +115,6 @@
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
 extern int register_android_graphics_Xfermode(JNIEnv* env);
 extern int register_android_graphics_PixelFormat(JNIEnv* env);
-extern int register_com_android_internal_graphics_NativeUtils(JNIEnv *env);
 extern int register_android_view_Display(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
@@ -1147,7 +1146,6 @@
     REG_JNI(register_android_graphics_Typeface),
     REG_JNI(register_android_graphics_Xfermode),
     REG_JNI(register_android_graphics_YuvImage),
-    REG_JNI(register_com_android_internal_graphics_NativeUtils),
 
     REG_JNI(register_android_database_CursorWindow),
     REG_JNI(register_android_database_SQLiteCompiledSql),
diff --git a/core/jni/com_android_internal_graphics_NativeUtils.cpp b/core/jni/com_android_internal_graphics_NativeUtils.cpp
deleted file mode 100644
index 9cc43606..0000000
--- a/core/jni/com_android_internal_graphics_NativeUtils.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AWT"
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPicture.h"
-#include "SkTemplates.h"
-
-namespace android
-{
-
-static jboolean scrollRect(JNIEnv* env, jobject graphics2D, jobject canvas, jobject rect, int dx, int dy) {
-    if (canvas == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return false;
-    }
-
-    SkIRect src, *srcPtr = NULL;
-    if (NULL != rect) {
-        GraphicsJNI::jrect_to_irect(env, rect, &src);
-        srcPtr = &src;
-    }
-    SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas);
-    const SkBitmap& bitmap = c->getDevice()->accessBitmap(true);
-    return bitmap.scrollRect(srcPtr, dx, dy, NULL);
-}
-
-static JNINativeMethod method_table[] = {
-    { "nativeScrollRect",
-      "(Landroid/graphics/Canvas;Landroid/graphics/Rect;II)Z",
-      (void*)scrollRect}
-};
-
-int register_com_android_internal_graphics_NativeUtils(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(
-        env, "com/android/internal/graphics/NativeUtils",
-        method_table, NELEM(method_table));
-}
-
-}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 49eaf19..0397dfa 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1489,9 +1489,6 @@
                 android:excludeFromRecents="true">
         </activity>
 
-        <service android:name="com.android.server.LoadAverageService"
-                android:exported="true" />
-
         <service android:name="com.android.internal.service.wallpaper.ImageWallpaper"
                 android:permission="android.permission.BIND_WALLPAPER">
         </service>
diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml
index a885211..27dd9b8 100644
--- a/core/res/res/layout/list_menu_item_icon.xml
+++ b/core/res/res/layout/list_menu_item_icon.xml
@@ -21,6 +21,8 @@
     android:layout_gravity="center_vertical"
     android:layout_marginLeft="8dip"
     android:layout_marginRight="-8dip"
-    android:scaleType="center"
+    android:layout_marginTop="8dip"
+    android:layout_marginBottom="8dip"
+    android:scaleType="centerInside"
     android:duplicateParentState="true" />
 
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index fb898ee..4e7981a 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -44,8 +44,8 @@
                 android:layout_width="match_parent"
                 android:layout_height="0px"
                 android:layout_weight="1"
-                android:paddingTop="16dp"
-                android:paddingBottom="16dp"
+                android:paddingTop="@dimen/preference_screen_header_vertical_padding"
+                android:paddingBottom="@dimen/preference_screen_header_vertical_padding"
                 android:drawSelectorOnTop="false"
                 android:cacheColorHint="@android:color/transparent"
                 android:listPreferredItemHeight="48dp"
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 9ffe6b1..17bf561 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -44,5 +44,7 @@
     <!-- Size of status line font in LockScreen. -->
     <dimen name="keyguard_pattern_unlock_status_line_font_size">14sp</dimen>
 
+    <!-- Preference activity, vertical padding for the header list -->
+    <dimen name="reference_screen_header_vertical_padding">16dp</dimen>
 </resources>
 
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 0f04a67..bcf1991 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -22,6 +22,15 @@
     <!-- Do not translate. These are all of the drawable resources that should be preloaded by
          the zygote process before it starts forking application processes. -->
     <array name="preloaded_drawables">
+       <item>@drawable/spinner_black_16</item>
+       <item>@drawable/spinner_black_20</item>
+       <item>@drawable/spinner_black_48</item>
+       <item>@drawable/spinner_black_76</item>
+       <item>@drawable/spinner_white_16</item>
+       <item>@drawable/spinner_white_48</item>
+       <item>@drawable/spinner_white_76</item>
+       <item>@drawable/toast_frame</item>
+       <item>@drawable/toast_frame_holo</item>
        <item>@drawable/btn_check_on_selected</item>
        <item>@drawable/btn_check_on_pressed_holo_light</item>
        <item>@drawable/btn_check_on_pressed_holo_dark</item>
@@ -109,6 +118,11 @@
        <item>@drawable/btn_default_disabled_holo_dark</item>
        <item>@drawable/btn_default_disabled_focused_holo_light</item>
        <item>@drawable/btn_default_disabled_focused_holo_dark</item>
+       <item>@drawable/btn_dropdown_disabled</item>
+       <item>@drawable/btn_dropdown_disabled_focused</item>
+       <item>@drawable/btn_dropdown_normal</item>
+       <item>@drawable/btn_dropdown_pressed</item>
+       <item>@drawable/btn_dropdown_selected</item>
        <item>@drawable/btn_toggle_on_pressed_holo_light</item>
        <item>@drawable/btn_toggle_on_pressed_holo_dark</item>
        <item>@drawable/btn_toggle_on_normal_holo_light</item>
@@ -141,6 +155,10 @@
        <item>@drawable/edit_text_holo_light</item>
        <item>@drawable/edit_text_holo_dark</item>
        <item>@drawable/edit_text</item>
+       <item>@drawable/expander_close_holo_dark</item>
+       <item>@drawable/expander_close_holo_light</item>
+       <item>@drawable/expander_ic_maximized</item>
+       <item>@drawable/expander_ic_minimized</item>
        <item>@drawable/expander_group</item>
        <item>@drawable/expander_group_holo_dark</item>
        <item>@drawable/expander_group_holo_light</item>
@@ -157,6 +175,8 @@
        <item>@drawable/menu_background_fill_parent_width</item>
        <item>@drawable/menu_submenu_background</item>
        <item>@drawable/menu_selector</item>
+       <item>@drawable/overscroll_edge</item>
+       <item>@drawable/overscroll_glow</item>
        <item>@drawable/panel_background</item>
        <item>@drawable/popup_bottom_bright</item>
        <item>@drawable/popup_bottom_dark</item>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0f6e5cf..abc56ec 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -96,6 +96,8 @@
          is along the major axis (that is the screen is landscape).  This may
          be either a fraction or a dimension. -->
     <item type="dimen" name="dialog_min_width_major">65%</item>
+    <!-- Preference activity, vertical padding for the header list -->
+    <dimen name="preference_screen_header_vertical_padding">0dp</dimen>
 
     <!-- The platform's desired minimum size for a dialog's width when it
          is along the minor axis (that is the screen is portrait).  This may
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index a5cd6e3..6b75146 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1672,6 +1672,7 @@
 
     <style name="Widget.Holo.ProgressBar.Small" parent="Widget.ProgressBar.Small">
         <item name="android:indeterminateDrawable">@android:drawable/progress_small_holo</item>
+        <item name="android:animationResolution">33</item>
     </style>
 
     <style name="Widget.Holo.ProgressBar.Small.Title">
@@ -1679,6 +1680,7 @@
 
     <style name="Widget.Holo.ProgressBar.Large" parent="Widget.ProgressBar.Large">
         <item name="android:indeterminateDrawable">@android:drawable/progress_large_holo</item>
+        <item name="android:animationResolution">33</item>
     </style>
 
     <style name="Widget.Holo.ProgressBar.Inverse">
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index c54e4a1..7dc95db 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -16,13 +16,13 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.GetChars;
 import android.view.View;
 
+import com.android.frameworks.coretests.R;
+
 /**
  * TextViewTest tests {@link TextView}.
  */
@@ -240,12 +240,12 @@
 
         getActivity().runOnUiThread(new Runnable() {
             public void run() {
-                tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+                ll.setTextDirection(View.TEXT_DIRECTION_RTL);
+                tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
                 assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
-                assertEquals(true, tv.isResolvedTextDirection());
 
                 ll.removeView(tv);
-                assertEquals(false, tv.isResolvedTextDirection());
+                assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getResolvedTextDirection());
             }
         });
     }
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
new file mode 100644
index 0000000..c0d9153
--- /dev/null
+++ b/data/fonts/fallback_fonts.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Fallback Fonts
+
+    This file specifies the fonts, and the priority order, that will be searched for any
+    glyphs not handled by the default fonts specified in /system/etc/system_fonts.xml.
+    Each entry consists of a family tag and a list of files (file names) which support that
+    family. The fonts for each family are listed in the order of the styles that they
+    handle (the order is: regular, bold, italic, and bold-italic). The order in which the
+    families are listed in this file represents the order in which these fallback fonts
+    will be searched for glyphs that are not supported by the default system fonts (which are
+    found in /system/etc/system_fonts.xml).
+
+    Note that there is not nameset for fallback fonts, unlike the fonts specified in
+    system_fonts.xml. The ability to support specific names in fallback fonts may be supported
+    in the future. For now, the lack of files entries here is an indicator to the system that
+    these are fallback fonts, instead of default named system fonts.
+
+    There is another optional file in /vendor/etc/fallback_fonts.xml. That file can be used to
+    provide references to other font families that should be used in addition to the default
+    fallback fonts. That file can also specify the order in which the fallback fonts should be
+    searched, to ensure that a vendor-provided font will be used before another fallback font
+    which happens to handle the same glyph.
+-->
+<familyset>
+    <family>
+        <fileset>
+            <file>DroidSansArabic.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>DroidSansHebrew-Regular.ttf</file>
+            <file>DroidSansHebrew-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>DroidSansThai.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>DroidSansFallback.ttf</file>
+        </fileset>
+    </family>
+</familyset>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index d222c0b..57a1bab 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -30,4 +30,6 @@
     frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf \
     frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
     frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \
-    frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf
+    frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf \
+    frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \
+    frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml
diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml
new file mode 100644
index 0000000..8d8d020
--- /dev/null
+++ b/data/fonts/system_fonts.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    System Fonts
+
+    This file lists the font families that will be used by default for all supported glyphs.
+    Each entry consists of a family, various names that are supported by that family, and
+    up to four font files. The font files are listed in the order of the styles which they
+    support: regular, bold, italic and bold-italic. If less than four styles are listed, then
+    the styles with no associated font file will be supported by the other font files listed.
+
+    The first family is also the default font, which handles font request that have not specified
+    specific font names.
+
+    Any glyph that is not handled by the system fonts will cause a search of the fallback fonts.
+    The default fallback fonts are specified in the file /system/etc/fallback_fonts.xml, and there
+    is an optional file which may be supplied by vendors to specify other fallback fonts to use
+    in /vendor/etc/fallback_fonts.xml.
+-->
+<familyset>
+
+    <family>
+        <nameset>
+            <name>sans-serif</name>
+            <name>arial</name>
+            <name>helvetica</name>
+            <name>tahoma</name>
+            <name>verdana</name>
+        </nameset>
+        <fileset>
+            <file>DroidSans.ttf</file>
+            <file>DroidSans-Bold.ttf</file>
+        </fileset>
+    </family>
+
+    <family>
+        <nameset>
+            <name>serif</name>
+            <name>times</name>
+            <name>times new roman</name>
+            <name>palatino</name>
+            <name>georgia</name>
+            <name>baskerville</name>
+            <name>goudy</name>
+            <name>fantasy</name>
+            <name>cursive</name>
+            <name>ITC Stone Serif</name>
+        </nameset>
+        <fileset>
+            <file>DroidSerif-Regular.ttf</file>
+            <file>DroidSerif-Bold.ttf</file>
+            <file>DroidSerif-Italic.ttf</file>
+            <file>DroidSerif-BoldItalic.ttf</file>
+        </fileset>
+    </family>
+
+    <family>
+        <nameset>
+            <name>monospace</name>
+            <name>courier</name>
+            <name>courier new</name>
+            <name>monaco</name>
+        </nameset>
+        <fileset>
+            <file>DroidSansMono.ttf</file>
+        </fileset>
+    </family>
+
+</familyset>
diff --git a/data/fonts/vendor_fonts.xml b/data/fonts/vendor_fonts.xml
new file mode 100644
index 0000000..fe51fd2
--- /dev/null
+++ b/data/fonts/vendor_fonts.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Vendor-provided fallback fonts
+
+    This file can be edited to add references to fonts that are not installed or referenced in the
+    default system. The file should then be placed in /vendor/etc/fallback_fonts.xml.
+
+    For example, vendors might want to build configurations for locales that are
+    better served by fonts which either handle glyphs not supported in the default fonts or which
+    handle these glyphs differently than the default fallback fonts.
+    Each entry in this list is a "family", which consists of a list of "files"
+    (the filenames for that family). The files objects are
+    provided in the order of the styles supported for that family: regular, bold, italic, and
+    bold-italic. Only providing one font means that all styles will be rendered with that font.
+    Providing two means that these two fonts will render regular and bold fonts (italics will
+    be mapped to these two fonts).
+
+    There is also an optional "order" attribute on the Family tag. This specifies the index at
+    which that family of fonts should be inserted in the fallback font list, where the
+    default fallback fonts on the system (in /system/etc/fallback_fonts.xml) start at index 0.
+    If no 'order' attribute is supplied, that family will be inserted either at the end of the
+    current fallback list (if no order was supplied for any previous family in this file) or
+    after the previous family (if there was an order specified previously). Typically, vendors
+    may want to supply an order for the first family that puts this set of fonts at the appropriate
+    place in the overall fallback fonts. The order of this list determines which fallback font
+    will be used to support any glyphs that are not handled by the default system fonts.
+
+    The sample configuration below is an example of how one might provide two families of fonts
+    that get inserted at the first and second (0  and 1) position in the overall fallback fonts.
+
+    See /system/etc/system_fonts.xml and /system/etc/fallback_fonts.xml for more information
+    and to understand the order in which the default system fonts are loaded and structured for
+    lookup.
+-->
+
+<!-- Sample fallback font additions to the default fallback list. These fonts will be added
+    to the top two positions of the fallback list, since the first has an order of 0. -->
+<!--
+<familyset>
+    <family order="0">
+        <fileset>
+            <file>MyFont.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>MyOtherFont.ttf</file>
+        </fileset>
+    </family>
+</familyset>
+-->
\ No newline at end of file
diff --git a/data/sounds/effects/ogg/Dock.ogg b/data/sounds/effects/ogg/Dock.ogg
old mode 100644
new mode 100755
index 626d695..a1c1f2c
--- a/data/sounds/effects/ogg/Dock.ogg
+++ b/data/sounds/effects/ogg/Dock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressDelete.ogg b/data/sounds/effects/ogg/KeypressDelete.ogg
old mode 100644
new mode 100755
index 5e724f4..38c3244
--- a/data/sounds/effects/ogg/KeypressDelete.ogg
+++ b/data/sounds/effects/ogg/KeypressDelete.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressReturn.ogg b/data/sounds/effects/ogg/KeypressReturn.ogg
old mode 100644
new mode 100755
index a1200b2..1bd5b73
--- a/data/sounds/effects/ogg/KeypressReturn.ogg
+++ b/data/sounds/effects/ogg/KeypressReturn.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressSpacebar.ogg b/data/sounds/effects/ogg/KeypressSpacebar.ogg
old mode 100644
new mode 100755
index 0d0fbf1..3a83594
--- a/data/sounds/effects/ogg/KeypressSpacebar.ogg
+++ b/data/sounds/effects/ogg/KeypressSpacebar.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressStandard.ogg b/data/sounds/effects/ogg/KeypressStandard.ogg
old mode 100644
new mode 100755
index 5878135..4b8d128
--- a/data/sounds/effects/ogg/KeypressStandard.ogg
+++ b/data/sounds/effects/ogg/KeypressStandard.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg
old mode 100644
new mode 100755
index a25513f..deeba68
--- a/data/sounds/effects/ogg/Lock.ogg
+++ b/data/sounds/effects/ogg/Lock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/LowBattery.ogg b/data/sounds/effects/ogg/LowBattery.ogg
old mode 100644
new mode 100755
index c21218c..370c86c
--- a/data/sounds/effects/ogg/LowBattery.ogg
+++ b/data/sounds/effects/ogg/LowBattery.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Undock.ogg b/data/sounds/effects/ogg/Undock.ogg
old mode 100644
new mode 100755
index dd132c4..91e410e
--- a/data/sounds/effects/ogg/Undock.ogg
+++ b/data/sounds/effects/ogg/Undock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Unlock.ogg b/data/sounds/effects/ogg/Unlock.ogg
old mode 100644
new mode 100755
index 490f98e..ac50288
--- a/data/sounds/effects/ogg/Unlock.ogg
+++ b/data/sounds/effects/ogg/Unlock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/camera_click.ogg b/data/sounds/effects/ogg/camera_click.ogg
old mode 100644
new mode 100755
index eee590f..bfb2a68
--- a/data/sounds/effects/ogg/camera_click.ogg
+++ b/data/sounds/effects/ogg/camera_click.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Argon.ogg b/data/sounds/notifications/ogg/Argon.ogg
old mode 100644
new mode 100755
index f160562..e58b3b6
--- a/data/sounds/notifications/ogg/Argon.ogg
+++ b/data/sounds/notifications/ogg/Argon.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Beryllium.ogg b/data/sounds/notifications/ogg/Beryllium.ogg
old mode 100644
new mode 100755
index 5f7bd3c..2c5b4fe
--- a/data/sounds/notifications/ogg/Beryllium.ogg
+++ b/data/sounds/notifications/ogg/Beryllium.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Cobalt.ogg b/data/sounds/notifications/ogg/Cobalt.ogg
old mode 100644
new mode 100755
index a9adeb8..b6e253a
--- a/data/sounds/notifications/ogg/Cobalt.ogg
+++ b/data/sounds/notifications/ogg/Cobalt.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Fluorine.ogg b/data/sounds/notifications/ogg/Fluorine.ogg
old mode 100644
new mode 100755
index 6340cf3..fd884f5
--- a/data/sounds/notifications/ogg/Fluorine.ogg
+++ b/data/sounds/notifications/ogg/Fluorine.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Gallium.ogg b/data/sounds/notifications/ogg/Gallium.ogg
old mode 100644
new mode 100755
index b0446b2..3c7e1156
--- a/data/sounds/notifications/ogg/Gallium.ogg
+++ b/data/sounds/notifications/ogg/Gallium.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Radon.ogg b/data/sounds/notifications/ogg/Radon.ogg
old mode 100644
new mode 100755
index 1d70c11..550cddd
--- a/data/sounds/notifications/ogg/Radon.ogg
+++ b/data/sounds/notifications/ogg/Radon.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Selenium.ogg b/data/sounds/notifications/ogg/Selenium.ogg
old mode 100644
new mode 100755
index 7624eff..9d60917
--- a/data/sounds/notifications/ogg/Selenium.ogg
+++ b/data/sounds/notifications/ogg/Selenium.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Zirconium.ogg b/data/sounds/notifications/ogg/Zirconium.ogg
old mode 100644
new mode 100755
index 31e40c1..d84b59e
--- a/data/sounds/notifications/ogg/Zirconium.ogg
+++ b/data/sounds/notifications/ogg/Zirconium.ogg
Binary files differ
diff --git a/graphics/java/com/android/internal/graphics/NativeUtils.java b/graphics/java/com/android/internal/graphics/NativeUtils.java
deleted file mode 100644
index c91b7d9..0000000
--- a/graphics/java/com/android/internal/graphics/NativeUtils.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.graphics;
-
-import android.graphics.Canvas;
-import android.graphics.Rect;
-
-public final class NativeUtils {
-    /**
-     * This class is uninstantiable.
-     */
-    private NativeUtils() {
-        // This space intentionally left blank.
-    }
-
-    /**
-     * Scroll a rectangular portion of a canvas.
-     * 
-     * @param canvas the canvas to manipulate
-     * @param src the source rectangle
-     * @param dx horizontal offset
-     * @param dy vertical offset
-     */
-    public static native boolean nativeScrollRect(Canvas canvas, Rect src,
-            int dx, int dy);
-}
diff --git a/include/cpustats/CentralTendencyStatistics.h b/include/cpustats/CentralTendencyStatistics.h
new file mode 100644
index 0000000..21b6981
--- /dev/null
+++ b/include/cpustats/CentralTendencyStatistics.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CENTRAL_TENDENCY_STATISTICS_H
+#define _CENTRAL_TENDENCY_STATISTICS_H
+
+#include <math.h>
+
+// Not multithread safe
+class CentralTendencyStatistics {
+
+public:
+
+    CentralTendencyStatistics() :
+            mMean(NAN), mMedian(NAN), mMinimum(INFINITY), mMaximum(-INFINITY), mN(0), mM2(0),
+            mVariance(NAN), mVarianceKnownForN(0), mStddev(NAN), mStddevKnownForN(0) { }
+
+    ~CentralTendencyStatistics() { }
+
+    // add x to the set of samples
+    void sample(double x);
+
+    // return the arithmetic mean of all samples so far
+    double mean() const { return mMean; }
+
+    // return the minimum of all samples so far
+    double minimum() const { return mMinimum; }
+
+    // return the maximum of all samples so far
+    double maximum() const { return mMaximum; }
+
+    // return the variance of all samples so far
+    double variance() const;
+
+    // return the standard deviation of all samples so far
+    double stddev() const;
+
+    // return the number of samples added so far
+    unsigned n() const { return mN; }
+
+    // reset the set of samples to be empty
+    void reset();
+
+private:
+    double mMean;
+    double mMedian;
+    double mMinimum;
+    double mMaximum;
+    unsigned mN;    // number of samples so far
+    double mM2;
+
+    // cached variance, and n at time of caching
+    mutable double mVariance;
+    mutable unsigned mVarianceKnownForN;
+
+    // cached standard deviation, and n at time of caching
+    mutable double mStddev;
+    mutable unsigned mStddevKnownForN;
+
+};
+
+#endif // _CENTRAL_TENDENCY_STATISTICS_H
diff --git a/include/cpustats/README.txt b/include/cpustats/README.txt
new file mode 100644
index 0000000..14439f0
--- /dev/null
+++ b/include/cpustats/README.txt
@@ -0,0 +1,6 @@
+This is a static library of CPU usage statistics, originally written
+for audio but most are not actually specific to audio.
+
+Requirements to be here:
+ * should be related to CPU usage statistics
+ * should be portable to host; avoid Android OS dependencies without a conditional
diff --git a/include/cpustats/ThreadCpuUsage.h b/include/cpustats/ThreadCpuUsage.h
new file mode 100644
index 0000000..24012a4
--- /dev/null
+++ b/include/cpustats/ThreadCpuUsage.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _THREAD_CPU_USAGE_H
+#define _THREAD_CPU_USAGE_H
+
+#include <cpustats/CentralTendencyStatistics.h>
+
+// Track CPU usage for the current thread, and maintain statistics on
+// the CPU usage.  Units are in per-thread CPU ns, as reported by
+// clock_gettime(CLOCK_THREAD_CPUTIME_ID).  Simple usage: for cyclic
+// threads where you want to measure the execution time of the whole
+// cycle, just call sampleAndEnable() at the start of each cycle.
+// Then call statistics() to get the results, and resetStatistics()
+// to start a new set of measurements.
+// For acyclic threads, or for cyclic threads where you want to measure
+// only part of each cycle, call enable(), disable(), and/or setEnabled()
+// to demarcate the region(s) of interest, and then call sample() periodically.
+// This class is not thread-safe for concurrent calls from multiple threads;
+// the methods of this class may only be called by the current thread
+// which constructed the object.
+
+class ThreadCpuUsage
+{
+
+public:
+    ThreadCpuUsage() :
+        mIsEnabled(false),
+        mWasEverEnabled(false),
+        mAccumulator(0),
+        // mPreviousTs
+        // mMonotonicTs
+        mMonotonicKnown(false)
+        // mStatistics
+        { }
+
+    ~ThreadCpuUsage() { }
+
+    // Return whether currently tracking CPU usage by current thread
+    bool isEnabled()    { return mIsEnabled; }
+
+    // Enable tracking of CPU usage by current thread;
+    // any CPU used from this point forward will be tracked.
+    // Returns the previous enabled status.
+    bool enable()       { return setEnabled(true); }
+
+    // Disable tracking of CPU usage by current thread;
+    // any CPU used from this point forward will be ignored.
+    // Returns the previous enabled status.
+    bool disable()      { return setEnabled(false); }
+
+    // Set the enabled status and return the previous enabled status.
+    // This method is intended to be used for safe nested enable/disabling.
+    bool setEnabled(bool isEnabled);
+
+    // Add a sample point for central tendency statistics, and also
+    // enable tracking if needed.  If tracking has never been enabled, then
+    // enables tracking but does not add a sample (it is not possible to add
+    // a sample the first time because no previous).  Otherwise if tracking is
+    // enabled, then adds a sample for tracked CPU ns since the previous
+    // sample, or since the first call to sampleAndEnable(), enable(), or
+    // setEnabled(true).  If there was a previous sample but tracking is
+    // now disabled, then adds a sample for the tracked CPU ns accumulated
+    // up until the most recent disable(), resets this accumulator, and then
+    // enables tracking.  Calling this method rather than enable() followed
+    // by sample() avoids a race condition for the first sample.
+    void sampleAndEnable();
+
+    // Add a sample point for central tendency statistics, but do not
+    // change the tracking enabled status.  If tracking has either never been
+    // enabled, or has never been enabled since the last sample, then log a warning
+    // and don't add sample.  Otherwise, adds a sample for tracked CPU ns since
+    // the previous sample or since the first call to sampleAndEnable(),
+    // enable(), or setEnabled(true) if no previous sample.
+    void sample();
+
+    // Return the elapsed delta wall clock ns since initial enable or statistics reset,
+    // as reported by clock_gettime(CLOCK_MONOTONIC).
+    long long elapsed() const;
+
+    // Reset statistics and elapsed.  Has no effect on tracking or accumulator.
+    void resetStatistics();
+
+    // Return a const reference to the central tendency statistics.
+    // Note that only the const methods can be called on this object.
+    const CentralTendencyStatistics& statistics() const {
+        return mStatistics;
+    }
+
+private:
+    bool mIsEnabled;                // whether tracking is currently enabled
+    bool mWasEverEnabled;           // whether tracking was ever enabled
+    long long mAccumulator;         // accumulated thread CPU time since last sample, in ns
+    struct timespec mPreviousTs;    // most recent thread CPU time, valid only if mIsEnabled is true
+    struct timespec mMonotonicTs;   // most recent monotonic time
+    bool mMonotonicKnown;           // whether mMonotonicTs has been set
+    CentralTendencyStatistics mStatistics;
+};
+
+#endif //  _THREAD_CPU_USAGE_H
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index e705c6f..5b5b731 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -104,6 +104,24 @@
     // queued buffers will be retired in order.
     // The default mode is asynchronous.
     virtual status_t setSynchronousMode(bool enabled) = 0;
+
+    // connect attempts to connect a client API to the SurfaceTexture.  This
+    // must be called before any other ISurfaceTexture methods are called except
+    // for getAllocator.
+    //
+    // This method will fail if the connect was previously called on the
+    // SurfaceTexture and no corresponding disconnect call was made.
+    virtual status_t connect(int api) = 0;
+
+    // disconnect attempts to disconnect a client API from the SurfaceTexture.
+    // Calling this method will cause any subsequent calls to other
+    // ISurfaceTexture methods to fail except for getAllocator and connect.
+    // Successfully calling connect after this will allow the other methods to
+    // succeed again.
+    //
+    // This method will fail if the the SurfaceTexture is not currently
+    // connected to the specified client API.
+    virtual status_t disconnect(int api) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index e36360c..4080f27 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -44,6 +44,7 @@
         MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
     };
     enum { NUM_BUFFER_SLOTS = 32 };
+    enum { NO_CONNECTED_API = 0 };
 
     struct FrameAvailableListener : public virtual RefBase {
         // onFrameAvailable() is called from queueBuffer() each time an
@@ -97,6 +98,24 @@
     // The default mode is asynchronous.
     virtual status_t setSynchronousMode(bool enabled);
 
+    // connect attempts to connect a client API to the SurfaceTexture.  This
+    // must be called before any other ISurfaceTexture methods are called except
+    // for getAllocator.
+    //
+    // This method will fail if the connect was previously called on the
+    // SurfaceTexture and no corresponding disconnect call was made.
+    virtual status_t connect(int api);
+
+    // disconnect attempts to disconnect a client API from the SurfaceTexture.
+    // Calling this method will cause any subsequent calls to other
+    // ISurfaceTexture methods to fail except for getAllocator and connect.
+    // Successfully calling connect after this will allow the other methods to
+    // succeed again.
+    //
+    // This method will fail if the the SurfaceTexture is not currently
+    // connected to the specified client API.
+    virtual status_t disconnect(int api);
+
     // updateTexImage sets the image contents of the target texture to that of
     // the most recently queued buffer.
     //
@@ -362,6 +381,11 @@
     // mAllowSynchronousMode whether we allow synchronous mode or not
     const bool mAllowSynchronousMode;
 
+    // mConnectedApi indicates the API that is currently connected to this
+    // SurfaceTexture.  It defaults to NO_CONNECTED_API (= 0), and gets updated
+    // by the connect and disconnect methods.
+    int mConnectedApi;
+
     // mDequeueCondition condition used for dequeueBuffer in synchronous mode
     mutable Condition mDequeueCondition;
 
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index 9db7364..5ec469e 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -129,9 +129,6 @@
     // a timestamp is auto-generated when queueBuffer is called.
     int64_t mTimestamp;
 
-    // mConnectedApi holds the currently connected API to this surface
-    int mConnectedApi;
-
     // mQueryWidth is the width returned by query(). It is set to width
     // of the last dequeued buffer or to mReqWidth if no buffer was dequeued.
     uint32_t mQueryWidth;
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 23ffd59..f38f6ce 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -23,8 +23,8 @@
  */
 interface IKeyChainService {
     // APIs used by KeyChain
-    byte[] getPrivateKey(String alias, String authToken);
-    byte[] getCertificate(String alias, String authToken);
+    byte[] getPrivateKey(String alias);
+    byte[] getCertificate(String alias);
 
     // APIs used by CertInstaller
     void installCaCertificate(in byte[] caCertificate);
@@ -32,4 +32,8 @@
     // APIs used by Settings
     boolean deleteCaCertificate(String alias);
     boolean reset();
+
+    // APIs used by KeyChainActivity
+    void setGrant(int uid, String alias, boolean value);
+    boolean hasGrant(int uid, String alias);
 }
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 6229331..db6388a 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -15,36 +15,25 @@
  */
 package android.security;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.io.IOException;
-import java.security.KeyFactory;
 import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
@@ -266,7 +255,7 @@
             throw new NullPointerException("response == null");
         }
         Intent intent = new Intent(ACTION_CHOOSER);
-        intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response));
+        intent.putExtra(EXTRA_RESPONSE, new AliasResponse(response));
         intent.putExtra(EXTRA_HOST, host);
         intent.putExtra(EXTRA_PORT, port);
         intent.putExtra(EXTRA_ALIAS, alias);
@@ -276,56 +265,12 @@
     }
 
     private static class AliasResponse extends IKeyChainAliasCallback.Stub {
-        private final Activity activity;
         private final KeyChainAliasCallback keyChainAliasResponse;
-        private AliasResponse(Activity activity, KeyChainAliasCallback keyChainAliasResponse) {
-            this.activity = activity;
+        private AliasResponse(KeyChainAliasCallback keyChainAliasResponse) {
             this.keyChainAliasResponse = keyChainAliasResponse;
         }
         @Override public void alias(String alias) {
-            if (alias == null) {
-                keyChainAliasResponse.alias(null);
-                return;
-            }
-            AccountManager accountManager = AccountManager.get(activity);
-            accountManager.getAuthToken(getAccount(activity),
-                                        alias,
-                                        null,
-                                        activity,
-                                        new AliasAccountManagerCallback(keyChainAliasResponse,
-                                                                        alias),
-                                        null);
-        }
-    }
-
-    private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> {
-        private final KeyChainAliasCallback keyChainAliasResponse;
-        private final String alias;
-        private AliasAccountManagerCallback(KeyChainAliasCallback keyChainAliasResponse,
-                                            String alias) {
-            this.keyChainAliasResponse = keyChainAliasResponse;
-            this.alias = alias;
-        }
-        @Override public void run(AccountManagerFuture<Bundle> future) {
-            Bundle bundle;
-            try {
-                bundle = future.getResult();
-            } catch (OperationCanceledException e) {
-                keyChainAliasResponse.alias(null);
-                return;
-            } catch (IOException e) {
-                keyChainAliasResponse.alias(null);
-                return;
-            } catch (AuthenticatorException e) {
-                keyChainAliasResponse.alias(null);
-                return;
-            }
-            String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
-            if (authToken != null) {
-                keyChainAliasResponse.alias(alias);
-            } else {
-                keyChainAliasResponse.alias(null);
-            }
+            keyChainAliasResponse.alias(alias);
         }
     }
 
@@ -347,12 +292,8 @@
         }
         KeyChainConnection keyChainConnection = bind(context);
         try {
-            String authToken = authToken(context, alias);
-            if (authToken == null) {
-                return null;
-            }
             IKeyChainService keyChainService = keyChainConnection.getService();
-            byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken);
+            byte[] privateKeyBytes = keyChainService.getPrivateKey(alias);
             return toPrivateKey(privateKeyBytes);
         } catch (RemoteException e) {
             throw new KeyChainException(e);
@@ -382,12 +323,8 @@
         }
         KeyChainConnection keyChainConnection = bind(context);
         try {
-            String authToken = authToken(context, alias);
-            if (authToken == null) {
-                return null;
-            }
             IKeyChainService keyChainService = keyChainConnection.getService();
-            byte[] certificateBytes = keyChainService.getCertificate(alias, authToken);
+            byte[] certificateBytes = keyChainService.getCertificate(alias);
             List<X509Certificate> chain = new ArrayList<X509Certificate>();
             chain.add(toCertificate(certificateBytes));
             TrustedCertificateStore store = new TrustedCertificateStore();
@@ -438,50 +375,6 @@
         }
     }
 
-    private static String authToken(Context context, String alias) {
-        AccountManager accountManager = AccountManager.get(context);
-        AccountManagerFuture<Bundle> future = accountManager.getAuthToken(getAccount(context),
-                                                                          alias,
-                                                                          false,
-                                                                          null,
-                                                                          null);
-        Bundle bundle;
-        try {
-            bundle = future.getResult();
-        } catch (OperationCanceledException e) {
-            throw new AssertionError(e);
-        } catch (IOException e) {
-            // KeyChainAccountAuthenticator doesn't do I/O
-            throw new AssertionError(e);
-        } catch (AuthenticatorException e) {
-            throw new AssertionError(e);
-        }
-        Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
-        if (intent != null) {
-            return null;
-        }
-        String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
-        if (authToken == null) {
-            throw new AssertionError("Invalid authtoken");
-        }
-        return authToken;
-    }
-
-    private static Account getAccount(Context context) {
-        AccountManager accountManager = AccountManager.get(context);
-        Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
-        if (accounts.length == 0) {
-            try {
-                // Account is created if necessary during binding of the IKeyChainService
-                bind(context).close();
-            } catch (InterruptedException e) {
-                throw new AssertionError(e);
-            }
-            accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
-        }
-        return accounts[0];
-    }
-
     /**
      * @hide for reuse by CertInstaller and Settings.
      * @see KeyChain#bind
@@ -517,11 +410,15 @@
         ensureNotOnMainThread(context);
         final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1);
         ServiceConnection keyChainServiceConnection = new ServiceConnection() {
+            volatile boolean mConnectedAtLeastOnce = false;
             @Override public void onServiceConnected(ComponentName name, IBinder service) {
-                try {
-                    q.put(IKeyChainService.Stub.asInterface(service));
-                } catch (InterruptedException e) {
-                    throw new AssertionError(e);
+                if (!mConnectedAtLeastOnce) {
+                    mConnectedAtLeastOnce = true;
+                    try {
+                        q.put(IKeyChainService.Stub.asInterface(service));
+                    } catch (InterruptedException e) {
+                        // will never happen, since the queue starts with one available slot
+                    }
                 }
             }
             @Override public void onServiceDisconnected(ComponentName name) {}
diff --git a/libs/cpustats/Android.mk b/libs/cpustats/Android.mk
new file mode 100644
index 0000000..5c1074d
--- /dev/null
+++ b/libs/cpustats/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES :=     \
+        CentralTendencyStatistics.cpp \
+        ThreadCpuUsage.cpp
+
+LOCAL_MODULE := libcpustats
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES :=     \
+        CentralTendencyStatistics.cpp \
+        ThreadCpuUsage.cpp
+
+LOCAL_MODULE := libcpustats
+
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libs/cpustats/CentralTendencyStatistics.cpp b/libs/cpustats/CentralTendencyStatistics.cpp
new file mode 100644
index 0000000..42ab62b
--- /dev/null
+++ b/libs/cpustats/CentralTendencyStatistics.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <cpustats/CentralTendencyStatistics.h>
+
+void CentralTendencyStatistics::sample(double x)
+{
+    // update min and max
+    if (x < mMinimum)
+        mMinimum = x;
+    if (x > mMaximum)
+        mMaximum = x;
+    // Knuth
+    if (mN == 0) {
+        mMean = 0;
+    }
+    ++mN;
+    double delta = x - mMean;
+    mMean += delta / mN;
+    mM2 += delta * (x - mMean);
+}
+
+void CentralTendencyStatistics::reset()
+{
+    mMean = NAN;
+    mMedian = NAN;
+    mMinimum = INFINITY;
+    mMaximum = -INFINITY;
+    mN = 0;
+    mM2 = 0;
+    mVariance = NAN;
+    mVarianceKnownForN = 0;
+    mStddev = NAN;
+    mStddevKnownForN = 0;
+}
+
+double CentralTendencyStatistics::variance() const
+{
+    double variance;
+    if (mVarianceKnownForN != mN) {
+        if (mN > 1) {
+            // double variance_n = M2/n;
+            variance = mM2 / (mN - 1);
+        } else {
+            variance = NAN;
+        }
+        mVariance = variance;
+        mVarianceKnownForN = mN;
+    } else {
+        variance = mVariance;
+    }
+    return variance;
+}
+
+double CentralTendencyStatistics::stddev() const
+{
+    double stddev;
+    if (mStddevKnownForN != mN) {
+        stddev = sqrt(variance());
+        mStddev = stddev;
+        mStddevKnownForN = mN;
+    } else {
+        stddev = mStddev;
+    }
+    return stddev;
+}
diff --git a/libs/cpustats/ThreadCpuUsage.cpp b/libs/cpustats/ThreadCpuUsage.cpp
new file mode 100644
index 0000000..4bfbdf3
--- /dev/null
+++ b/libs/cpustats/ThreadCpuUsage.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <time.h>
+
+#include <utils/Log.h>
+
+#include <cpustats/ThreadCpuUsage.h>
+
+bool ThreadCpuUsage::setEnabled(bool isEnabled)
+{
+    bool wasEnabled = mIsEnabled;
+    // only do something if there is a change
+    if (isEnabled != wasEnabled) {
+        int rc;
+        // enabling
+        if (isEnabled) {
+            rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs);
+            if (rc) {
+                LOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
+                isEnabled = false;
+            } else {
+                mWasEverEnabled = true;
+                // record wall clock time at first enable
+                if (!mMonotonicKnown) {
+                    rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
+                    if (rc) {
+                        LOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
+                    } else {
+                        mMonotonicKnown = true;
+                    }
+                }
+            }
+        // disabling
+        } else {
+            struct timespec ts;
+            rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+            if (rc) {
+                LOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
+            } else {
+                long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
+                        (ts.tv_nsec - mPreviousTs.tv_nsec);
+                mAccumulator += delta;
+#if 0
+                mPreviousTs = ts;
+#endif
+            }
+        }
+        mIsEnabled = isEnabled;
+    }
+    return wasEnabled;
+}
+
+void ThreadCpuUsage::sampleAndEnable()
+{
+    bool wasEverEnabled = mWasEverEnabled;
+    if (enable()) {
+        // already enabled, so add a new sample relative to previous
+        sample();
+    } else if (wasEverEnabled) {
+        // was disabled, but add sample for accumulated time while enabled
+        mStatistics.sample((double) mAccumulator);
+        mAccumulator = 0;
+    }
+}
+
+void ThreadCpuUsage::sample()
+{
+    if (mWasEverEnabled) {
+        if (mIsEnabled) {
+            struct timespec ts;
+            int rc;
+            rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+            if (rc) {
+                LOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
+            } else {
+                long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
+                        (ts.tv_nsec - mPreviousTs.tv_nsec);
+                mAccumulator += delta;
+                mPreviousTs = ts;
+            }
+        } else {
+            mWasEverEnabled = false;
+        }
+        mStatistics.sample((double) mAccumulator);
+        mAccumulator = 0;
+    } else {
+        LOGW("Can't add sample because measurements have never been enabled");
+    }
+}
+
+long long ThreadCpuUsage::elapsed() const
+{
+    long long elapsed;
+    if (mMonotonicKnown) {
+        struct timespec ts;
+        int rc;
+        rc = clock_gettime(CLOCK_MONOTONIC, &ts);
+        if (rc) {
+            LOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
+            elapsed = 0;
+        } else {
+            // mMonotonicTs is updated only at first enable and resetStatistics
+            elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL +
+                    (ts.tv_nsec - mMonotonicTs.tv_nsec);
+        }
+    } else {
+        LOGW("Can't compute elapsed time because measurements have never been enabled");
+        elapsed = 0;
+    }
+    return elapsed;
+}
+
+void ThreadCpuUsage::resetStatistics()
+{
+    mStatistics.reset();
+    if (mMonotonicKnown) {
+        int rc;
+        rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
+        if (rc) {
+            LOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
+            mMonotonicKnown = false;
+        }
+    }
+}
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index 16e3780..41434a4 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -41,6 +41,8 @@
     GET_ALLOCATOR,
     QUERY,
     SET_SYNCHRONOUS_MODE,
+    CONNECT,
+    DISCONNECT,
 };
 
 
@@ -154,7 +156,23 @@
         return result;
     }
 
+    virtual status_t connect(int api) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+        data.writeInt32(api);
+        remote()->transact(CONNECT, data, &reply);
+        status_t result = reply.readInt32();
+        return result;
+    }
 
+    virtual status_t disconnect(int api) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+        data.writeInt32(api);
+        remote()->transact(DISCONNECT, data, &reply);
+        status_t result = reply.readInt32();
+        return result;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
@@ -248,6 +266,20 @@
             reply->writeInt32(res);
             return NO_ERROR;
         } break;
+        case CONNECT: {
+            CHECK_INTERFACE(ISurfaceTexture, data, reply);
+            int api = data.readInt32();
+            status_t res = connect(api);
+            reply->writeInt32(res);
+            return NO_ERROR;
+        } break;
+        case DISCONNECT: {
+            CHECK_INTERFACE(ISurfaceTexture, data, reply);
+            int api = data.readInt32();
+            status_t res = disconnect(api);
+            reply->writeInt32(res);
+            return NO_ERROR;
+        } break;
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 886a3fb..1410481 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -92,7 +92,8 @@
     mNextTransform(0),
     mTexName(tex),
     mSynchronousMode(false),
-    mAllowSynchronousMode(allowSynchronousMode) {
+    mAllowSynchronousMode(allowSynchronousMode),
+    mConnectedApi(NO_CONNECTED_API) {
     LOGV("SurfaceTexture::SurfaceTexture");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -493,6 +494,50 @@
     return OK;
 }
 
+status_t SurfaceTexture::connect(int api) {
+    LOGV("SurfaceTexture::connect");
+    Mutex::Autolock lock(mMutex);
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            if (mConnectedApi != NO_CONNECTED_API) {
+                err = -EINVAL;
+            } else {
+                mConnectedApi = api;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
+}
+
+status_t SurfaceTexture::disconnect(int api) {
+    LOGV("SurfaceTexture::disconnect");
+    Mutex::Autolock lock(mMutex);
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            if (mConnectedApi == api) {
+                mConnectedApi = NO_CONNECTED_API;
+            } else {
+                err = -EINVAL;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
+}
+
 status_t SurfaceTexture::updateTexImage() {
     LOGV("SurfaceTexture::updateTexImage");
     Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index e203035..f39cabf 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -27,7 +27,7 @@
         const sp<ISurfaceTexture>& surfaceTexture):
         mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
         mReqHeight(0), mReqFormat(0), mReqUsage(0),
-        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0),
+        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO),
         mQueryWidth(0), mQueryHeight(0), mQueryFormat(0),
         mMutex() {
     // Initialize the ANativeWindow function pointers.
@@ -327,45 +327,22 @@
 int SurfaceTextureClient::connect(int api) {
     LOGV("SurfaceTextureClient::connect");
     Mutex::Autolock lock(mMutex);
-    int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-            if (mConnectedApi) {
-                err = -EINVAL;
-            } else {
-                mConnectedApi = api;
-            }
-            break;
-        default:
-            err = -EINVAL;
-            break;
-    }
-    return err;
+    return mSurfaceTexture->connect(api);
 }
 
 int SurfaceTextureClient::disconnect(int api) {
     LOGV("SurfaceTextureClient::disconnect");
     Mutex::Autolock lock(mMutex);
-    int err = NO_ERROR;
-    switch (api) {
-        case NATIVE_WINDOW_API_EGL:
-            if (mConnectedApi == api) {
-                mConnectedApi = 0;
-            } else {
-                err = -EINVAL;
-            }
-            break;
-        default:
-            err = -EINVAL;
-            break;
-    }
-    return err;
+    return mSurfaceTexture->disconnect(api);
 }
 
 int SurfaceTextureClient::getConnectedApi() const
 {
+    // XXX: This method will be going away shortly, and is currently bogus.  It
+    // always returns "nothing is connected".  It will go away once Surface gets
+    // updated to actually connect as the 'CPU' API when locking a buffer.
     Mutex::Autolock lock(mMutex);
-    return mConnectedApi;
+    return 0;
 }
 
 
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 88433fb..9abe89d 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "SurfaceTexture_test"
 //#define LOG_NDEBUG 0
 
 #include <gtest/gtest.h>
@@ -379,6 +380,13 @@
         ASSERT_NE(-1, mTexMatrixHandle);
     }
 
+    virtual void TearDown() {
+        mANW.clear();
+        mSTC.clear();
+        mST.clear();
+        GLTest::TearDown();
+    }
+
     // drawTexture draws the SurfaceTexture over the entire GL viewport.
     void drawTexture() {
         const GLfloat triangleVertices[] = {
@@ -1089,13 +1097,21 @@
     // synchronously from SurfaceTexture::queueBuffer.
     class FrameCondition : public SurfaceTexture::FrameAvailableListener {
     public:
+        FrameCondition():
+                mFrameAvailable(false),
+                mFrameFinished(false) {
+        }
+
         // waitForFrame waits for the next frame to arrive.  This should be
         // called from the consumer thread once for every frame expected by the
         // test.
         void waitForFrame() {
-            LOGV("+waitForFrame");
             Mutex::Autolock lock(mMutex);
-            status_t result = mFrameAvailableCondition.wait(mMutex);
+            LOGV("+waitForFrame");
+            while (!mFrameAvailable) {
+                mFrameAvailableCondition.wait(mMutex);
+            }
+            mFrameAvailable = false;
             LOGV("-waitForFrame");
         }
 
@@ -1103,22 +1119,30 @@
         // on to produce the next frame.  This should be called by the consumer
         // thread once for every frame expected by the test.
         void finishFrame() {
-            LOGV("+finishFrame");
             Mutex::Autolock lock(mMutex);
+            LOGV("+finishFrame");
+            mFrameFinished = true;
             mFrameFinishCondition.signal();
             LOGV("-finishFrame");
         }
 
         // This should be called by SurfaceTexture on the producer thread.
         virtual void onFrameAvailable() {
-            LOGV("+onFrameAvailable");
             Mutex::Autolock lock(mMutex);
+            LOGV("+onFrameAvailable");
+            mFrameAvailable = true;
             mFrameAvailableCondition.signal();
-            mFrameFinishCondition.wait(mMutex);
+            while (!mFrameFinished) {
+                mFrameFinishCondition.wait(mMutex);
+            }
+            mFrameFinished = false;
             LOGV("-onFrameAvailable");
         }
 
     protected:
+        bool mFrameAvailable;
+        bool mFrameFinished;
+
         Mutex mMutex;
         Condition mFrameAvailableCondition;
         Condition mFrameFinishCondition;
@@ -1164,6 +1188,7 @@
         }
         mProducerThread.clear();
         mFC.clear();
+        SurfaceTextureGLTest::TearDown();
     }
 
     void runProducerThread(const sp<ProducerThread> producerThread) {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 8798612..447a7ff 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -340,10 +340,6 @@
 Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc) {
     Context * rsc = new Context();
 
-    // Temporary to avoid breaking the tools
-    if (!dev) {
-        return rsc;
-    }
     if (!rsc->initContext(dev, sc)) {
         delete rsc;
         return NULL;
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index 70b7278..7023a1f 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -21,14 +21,16 @@
 using namespace android;
 using namespace android::renderscript;
 
-LocklessCommandFifo::LocklessCommandFifo() {
+LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0) {
 }
 
 LocklessCommandFifo::~LocklessCommandFifo() {
     if (!mInShutdown) {
         shutdown();
     }
-    free(mBuffer);
+    if (mBuffer) {
+        free(mBuffer);
+    }
 }
 
 void LocklessCommandFifo::shutdown() {
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index ab164c3..1c8b89c 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -21,8 +21,7 @@
 using namespace android;
 using namespace android::renderscript;
 
-ThreadIO::ThreadIO() {
-    mToCore.init(16 * 1024);
+ThreadIO::ThreadIO() : mUsingSocket(false) {
 }
 
 ThreadIO::~ThreadIO() {
@@ -30,6 +29,7 @@
 
 void ThreadIO::init(bool useSocket) {
     mUsingSocket = useSocket;
+    mToCore.init(16 * 1024);
 
     if (mUsingSocket) {
         mToClientSocket.init();
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 50312e7..d18c0a2 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -161,6 +161,7 @@
     pthread_t thread;
     int result = pthread_create(&thread, &attr,
                     (android_pthread_entry)entryFunction, userData);
+    pthread_attr_destroy(&attr);
     if (result != 0) {
         LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
              "(android threadPriority=%d)",
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index 87ae3d5..bfb37a6 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -252,13 +252,15 @@
         "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
 
     void* item = editItemLocation(index);
-    if (item == 0)
-        return NO_MEMORY;
-    _do_destroy(item, 1);
-    if (prototype == 0) {
-        _do_construct(item, 1);
-    } else {
-        _do_copy(item, prototype, 1);
+    if (item != prototype) {
+        if (item == 0)
+            return NO_MEMORY;
+        _do_destroy(item, 1);
+        if (prototype == 0) {
+            _do_construct(item, 1);
+        } else {
+            _do_copy(item, prototype, 1);
+        }
     }
     return ssize_t(index);
 }
@@ -347,9 +349,10 @@
 //    LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
 //        this, (int)where, (int)amount, (int)mCount, (int)capacity());
 
-    if (where > mCount)
-        where = mCount;
-      
+    LOG_ASSERT(where <= mCount,
+            "[%p] _grow: where=%d, amount=%d, count=%d",
+            this, (int)where, (int)amount, (int)mCount); // caller already checked
+
     const size_t new_size = mCount + amount;
     if (capacity() < new_size) {
         const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
@@ -366,10 +369,10 @@
             SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
             if (sb) {
                 void* array = sb->data();
-                if (where>0) {
+                if (where != 0) {
                     _do_copy(array, mStorage, where);
                 }
-                if (mCount>where) {
+                if (where != mCount) {
                     const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
                     void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
                     _do_copy(dest, from, mCount-where);
@@ -379,15 +382,14 @@
             }
         }
     } else {
-        ssize_t s = mCount-where;
-        if (s>0) {
-            void* array = editArrayImpl();    
-            void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+        if (where != mCount) {
+            void* array = editArrayImpl();
             const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
-            _do_move_forward(to, from, s);
+            void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
+            _do_move_forward(to, from, mCount - where);
         }
     }
-    mCount += amount;
+    mCount = new_size;
     void* free_space = const_cast<void*>(itemLocation(where));
     return free_space;
 }
@@ -400,14 +402,15 @@
 //    LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
 //        this, (int)where, (int)amount, (int)mCount, (int)capacity());
 
-    if (where >= mCount)
-        where = mCount - amount;
+    LOG_ASSERT(where + amount <= mCount,
+            "[%p] _shrink: where=%d, amount=%d, count=%d",
+            this, (int)where, (int)amount, (int)mCount); // caller already checked
 
     const size_t new_size = mCount - amount;
     if (new_size*3 < capacity()) {
         const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
 //        LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
-        if ((where == mCount-amount) &&
+        if ((where == new_size) &&
             (mFlags & HAS_TRIVIAL_COPY) &&
             (mFlags & HAS_TRIVIAL_DTOR))
         {
@@ -418,31 +421,28 @@
             SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
             if (sb) {
                 void* array = sb->data();
-                if (where>0) {
+                if (where != 0) {
                     _do_copy(array, mStorage, where);
                 }
-                if (mCount > where+amount) {
+                if (where != new_size) {
                     const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
                     void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
-                    _do_copy(dest, from, mCount-(where+amount));
+                    _do_copy(dest, from, new_size - where);
                 }
                 release_storage();
                 mStorage = const_cast<void*>(array);
             }
         }
     } else {
-        void* array = editArrayImpl();    
+        void* array = editArrayImpl();
         void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
         _do_destroy(to, amount);
-        ssize_t s = mCount-(where+amount);
-        if (s>0) {
+        if (where != new_size) {
             const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
-            _do_move_backward(to, from, s);
+            _do_move_backward(to, from, new_size - where);
         }
     }
-
-    // adjust the number of items...
-    mCount -= amount;
+    mCount = new_size;
 }
 
 size_t VectorImpl::itemSize() const {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index e89be08..8c8569a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -707,7 +707,9 @@
             map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
             map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
 
-            if (!mNoMedia) {
+            if (mNoMedia) {
+                map.put(MediaStore.MediaColumns.NO_MEDIA, true);
+            } else {
                 if (MediaFile.isVideoFileType(mFileType)) {
                     map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
                             ? mArtist : MediaStore.UNKNOWN_STRING));
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 788464e..0098537 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -453,7 +453,6 @@
 }
 
 void AwesomePlayer::reset() {
-    LOGI("reset");
     Mutex::Autolock autoLock(mLock);
     reset_l();
 }
@@ -467,10 +466,8 @@
                     Playback::STOP, 0);
             mDecryptHandle = NULL;
             mDrmManagerClient = NULL;
-            LOGI("DRM manager client stopped");
     }
 
-
     if (mFlags & PLAYING) {
         uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
         if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
@@ -503,7 +500,6 @@
         mPreparedCondition.wait(mLock);
     }
 
-    LOGI("cancel player events");
     cancelPlayerEvents();
 
     mWVMExtractor.clear();
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index b7b0dc0f..5cab60e 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3539,7 +3539,7 @@
 }
 
 status_t OMXCodec::stop() {
-    CODEC_LOGI("stop mState=%d", mState);
+    CODEC_LOGV("stop mState=%d", mState);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -3601,7 +3601,6 @@
         mLeftOverBuffer = NULL;
     }
 
-    CODEC_LOGI("stopping video source");
     mSource->stop();
 
     CODEC_LOGI("stopped in state %d", mState);
diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp
index 0d0d6c2..2091381 100644
--- a/media/libstagefright/XINGSeeker.cpp
+++ b/media/libstagefright/XINGSeeker.cpp
@@ -24,8 +24,8 @@
 static bool parse_xing_header(
         const sp<DataSource> &source, off64_t first_frame_pos,
         int32_t *frame_number = NULL, int32_t *byte_number = NULL,
-        char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
-        int64_t *duration = NULL);
+        unsigned char *table_of_contents = NULL,
+        int32_t *quality_indicator = NULL, int64_t *duration = NULL);
 
 // static
 sp<XINGSeeker> XINGSeeker::CreateFromSource(
@@ -94,7 +94,7 @@
 static bool parse_xing_header(
         const sp<DataSource> &source, off64_t first_frame_pos,
         int32_t *frame_number, int32_t *byte_number,
-        char *table_of_contents, int32_t *quality_indicator,
+        unsigned char *table_of_contents, int32_t *quality_indicator,
         int64_t *duration) {
     if (frame_number) {
         *frame_number = 0;
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 8ecc17c..ca61b3d 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -36,11 +36,10 @@
 
 #include <ctype.h>
 #include <openssl/aes.h>
+#include <openssl/md5.h>
 
 namespace android {
 
-const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll;
-
 LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
     : mFlags(flags),
       mUIDValid(uidValid),
@@ -59,7 +58,8 @@
       mDurationUs(-1),
       mSeekDone(false),
       mDisconnectPending(false),
-      mMonitorQueueGeneration(0) {
+      mMonitorQueueGeneration(0),
+      mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) {
     if (mUIDValid) {
         mHTTPDataSource->setUID(mUID);
     }
@@ -175,7 +175,8 @@
 
     mMasterURL = url;
 
-    sp<M3UParser> playlist = fetchPlaylist(url.c_str());
+    bool dummy;
+    sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &dummy);
 
     if (playlist == NULL) {
         LOGE("unable to fetch master playlist '%s'.", url.c_str());
@@ -289,7 +290,9 @@
     return OK;
 }
 
-sp<M3UParser> LiveSession::fetchPlaylist(const char *url) {
+sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
+    *unchanged = false;
+
     sp<ABuffer> buffer;
     status_t err = fetchFile(url, &buffer);
 
@@ -297,6 +300,38 @@
         return NULL;
     }
 
+    // MD5 functionality is not available on the simulator, treat all
+    // playlists as changed.
+
+#if defined(HAVE_ANDROID_OS)
+    uint8_t hash[16];
+
+    MD5_CTX m;
+    MD5_Init(&m);
+    MD5_Update(&m, buffer->data(), buffer->size());
+
+    MD5_Final(hash, &m);
+
+    if (mPlaylist != NULL && !memcmp(hash, mPlaylistHash, 16)) {
+        // playlist unchanged
+
+        if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) {
+            mRefreshState = (RefreshState)(mRefreshState + 1);
+        }
+
+        *unchanged = true;
+
+        LOGV("Playlist unchanged, refresh state is now %d",
+             (int)mRefreshState);
+
+        return NULL;
+    }
+
+    memcpy(mPlaylistHash, hash, sizeof(hash));
+
+    mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
+#endif
+
     sp<M3UParser> playlist =
         new M3UParser(url, buffer->data(), buffer->size());
 
@@ -384,6 +419,63 @@
     return index;
 }
 
+bool LiveSession::timeToRefreshPlaylist(int64_t nowUs) const {
+    if (mPlaylist == NULL) {
+        CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY);
+        return true;
+    }
+
+    int32_t targetDurationSecs;
+    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
+
+    int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+
+    int64_t minPlaylistAgeUs;
+
+    switch (mRefreshState) {
+        case INITIAL_MINIMUM_RELOAD_DELAY:
+        {
+            size_t n = mPlaylist->size();
+            if (n > 0) {
+                sp<AMessage> itemMeta;
+                CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta));
+
+                int64_t itemDurationUs;
+                CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+                minPlaylistAgeUs = itemDurationUs;
+                break;
+            }
+
+            // fall through
+        }
+
+        case FIRST_UNCHANGED_RELOAD_ATTEMPT:
+        {
+            minPlaylistAgeUs = targetDurationUs / 2;
+            break;
+        }
+
+        case SECOND_UNCHANGED_RELOAD_ATTEMPT:
+        {
+            minPlaylistAgeUs = (targetDurationUs * 3) / 2;
+            break;
+        }
+
+        case THIRD_UNCHANGED_RELOAD_ATTEMPT:
+        {
+            minPlaylistAgeUs = targetDurationUs * 3;
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+
+    return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs;
+}
+
 void LiveSession::onDownloadNext() {
     size_t bandwidthIndex = getBandwidthIndex();
 
@@ -392,8 +484,7 @@
 
     if (mLastPlaylistFetchTimeUs < 0
             || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
-            || (!mPlaylist->isComplete()
-                && mLastPlaylistFetchTimeUs + kMaxPlaylistAgeUs <= nowUs)) {
+            || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) {
         AString url;
         if (mBandwidthItems.size() > 0) {
             url = mBandwidthItems.editItemAt(bandwidthIndex).mURI;
@@ -403,11 +494,19 @@
 
         bool firstTime = (mPlaylist == NULL);
 
-        mPlaylist = fetchPlaylist(url.c_str());
-        if (mPlaylist == NULL) {
-            LOGE("failed to load playlist at url '%s'", url.c_str());
-            mDataSource->queueEOS(ERROR_IO);
-            return;
+        bool unchanged;
+        sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged);
+        if (playlist == NULL) {
+            if (unchanged) {
+                // We succeeded in fetching the playlist, but it was
+                // unchanged from the last time we tried.
+            } else {
+                LOGE("failed to load playlist at url '%s'", url.c_str());
+                mDataSource->queueEOS(ERROR_IO);
+                return;
+            }
+        } else {
+            mPlaylist = playlist;
         }
 
         if (firstTime) {
diff --git a/media/libstagefright/include/LiveSession.h b/media/libstagefright/include/LiveSession.h
index 188ef5e..116ed0e 100644
--- a/media/libstagefright/include/LiveSession.h
+++ b/media/libstagefright/include/LiveSession.h
@@ -62,8 +62,6 @@
         kMaxNumRetries         = 5,
     };
 
-    static const int64_t kMaxPlaylistAgeUs;
-
     enum {
         kWhatConnect        = 'conn',
         kWhatDisconnect     = 'disc',
@@ -106,6 +104,16 @@
 
     int32_t mMonitorQueueGeneration;
 
+    enum RefreshState {
+        INITIAL_MINIMUM_RELOAD_DELAY,
+        FIRST_UNCHANGED_RELOAD_ATTEMPT,
+        SECOND_UNCHANGED_RELOAD_ATTEMPT,
+        THIRD_UNCHANGED_RELOAD_ATTEMPT
+    };
+    RefreshState mRefreshState;
+
+    uint8_t mPlaylistHash[16];
+
     void onConnect(const sp<AMessage> &msg);
     void onDisconnect();
     void onDownloadNext();
@@ -113,7 +121,7 @@
     void onSeek(const sp<AMessage> &msg);
 
     status_t fetchFile(const char *url, sp<ABuffer> *out);
-    sp<M3UParser> fetchPlaylist(const char *url);
+    sp<M3UParser> fetchPlaylist(const char *url, bool *unchanged);
     size_t getBandwidthIndex();
 
     status_t decryptBuffer(
@@ -121,6 +129,8 @@
 
     void postMonitorQueue(int64_t delayUs = 0);
 
+    bool timeToRefreshPlaylist(int64_t nowUs) const;
+
     static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
 
     DISALLOW_EVIL_CONSTRUCTORS(LiveSession);
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
index d5a484e..ec5bd9b 100644
--- a/media/libstagefright/include/XINGSeeker.h
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -37,7 +37,7 @@
     int32_t mSizeBytes;
 
     // TOC entries in XING header. Skip the first one since it's always 0.
-    char mTableOfContents[99];
+    unsigned char mTableOfContents[99];
 
     XINGSeeker();
 
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 25f30d6..bc24dbb 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -96,9 +96,14 @@
         mQueueChanged.signal();
     }
 
-    // Don't call join on myself
+    // A join on self can happen if the last ref to CallbackDispatcher
+    // is released within the CallbackDispatcherThread loop
     status_t status = mThread->join();
-    CHECK(status == NO_ERROR);
+    if (status != WOULD_BLOCK) {
+        // Other than join to self, the only other error return codes are
+        // whatever readyToRun() returns, and we don't override that
+        CHECK_EQ(status, NO_ERROR);
+    }
 }
 
 void OMX::CallbackDispatcher::post(const omx_message &msg) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java
index 59783e5..4d517db 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPropertiesTest.java
@@ -91,8 +91,12 @@
         assertEquals("AudioType Mismatch ", audioCodecType, mvi.getAudioType());
         assertEquals("Audio Sampling " + mvi.getAudioSamplingFrequency(),
             audioSamplingFrequency, mvi.getAudioSamplingFrequency());
-        assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel,
-            mvi.getAudioChannels());
+        // PV SW AAC codec always returns number of channels as Stereo.
+        // So we do not assert for number of audio channels for AAC_LC
+        if ( audioCodecType != MediaProperties.ACODEC_AAC_LC ) {
+            assertEquals("Audio Channels " + mvi.getAudioChannels(), audioChannel,
+                mvi.getAudioChannels());
+        }
     }
 
     protected void validateAudioProperties(int audioCodecType, int duration,
@@ -103,8 +107,12 @@
             duration, aT.getDuration(), 10));
         assertEquals("Audio Sampling " + aT.getAudioSamplingFrequency(),
             audioSamplingFrequency, aT.getAudioSamplingFrequency());
-        assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel,
-            aT.getAudioChannels());
+        // PV SW AAC codec always returns number of channels as Stereo.
+        // So we do not assert for number of audio channels for AAC_LC
+        if ( audioCodecType != MediaProperties.ACODEC_AAC_LC ) {
+            assertEquals("Audio Channels " + aT.getAudioChannels(), audioChannel,
+                aT.getAudioChannels());
+        }
     }
 
     protected void validateImageProperties(int aspectRatio, int fileType,
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
index e0c38b1..99cbb86 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
@@ -67,7 +67,11 @@
 
     /** get service handles */
     private static void initService() {
-        sService = sAdapter.getNfcAdapterExtrasInterface();
+        final INfcAdapterExtras service = sAdapter.getNfcAdapterExtrasInterface();
+        if (service != null) {
+            // Leave stale rather than receive a null value.
+            sService = service;
+        }
     }
 
     /**
@@ -84,18 +88,19 @@
             if (sSingleton == null) {
                 try {
                     sAdapter = adapter;
-                    sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
                     sSingleton = new NfcAdapterExtras();
                     sEmbeddedEe = new NfcExecutionEnvironment(sSingleton);
+                    sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
                     sRouteOnWhenScreenOn = new CardEmulationRoute(
                             CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe);
                     initService();
                 } finally {
-                    if (sSingleton == null) {
-                        sService = null;
-                        sEmbeddedEe = null;
-                        sRouteOff = null;
+                    if (sService == null) {
                         sRouteOnWhenScreenOn = null;
+                        sRouteOff = null;
+                        sEmbeddedEe = null;
+                        sSingleton = null;
+                        sAdapter = null;
                     }
                 }
             }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 26ea225..d32df6e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -30,6 +30,15 @@
         <service android:name=".screenshot.TakeScreenshotService"
             android:exported="false" />
 
+        <service android:name=".LoadAverageService"
+                android:exported="true" />
+
+        <receiver android:name=".BootReceiver" >
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
         <activity android:name=".usb.UsbStorageActivity"
                 android:excludeFromRecents="true">
         </activity>
diff --git a/packages/SystemUI/src/com/android/systemui/BootReceiver.java b/packages/SystemUI/src/com/android/systemui/BootReceiver.java
new file mode 100644
index 0000000..de005aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/BootReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.util.Slog;
+
+/**
+ * Performs a number of miscellaneous, non-system-critical actions
+ * after the system has finished booting.
+ */
+public class BootReceiver extends BroadcastReceiver {
+    private static final String TAG = "SystemUIBootReceiver";
+
+    @Override
+    public void onReceive(final Context context, Intent intent) {
+        try {
+            // Start the load average overlay, if activated
+            ContentResolver res = context.getContentResolver();
+            if (Settings.System.getInt(res, Settings.System.SHOW_PROCESSES, 0) != 0) {
+                Intent loadavg = new Intent(context, com.android.systemui.LoadAverageService.class);
+                context.startService(loadavg);
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Can't start load average service", e);
+        }
+    }
+}
diff --git a/services/java/com/android/server/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
similarity index 98%
rename from services/java/com/android/server/LoadAverageService.java
rename to packages/SystemUI/src/com/android/systemui/LoadAverageService.java
index e05b570..67dc3cd 100644
--- a/services/java/com/android/server/LoadAverageService.java
+++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.systemui;
+
+import com.android.internal.os.ProcessStats;
 
 import android.app.Service;
 import android.content.Context;
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index c0b0a08..8554b77 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -4,6 +4,7 @@
 
     <application android:label="VpnDialogs">
         <activity android:name=".ConfirmDialog"
+                android:permission="android.permission.VPN"
                 android:theme="@style/transparent">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml
index 5ab6ee2..11a247a 100644
--- a/packages/VpnDialogs/res/layout/confirm.xml
+++ b/packages/VpnDialogs/res/layout/confirm.xml
@@ -18,7 +18,7 @@
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="5mm">
+        android:padding="3mm">
 
     <ImageView android:id="@+id/icon"
             android:layout_width="@android:dimen/app_icon_size"
diff --git a/packages/VpnDialogs/res/layout/manage.xml b/packages/VpnDialogs/res/layout/manage.xml
index 330b8e3..3dcbb46 100644
--- a/packages/VpnDialogs/res/layout/manage.xml
+++ b/packages/VpnDialogs/res/layout/manage.xml
@@ -18,6 +18,7 @@
 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:padding="3mm"
         android:stretchColumns="0,1">
 
     <TableRow>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 21e916b..40c0a02 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -58,6 +58,13 @@
     @Override
     protected void onResume() {
         super.onResume();
+
+        if (getCallingPackage() != null) {
+            Log.e(TAG, getCallingPackage() + " cannot start this activity");
+            finish();
+            return;
+        }
+
         try {
             mConfig = getIntent().getParcelableExtra("config");
 
@@ -83,7 +90,6 @@
             } else {
                 PackageManager pm = getPackageManager();
                 ApplicationInfo app = pm.getApplicationInfo(mConfig.packagz, 0);
-
                 mDialog = new AlertDialog.Builder(this)
                         .setIcon(app.loadIcon(pm))
                         .setTitle(app.loadLabel(pm))
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 8ba235b..935f4ad 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -689,13 +689,11 @@
 
         switch (simState) {
             case ABSENT:
-            case PERM_DISABLED:
                 // only force lock screen in case of missing sim if user hasn't
                 // gone through setup wizard
                 if (!mUpdateMonitor.isDeviceProvisioned()) {
                     if (!isShowing()) {
-                        if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_ABSENT "
-                                + "or PERM_DISABLED and keygaurd isn't showing,"
+                        if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
                                 + " we need to show the keyguard since the "
                                 + "device isn't provisioned yet.");
                         doKeyguard();
@@ -713,7 +711,17 @@
                 } else {
                     resetStateLocked();
                 }
-
+                break;
+            case PERM_DISABLED:
+                if (!isShowing()) {
+                    if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+                          + "keygaurd isn't showing.");
+                    doKeyguard();
+                } else {
+                    if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+                          + "show permanently disabled message in lockscreen.");
+                    resetStateLocked();
+                }
                 break;
             case READY:
                 if (isShowing()) {
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index 75e799c..e177565 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -75,6 +75,7 @@
 
     private StatusView mStatusView;
     private final boolean mUseSystemIME = true; // TODO: Make configurable
+    private boolean mResuming; // used to prevent poking the wakelock during onResume()
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     // any passwords with length less than or equal to this length.
@@ -185,7 +186,9 @@
             }
 
             public void afterTextChanged(Editable s) {
-                mCallback.pokeWakelock();
+                if (!mResuming) {
+                    mCallback.pokeWakelock();
+                }
             }
         });
     }
@@ -208,6 +211,7 @@
 
     /** {@inheritDoc} */
     public void onResume() {
+        mResuming = true;
         // reset status
         mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils);
 
@@ -222,6 +226,7 @@
         if (deadline != 0) {
             handleAttemptLockout(deadline);
         }
+        mResuming = false;
     }
 
     /** {@inheritDoc} */
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index dff0556..4be00c5 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2240,7 +2240,7 @@
             }
 
             PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-            if (st != null && st.menu != null) {
+            if (st != null && st.menu != null && mFeatureId < 0) {
                 st.menu.close();
             }
         }
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 6bb1f56..a0407b9 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -24,6 +24,7 @@
     libdl
 
 LOCAL_STATIC_LIBRARIES := \
+    libcpustats \
     libmedia_helper
 
 LOCAL_MODULE:= libaudioflinger
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index daf94f2..86d4cc3 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -52,6 +52,9 @@
 #include <media/EffectsFactoryApi.h>
 #include <audio_effects/effect_visualizer.h>
 
+#include <cpustats/ThreadCpuUsage.h>
+// #define DEBUG_CPU_USAGE 10  // log statistics every n wall clock seconds
+
 // ----------------------------------------------------------------------------
 
 
@@ -1529,9 +1532,40 @@
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
     Vector< sp<EffectChain> > effectChains;
+#ifdef DEBUG_CPU_USAGE
+    ThreadCpuUsage cpu;
+    const CentralTendencyStatistics& stats = cpu.statistics();
+#endif
 
     while (!exitPending())
     {
+#ifdef DEBUG_CPU_USAGE
+        cpu.sampleAndEnable();
+        unsigned n = stats.n();
+        // cpu.elapsed() is expensive, so don't call it every loop
+        if ((n & 127) == 1) {
+            long long elapsed = cpu.elapsed();
+            if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) {
+                double perLoop = elapsed / (double) n;
+                double perLoop100 = perLoop * 0.01;
+                double mean = stats.mean();
+                double stddev = stats.stddev();
+                double minimum = stats.minimum();
+                double maximum = stats.maximum();
+                cpu.resetStatistics();
+                LOGI("CPU usage over past %.1f secs (%u mixer loops at %.1f mean ms per loop):\n  us per mix loop: mean=%.0f stddev=%.0f min=%.0f max=%.0f\n  %% of wall: mean=%.1f stddev=%.1f min=%.1f max=%.1f",
+                        elapsed * .000000001, n, perLoop * .000001,
+                        mean * .001,
+                        stddev * .001,
+                        minimum * .001,
+                        maximum * .001,
+                        mean / perLoop100,
+                        stddev / perLoop100,
+                        minimum / perLoop100,
+                        maximum / perLoop100);
+            }
+        }
+#endif
         processConfigEvents();
 
         mixerStatus = MIXER_IDLE;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 49cb864..b2fbcb1 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -181,25 +181,6 @@
                     | AMOTION_EVENT_BUTTON_TERTIARY);
 }
 
-static int32_t calculateEdgeFlagsUsingPointerBounds(
-        const sp<PointerControllerInterface>& pointerController, float x, float y) {
-    int32_t edgeFlags = 0;
-    float minX, minY, maxX, maxY;
-    if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-        if (x <= minX) {
-            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
-        } else if (x >= maxX) {
-            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
-        }
-        if (y <= minY) {
-            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
-        } else if (y >= maxY) {
-            edgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
-        }
-    }
-    return edgeFlags;
-}
-
 static float calculateCommonVector(float a, float b) {
     if (a > 0 && b > 0) {
         return a < b ? a : b;
@@ -1619,7 +1600,6 @@
     }
 
     int32_t motionEventAction;
-    int32_t motionEventEdgeFlags;
     int32_t lastButtonState, currentButtonState;
     PointerProperties pointerProperties;
     PointerCoords pointerCoords;
@@ -1697,8 +1677,6 @@
             }
         }
 
-        motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
-
         pointerProperties.clear();
         pointerProperties.id = 0;
         pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
@@ -1742,11 +1720,6 @@
             mPointerController->getPosition(&x, &y);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-
-            if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
-                motionEventEdgeFlags = calculateEdgeFlagsUsingPointerBounds(
-                        mPointerController, x, y);
-            }
         } else {
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
@@ -1771,7 +1744,7 @@
     // Send motion event.
     int32_t metaState = mContext->getGlobalMetaState();
     getDispatcher()->notifyMotion(when, getDeviceId(), mSource, policyFlags,
-            motionEventAction, 0, metaState, currentButtonState, motionEventEdgeFlags,
+            motionEventAction, 0, metaState, currentButtonState, 0,
             1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
 
     // Send hover move after UP to tell the application that the mouse is hovering now.
@@ -3168,9 +3141,8 @@
     }
 
     // Update current touch coordinates.
-    int32_t edgeFlags;
     float xPrecision, yPrecision;
-    prepareTouches(&edgeFlags, &xPrecision, &yPrecision);
+    prepareTouches(&xPrecision, &yPrecision);
 
     // Dispatch motions.
     BitSet32 currentIdBits = mCurrentTouch.idBits;
@@ -3239,13 +3211,10 @@
             if (dispatchedIdBits.count() == 1) {
                 // First pointer is going down.  Set down time.
                 mDownTime = when;
-            } else {
-                // Only send edge flags with first pointer down.
-                edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
             }
 
             dispatchMotion(when, policyFlags, mTouchSource,
-                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, edgeFlags,
+                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
                     mCurrentTouchProperties, mCurrentTouchCoords,
                     mCurrentTouch.idToIndex, dispatchedIdBits, downId,
                     xPrecision, yPrecision, mDownTime);
@@ -3259,8 +3228,7 @@
     }
 }
 
-void TouchInputMapper::prepareTouches(int32_t* outEdgeFlags,
-        float* outXPrecision, float* outYPrecision) {
+void TouchInputMapper::prepareTouches(float* outXPrecision, float* outYPrecision) {
     uint32_t currentPointerCount = mCurrentTouch.pointerCount;
     uint32_t lastPointerCount = mLastTouch.pointerCount;
 
@@ -3471,28 +3439,6 @@
         properties.toolType = getTouchToolType(mCurrentTouch.pointers[i].isStylus);
     }
 
-    // Check edge flags by looking only at the first pointer since the flags are
-    // global to the event.
-    *outEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
-    if (lastPointerCount == 0 && currentPointerCount > 0) {
-        const PointerData& in = mCurrentTouch.pointers[0];
-
-        if (in.x <= mRawAxes.x.minValue) {
-            *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT,
-                    mLocked.surfaceOrientation);
-        } else if (in.x >= mRawAxes.x.maxValue) {
-            *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT,
-                    mLocked.surfaceOrientation);
-        }
-        if (in.y <= mRawAxes.y.minValue) {
-            *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP,
-                    mLocked.surfaceOrientation);
-        } else if (in.y >= mRawAxes.y.maxValue) {
-            *outEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM,
-                    mLocked.surfaceOrientation);
-        }
-    }
-
     *outXPrecision = mLocked.orientedXPrecision;
     *outYPrecision = mLocked.orientedYPrecision;
 }
@@ -3640,19 +3586,12 @@
             downGestureIdBits.clearBit(id);
             dispatchedGestureIdBits.markBit(id);
 
-            int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
             if (dispatchedGestureIdBits.count() == 1) {
-                // First pointer is going down.  Calculate edge flags and set down time.
-                uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
-                const PointerCoords& downCoords = mPointerGesture.currentGestureCoords[index];
-                edgeFlags = calculateEdgeFlagsUsingPointerBounds(mPointerController,
-                        downCoords.getAxisValue(AMOTION_EVENT_AXIS_X),
-                        downCoords.getAxisValue(AMOTION_EVENT_AXIS_Y));
                 mPointerGesture.downTime = when;
             }
 
             dispatchMotion(when, policyFlags, mPointerSource,
-                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, edgeFlags,
+                    AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
                     mPointerGesture.currentGestureProperties,
                     mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
                     dispatchedGestureIdBits, id,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 69fa6b4..b1fdcf2 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1176,7 +1176,7 @@
 
     TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags);
     void dispatchTouches(nsecs_t when, uint32_t policyFlags);
-    void prepareTouches(int32_t* outEdgeFlags, float* outXPrecision, float* outYPrecision);
+    void prepareTouches(float* outXPrecision, float* outYPrecision);
     void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
     bool preparePointerGestures(nsecs_t when,
             bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 168b894..e9e66cb 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -87,8 +87,10 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -109,6 +111,8 @@
     // Name and current contents version of the full-backup manifest file
     static final String BACKUP_MANIFEST_FILENAME = "_manifest";
     static final int BACKUP_MANIFEST_VERSION = 1;
+    static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
+    static final int BACKUP_FILE_VERSION = 1;
 
     // How often we perform a backup pass.  Privileged external callers can
     // trigger an immediate pass.
@@ -1791,16 +1795,42 @@
                 }
             }
 
-            // Set up the compression stage
             FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
+
+            // Set up the compression stage
             Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
             DeflaterOutputStream out = new DeflaterOutputStream(ofstream, deflater, true);
 
-            // !!! TODO: if using encryption, set up the encryption stage
-            // and emit the tar header stating the password salt.
-
             PackageInfo pkg = null;
             try {
+
+                // !!! TODO: if using encryption, set up the encryption stage
+                // and emit the tar header stating the password salt.
+
+                // Write the global file header.  All strings are UTF-8 encoded; lines end
+                // with a '\n' byte.  Actual backup data begins immediately following the
+                // final '\n'.
+                //
+                // line 1: "ANDROID BACKUP"
+                // line 2: backup file format version, currently "1"
+                // line 3: compressed?  "0" if not compressed, "1" if compressed.
+                // line 4: encryption salt?  "-" if not encrypted, otherwise this
+                //         line contains the encryption salt with which the user-
+                //         supplied password is to be expanded, in hexadecimal.
+                StringBuffer headerbuf = new StringBuffer(256);
+                // !!! TODO: programmatically build the compressed / encryption salt fields
+                headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
+                headerbuf.append("1\n1\n-\n");
+
+                try {
+                    byte[] header = headerbuf.toString().getBytes("UTF-8");
+                    ofstream.write(header);
+                } catch (Exception e) {
+                    // Should never happen!
+                    Slog.e(TAG, "Unable to emit archive header", e);
+                    return;
+                }
+
                 // Now back up the app data via the agent mechanism
                 int N = packagesToBackup.size();
                 for (int i = 0; i < N; i++) {
@@ -2176,7 +2206,46 @@
                 mBytes = 0;
                 byte[] buffer = new byte[32 * 1024];
                 FileInputStream rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
-                InflaterInputStream in = new InflaterInputStream(rawInStream);
+
+                // First, parse out the unencrypted/uncompressed header
+                boolean compressed = false;
+                boolean encrypted = false;
+                final InputStream in;
+
+                boolean okay = false;
+                final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
+                byte[] streamHeader = new byte[headerLen];
+                try {
+                    int got;
+                    if ((got = rawInStream.read(streamHeader, 0, headerLen)) == headerLen) {
+                        byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
+                        if (Arrays.equals(magicBytes, streamHeader)) {
+                            // okay, header looks good.  now parse out the rest of the fields.
+                            String s = readHeaderLine(rawInStream);
+                            if (Integer.parseInt(s) == BACKUP_FILE_VERSION) {
+                                // okay, it's a version we recognize
+                                s = readHeaderLine(rawInStream);
+                                compressed = (Integer.parseInt(s) != 0);
+                                s = readHeaderLine(rawInStream);
+                                if (!s.startsWith("-")) {
+                                    encrypted = true;
+                                    // TODO: parse out the salt here and process with the user pw
+                                }
+                                okay = true;
+                            } else Slog.e(TAG, "Wrong header version: " + s);
+                        } else Slog.e(TAG, "Didn't read the right header magic");
+                    } else Slog.e(TAG, "Only read " + got + " bytes of header");
+                } catch (NumberFormatException e) {
+                    Slog.e(TAG, "Can't parse restore data header");
+                }
+
+                if (!okay) {
+                    Slog.e(TAG, "Invalid restore data; aborting.");
+                    return;
+                }
+
+                // okay, use the right stream layer based on compression
+                in = (compressed) ? new InflaterInputStream(rawInStream) : rawInStream;
 
                 boolean didRestore;
                 do {
@@ -2184,6 +2253,8 @@
                 } while (didRestore);
 
                 if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to read restore input");
             } finally {
                 tearDownPipes();
                 tearDownAgent(mTargetApp);
@@ -2207,6 +2278,16 @@
             }
         }
 
+        String readHeaderLine(InputStream in) throws IOException {
+            int c;
+            StringBuffer buffer = new StringBuffer(80);
+            while ((c = in.read()) >= 0) {
+                if (c == '\n') break;   // consume and discard the newlines
+                buffer.append((char)c);
+            }
+            return buffer.toString();
+        }
+
         boolean restoreOneFile(InputStream instream, byte[] buffer) {
             FileMetadata info;
             try {
diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java
index b9ff8d0..6665614 100644
--- a/services/java/com/android/server/BootReceiver.java
+++ b/services/java/com/android/server/BootReceiver.java
@@ -17,7 +17,6 @@
 package com.android.server;
 
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -28,7 +27,6 @@
 import android.os.FileUtils;
 import android.os.RecoverySystem;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.util.Slog;
 
 import java.io.File;
@@ -59,17 +57,6 @@
 
     @Override
     public void onReceive(final Context context, Intent intent) {
-        try {
-            // Start the load average overlay, if activated
-            ContentResolver res = context.getContentResolver();
-            if (Settings.System.getInt(res, Settings.System.SHOW_PROCESSES, 0) != 0) {
-                Intent loadavg = new Intent(context, com.android.server.LoadAverageService.class);
-                context.startService(loadavg);
-            }
-        } catch (Exception e) {
-            Slog.e(TAG, "Can't start load average service", e);
-        }
-
         // Log boot events in the background to avoid blocking the main thread with I/O
         new Thread() {
             @Override
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 85891a2..55c92e8 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -35,7 +35,7 @@
 import android.net.INetworkPolicyManager;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.LinkProperties.CompareAddressesResult;
+import android.net.LinkProperties.CompareResult;
 import android.net.MobileDataStateTracker;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
@@ -260,6 +260,10 @@
 
     private InetAddress mDefaultDns;
 
+    // this collection is used to refcount the added routes - if there are none left
+    // it's time to remove the route from the route table
+    private Collection<RouteInfo> mAddedRoutes = new ArrayList<RouteInfo>();
+
     // used in DBG mode to track inet condition reports
     private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
     private ArrayList mInetLog;
@@ -479,7 +483,7 @@
                         mNetConfigs[netType].radio);
                 continue;
             }
-            mCurrentLinkProperties[netType] = mNetTrackers[netType].getLinkProperties();
+            mCurrentLinkProperties[netType] = null;
         }
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -1053,62 +1057,71 @@
         }
         try {
             InetAddress addr = InetAddress.getByAddress(hostAddress);
-            return addHostRoute(tracker, addr, 0);
+            LinkProperties lp = tracker.getLinkProperties();
+            return addRoute(lp, RouteInfo.makeHostRoute(addr));
         } catch (UnknownHostException e) {}
         return false;
     }
 
-    /**
-     * Ensure that a network route exists to deliver traffic to the specified
-     * host via the mobile data network.
-     * @param hostAddress the IP address of the host to which the route is desired,
-     * in network byte order.
-     * TODO - deprecate
-     * @return {@code true} on success, {@code false} on failure
-     */
-    private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) {
-        LinkProperties lp = nt.getLinkProperties();
-        if ((lp == null) || (hostAddress == null)) return false;
-
-        String interfaceName = lp.getInterfaceName();
-        if (DBG) {
-            log("Requested host route to " + hostAddress + "(" + interfaceName + "), cycleCount=" +
-                    cycleCount);
-        }
-        if (interfaceName == null) {
-            if (DBG) loge("addHostRoute failed due to null interface name");
-            return false;
-        }
-
-        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress);
-        InetAddress gatewayAddress = null;
-        if (bestRoute != null) {
-            gatewayAddress = bestRoute.getGateway();
-            // if the best route is ourself, don't relf-reference, just add the host route
-            if (hostAddress.equals(gatewayAddress)) gatewayAddress = null;
-        }
-        if (gatewayAddress != null) {
-            if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
-                loge("Error adding hostroute - too much recursion");
-                return false;
-            }
-            if (!addHostRoute(nt, gatewayAddress, cycleCount+1)) return false;
-        }
-
-        RouteInfo route = RouteInfo.makeHostRoute(hostAddress, gatewayAddress);
-
-        try {
-            mNetd.addRoute(interfaceName, route);
-            return true;
-        } catch (Exception ex) {
-            return false;
-        }
+    private boolean addRoute(LinkProperties p, RouteInfo r) {
+        return modifyRoute(p.getInterfaceName(), p, r, 0, true);
     }
 
-    // TODO support the removal of single host routes.  Keep a ref count of them so we
-    // aren't over-zealous
-    private boolean removeHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
-        return false;
+    private boolean removeRoute(LinkProperties p, RouteInfo r) {
+        return modifyRoute(p.getInterfaceName(), p, r, 0, false);
+    }
+
+    private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
+            boolean doAdd) {
+        if ((ifaceName == null) || (lp == null) || (r == null)) return false;
+
+        if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
+            loge("Error adding route - too much recursion");
+            return false;
+        }
+
+        if (r.isHostRoute() == false) {
+            RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), r.getGateway());
+            if (bestRoute != null) {
+                if (bestRoute.getGateway().equals(r.getGateway())) {
+                    // if there is no better route, add the implied hostroute for our gateway
+                    bestRoute = RouteInfo.makeHostRoute(r.getGateway());
+                } else {
+                    // if we will connect to our gateway through another route, add a direct
+                    // route to it's gateway
+                    bestRoute = RouteInfo.makeHostRoute(r.getGateway(), bestRoute.getGateway());
+                }
+                modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd);
+            }
+        }
+        if (doAdd) {
+            if (DBG) log("Adding " + r + " for interface " + ifaceName);
+            mAddedRoutes.add(r);
+            try {
+                mNetd.addRoute(ifaceName, r);
+            } catch (Exception e) {
+                // never crash - catch them all
+                loge("Exception trying to add a route: " + e);
+                return false;
+            }
+        } else {
+            // if we remove this one and there are no more like it, then refcount==0 and
+            // we can remove it from the table
+            mAddedRoutes.remove(r);
+            if (mAddedRoutes.contains(r) == false) {
+                if (DBG) log("Removing " + r + " for interface " + ifaceName);
+                try {
+                    mNetd.removeRoute(ifaceName, r);
+                } catch (Exception e) {
+                    // never crash - catch them all
+                    loge("Exception trying to remove a route: " + e);
+                    return false;
+                }
+            } else {
+                if (DBG) log("not removing " + r + " as it's still in use");
+            }
+        }
+        return true;
     }
 
     /**
@@ -1157,9 +1170,6 @@
     public void setDataDependency(int networkType, boolean met) {
         enforceConnectivityInternalPermission();
 
-        if (DBG) {
-            log("setDataDependency(" + networkType + ", " + met + ")");
-        }
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_DEPENDENCY_MET,
                 (met ? ENABLED : DISABLED), networkType));
     }
@@ -1583,10 +1593,11 @@
          */
         handleDnsConfigurationChange(netType);
 
+        LinkProperties curLp = mCurrentLinkProperties[netType];
+        LinkProperties newLp = null;
+
         if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
-            LinkProperties newLp = mNetTrackers[netType].getLinkProperties();
-            LinkProperties curLp = mCurrentLinkProperties[netType];
-            mCurrentLinkProperties[netType] = newLp;
+            newLp = mNetTrackers[netType].getLinkProperties();
             if (VDBG) {
                 log("handleConnectivityChange: changed linkProperty[" + netType + "]:" +
                         " doReset=" + doReset + " resetMask=" + resetMask +
@@ -1594,61 +1605,50 @@
                         "\n   newLp=" + newLp);
             }
 
-            if (curLp.isIdenticalInterfaceName(newLp)) {
-                CompareAddressesResult car = curLp.compareAddresses(newLp);
-                if ((car.removed.size() != 0) || (car.added.size() != 0)) {
-                    for (LinkAddress linkAddr : car.removed) {
-                        if (linkAddr.getAddress() instanceof Inet4Address) {
-                            resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES;
+            if (curLp != null) {
+                if (curLp.isIdenticalInterfaceName(newLp)) {
+                    CompareResult<LinkAddress> car = curLp.compareAddresses(newLp);
+                    if ((car.removed.size() != 0) || (car.added.size() != 0)) {
+                        for (LinkAddress linkAddr : car.removed) {
+                            if (linkAddr.getAddress() instanceof Inet4Address) {
+                                resetMask |= NetworkUtils.RESET_IPV4_ADDRESSES;
+                            }
+                            if (linkAddr.getAddress() instanceof Inet6Address) {
+                                resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES;
+                            }
                         }
-                        if (linkAddr.getAddress() instanceof Inet6Address) {
-                            resetMask |= NetworkUtils.RESET_IPV6_ADDRESSES;
+                        if (DBG) {
+                            log("handleConnectivityChange: addresses changed" +
+                                    " linkProperty[" + netType + "]:" + " resetMask=" + resetMask +
+                                    "\n   car=" + car);
                         }
-                    }
-                    if (DBG) {
-                        log("handleConnectivityChange: addresses changed" +
-                                " linkProperty[" + netType + "]:" + " resetMask=" + resetMask +
-                                "\n   car=" + car);
+                    } else {
+                        if (DBG) {
+                            log("handleConnectivityChange: address are the same reset per doReset" +
+                                   " linkProperty[" + netType + "]:" +
+                                   " resetMask=" + resetMask);
+                        }
                     }
                 } else {
-                    if (DBG) {
-                        log("handleConnectivityChange: address are the same reset per doReset" +
-                               " linkProperty[" + netType + "]:" +
-                               " resetMask=" + resetMask);
-                    }
+                    resetMask = NetworkUtils.RESET_ALL_ADDRESSES;
+                    log("handleConnectivityChange: interface not not equivalent reset both" +
+                            " linkProperty[" + netType + "]:" +
+                            " resetMask=" + resetMask);
                 }
-            } else {
-                resetMask = NetworkUtils.RESET_ALL_ADDRESSES;
-                log("handleConnectivityChange: interface not not equivalent reset both" +
-                        " linkProperty[" + netType + "]:" +
-                        " resetMask=" + resetMask);
             }
             if (mNetConfigs[netType].isDefault()) {
                 handleApplyDefaultProxy(netType);
-                addDefaultRoute(mNetTrackers[netType]);
-            } else {
-                // many radios add a default route even when we don't want one.
-                // remove the default route unless we need it for our active network
-                if (mActiveDefaultNetwork != -1) {
-                    LinkProperties defaultLinkProperties =
-                            mNetTrackers[mActiveDefaultNetwork].getLinkProperties();
-                    LinkProperties newLinkProperties =
-                            mNetTrackers[netType].getLinkProperties();
-                    String defaultIface = defaultLinkProperties.getInterfaceName();
-                    if (defaultIface != null &&
-                            !defaultIface.equals(newLinkProperties.getInterfaceName())) {
-                        removeDefaultRoute(mNetTrackers[netType]);
-                    }
-                }
-                addPrivateDnsRoutes(mNetTrackers[netType]);
             }
         } else {
-            if (mNetConfigs[netType].isDefault()) {
-                removeDefaultRoute(mNetTrackers[netType]);
-            } else {
-                removePrivateDnsRoutes(mNetTrackers[netType]);
+            if (VDBG) {
+                log("handleConnectivityChange: changed linkProperty[" + netType + "]:" +
+                        " doReset=" + doReset + " resetMask=" + resetMask +
+                        "\n  curLp=" + curLp +
+                        "\n  newLp= null");
             }
         }
+        mCurrentLinkProperties[netType] = newLp;
+        updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
 
         if (doReset || resetMask != 0) {
             LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties();
@@ -1672,108 +1672,64 @@
         }
     }
 
-    private void addPrivateDnsRoutes(NetworkStateTracker nt) {
-        boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
-        LinkProperties p = nt.getLinkProperties();
-        if (p == null) return;
-        String interfaceName = p.getInterfaceName();
+    /**
+     * Add and remove routes using the old properties (null if not previously connected),
+     * new properties (null if becoming disconnected).  May even be double null, which
+     * is a noop.
+     * Uses isLinkDefault to determine if default routes should be set or conversely if
+     * host routes should be set to the dns servers
+     */
+    private void updateRoutes(LinkProperties newLp, LinkProperties curLp, boolean isLinkDefault) {
+        Collection<RouteInfo> routesToAdd = null;
+        CompareResult<InetAddress> dnsDiff = null;
 
-        if (DBG) {
-            log("addPrivateDnsRoutes for " + nt +
-                    "(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet);
-        }
-        if (interfaceName != null && !privateDnsRouteSet) {
-            Collection<InetAddress> dnsList = p.getDnses();
-            for (InetAddress dns : dnsList) {
-                addHostRoute(nt, dns, 0);
-            }
-            nt.privateDnsRouteSet(true);
-        }
-    }
+        if (curLp != null) {
+            // check for the delta between the current set and the new
+            CompareResult<RouteInfo> routeDiff = curLp.compareRoutes(newLp);
+            dnsDiff = curLp.compareDnses(newLp);
 
-    private void removePrivateDnsRoutes(NetworkStateTracker nt) {
-        LinkProperties p = nt.getLinkProperties();
-        if (p == null) return;
-        String interfaceName = p.getInterfaceName();
-        boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
-        if (interfaceName != null && privateDnsRouteSet) {
-            if (DBG) {
-                log("removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
-                        " (" + interfaceName + ")");
-            }
-
-            Collection<InetAddress> dnsList = p.getDnses();
-            for (InetAddress dns : dnsList) {
-                if (DBG) log("  removing " + dns);
-                RouteInfo route = RouteInfo.makeHostRoute(dns);
-                try {
-                    mNetd.removeRoute(interfaceName, route);
-                } catch (Exception ex) {
-                    loge("error (" + ex + ") removing dns route " + route);
+            for (RouteInfo r : routeDiff.removed) {
+                if (isLinkDefault || ! r.isDefaultRoute()) {
+                    removeRoute(curLp, r);
                 }
             }
-            nt.privateDnsRouteSet(false);
+            routesToAdd = routeDiff.added;
         }
-    }
 
+        if (newLp != null) {
+            // if we didn't get a diff from cur -> new, then just use the new
+            if (routesToAdd == null) {
+                routesToAdd = newLp.getRoutes();
+            }
 
-    private void addDefaultRoute(NetworkStateTracker nt) {
-        LinkProperties p = nt.getLinkProperties();
-        if (p == null) return;
-        String interfaceName = p.getInterfaceName();
-        if (TextUtils.isEmpty(interfaceName)) return;
+            for (RouteInfo r :  routesToAdd) {
+                if (isLinkDefault || ! r.isDefaultRoute()) {
+                    addRoute(newLp, r);
+                }
+            }
+        }
 
-        for (RouteInfo route : p.getRoutes()) {
-            //TODO - handle non-default routes
-            if (route.isDefaultRoute()) {
-                if (DBG) log("adding default route " + route);
-                InetAddress gateway = route.getGateway();
-                if (addHostRoute(nt, gateway, 0)) {
-                    try {
-                        mNetd.addRoute(interfaceName, route);
-                    } catch (Exception e) {
-                        loge("error adding default route " + route);
-                        continue;
-                    }
-                    if (DBG) {
-                        NetworkInfo networkInfo = nt.getNetworkInfo();
-                        log("addDefaultRoute for " + networkInfo.getTypeName() +
-                                " (" + interfaceName + "), GatewayAddr=" +
-                                gateway.getHostAddress());
-                    }
-                } else {
-                    loge("error adding host route for default route " + route);
+        if (!isLinkDefault) {
+            // handle DNS routes
+            Collection<InetAddress> dnsToAdd = null;
+            if (dnsDiff != null) {
+                dnsToAdd = dnsDiff.added;
+                for (InetAddress dnsAddress : dnsDiff.removed) {
+                    removeRoute(curLp, RouteInfo.makeHostRoute(dnsAddress));
+                }
+            }
+            if (newLp != null) {
+                if (dnsToAdd == null) {
+                    dnsToAdd = newLp.getDnses();
+                }
+                for(InetAddress dnsAddress : dnsToAdd) {
+                    addRoute(newLp, RouteInfo.makeHostRoute(dnsAddress));
                 }
             }
         }
     }
 
 
-    public void removeDefaultRoute(NetworkStateTracker nt) {
-        LinkProperties p = nt.getLinkProperties();
-        if (p == null) return;
-        String interfaceName = p.getInterfaceName();
-
-        if (interfaceName == null) return;
-
-        for (RouteInfo route : p.getRoutes()) {
-            //TODO - handle non-default routes
-            if (route.isDefaultRoute()) {
-                try {
-                    mNetd.removeRoute(interfaceName, route);
-                } catch (Exception ex) {
-                    loge("error (" + ex + ") removing default route " + route);
-                    continue;
-                }
-                if (DBG) {
-                    NetworkInfo networkInfo = nt.getNetworkInfo();
-                    log("removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
-                            interfaceName + ")");
-                }
-            }
-        }
-    }
-
    /**
      * Reads the network specific TCP buffer sizes from SystemProperties
      * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
@@ -2528,8 +2484,23 @@
      * @hide
      */
     @Override
-    public void protectVpn(ParcelFileDescriptor socket) {
-        mVpn.protect(socket, getDefaultInterface());
+    public boolean protectVpn(ParcelFileDescriptor socket) {
+        try {
+            int type = mActiveDefaultNetwork;
+            if (ConnectivityManager.isNetworkTypeValid(type)) {
+                mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName());
+                return true;
+            }
+        } catch (Exception e) {
+            // ignore
+        } finally {
+            try {
+                socket.close();
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+        return false;
     }
 
     /**
@@ -2577,19 +2548,6 @@
         return mVpn.getLegacyVpnInfo();
     }
 
-    private String getDefaultInterface() {
-        if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) {
-            NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
-            if (tracker != null) {
-                LinkProperties properties = tracker.getLinkProperties();
-                if (properties != null) {
-                    return properties.getInterfaceName();
-                }
-            }
-        }
-        throw new IllegalStateException("No default interface");
-    }
-
     /**
      * Callback for VPN subsystem. Currently VPN is not adapted to the service
      * through NetworkStateTracker since it works differently. For example, it
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5f0922e..7112553 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -35,8 +35,8 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiStateMachine;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiWatchdogStateMachine;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiWatchdogService;
 import android.net.wifi.WpsConfiguration;
 import android.net.wifi.WpsResult;
 import android.net.ConnectivityManager;
@@ -343,7 +343,7 @@
      * Protected by mWifiStateTracker lock.
      */
     private final WorkSource mTmpWorkSource = new WorkSource();
-    private WifiWatchdogService mWifiWatchdogService;
+    private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
 
     WifiService(Context context) {
         mContext = context;
@@ -434,8 +434,9 @@
                 (wifiEnabled ? "enabled" : "disabled"));
         setWifiEnabled(wifiEnabled);
 
-        //TODO: as part of WWS refactor, create only when needed
-        mWifiWatchdogService = new WifiWatchdogService(mContext);
+        mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
+               makeWifiWatchdogStateMachine(mContext);
+
     }
 
     private boolean testAndClearWifiSavedState() {
@@ -1162,8 +1163,8 @@
         mLocks.dump(pw);
 
         pw.println();
-        pw.println("WifiWatchdogService dump");
-        mWifiWatchdogService.dump(pw);
+        pw.println("WifiWatchdogStateMachine dump");
+        mWifiWatchdogStateMachine.dump(pw);
     }
 
     private class WifiLock extends DeathRecipient {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index fd93bcf..f546cf1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -18,10 +18,10 @@
 
 import com.android.internal.R;
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.ProcessStats;
 import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
 import com.android.server.ProcessMap;
-import com.android.server.ProcessStats;
 import com.android.server.SystemServer;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index c185012..05e95a7 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -41,6 +41,9 @@
 import com.android.internal.net.VpnConfig;
 import com.android.server.ConnectivityService.VpnCallback;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.Charsets;
 import java.util.Arrays;
@@ -67,22 +70,14 @@
 
     /**
      * Protect a socket from routing changes by binding it to the given
-     * interface. The socket IS closed by this method.
+     * interface. The socket is NOT closed by this method.
      *
      * @param socket The socket to be bound.
      * @param name The name of the interface.
      */
     public void protect(ParcelFileDescriptor socket, String interfaze) {
-        try {
-            mContext.enforceCallingPermission(VPN, "protect");
-            jniProtect(socket.getFd(), interfaze);
-        } finally {
-            try {
-                socket.close();
-            } catch (Exception e) {
-                // ignore
-            }
-        }
+        mContext.enforceCallingPermission(VPN, "protect");
+        jniProtect(socket.getFd(), interfaze);
     }
 
     /**
@@ -192,10 +187,15 @@
         }
 
         // Configure the interface. Abort if any of these steps fails.
-        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(
-                jniConfigure(config.mtu, config.addresses, config.routes));
+        ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
         try {
             String interfaze = jniGetName(tun.getFd());
+            if (jniSetAddresses(interfaze, config.addresses) < 1) {
+                throw new IllegalArgumentException("At least one address must be specified");
+            }
+            if (config.routes != null) {
+                jniSetRoutes(interfaze, config.routes);
+            }
             if (mInterface != null && !mInterface.equals(interfaze)) {
                 jniReset(mInterface);
             }
@@ -222,18 +222,24 @@
     }
 
     // INetworkManagementEventObserver.Stub
-    public void interfaceStatusChanged(String interfaze, boolean up) {
-    }
-
-    // INetworkManagementEventObserver.Stub
-    public void interfaceLinkStateChanged(String interfaze, boolean up) {
-    }
-
-    // INetworkManagementEventObserver.Stub
     public void interfaceAdded(String interfaze) {
     }
 
     // INetworkManagementEventObserver.Stub
+    public synchronized void interfaceStatusChanged(String interfaze, boolean up) {
+        if (!up && mLegacyVpnRunner != null) {
+            mLegacyVpnRunner.check(interfaze);
+        }
+    }
+
+    // INetworkManagementEventObserver.Stub
+    public synchronized void interfaceLinkStateChanged(String interfaze, boolean up) {
+        if (!up && mLegacyVpnRunner != null) {
+            mLegacyVpnRunner.check(interfaze);
+        }
+    }
+
+    // INetworkManagementEventObserver.Stub
     public synchronized void interfaceRemoved(String interfaze) {
         if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
             mCallback.restore();
@@ -279,8 +285,10 @@
         }
     }
 
-    private native int jniConfigure(int mtu, String addresses, String routes);
+    private native int jniCreate(int mtu);
     private native String jniGetName(int tun);
+    private native int jniSetAddresses(String interfaze, String addresses);
+    private native int jniSetRoutes(String interfaze, String routes);
     private native void jniReset(String interfaze);
     private native int jniCheck(String interfaze);
     private native void jniProtect(int socket, String interfaze);
@@ -323,11 +331,11 @@
      */
     private class LegacyVpnRunner extends Thread {
         private static final String TAG = "LegacyVpnRunner";
-        private static final String NONE = "--";
 
         private final VpnConfig mConfig;
         private final String[] mDaemons;
         private final String[][] mArguments;
+        private final String mOuterInterface;
         private final LegacyVpnInfo mInfo;
 
         private long mTimer = -1;
@@ -339,17 +347,27 @@
             mArguments = new String[][] {racoon, mtpd};
             mInfo = new LegacyVpnInfo();
 
+            // This is the interface which VPN is running on.
+            mOuterInterface = mConfig.interfaze;
+
             // Legacy VPN is not a real package, so we use it to carry the key.
             mInfo.key = mConfig.packagz;
             mConfig.packagz = VpnConfig.LEGACY_VPN;
         }
 
+        public void check(String interfaze) {
+            if (interfaze.equals(mOuterInterface)) {
+                Log.i(TAG, "Legacy VPN is going down with " + interfaze);
+                exit();
+            }
+        }
+
         public void exit() {
             // We assume that everything is reset after the daemons die.
+            interrupt();
             for (String daemon : mDaemons) {
                 SystemProperties.set("ctl.stop", daemon);
             }
-            interrupt();
         }
 
         public LegacyVpnInfo getInfo() {
@@ -380,7 +398,7 @@
                 Thread.sleep(yield ? 200 : 1);
             } else {
                 mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
-                throw new IllegalStateException("time is up");
+                throw new IllegalStateException("Time is up");
             }
         }
 
@@ -404,12 +422,11 @@
                     }
                 }
 
-                // Reset the properties.
-                SystemProperties.set("vpn.dns", NONE);
-                SystemProperties.set("vpn.via", NONE);
-                while (!NONE.equals(SystemProperties.get("vpn.dns")) ||
-                        !NONE.equals(SystemProperties.get("vpn.via"))) {
-                    checkpoint(true);
+                // Clear the previous state.
+                File state = new File("/data/misc/vpn/state");
+                state.delete();
+                if (state.exists()) {
+                    throw new IllegalStateException("Cannot delete the state");
                 }
 
                 // Check if we need to restart any of the daemons.
@@ -461,29 +478,34 @@
                     OutputStream out = socket.getOutputStream();
                     for (String argument : arguments) {
                         byte[] bytes = argument.getBytes(Charsets.UTF_8);
-                        if (bytes.length >= 0xFFFF) {
-                            throw new IllegalArgumentException("argument is too large");
+                        if (bytes.length > 0xFFFF) {
+                            throw new IllegalArgumentException("Argument is too large");
                         }
                         out.write(bytes.length >> 8);
                         out.write(bytes.length);
                         out.write(bytes);
                         checkpoint(false);
                     }
-
-                    // Send End-Of-Arguments.
-                    out.write(0xFF);
-                    out.write(0xFF);
                     out.flush();
+                    socket.shutdownOutput();
+
+                    // Wait for End-of-File.
+                    InputStream in = socket.getInputStream();
+                    while (true) {
+                        try {
+                            if (in.read() == -1) {
+                                break;
+                            }
+                        } catch (Exception e) {
+                            // ignore
+                        }
+                        checkpoint(true);
+                    }
                     socket.close();
                 }
 
-                // Now here is the beast from the old days. We check few
-                // properties to figure out the current status. Ideally we
-                // can read things back from the sockets and get rid of the
-                // properties, but we have no time...
-                while (NONE.equals(SystemProperties.get("vpn.dns")) ||
-                        NONE.equals(SystemProperties.get("vpn.via"))) {
-
+                // Wait for the daemons to create the new state.
+                while (!state.exists()) {
                     // Check if a running daemon is dead.
                     for (int i = 0; i < mDaemons.length; ++i) {
                         String daemon = mDaemons[i];
@@ -495,20 +517,45 @@
                     checkpoint(true);
                 }
 
-                // Now we are connected. Get the interface.
-                mConfig.interfaze = SystemProperties.get("vpn.via");
+                // Now we are connected. Read and parse the new state.
+                byte[] buffer = new byte[(int) state.length()];
+                if (new FileInputStream(state).read(buffer) != buffer.length) {
+                    throw new IllegalStateException("Cannot read the state");
+                }
+                String[] parameters = new String(buffer, Charsets.UTF_8).split("\n", -1);
+                if (parameters.length != 6) {
+                    throw new IllegalStateException("Cannot parse the state");
+                }
 
-                // Get the DNS servers if they are not set in config.
+                // Set the interface and the addresses in the config.
+                mConfig.interfaze = parameters[0].trim();
+                mConfig.addresses = parameters[1].trim();
+
+                // Set the routes if they are not set in the config.
+                if (mConfig.routes == null || mConfig.routes.isEmpty()) {
+                    mConfig.routes = parameters[2].trim();
+                }
+
+                // Set the DNS servers if they are not set in the config.
                 if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
-                    String dnsServers = SystemProperties.get("vpn.dns").trim();
+                    String dnsServers = parameters[3].trim();
                     if (!dnsServers.isEmpty()) {
                         mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
                     }
                 }
 
-                // TODO: support search domains from ISAKMP mode config.
+                // Set the search domains if they are not set in the config.
+                if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
+                    String searchDomains = parameters[4].trim();
+                    if (!searchDomains.isEmpty()) {
+                        mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
+                    }
+                }
 
-                // The final step must be synchronized.
+                // Set the routes.
+                jniSetRoutes(mConfig.interfaze, mConfig.routes);
+
+                // Here is the last step and it must be done synchronously.
                 synchronized (Vpn.this) {
                     // Check if the thread is interrupted while we are waiting.
                     checkpoint(false);
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index d30b66b..0c78fe7 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -24,8 +24,8 @@
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
+import static android.net.ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.*;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -42,7 +42,7 @@
 import static android.net.NetworkPolicyManager.isUidValidForPolicy;
 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
 import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
-import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
@@ -678,7 +678,7 @@
             time.setToNow();
             final int cycleDay = time.monthDay;
 
-            final NetworkTemplate template = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
+            final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
             mNetworkPolicy.add(
                     new NetworkPolicy(template, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED));
             writePolicyLocked();
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 54e94db..7ec6b81 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -73,11 +73,12 @@
 import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -719,10 +720,9 @@
         // clear any existing stats and read from disk
         mNetworkStats.clear();
 
-        FileInputStream fis = null;
+        DataInputStream in = null;
         try {
-            fis = mNetworkFile.openRead();
-            final DataInputStream in = new DataInputStream(fis);
+            in = new DataInputStream(new BufferedInputStream(mNetworkFile.openRead()));
 
             // verify file magic header intact
             final int magic = in.readInt();
@@ -751,7 +751,7 @@
         } catch (IOException e) {
             Slog.e(TAG, "problem reading network stats", e);
         } finally {
-            IoUtils.closeQuietly(fis);
+            IoUtils.closeQuietly(in);
         }
     }
 
@@ -768,10 +768,9 @@
         // clear any existing stats and read from disk
         mUidStats.clear();
 
-        FileInputStream fis = null;
+        DataInputStream in = null;
         try {
-            fis = mUidFile.openRead();
-            final DataInputStream in = new DataInputStream(fis);
+            in = new DataInputStream(new BufferedInputStream(mUidFile.openRead()));
 
             // verify file magic header intact
             final int magic = in.readInt();
@@ -826,7 +825,7 @@
         } catch (IOException e) {
             Slog.e(TAG, "problem reading uid stats", e);
         } finally {
-            IoUtils.closeQuietly(fis);
+            IoUtils.closeQuietly(in);
         }
     }
 
@@ -838,7 +837,7 @@
         FileOutputStream fos = null;
         try {
             fos = mNetworkFile.startWrite();
-            final DataOutputStream out = new DataOutputStream(fos);
+            final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
 
             out.writeInt(FILE_MAGIC);
             out.writeInt(VERSION_NETWORK_INIT);
@@ -850,6 +849,7 @@
                 history.writeToStream(out);
             }
 
+            out.flush();
             mNetworkFile.finishWrite(fos);
         } catch (IOException e) {
             if (fos != null) {
@@ -871,7 +871,7 @@
         FileOutputStream fos = null;
         try {
             fos = mUidFile.startWrite();
-            final DataOutputStream out = new DataOutputStream(fos);
+            final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fos));
 
             out.writeInt(FILE_MAGIC);
             out.writeInt(VERSION_UID_WITH_TAG);
@@ -895,6 +895,7 @@
                 }
             }
 
+            out.flush();
             mUidFile.finishWrite(fos);
         } catch (IOException e) {
             if (fos != null) {
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
index 5f920f1..d28a6b4 100644
--- a/services/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -18,7 +18,6 @@
 
 #define LOG_TAG "VpnJni"
 #include <cutils/log.h>
-#include <cutils/properties.h>
 
 #include <stdio.h>
 #include <string.h>
@@ -54,7 +53,7 @@
 #define SYSTEM_ERROR -1
 #define BAD_ARGUMENT -2
 
-static int create_interface(char *name, int *index, int mtu)
+static int create_interface(int mtu)
 {
     int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
 
@@ -82,14 +81,6 @@
         goto error;
     }
 
-    // Get interface index.
-    if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
-        LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno));
-        goto error;
-    }
-
-    strncpy(name, ifr4.ifr_name, IFNAMSIZ);
-    *index = ifr4.ifr_ifindex;
     return tun;
 
 error:
@@ -97,12 +88,40 @@
     return SYSTEM_ERROR;
 }
 
-static int set_addresses(const char *name, int index, const char *addresses)
+static int get_interface_name(char *name, int tun)
 {
     ifreq ifr4;
+    if (ioctl(tun, TUNGETIFF, &ifr4)) {
+        LOGE("Cannot get interface name: %s", strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    strncpy(name, ifr4.ifr_name, IFNAMSIZ);
+    return 0;
+}
+
+static int get_interface_index(const char *name)
+{
+    ifreq ifr4;
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+    if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
+        LOGE("Cannot get index of %s: %s", name, strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    return ifr4.ifr_ifindex;
+}
+
+static int set_addresses(const char *name, const char *addresses)
+{
+    int index = get_interface_index(name);
+    if (index < 0) {
+        return index;
+    }
+
+    ifreq ifr4;
     memset(&ifr4, 0, sizeof(ifr4));
     strncpy(ifr4.ifr_name, name, IFNAMSIZ);
     ifr4.ifr_addr.sa_family = AF_INET;
+    ifr4.ifr_netmask.sa_family = AF_INET;
 
     in6_ifreq ifr6;
     memset(&ifr6, 0, sizeof(ifr6));
@@ -146,7 +165,7 @@
             }
 
             in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
-            *as_in_addr(&ifr4.ifr_addr) = htonl(mask);
+            *as_in_addr(&ifr4.ifr_netmask) = htonl(mask);
             if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
                 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
                 break;
@@ -168,8 +187,13 @@
     return count;
 }
 
-static int set_routes(const char *name, int index, const char *routes)
+static int set_routes(const char *name, const char *routes)
 {
+    int index = get_interface_index(name);
+    if (index < 0) {
+        return index;
+    }
+
     rtentry rt4;
     memset(&rt4, 0, sizeof(rt4));
     rt4.rt_dev = (char *)name;
@@ -253,17 +277,6 @@
     return count;
 }
 
-static int get_interface_name(char *name, int tun)
-{
-    ifreq ifr4;
-    if (ioctl(tun, TUNGETIFF, &ifr4)) {
-        LOGE("Cannot get interface name: %s", strerror(errno));
-        return SYSTEM_ERROR;
-    }
-    strncpy(name, ifr4.ifr_name, IFNAMSIZ);
-    return 0;
-}
-
 static int reset_interface(const char *name)
 {
     ifreq ifr4;
@@ -309,53 +322,14 @@
     }
 }
 
-static jint configure(JNIEnv *env, jobject thiz,
-        jint mtu, jstring jAddresses, jstring jRoutes)
+static jint create(JNIEnv *env, jobject thiz, jint mtu)
 {
-    char name[IFNAMSIZ];
-    int index;
-    int tun = create_interface(name, &index, mtu);
+    int tun = create_interface(mtu);
     if (tun < 0) {
         throwException(env, tun, "Cannot create interface");
         return -1;
     }
-
-    const char *addresses = NULL;
-    const char *routes = NULL;
-    int count;
-
-    // At least one address must be set.
-    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
-    if (!addresses) {
-        jniThrowNullPointerException(env, "address");
-        goto error;
-    }
-    count = set_addresses(name, index, addresses);
-    env->ReleaseStringUTFChars(jAddresses, addresses);
-    if (count <= 0) {
-        throwException(env, count, "Cannot set address");
-        goto error;
-    }
-    LOGD("Configured %d address(es) on %s", count, name);
-
-    // On the contrary, routes are optional.
-    routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
-    if (routes) {
-        count = set_routes(name, index, routes);
-        env->ReleaseStringUTFChars(jRoutes, routes);
-        if (count < 0) {
-            throwException(env, count, "Cannot set route");
-            goto error;
-        }
-        LOGD("Configured %d route(s) on %s", count, name);
-    }
-
     return tun;
-
-error:
-    close(tun);
-    LOGD("%s is destroyed", name);
-    return -1;
 }
 
 static jstring getName(JNIEnv *env, jobject thiz, jint tun)
@@ -368,6 +342,72 @@
     return env->NewStringUTF(name);
 }
 
+static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
+        jstring jAddresses)
+{
+    const char *name = NULL;
+    const char *addresses = NULL;
+    int count = -1;
+
+    name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        goto error;
+    }
+    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
+    if (!addresses) {
+        jniThrowNullPointerException(env, "addresses");
+        goto error;
+    }
+    count = set_addresses(name, addresses);
+    if (count < 0) {
+        throwException(env, count, "Cannot set address");
+        count = -1;
+    }
+
+error:
+    if (name) {
+        env->ReleaseStringUTFChars(jName, name);
+    }
+    if (addresses) {
+        env->ReleaseStringUTFChars(jAddresses, addresses);
+    }
+    return count;
+}
+
+static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
+        jstring jRoutes)
+{
+    const char *name = NULL;
+    const char *routes = NULL;
+    int count = -1;
+
+    name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        goto error;
+    }
+    routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
+    if (!routes) {
+        jniThrowNullPointerException(env, "routes");
+        goto error;
+    }
+    count = set_routes(name, routes);
+    if (count < 0) {
+        throwException(env, count, "Cannot set route");
+        count = -1;
+    }
+
+error:
+    if (name) {
+        env->ReleaseStringUTFChars(jName, name);
+    }
+    if (routes) {
+        env->ReleaseStringUTFChars(jRoutes, routes);
+    }
+    return count;
+}
+
 static void reset(JNIEnv *env, jobject thiz, jstring jName)
 {
     const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
@@ -409,8 +449,10 @@
 //------------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"jniConfigure", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)configure},
+    {"jniCreate", "(I)I", (void *)create},
     {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
+    {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
+    {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes},
     {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
     {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
     {"jniProtect", "(ILjava/lang/String;)V", (void *)protect},
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 4ec0c8c..518a1bb7 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -28,23 +28,25 @@
       mEnabled(false), mGyroTime(0)
 {
     sensor_t const* list;
-    size_t count = mSensorDevice.getSensorList(&list);
-    for (size_t i=0 ; i<count ; i++) {
-        if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
-            mAcc = Sensor(list + i);
+    ssize_t count = mSensorDevice.getSensorList(&list);
+    if (count > 0) {
+        for (size_t i=0 ; i<size_t(count) ; i++) {
+            if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
+                mAcc = Sensor(list + i);
+            }
+            if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
+                mMag = Sensor(list + i);
+            }
+            if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
+                mGyro = Sensor(list + i);
+                // 200 Hz for gyro events is a good compromise between precision
+                // and power/cpu usage.
+                mGyroRate = 200;
+                mTargetDelayNs = 1000000000LL/mGyroRate;
+            }
         }
-        if (list[i].type == SENSOR_TYPE_MAGNETIC_FIELD) {
-            mMag = Sensor(list + i);
-        }
-        if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
-            mGyro = Sensor(list + i);
-            // 200 Hz for gyro events is a good compromise between precision
-            // and power/cpu usage.
-            mGyroRate = 200;
-            mTargetDelayNs = 1000000000LL/mGyroRate;
-        }
+        mFusion.init();
     }
-    mFusion.init();
 }
 
 void SensorFusion::process(const sensors_event_t& event) {
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 64d214b..e0dce1f 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -70,73 +70,76 @@
     SensorDevice& dev(SensorDevice::getInstance());
 
     if (dev.initCheck() == NO_ERROR) {
-        ssize_t orientationIndex = -1;
-        bool hasGyro = false;
-        uint32_t virtualSensorsNeeds =
-                (1<<SENSOR_TYPE_GRAVITY) |
-                (1<<SENSOR_TYPE_LINEAR_ACCELERATION) |
-                (1<<SENSOR_TYPE_ROTATION_VECTOR);
         sensor_t const* list;
-        int count = dev.getSensorList(&list);
-        mLastEventSeen.setCapacity(count);
-        for (int i=0 ; i<count ; i++) {
-            registerSensor( new HardwareSensor(list[i]) );
-            switch (list[i].type) {
-                case SENSOR_TYPE_ORIENTATION:
-                    orientationIndex = i;
-                    break;
-                case SENSOR_TYPE_GYROSCOPE:
-                    hasGyro = true;
-                    break;
-                case SENSOR_TYPE_GRAVITY:
-                case SENSOR_TYPE_LINEAR_ACCELERATION:
-                case SENSOR_TYPE_ROTATION_VECTOR:
-                    virtualSensorsNeeds &= ~(1<<list[i].type);
-                    break;
+        ssize_t count = dev.getSensorList(&list);
+        if (count > 0) {
+            ssize_t orientationIndex = -1;
+            bool hasGyro = false;
+            uint32_t virtualSensorsNeeds =
+                    (1<<SENSOR_TYPE_GRAVITY) |
+                    (1<<SENSOR_TYPE_LINEAR_ACCELERATION) |
+                    (1<<SENSOR_TYPE_ROTATION_VECTOR);
+
+            mLastEventSeen.setCapacity(count);
+            for (ssize_t i=0 ; i<count ; i++) {
+                registerSensor( new HardwareSensor(list[i]) );
+                switch (list[i].type) {
+                    case SENSOR_TYPE_ORIENTATION:
+                        orientationIndex = i;
+                        break;
+                    case SENSOR_TYPE_GYROSCOPE:
+                        hasGyro = true;
+                        break;
+                    case SENSOR_TYPE_GRAVITY:
+                    case SENSOR_TYPE_LINEAR_ACCELERATION:
+                    case SENSOR_TYPE_ROTATION_VECTOR:
+                        virtualSensorsNeeds &= ~(1<<list[i].type);
+                        break;
+                }
             }
-        }
 
-        // it's safe to instantiate the SensorFusion object here
-        // (it wants to be instantiated after h/w sensors have been
-        // registered)
-        const SensorFusion& fusion(SensorFusion::getInstance());
+            // it's safe to instantiate the SensorFusion object here
+            // (it wants to be instantiated after h/w sensors have been
+            // registered)
+            const SensorFusion& fusion(SensorFusion::getInstance());
 
-        if (hasGyro) {
-            // Always instantiate Android's virtual sensors. Since they are
-            // instantiated behind sensors from the HAL, they won't
-            // interfere with applications, unless they looks specifically
-            // for them (by name).
+            if (hasGyro) {
+                // Always instantiate Android's virtual sensors. Since they are
+                // instantiated behind sensors from the HAL, they won't
+                // interfere with applications, unless they looks specifically
+                // for them (by name).
 
-            registerVirtualSensor( new RotationVectorSensor() );
-            registerVirtualSensor( new GravitySensor(list, count) );
-            registerVirtualSensor( new LinearAccelerationSensor(list, count) );
+                registerVirtualSensor( new RotationVectorSensor() );
+                registerVirtualSensor( new GravitySensor(list, count) );
+                registerVirtualSensor( new LinearAccelerationSensor(list, count) );
 
-            // these are optional
-            registerVirtualSensor( new OrientationSensor() );
-            registerVirtualSensor( new CorrectedGyroSensor(list, count) );
+                // these are optional
+                registerVirtualSensor( new OrientationSensor() );
+                registerVirtualSensor( new CorrectedGyroSensor(list, count) );
 
-            // virtual debugging sensors...
-            char value[PROPERTY_VALUE_MAX];
-            property_get("debug.sensors", value, "0");
-            if (atoi(value)) {
-                registerVirtualSensor( new GyroDriftSensor() );
+                // virtual debugging sensors...
+                char value[PROPERTY_VALUE_MAX];
+                property_get("debug.sensors", value, "0");
+                if (atoi(value)) {
+                    registerVirtualSensor( new GyroDriftSensor() );
+                }
             }
-        }
 
-        // build the sensor list returned to users
-        mUserSensorList = mSensorList;
-        if (hasGyro &&
-                (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR))) {
-            // if we have the fancy sensor fusion, and it's not provided by the
-            // HAL, use our own (fused) orientation sensor by removing the
-            // HAL supplied one form the user list.
-            if (orientationIndex >= 0) {
-                mUserSensorList.removeItemsAt(orientationIndex);
+            // build the sensor list returned to users
+            mUserSensorList = mSensorList;
+            if (hasGyro &&
+                    (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR))) {
+                // if we have the fancy sensor fusion, and it's not provided by the
+                // HAL, use our own (fused) orientation sensor by removing the
+                // HAL supplied one form the user list.
+                if (orientationIndex >= 0) {
+                    mUserSensorList.removeItemsAt(orientationIndex);
+                }
             }
-        }
 
-        run("SensorService", PRIORITY_URGENT_DISPLAY);
-        mInitCheck = NO_ERROR;
+            run("SensorService", PRIORITY_URGENT_DISPLAY);
+            mInitCheck = NO_ERROR;
+        }
     }
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b0881a4..680814c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1551,8 +1551,18 @@
          * Dump SurfaceFlinger global state
          */
 
-        snprintf(buffer, SIZE, "SurfaceFlinger global state\n");
+        snprintf(buffer, SIZE, "SurfaceFlinger global state:\n");
         result.append(buffer);
+
+        const GLExtensions& extensions(GLExtensions::getInstance());
+        snprintf(buffer, SIZE, "GLES: %s, %s, %s\n",
+                extensions.getVendor(),
+                extensions.getRenderer(),
+                extensions.getVersion());
+        result.append(buffer);
+        snprintf(buffer, SIZE, "EXTS: %s\n", extensions.getExtension());
+        result.append(buffer);
+
         mWormholeRegion.dump(result, "WormholeRegion");
         const DisplayHardware& hw(graphicPlane(0).displayHardware());
         snprintf(buffer, SIZE,
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 33fd355..504ba42 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -27,7 +27,6 @@
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkTemplate.MATCH_WIFI;
 import static org.easymock.EasyMock.anyInt;
 import static org.easymock.EasyMock.aryEq;
 import static org.easymock.EasyMock.capture;
@@ -88,7 +87,7 @@
     private static final long TEST_START = 1194220800000L;
     private static final String TEST_IFACE = "test0";
 
-    private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null);
+    private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
 
     private BroadcastInterceptingContext mServiceContext;
     private File mPolicyDir;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index ac74063..bd80af9 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -25,8 +25,8 @@
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
-import static android.net.NetworkTemplate.MATCH_WIFI;
+import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.UID_REMOVED;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
@@ -81,9 +81,9 @@
     private static final String IMSI_1 = "310004";
     private static final String IMSI_2 = "310260";
 
-    private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null);
-    private static NetworkTemplate sTemplateImsi1 = new NetworkTemplate(MATCH_MOBILE_ALL, IMSI_1);
-    private static NetworkTemplate sTemplateImsi2 = new NetworkTemplate(MATCH_MOBILE_ALL, IMSI_2);
+    private static NetworkTemplate sTemplateWifi = buildTemplateWifi();
+    private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
+    private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
 
     private static final int UID_RED = 1001;
     private static final int UID_BLUE = 1002;
@@ -290,7 +290,7 @@
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify service recorded history
-        history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null));
+        history = mService.getHistoryForNetwork(sTemplateWifi);
         assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
         assertEquals(HOUR_IN_MILLIS, history.getBucketDuration());
         assertEquals(2, history.size());
@@ -307,7 +307,7 @@
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
 
         // verify identical stats, but spread across 4 buckets now
-        history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null));
+        history = mService.getHistoryForNetwork(sTemplateWifi);
         assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 512L);
         assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration());
         assertEquals(4, history.size());
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 1bba8e3..8978f1d 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -26,7 +26,6 @@
 import android.app.PendingIntent;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
-import android.net.LinkProperties.CompareAddressesResult;
 import android.net.ProxyProperties;
 import android.os.AsyncResult;
 import android.os.Message;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index 9e185e5..a9f2cd1 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -23,7 +23,6 @@
 import android.app.PendingIntent;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
-import android.net.LinkProperties.CompareAddressesResult;
 import android.net.ProxyProperties;
 import android.os.Message;
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index bf964b7..ccdb0bf 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -27,7 +27,7 @@
 import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
-import android.net.LinkProperties.CompareAddressesResult;
+import android.net.LinkProperties.CompareResult;
 import android.net.NetworkUtils;
 import android.net.ProxyProperties;
 import android.net.TrafficStats;
@@ -1152,7 +1152,7 @@
                                     ! result.oldLp.isIdenticalHttpProxy(result.newLp) ||
                                     ! result.oldLp.isIdenticalAddresses(result.newLp)) {
                                 // If the same address type was removed and added we need to cleanup
-                                CompareAddressesResult car =
+                                CompareResult<LinkAddress> car =
                                     result.oldLp.compareAddresses(result.newLp);
                                 boolean needToClean = false;
                                 for (LinkAddress added : car.added) {
diff --git a/tests/BiDiTests/res/layout/textview_direction_ltr.xml b/tests/BiDiTests/res/layout/textview_direction_ltr.xml
index f7b7b8e..2c790ec 100644
--- a/tests/BiDiTests/res/layout/textview_direction_ltr.xml
+++ b/tests/BiDiTests/res/layout/textview_direction_ltr.xml
@@ -18,95 +18,232 @@
         android:id="@+id/textview_direction_ltr"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:layoutDirection="ltr">
+        android:layoutDirection="ltr"
+        android:textDirection="ltr">
 
-    <LinearLayout android:orientation="vertical"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:textDirection="ltr">
-
-        <LinearLayout android:orientation="vertical"
+        <TableLayout android:orientation="vertical"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content">
 
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="inherit"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="firstStrong"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="anyRtl"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="ltr"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="rtl"
-                    />
-        </LinearLayout>
+            <TableRow>
+                <TextView android:text="(unspecified)"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-        <LinearLayout android:orientation="vertical"
-                      android:layout_width="wrap_content"
-                      android:layout_height="wrap_content">
+            <TableRow>
+                <TextView android:text="inherit"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="inherit"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="firstStrong"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="anyRtl"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="ltr"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="rtl"
-                    />
-        </LinearLayout>
+            <TableRow>
+                <TextView android:text="firstStrong"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="firstStrong"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="firstStrong"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="firstStrong"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-    </LinearLayout>
+            <TableRow>
+                <TextView android:text="anyRtl"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="anyRtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="anyRtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="anyRtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-</FrameLayout>
\ No newline at end of file
+            <TableRow>
+                <TextView android:text="ltr"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="ltr"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="ltr"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="ltr"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="rtl"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="rtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="rtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="rtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+        </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/res/layout/textview_direction_rtl.xml b/tests/BiDiTests/res/layout/textview_direction_rtl.xml
index 81c5411..1df100d 100644
--- a/tests/BiDiTests/res/layout/textview_direction_rtl.xml
+++ b/tests/BiDiTests/res/layout/textview_direction_rtl.xml
@@ -18,95 +18,232 @@
         android:id="@+id/textview_direction_rtl"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:layoutDirection="rtl">
+        android:layoutDirection="rtl"
+        android:textDirection="rtl">
 
-    <LinearLayout android:orientation="vertical"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:textDirection="rtl">
-
-        <LinearLayout android:orientation="vertical"
+        <TableLayout android:orientation="vertical"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content">
 
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="inherit"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="firstStrong"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="anyRtl"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="ltr"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_text"
-                      android:textDirection="rtl"
-                    />
-        </LinearLayout>
+            <TableRow>
+                <TextView android:text="(unspecified)"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-        <LinearLayout android:orientation="vertical"
-                      android:layout_width="wrap_content"
-                      android:layout_height="wrap_content">
+            <TableRow>
+                <TextView android:text="inherit"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="inherit"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="inherit"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="firstStrong"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="anyRtl"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="ltr"
-                    />
-            <TextView android:layout_height="wrap_content"
-                      android:layout_width="wrap_content"
-                      android:textSize="24dip"
-                      android:text="@string/textview_hebrew_text"
-                      android:textDirection="rtl"
-                    />
-        </LinearLayout>
+            <TableRow>
+                <TextView android:text="firstStrong"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="firstStrong"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="firstStrong"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="firstStrong"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-    </LinearLayout>
+            <TableRow>
+                <TextView android:text="anyRtl"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="anyRtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="anyRtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="anyRtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
 
-</FrameLayout>
\ No newline at end of file
+            <TableRow>
+                <TextView android:text="ltr"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="ltr"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="ltr"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="ltr"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+            <TableRow>
+                <TextView android:text="rtl"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:typeface="serif"
+                          android:layout_marginRight="7dip"
+                          android:layout_marginLeft="7dip"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_hebrew_text"
+                          android:textDirection="rtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_latin_text"
+                          android:textDirection="rtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+                <TextView android:layout_height="wrap_content"
+                          android:layout_width="wrap_content"
+                          android:textSize="24dip"
+                          android:text="@string/textview_multiline_text"
+                          android:textDirection="rtl"
+                          android:layout_marginLeft="7dip"
+                          android:layout_marginRight="7dip"
+                          android:background="#444444"
+                          />
+            </TableRow>
+
+        </TableLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index c0bbe94..9a486c1 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -40,6 +40,6 @@
     <string name="menu_delete">Delete</string>
     <string name="textview_hebrew_text">&#x05DD;&#x05DE;ab?!</string>
     <string name="textview_latin_text">ab&#x05DD;&#x05DE;?!</string>
-    <string name="textview_multiline_text">&#x05DD;&#x05DE;?!\nab?!</string>
+    <string name="textview_multiline_text">&#x05DD;&#x05DE;?!\nab?!\n?!</string>
 </resources>
 
diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml
index 6bba370..1a5d3d9 100644
--- a/tests/HwAccelerationTest/res/layout/list_activity.xml
+++ b/tests/HwAccelerationTest/res/layout/list_activity.xml
@@ -30,8 +30,10 @@
             android:layout_height="wrap_content"
             android:layout_marginLeft="10dip"
             android:layout_marginRight="3dip"
+            
+            android:onClick="startProfiling"
 
-            android:text="Add" />
+            android:text="Start" />
         
         <Button
             android:layout_width="0dip"
@@ -39,8 +41,10 @@
             android:layout_height="wrap_content"
             android:layout_marginLeft="3dip"
             android:layout_marginRight="10dip"
+            
+            android:onClick="stopProfiling"
 
-            android:text="Remove" />
+            android:text="Stop" />
         
     </LinearLayout>
     
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
index 8fd4f6b..1493ab9 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -20,15 +20,19 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.Environment;
 import android.util.DisplayMetrics;
 import android.view.ContextMenu;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
 
+import java.io.File;
+
 @SuppressWarnings({"UnusedDeclaration"})
 public class ListActivity extends Activity {
     private static final String[] DATA_LIST = {
@@ -87,6 +91,15 @@
         
         registerForContextMenu(list);
     }
+    
+    public void startProfiling(View v) {
+        ViewDebug.startLooperProfiling(new File(Environment.getExternalStorageDirectory(),
+                "looper.trace"));
+    }
+    
+    public void stopProfiling(View v) {
+        ViewDebug.stopLooperProfiling();
+    }
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp
index a44072d..6b69864 100755
--- a/tools/aidl/Type.cpp
+++ b/tools/aidl/Type.cpp
@@ -198,7 +198,7 @@
 }
 
 void
-Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
             __FILE__, __LINE__, m_qualifiedName.c_str());
@@ -207,7 +207,7 @@
 }
 
 void
-Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
             __FILE__, __LINE__, m_qualifiedName.c_str());
@@ -226,7 +226,7 @@
 
 void
 Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
             __FILE__, __LINE__, m_qualifiedName.c_str());
@@ -235,7 +235,7 @@
 }
 
 void
-Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n",
             __FILE__, __LINE__, m_qualifiedName.c_str());
@@ -284,7 +284,7 @@
 }
 
 void
-BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallMethod)));
 }
@@ -303,13 +303,13 @@
 
 void
 BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayMethod)));
 }
 
 void
-BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new MethodCall(parcel, m_readArrayMethod, 1, v));
 }
@@ -331,7 +331,7 @@
 }
 
 void
-BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new Comparison(new LiteralExpression("0"),
                     "!=", new MethodCall(parcel, "readInt"))));
@@ -351,13 +351,13 @@
 
 void
 BooleanType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray")));
 }
 
 void
-BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v));
 }
@@ -378,7 +378,7 @@
 }
 
 void
-CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this));
 }
@@ -397,13 +397,13 @@
 
 void
 CharType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray")));
 }
 
 void
-CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new MethodCall(parcel, "readCharArray", 1, v));
 }
@@ -428,7 +428,7 @@
 }
 
 void
-StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "readString")));
 }
@@ -447,13 +447,13 @@
 
 void
 StringType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray")));
 }
 
 void
-StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new MethodCall(parcel, "readStringArray", 1, v));
 }
@@ -496,7 +496,7 @@
 
 void
 CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                Variable* parcel)
+                                Variable* parcel, Variable**)
 {
     // if (0 != parcel.readInt()) {
     //     v = TextUtils.createFromParcel(parcel)
@@ -532,7 +532,7 @@
 }
 
 void
-RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -551,7 +551,7 @@
 }
 
 void
-RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -571,7 +571,7 @@
 }
 
 void
-IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder")));
 }
@@ -584,13 +584,13 @@
 
 void
 IBinderType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray")));
 }
 
 void
-IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v));
 }
@@ -610,7 +610,7 @@
 }
 
 void
-IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -631,7 +631,7 @@
 
 void
 BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel)
+                                    Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -652,7 +652,7 @@
 
 void
 BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel)
+                                    Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -672,7 +672,7 @@
 }
 
 void
-ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -691,7 +691,7 @@
 }
 
 void
-ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
 }
@@ -709,25 +709,31 @@
     addTo->Add(new MethodCall(parcel, "writeMap", 1, v));
 }
 
-void
-MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+static void EnsureClassLoader(StatementBlock* addTo, Variable** cl)
 {
-    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
-    addTo->Add(new VariableDeclaration(cl,
-        new LiteralExpression("this.getClass().getClassLoader()"),
-        CLASSLOADER_TYPE));
-    addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, cl)));
+    // We don't want to look up the class loader once for every
+    // collection argument, so ensure we do it at most once per method.
+    if (*cl == NULL) {
+        *cl = new Variable(CLASSLOADER_TYPE, "cl");
+        addTo->Add(new VariableDeclaration(*cl,
+                new LiteralExpression("this.getClass().getClassLoader()"),
+                CLASSLOADER_TYPE));
+    }
+}
+
+void
+MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl)
+{
+    EnsureClassLoader(addTo, cl);
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, *cl)));
 }
 
 void
 MapType::ReadFromParcel(StatementBlock* addTo, Variable* v,
-                    Variable* parcel)
+                    Variable* parcel, Variable** cl)
 {
-    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
-    addTo->Add(new VariableDeclaration(cl, 
-        new LiteralExpression("this.getClass().getClassLoader()"),
-        CLASSLOADER_TYPE));
-    addTo->Add(new MethodCall(parcel, "readMap", 2, v, cl));
+    EnsureClassLoader(addTo, cl);
+    addTo->Add(new MethodCall(parcel, "readMap", 2, v, *cl));
 }
 
 
@@ -751,24 +757,18 @@
 }
 
 void
-ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl)
 {
-    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
-    addTo->Add(new VariableDeclaration(cl, 
-        new LiteralExpression("this.getClass().getClassLoader()"),
-        CLASSLOADER_TYPE));
-    addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, cl)));
+    EnsureClassLoader(addTo, cl);
+    addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, *cl)));
 }
 
 void
 ListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
-                    Variable* parcel)
+                    Variable* parcel, Variable** cl)
 {
-    Variable *cl = new Variable(CLASSLOADER_TYPE, "cl");
-    addTo->Add(new VariableDeclaration(cl, 
-        new LiteralExpression("this.getClass().getClassLoader()"),
-        CLASSLOADER_TYPE));
-    addTo->Add(new MethodCall(parcel, "readList", 2, v, cl));
+    EnsureClassLoader(addTo, cl);
+    addTo->Add(new MethodCall(parcel, "readList", 2, v, *cl));
 }
 
 
@@ -811,7 +811,7 @@
 }
 
 void
-ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+ParcelableType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     // if (0 != parcel.readInt()) {
     //     v = CLASS.CREATOR.createFromParcel(parcel)
@@ -833,7 +833,7 @@
 
 void
 ParcelableType::ReadFromParcel(StatementBlock* addTo, Variable* v,
-                    Variable* parcel)
+                    Variable* parcel, Variable**)
 {
     // TODO: really, we don't need to have this extra check, but we
     // don't have two separate marshalling code paths
@@ -862,7 +862,7 @@
 
 void
 ParcelableType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     string creator = v->type->QualifiedName() + ".CREATOR";
     addTo->Add(new Assignment(v, new MethodCall(parcel,
@@ -870,7 +870,7 @@
 }
 
 void
-ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+ParcelableType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     string creator = v->type->QualifiedName() + ".CREATOR";
     addTo->Add(new MethodCall(parcel, "readTypedArray", 2,
@@ -907,7 +907,7 @@
 }
 
 void
-InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     // v = Interface.asInterface(parcel.readStrongBinder());
     string type = v->type->QualifiedName();
@@ -961,14 +961,14 @@
 }
 
 void
-GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     fprintf(stderr, "implement GenericType::CreateFromParcel\n");
 }
 
 void
 GenericType::ReadFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     fprintf(stderr, "implement GenericType::ReadFromParcel\n");
 }
@@ -1009,7 +1009,7 @@
 }
 
 void
-GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel)
+GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**)
 {
     if (m_creator == STRING_TYPE->CreatorName()) {
         addTo->Add(new Assignment(v,
@@ -1027,7 +1027,7 @@
 
 void
 GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable**)
 {
     if (m_creator == STRING_TYPE->CreatorName()) {
         addTo->Add(new MethodCall(parcel, "readStringList", 1, v));
diff --git a/tools/aidl/Type.h b/tools/aidl/Type.h
index 2ea3ac9..662e3a2 100755
--- a/tools/aidl/Type.h
+++ b/tools/aidl/Type.h
@@ -46,18 +46,18 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual bool    CanBeArray() const;
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
 protected:
     void SetQualifiedName(const string& qualified);
@@ -89,16 +89,16 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual bool    CanBeArray() const;
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
 private:
     string m_marshallMethod;
@@ -116,16 +116,16 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual bool    CanBeArray() const;
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class CharType : public Type
@@ -136,16 +136,16 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual bool    CanBeArray() const;
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 
@@ -159,16 +159,16 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual bool    CanBeArray() const;
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class CharSequenceType : public Type
@@ -181,7 +181,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class RemoteExceptionType : public Type
@@ -192,7 +192,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class RuntimeExceptionType : public Type
@@ -203,7 +203,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class IBinderType : public Type
@@ -214,14 +214,14 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class IInterfaceType : public Type
@@ -232,7 +232,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class BinderType : public Type
@@ -243,7 +243,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class BinderProxyType : public Type
@@ -254,7 +254,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class ParcelType : public Type
@@ -265,7 +265,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class ParcelableInterfaceType : public Type
@@ -276,7 +276,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class MapType : public Type
@@ -287,9 +287,9 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class ListType : public Type
@@ -302,9 +302,9 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class ParcelableType : public Type
@@ -318,18 +318,18 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
     virtual bool    CanBeArray() const;
 
     virtual void    WriteArrayToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadArrayFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 };
 
 class InterfaceType : public Type
@@ -344,7 +344,7 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
                                     
 private:
     bool m_oneway;
@@ -364,9 +364,9 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
 private:
     string m_genericArguments;
@@ -387,9 +387,9 @@
     virtual void    WriteToParcel(StatementBlock* addTo, Variable* v,
                                     Variable* parcel, int flags);
     virtual void    CreateFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
     virtual void    ReadFromParcel(StatementBlock* addTo, Variable* v,
-                                    Variable* parcel);
+                                    Variable* parcel, Variable** cl);
 
 private:
     string m_creator;
diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp
index f17f66b..92f5b64 100644
--- a/tools/aidl/aidl.cpp
+++ b/tools/aidl/aidl.cpp
@@ -948,8 +948,6 @@
 int
 main(int argc, const char **argv)
 {
-    int err = 0;
-
     Options options;
     int result = parse_options(argc, argv, &options);
     if (result) {
diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp
index 0f18132..83e3bbc 100644
--- a/tools/aidl/generate_java.cpp
+++ b/tools/aidl/generate_java.cpp
@@ -286,25 +286,25 @@
 
 static void
 generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable** cl)
 {
     if (v->dimension == 0) {
-        t->CreateFromParcel(addTo, v, parcel);
+        t->CreateFromParcel(addTo, v, parcel, cl);
     }
     if (v->dimension == 1) {
-        t->CreateArrayFromParcel(addTo, v, parcel);
+        t->CreateArrayFromParcel(addTo, v, parcel, cl);
     }
 }
 
 static void
 generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
-                            Variable* parcel)
+                            Variable* parcel, Variable** cl)
 {
     if (v->dimension == 0) {
-        t->ReadFromParcel(addTo, v, parcel);
+        t->ReadFromParcel(addTo, v, parcel, cl);
     }
     if (v->dimension == 1) {
-        t->ReadArrayFromParcel(addTo, v, parcel);
+        t->ReadArrayFromParcel(addTo, v, parcel, cl);
     }
 }
 
@@ -362,6 +362,7 @@
             "enforceInterface", 1, new LiteralExpression("DESCRIPTOR")));
 
     // args
+    Variable* cl = NULL;
     VariableFactory stubArgs("_arg");
     arg = method->args;
     while (arg != NULL) {
@@ -373,7 +374,7 @@
 
         if (convert_direction(arg->direction.data) & IN_PARAMETER) {
             generate_create_from_parcel(t, c->statements, v,
-                    stubClass->transact_data);
+                    stubClass->transact_data, &cl);
         } else {
             if (arg->type.dimension == 0) {
                 c->statements->Add(new Assignment(
@@ -531,7 +532,7 @@
     if (_reply != NULL) {
         if (_result != NULL) {
             generate_create_from_parcel(proxy->returnType,
-                                    tryStatement->statements, _result, _reply);
+                    tryStatement->statements, _result, _reply, &cl);
         }
 
         // the out/inout parameters
@@ -541,7 +542,7 @@
             Variable* v = new Variable(t, arg->name.data, arg->type.dimension);
             if (convert_direction(arg->direction.data) & OUT_PARAMETER) {
                 generate_read_from_parcel(t, tryStatement->statements,
-                                            v, _reply);
+                                            v, _reply, &cl);
             }
             arg = arg->next;
         }
diff --git a/wifi/java/android/net/wifi/WifiWatchdogService.java b/wifi/java/android/net/wifi/WifiWatchdogService.java
deleted file mode 100644
index bce4b3a..0000000
--- a/wifi/java/android/net/wifi/WifiWatchdogService.java
+++ /dev/null
@@ -1,765 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.DnsPinger;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Scanner;
-
-/**
- * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
- * network with multiple access points. After the framework successfully
- * connects to an access point, the watchdog verifies connectivity by 'pinging'
- * the configured DNS server using {@link DnsPinger}.
- * <p>
- * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
- * that another AP might have internet access; otherwise the SSID is disabled.
- * <p>
- * On DNS success, the WatchdogService initiates a walled garden check via an
- * http get. A browser windows is activated if a walled garden is detected.
- * 
- * @hide
- */
-public class WifiWatchdogService {
-
-    private static final String WWS_TAG = "WifiWatchdogService";
-
-    private static final boolean VDBG = true;
-    private static final boolean DBG = true;
-
-    // Used for verbose logging
-    private String mDNSCheckLogStr;
-
-    private Context mContext;
-    private ContentResolver mContentResolver;
-    private WifiManager mWifiManager;
-
-    private WifiWatchdogHandler mHandler;
-
-    private DnsPinger mDnsPinger;
-
-    private IntentFilter mIntentFilter;
-    private BroadcastReceiver mBroadcastReceiver;
-    private boolean mBroadcastsEnabled;
-
-    private static final int WIFI_SIGNAL_LEVELS = 4;
-
-    /**
-     * Low signal is defined as less than or equal to cut off
-     */
-    private static final int LOW_SIGNAL_CUTOFF = 0;
-
-    private static final long MIN_LOW_SIGNAL_CHECK_INTERVAL = 2 * 60 * 1000;
-    private static final long MIN_SINGLE_DNS_CHECK_INTERVAL = 10 * 60 * 1000;
-    private static final long MIN_WALLED_GARDEN_INTERVAL = 15 * 60 * 1000;
-
-    private static final int MAX_CHECKS_PER_SSID = 9;
-    private static final int NUM_DNS_PINGS = 7;
-    private static double MIN_RESPONSE_RATE = 0.50;
-
-    // TODO : Adjust multiple DNS downward to 250 on repeated failure
-    // private static final int MULTI_DNS_PING_TIMEOUT_MS = 250;
-
-    private static final int DNS_PING_TIMEOUT_MS = 800;
-    private static final long DNS_PING_INTERVAL = 250;
-
-    private static final long BLACKLIST_FOLLOWUP_INTERVAL = 15 * 1000;
-
-    private Status mStatus = new Status();
-
-    private static class Status {
-        String bssid = "";
-        String ssid = "";
-
-        HashSet<String> allBssids = new HashSet<String>();
-        int numFullDNSchecks = 0;
-
-        long lastSingleCheckTime = -24 * 60 * 60 * 1000;
-        long lastWalledGardenCheckTime = -24 * 60 * 60 * 1000;
-
-        WatchdogState state = WatchdogState.INACTIVE;
-
-        // Info for dns check
-        int dnsCheckTries = 0;
-        int dnsCheckSuccesses = 0;
-
-        public int signal = -200;
-
-    }
-
-    private enum WatchdogState {
-        /**
-         * Full DNS check in progress
-         */
-        DNS_FULL_CHECK,
-
-        /**
-         * Walled Garden detected, will pop up browser next round.
-         */
-        WALLED_GARDEN_DETECTED,
-
-        /**
-         * DNS failed, will blacklist/disable AP next round
-         */
-        DNS_CHECK_FAILURE,
-
-        /**
-         * Online or displaying walled garden auth page
-         */
-        CHECKS_COMPLETE,
-
-        /**
-         * Watchdog idle, network has been blacklisted or received disconnect
-         * msg
-         */
-        INACTIVE,
-
-        BLACKLISTED_AP
-    }
-
-    public WifiWatchdogService(Context context) {
-        mContext = context;
-        mContentResolver = context.getContentResolver();
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context,
-                ConnectivityManager.TYPE_WIFI);
-
-        HandlerThread handlerThread = new HandlerThread("WifiWatchdogServiceThread");
-        handlerThread.start();
-        mHandler = new WifiWatchdogHandler(handlerThread.getLooper());
-
-        setupNetworkReceiver();
-
-        // The content observer to listen needs a handler, which createThread
-        // creates
-        registerForSettingsChanges();
-
-        // Start things off
-        if (isWatchdogEnabled()) {
-            mHandler.sendEmptyMessage(WifiWatchdogHandler.MESSAGE_CONTEXT_EVENT);
-        }
-    }
-
-    /**
-     *
-     */
-    private void setupNetworkReceiver() {
-        mBroadcastReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                    mHandler.sendMessage(mHandler.obtainMessage(
-                            WifiWatchdogHandler.MESSAGE_NETWORK_EVENT,
-                            intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)
-                            ));
-                } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
-                    mHandler.sendEmptyMessage(WifiWatchdogHandler.RSSI_CHANGE_EVENT);
-                } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
-                    mHandler.sendEmptyMessage(WifiWatchdogHandler.SCAN_RESULTS_AVAILABLE);
-                } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                    mHandler.sendMessage(mHandler.obtainMessage(
-                            WifiWatchdogHandler.WIFI_STATE_CHANGE,
-                            intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 4)));
-                }
-            }
-        };
-
-        mIntentFilter = new IntentFilter();
-        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-    }
-
-    /**
-     * Observes the watchdog on/off setting, and takes action when changed.
-     */
-    private void registerForSettingsChanges() {
-        ContentObserver contentObserver = new ContentObserver(mHandler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                mHandler.sendEmptyMessage((WifiWatchdogHandler.MESSAGE_CONTEXT_EVENT));
-            }
-        };
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON),
-                false, contentObserver);
-    }
-
-    private void handleNewConnection() {
-        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        String newSsid = wifiInfo.getSSID();
-        String newBssid = wifiInfo.getBSSID();
-
-        if (VDBG) {
-            Slog.v(WWS_TAG, String.format("handleConnected:: old (%s, %s) ==> new (%s, %s)",
-                    mStatus.ssid, mStatus.bssid, newSsid, newBssid));
-        }
-
-        if (TextUtils.isEmpty(newSsid) || TextUtils.isEmpty(newBssid)) {
-            return;
-        }
-
-        if (!TextUtils.equals(mStatus.ssid, newSsid)) {
-            mStatus = new Status();
-            mStatus.ssid = newSsid;
-        }
-
-        mStatus.bssid = newBssid;
-        mStatus.allBssids.add(newBssid);
-        mStatus.signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), WIFI_SIGNAL_LEVELS);
-
-        initDnsFullCheck();
-    }
-
-    public void updateRssi() {
-        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        if (!TextUtils.equals(mStatus.ssid, wifiInfo.getSSID()) ||
-                !TextUtils.equals(mStatus.bssid, wifiInfo.getBSSID())) {
-            return;
-        }
-
-        mStatus.signal = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), WIFI_SIGNAL_LEVELS);
-    }
-
-    /**
-     * Single step in state machine
-     */
-    private void handleStateStep() {
-        // Slog.v(WWS_TAG, "handleStateStep:: " + mStatus.state);
-
-        switch (mStatus.state) {
-            case DNS_FULL_CHECK:
-                if (VDBG) {
-                    Slog.v(WWS_TAG, "DNS_FULL_CHECK: " + mDNSCheckLogStr);
-                }
-
-                long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
-                        DNS_PING_TIMEOUT_MS);
-
-                mStatus.dnsCheckTries++;
-                if (pingResponseTime >= 0)
-                    mStatus.dnsCheckSuccesses++;
-
-                if (DBG) {
-                    if (pingResponseTime >= 0) {
-                        mDNSCheckLogStr += " | " + pingResponseTime;
-                    } else {
-                        mDNSCheckLogStr += " | " + "x";
-                    }
-                }
-
-                switch (currentDnsCheckStatus()) {
-                    case SUCCESS:
-                        if (DBG) {
-                            Slog.d(WWS_TAG, mDNSCheckLogStr + " -- Success");
-                        }
-                        doWalledGardenCheck();
-                        break;
-                    case FAILURE:
-                        if (DBG) {
-                            Slog.d(WWS_TAG, mDNSCheckLogStr + " -- Failure");
-                        }
-                        mStatus.state = WatchdogState.DNS_CHECK_FAILURE;
-                        break;
-                    case INCOMPLETE:
-                        // Taking no action
-                        break;
-                }
-                break;
-            case DNS_CHECK_FAILURE:
-                WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-                if (!mStatus.ssid.equals(wifiInfo.getSSID()) ||
-                        !mStatus.bssid.equals(wifiInfo.getBSSID())) {
-                    Slog.i(WWS_TAG, "handleState DNS_CHECK_FAILURE:: network has changed!");
-                    mStatus.state = WatchdogState.INACTIVE;
-                    break;
-                }
-
-                if (mStatus.numFullDNSchecks >= mStatus.allBssids.size() ||
-                        mStatus.numFullDNSchecks >= MAX_CHECKS_PER_SSID) {
-                    disableAP(wifiInfo);
-                } else {
-                    blacklistAP();
-                }
-                break;
-            case WALLED_GARDEN_DETECTED:
-                popUpBrowser();
-                mStatus.state = WatchdogState.CHECKS_COMPLETE;
-                break;
-            case BLACKLISTED_AP:
-                WifiInfo wifiInfo2 = mWifiManager.getConnectionInfo();
-                if (wifiInfo2.getSupplicantState() != SupplicantState.COMPLETED) {
-                    Slog.d(WWS_TAG,
-                            "handleState::BlacklistedAP - offline, but didn't get disconnect!");
-                    mStatus.state = WatchdogState.INACTIVE;
-                    break;
-                }
-                if (mStatus.bssid.equals(wifiInfo2.getBSSID())) {
-                    Slog.d(WWS_TAG, "handleState::BlacklistedAP - connected to same bssid");
-                    if (!handleSingleDnsCheck()) {
-                        disableAP(wifiInfo2);
-                        break;
-                    }
-                }
-
-                Slog.d(WWS_TAG, "handleState::BlacklistedAP - Simiulating a new connection");
-                handleNewConnection();
-                break;
-        }
-    }
-
-    private void doWalledGardenCheck() {
-        if (!isWalledGardenTestEnabled()) {
-            if (VDBG)
-                Slog.v(WWS_TAG, "Skipping walled garden check - disabled");
-            mStatus.state = WatchdogState.CHECKS_COMPLETE;
-            return;
-        }
-        long waitTime = waitTime(MIN_WALLED_GARDEN_INTERVAL,
-                mStatus.lastWalledGardenCheckTime);
-        if (waitTime > 0) {
-            if (VDBG) {
-                Slog.v(WWS_TAG, "Skipping walled garden check - wait " +
-                        waitTime + " ms.");
-            }
-            mStatus.state = WatchdogState.CHECKS_COMPLETE;
-            return;
-        }
-
-        mStatus.lastWalledGardenCheckTime = SystemClock.elapsedRealtime();
-        if (isWalledGardenConnection()) {
-            if (DBG)
-                Slog.d(WWS_TAG,
-                        "Walled garden test complete - walled garden detected");
-            mStatus.state = WatchdogState.WALLED_GARDEN_DETECTED;
-        } else {
-            if (DBG)
-                Slog.d(WWS_TAG, "Walled garden test complete - online");
-            mStatus.state = WatchdogState.CHECKS_COMPLETE;
-        }
-    }
-
-    private boolean handleSingleDnsCheck() {
-        mStatus.lastSingleCheckTime = SystemClock.elapsedRealtime();
-        long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
-                DNS_PING_TIMEOUT_MS);
-        if (DBG) {
-            Slog.d(WWS_TAG, "Ran a single DNS ping. Response time: " + responseTime);
-        }
-        if (responseTime < 0) {
-            return false;
-        }
-        return true;
-
-    }
-
-    /**
-     * @return Delay in MS before next single DNS check can proceed.
-     */
-    private long timeToNextScheduledDNSCheck() {
-        if (mStatus.signal > LOW_SIGNAL_CUTOFF) {
-            return waitTime(MIN_SINGLE_DNS_CHECK_INTERVAL, mStatus.lastSingleCheckTime);
-        } else {
-            return waitTime(MIN_LOW_SIGNAL_CHECK_INTERVAL, mStatus.lastSingleCheckTime);
-        }
-    }
-
-    /**
-     * Helper to return wait time left given a min interval and last run
-     * 
-     * @param interval minimum wait interval
-     * @param lastTime last time action was performed in
-     *            SystemClock.elapsedRealtime()
-     * @return non negative time to wait
-     */
-    private static long waitTime(long interval, long lastTime) {
-        long wait = interval + lastTime - SystemClock.elapsedRealtime();
-        return wait > 0 ? wait : 0;
-    }
-
-    private void popUpBrowser() {
-        Uri uri = Uri.parse("http://www.google.com");
-        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-        intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
-                Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
-    }
-
-    private void disableAP(WifiInfo info) {
-        // TODO : Unban networks if they had low signal ?
-        Slog.i(WWS_TAG, String.format("Disabling current SSID, %s [bssid %s].  " +
-                "numChecks %d, numAPs %d", mStatus.ssid, mStatus.bssid,
-                mStatus.numFullDNSchecks, mStatus.allBssids.size()));
-        mWifiManager.disableNetwork(info.getNetworkId());
-        mStatus.state = WatchdogState.INACTIVE;
-    }
-
-    private void blacklistAP() {
-        Slog.i(WWS_TAG, String.format("Blacklisting current BSSID %s [ssid %s].  " +
-                "numChecks %d, numAPs %d", mStatus.bssid, mStatus.ssid,
-                mStatus.numFullDNSchecks, mStatus.allBssids.size()));
-
-        mWifiManager.addToBlacklist(mStatus.bssid);
-        mWifiManager.reassociate();
-        mStatus.state = WatchdogState.BLACKLISTED_AP;
-    }
-
-    /**
-     * Checks the scan for new BBIDs using current mSsid
-     */
-    private void updateBssids() {
-        String curSsid = mStatus.ssid;
-        HashSet<String> bssids = mStatus.allBssids;
-        List<ScanResult> results = mWifiManager.getScanResults();
-        int oldNumBssids = bssids.size();
-
-        if (results == null) {
-            if (VDBG) {
-                Slog.v(WWS_TAG, "updateBssids: Got null scan results!");
-            }
-            return;
-        }
-
-        for (ScanResult result : results) {
-            if (result != null && curSsid.equals(result.SSID))
-                bssids.add(result.BSSID);
-        }
-
-        // if (VDBG && bssids.size() - oldNumBssids > 0) {
-        // Slog.v(WWS_TAG,
-        // String.format("updateBssids:: Found %d new APs (total %d) on SSID %s",
-        // bssids.size() - oldNumBssids, bssids.size(), curSsid));
-        // }
-    }
-
-    enum DnsCheckStatus {
-        SUCCESS,
-        FAILURE,
-        INCOMPLETE
-    }
-
-    /**
-     * Computes the current results of the dns check, ends early if outcome is
-     * assured.
-     */
-    private DnsCheckStatus currentDnsCheckStatus() {
-        /**
-         * After a full ping count, if we have more responses than this cutoff,
-         * the outcome is success; else it is 'failure'.
-         */
-        double pingResponseCutoff = MIN_RESPONSE_RATE * NUM_DNS_PINGS;
-        int remainingChecks = NUM_DNS_PINGS - mStatus.dnsCheckTries;
-
-        /**
-         * Our final success count will be at least this big, so we're
-         * guaranteed to succeed.
-         */
-        if (mStatus.dnsCheckSuccesses >= pingResponseCutoff) {
-            return DnsCheckStatus.SUCCESS;
-        }
-
-        /**
-         * Our final count will be at most the current count plus the remaining
-         * pings - we're guaranteed to fail.
-         */
-        if (remainingChecks + mStatus.dnsCheckSuccesses < pingResponseCutoff) {
-            return DnsCheckStatus.FAILURE;
-        }
-
-        return DnsCheckStatus.INCOMPLETE;
-    }
-
-    private void initDnsFullCheck() {
-        if (DBG) {
-            Slog.d(WWS_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
-        }
-        mStatus.numFullDNSchecks++;
-        mStatus.dnsCheckSuccesses = 0;
-        mStatus.dnsCheckTries = 0;
-        mStatus.state = WatchdogState.DNS_FULL_CHECK;
-
-        if (DBG) {
-            mDNSCheckLogStr = String.format("Dns Check %d.  Pinging %s on ssid [%s]: ",
-                    mStatus.numFullDNSchecks, mDnsPinger.getDns(),
-                    mStatus.ssid);
-        }
-    }
-
-    /**
-     * DNS based detection techniques do not work at all hotspots. The one sure
-     * way to check a walled garden is to see if a URL fetch on a known address
-     * fetches the data we expect
-     */
-    private boolean isWalledGardenConnection() {
-        InputStream in = null;
-        HttpURLConnection urlConnection = null;
-        try {
-            URL url = new URL(getWalledGardenUrl());
-            urlConnection = (HttpURLConnection) url.openConnection();
-            in = new BufferedInputStream(urlConnection.getInputStream());
-            Scanner scanner = new Scanner(in);
-            if (scanner.findInLine(getWalledGardenPattern()) != null) {
-                return false;
-            } else {
-                return true;
-            }
-        } catch (IOException e) {
-            return false;
-        } finally {
-            if (in != null) {
-                try {
-                    in.close();
-                } catch (IOException e) {
-                }
-            }
-            if (urlConnection != null)
-                urlConnection.disconnect();
-        }
-    }
-
-    /**
-     * There is little logic inside this class, instead methods of the form
-     * "handle___" are called in the main {@link WifiWatchdogService}.
-     */
-    private class WifiWatchdogHandler extends Handler {
-        /**
-         * Major network event, object is NetworkInfo
-         */
-        static final int MESSAGE_NETWORK_EVENT = 1;
-        /**
-         * Change in settings, no object
-         */
-        static final int MESSAGE_CONTEXT_EVENT = 2;
-
-        /**
-         * Change in signal strength
-         */
-        static final int RSSI_CHANGE_EVENT = 3;
-        static final int SCAN_RESULTS_AVAILABLE = 4;
-
-        static final int WIFI_STATE_CHANGE = 5;
-
-        /**
-         * Single step of state machine. One DNS check, or one WalledGarden
-         * check, or one external action. We separate out external actions to
-         * increase chance of detecting that a check failure is caused by change
-         * in network status. Messages should have an arg1 which to sync status
-         * messages.
-         */
-        static final int CHECK_SEQUENCE_STEP = 10;
-        static final int SINGLE_DNS_CHECK = 11;
-
-        /**
-         * @param looper
-         */
-        public WifiWatchdogHandler(Looper looper) {
-            super(looper);
-        }
-
-        boolean singleCheckQueued = false;
-        long queuedSingleDnsCheckArrival;
-
-        /**
-         * Sends a singleDnsCheck message with shortest time - guards against
-         * multiple.
-         */
-        private boolean queueSingleDnsCheck() {
-            long delay = timeToNextScheduledDNSCheck();
-            long newArrival = delay + SystemClock.elapsedRealtime();
-            if (singleCheckQueued && queuedSingleDnsCheckArrival <= newArrival)
-                return true;
-            queuedSingleDnsCheckArrival = newArrival;
-            singleCheckQueued = true;
-            removeMessages(SINGLE_DNS_CHECK);
-            return sendMessageDelayed(obtainMessage(SINGLE_DNS_CHECK), delay);
-        }
-
-        boolean checkSequenceQueued = false;
-        long queuedCheckSequenceArrival;
-
-        /**
-         * Sends a state_machine_step message if the delay requested is lower
-         * than the current delay.
-         */
-        private boolean sendCheckSequenceStep(long delay) {
-            long newArrival = delay + SystemClock.elapsedRealtime();
-            if (checkSequenceQueued && queuedCheckSequenceArrival <= newArrival)
-                return true;
-            queuedCheckSequenceArrival = newArrival;
-            checkSequenceQueued = true;
-            removeMessages(CHECK_SEQUENCE_STEP);
-            return sendMessageDelayed(obtainMessage(CHECK_SEQUENCE_STEP), delay);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case CHECK_SEQUENCE_STEP:
-                    checkSequenceQueued = false;
-                    handleStateStep();
-                    if (mStatus.state == WatchdogState.CHECKS_COMPLETE) {
-                        queueSingleDnsCheck();
-                    } else if (mStatus.state == WatchdogState.DNS_FULL_CHECK) {
-                        sendCheckSequenceStep(DNS_PING_INTERVAL);
-                    } else if (mStatus.state == WatchdogState.BLACKLISTED_AP) {
-                        sendCheckSequenceStep(BLACKLIST_FOLLOWUP_INTERVAL);
-                    } else if (mStatus.state != WatchdogState.INACTIVE) {
-                        sendCheckSequenceStep(0);
-                    }
-                    return;
-                case MESSAGE_NETWORK_EVENT:
-                    if (!mBroadcastsEnabled) {
-                        Slog.e(WWS_TAG,
-                                "MessageNetworkEvent - WatchdogService not enabled... returning");
-                        return;
-                    }
-                    NetworkInfo info = (NetworkInfo) msg.obj;
-                    switch (info.getState()) {
-                        case DISCONNECTED:
-                            mStatus.state = WatchdogState.INACTIVE;
-                            return;
-                        case CONNECTED:
-                            handleNewConnection();
-                            sendCheckSequenceStep(0);
-                    }
-                    return;
-                case SINGLE_DNS_CHECK:
-                    singleCheckQueued = false;
-                    if (mStatus.state != WatchdogState.CHECKS_COMPLETE) {
-                        Slog.d(WWS_TAG, "Single check returning, curState: " + mStatus.state);
-                        break;
-                    }
-
-                    if (!handleSingleDnsCheck()) {
-                        initDnsFullCheck();
-                        sendCheckSequenceStep(0);
-                    } else {
-                        queueSingleDnsCheck();
-                    }
-
-                    break;
-                case RSSI_CHANGE_EVENT:
-                    updateRssi();
-                    if (mStatus.state == WatchdogState.CHECKS_COMPLETE)
-                        queueSingleDnsCheck();
-                    break;
-                case SCAN_RESULTS_AVAILABLE:
-                    updateBssids();
-                    break;
-                case WIFI_STATE_CHANGE:
-                    if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
-                        Slog.i(WWS_TAG, "WifiStateDisabling -- Resetting WatchdogState");
-                        mStatus = new Status();
-                    }
-                    break;
-                case MESSAGE_CONTEXT_EVENT:
-                    if (isWatchdogEnabled() && !mBroadcastsEnabled) {
-                        mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
-                        mBroadcastsEnabled = true;
-                        Slog.i(WWS_TAG, "WifiWatchdogService enabled");
-                    } else if (!isWatchdogEnabled() && mBroadcastsEnabled) {
-                        mContext.unregisterReceiver(mBroadcastReceiver);
-                        removeMessages(SINGLE_DNS_CHECK);
-                        removeMessages(CHECK_SEQUENCE_STEP);
-                        mBroadcastsEnabled = false;
-                        Slog.i(WWS_TAG, "WifiWatchdogService disabled");
-                    }
-                    break;
-            }
-        }
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.print("WatchdogStatus: ");
-        pw.print("State " + mStatus.state);
-        pw.println(", network [" + mStatus.ssid + ", " + mStatus.bssid + "]");
-        pw.print("checkCount " + mStatus.numFullDNSchecks);
-        pw.println(", bssids: " + mStatus.allBssids);
-        pw.print(", hasCheckMessages? " +
-                mHandler.hasMessages(WifiWatchdogHandler.CHECK_SEQUENCE_STEP));
-        pw.println(" hasSingleCheckMessages? " +
-                mHandler.hasMessages(WifiWatchdogHandler.SINGLE_DNS_CHECK));
-        pw.println("DNS check log str: " + mDNSCheckLogStr);
-        pw.println("lastSingleCheck: " + mStatus.lastSingleCheckTime);
-    }
-
-    /**
-     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
-     */
-    private Boolean isWalledGardenTestEnabled() {
-        return Settings.Secure.getInt(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
-    }
-
-    /**
-     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
-     */
-    private String getWalledGardenUrl() {
-        String url = Settings.Secure.getString(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
-        if (TextUtils.isEmpty(url))
-            return "http://www.google.com/";
-        return url;
-    }
-
-    /**
-     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
-     */
-    private String getWalledGardenPattern() {
-        String pattern = Settings.Secure.getString(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
-        if (TextUtils.isEmpty(pattern))
-            return "<title>.*Google.*</title>";
-        return pattern;
-    }
-
-    /**
-     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON
-     */
-    private boolean isWatchdogEnabled() {
-        return Settings.Secure.getInt(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1;
-    }
-}
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
new file mode 100644
index 0000000..0eb73b7
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.DnsPinger;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
+ * network with multiple access points. After the framework successfully
+ * connects to an access point, the watchdog verifies connectivity by 'pinging'
+ * the configured DNS server using {@link DnsPinger}.
+ * <p>
+ * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
+ * that another AP might have internet access; otherwise the SSID is disabled.
+ * <p>
+ * On DNS success, the WatchdogService initiates a walled garden check via an
+ * http get. A browser window is activated if a walled garden is detected.
+ *
+ * @hide
+ */
+public class WifiWatchdogStateMachine extends StateMachine {
+
+    private static final boolean VDBG = false;
+    private static final boolean DBG = true;
+    private static final String WWSM_TAG = "WifiWatchdogStateMachine";
+
+    private static final int WIFI_SIGNAL_LEVELS = 4;
+    /**
+     * Low signal is defined as less than or equal to cut off
+     */
+    private static final int LOW_SIGNAL_CUTOFF = 1;
+
+    private static final long MIN_LOW_SIGNAL_CHECK_INTERVAL_MS = 2 * 60 * 1000;
+    private static final long MIN_SINGLE_DNS_CHECK_INTERVAL_MS = 10 * 60 * 1000;
+    private static final long MIN_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
+
+    private static final int MAX_CHECKS_PER_SSID = 7;
+    private static final int NUM_DNS_PINGS = 5;
+    private static final double MIN_DNS_RESPONSE_RATE = 0.50;
+
+    private static final int DNS_PING_TIMEOUT_MS = 800;
+    private static final long DNS_PING_INTERVAL_MS = 100;
+
+    private static final long BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
+
+    private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
+
+    /**
+     * Indicates the enable setting of WWS may have changed
+     */
+    private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1;
+
+    /**
+     * Indicates the wifi network state has changed. Passed w/ original intent
+     * which has a non-null networkInfo object
+     */
+    private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
+    /**
+     * Indicates the signal has changed. Passed with arg1
+     * {@link #mNetEventCounter} and arg2 [raw signal strength]
+     */
+    private static final int EVENT_RSSI_CHANGE = BASE + 3;
+    private static final int EVENT_SCAN_RESULTS_AVAILABLE = BASE + 4;
+    private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
+
+    private static final int MESSAGE_CHECK_STEP = BASE + 100;
+    private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 101;
+    private static final int MESSAGE_HANDLE_BAD_AP = BASE + 102;
+    /**
+     * arg1 == mOnlineWatchState.checkCount
+     */
+    private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 103;
+    private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 104;
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+    private WifiManager mWifiManager;
+    private DnsPinger mDnsPinger;
+    private IntentFilter mIntentFilter;
+    private BroadcastReceiver mBroadcastReceiver;
+
+    private DefaultState mDefaultState = new DefaultState();
+    private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
+    private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
+    private NotConnectedState mNotConnectedState = new NotConnectedState();
+    private ConnectedState mConnectedState = new ConnectedState();
+    private DnsCheckingState mDnsCheckingState = new DnsCheckingState();
+    private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
+    private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
+    private WalledGardenState mWalledGardenState = new WalledGardenState();
+    private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
+
+    /**
+     * The {@link WifiInfo} object passed to WWSM on network broadcasts
+     */
+    private WifiInfo mInitialConnInfo;
+    private int mNetEventCounter = 0;
+
+    /**
+     * Currently maintained but not used, TODO
+     */
+    private HashSet<String> mBssids = new HashSet<String>();
+    private int mNumFullDNSchecks = 0;
+
+    private Long mLastWalledGardenCheckTime = null;
+
+    /**
+     * This is set by the blacklisted state and reset when connected to a new AP.
+     * It triggers a disableNetwork call if a DNS check fails.
+     */
+    public boolean mDisableAPNextFailure = false;
+
+    /**
+     * STATE MAP
+     *          Default
+     *         /       \
+     * Disabled     Enabled
+     *             /       \
+     * Disconnected      Connected
+     *                  /---------\
+     *               (all other states)
+     */
+    private WifiWatchdogStateMachine(Context context) {
+        super(WWSM_TAG);
+        mContext = context;
+        mContentResolver = context.getContentResolver();
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context,
+                ConnectivityManager.TYPE_WIFI);
+
+        setupNetworkReceiver();
+
+        // The content observer to listen needs a handler
+        registerForSettingsChanges();
+        addState(mDefaultState);
+            addState(mWatchdogDisabledState, mDefaultState);
+            addState(mWatchdogEnabledState, mDefaultState);
+                addState(mNotConnectedState, mWatchdogEnabledState);
+                addState(mConnectedState, mWatchdogEnabledState);
+                    addState(mDnsCheckingState, mConnectedState);
+                    addState(mDnsCheckFailureState, mConnectedState);
+                    addState(mWalledGardenState, mConnectedState);
+                    addState(mBlacklistedApState, mConnectedState);
+                    addState(mOnlineWatchState, mConnectedState);
+
+        setInitialState(mWatchdogDisabledState);
+    }
+
+    public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
+        WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
+        wwsm.start();
+        wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
+        return wwsm;
+    }
+
+    /**
+   *
+   */
+    private void setupNetworkReceiver() {
+        mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                    sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
+                } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+                    obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
+                            intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
+                } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                    sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
+                } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                    sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
+                            intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                                    WifiManager.WIFI_STATE_UNKNOWN));
+                }
+            }
+        };
+
+        mIntentFilter = new IntentFilter();
+        mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+    }
+
+    /**
+     * Observes the watchdog on/off setting, and takes action when changed.
+     */
+    private void registerForSettingsChanges() {
+        ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                sendMessage(EVENT_WATCHDOG_TOGGLED);
+            }
+        };
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON),
+                false, contentObserver);
+    }
+
+    /**
+     * DNS based detection techniques do not work at all hotspots. The one sure
+     * way to check a walled garden is to see if a URL fetch on a known address
+     * fetches the data we expect
+     */
+    private boolean isWalledGardenConnection() {
+        InputStream in = null;
+        HttpURLConnection urlConnection = null;
+        try {
+            URL url = new URL(getWalledGardenUrl());
+            urlConnection = (HttpURLConnection) url.openConnection();
+            in = new BufferedInputStream(urlConnection.getInputStream());
+            Scanner scanner = new Scanner(in);
+            if (scanner.findInLine(getWalledGardenPattern()) != null) {
+                return false;
+            } else {
+                return true;
+            }
+        } catch (IOException e) {
+            return false;
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                }
+            }
+            if (urlConnection != null)
+                urlConnection.disconnect();
+        }
+    }
+
+    private boolean rssiStrengthAboveCutoff(int rssi) {
+        return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.print("WatchdogStatus: ");
+        pw.print("State " + getCurrentState());
+        pw.println(", network [" + mInitialConnInfo + "]");
+        pw.print("checkCount " + mNumFullDNSchecks);
+        pw.println(", bssids: " + mBssids);
+        pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
+    }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
+     */
+    private Boolean isWalledGardenTestEnabled() {
+        return Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
+    }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
+     */
+    private String getWalledGardenUrl() {
+        String url = Settings.Secure.getString(mContentResolver,
+                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
+        if (TextUtils.isEmpty(url))
+            return "http://www.google.com/";
+        return url;
+    }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
+     */
+    private String getWalledGardenPattern() {
+        String pattern = Settings.Secure.getString(mContentResolver,
+                Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
+        if (TextUtils.isEmpty(pattern))
+            return "<title>.*Google.*</title>";
+        return pattern;
+    }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ON
+     */
+    private boolean isWatchdogEnabled() {
+        return Settings.Secure.getInt(mContentResolver,
+                Settings.Secure.WIFI_WATCHDOG_ON, 1) == 1;
+    }
+
+
+    /**
+     * Helper to return wait time left given a min interval and last run
+     *
+     * @param interval minimum wait interval
+     * @param lastTime last time action was performed in
+     *            SystemClock.elapsedRealtime(). Null if never.
+     * @return non negative time to wait
+     */
+    private static long waitTime(long interval, Long lastTime) {
+        if (lastTime == null)
+            return 0;
+        long wait = interval + lastTime - SystemClock.elapsedRealtime();
+        return wait > 0 ? wait : 0;
+    }
+
+    private static String wifiInfoToStr(WifiInfo wifiInfo) {
+        if (wifiInfo == null)
+            return "null";
+        return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
+    }
+
+    /**
+   *
+   */
+    private void resetWatchdogState() {
+        mInitialConnInfo = null;
+        mDisableAPNextFailure = false;
+        mLastWalledGardenCheckTime = null;
+        mNumFullDNSchecks = 0;
+        mBssids.clear();
+    }
+
+    private void popUpBrowser() {
+        Uri uri = Uri.parse("http://www.google.com");
+        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+        intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+                Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+
+    private void sendCheckStepMessage(long delay) {
+        sendMessageDelayed(obtainMessage(MESSAGE_CHECK_STEP, mNetEventCounter, 0), delay);
+    }
+
+    class DefaultState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            if (VDBG) {
+                Slog.v(WWSM_TAG, "Caught message " + msg.what + " in state " +
+                        getCurrentState().getName());
+            }
+            return HANDLED;
+        }
+    }
+
+    class WatchdogDisabledState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_WATCHDOG_TOGGLED:
+                    if (isWatchdogEnabled())
+                        transitionTo(mNotConnectedState);
+                    return HANDLED;
+            }
+            return NOT_HANDLED;
+        }
+    }
+
+    class WatchdogEnabledState extends State {
+        @Override
+        public void enter() {
+            resetWatchdogState();
+            mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
+            Slog.i(WWSM_TAG, "WifiWatchdogService enabled");
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_WATCHDOG_TOGGLED:
+                    if (!isWatchdogEnabled())
+                        transitionTo(mWatchdogDisabledState);
+                    return HANDLED;
+                case EVENT_NETWORK_STATE_CHANGE:
+                    Intent stateChangeIntent = (Intent) msg.obj;
+                    NetworkInfo networkInfo = (NetworkInfo)
+                            stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+                    switch (networkInfo.getState()) {
+                        case CONNECTED:
+                            // WifiInfo wifiInfo = (WifiInfo)
+                            //         stateChangeIntent
+                            //                 .getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
+                            // TODO : Replace with above code when API is changed
+                            WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+                            if (wifiInfo == null) {
+                                Slog.e(WWSM_TAG, "Connected --> WifiInfo object null!");
+                                return HANDLED;
+                            }
+
+                            if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
+                                Slog.e(WWSM_TAG, "Received wifiInfo object with null elts: "
+                                        + wifiInfoToStr(wifiInfo));
+                                return HANDLED;
+                            }
+
+                            initConnection(wifiInfo);
+                            transitionTo(mDnsCheckingState);
+                            mNetEventCounter++;
+                            return HANDLED;
+                        case DISCONNECTED:
+                        case DISCONNECTING:
+                            mNetEventCounter++;
+                            transitionTo(mNotConnectedState);
+                            return HANDLED;
+                    }
+                    return HANDLED;
+                case EVENT_WIFI_RADIO_STATE_CHANGE:
+                    if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
+                        Slog.i(WWSM_TAG, "WifiStateDisabling -- Resetting WatchdogState");
+                        resetWatchdogState();
+                        mNetEventCounter++;
+                        transitionTo(mNotConnectedState);
+                    }
+                    return HANDLED;
+            }
+
+            return NOT_HANDLED;
+        }
+
+        /**
+         * @param wifiInfo Info object with non-null ssid and bssid
+         */
+        private void initConnection(WifiInfo wifiInfo) {
+            if (VDBG) {
+                Slog.v(WWSM_TAG, "Connected:: old " + wifiInfoToStr(mInitialConnInfo) +
+                        " ==> new " + wifiInfoToStr(wifiInfo));
+            }
+
+            if (mInitialConnInfo == null || !wifiInfo.getSSID().equals(mInitialConnInfo.getSSID())) {
+                resetWatchdogState();
+            } else if (!wifiInfo.getBSSID().equals(mInitialConnInfo.getBSSID())) {
+                mDisableAPNextFailure = false;
+            }
+            mInitialConnInfo = wifiInfo;
+        }
+
+        @Override
+        public void exit() {
+            mContext.unregisterReceiver(mBroadcastReceiver);
+            Slog.i(WWSM_TAG, "WifiWatchdogService disabled");
+        }
+    }
+
+    class NotConnectedState extends State {
+    }
+
+    class ConnectedState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_SCAN_RESULTS_AVAILABLE:
+                    String curSsid = mInitialConnInfo.getSSID();
+                    List<ScanResult> results = mWifiManager.getScanResults();
+                    int oldNumBssids = mBssids.size();
+
+                    if (results == null) {
+                        if (DBG) {
+                            Slog.d(WWSM_TAG, "updateBssids: Got null scan results!");
+                        }
+                        return HANDLED;
+                    }
+
+                    for (ScanResult result : results) {
+                        if (result == null || result.SSID == null) {
+                            if (VDBG) {
+                                Slog.v(WWSM_TAG, "Received invalid scan result: " + result);
+                            }
+                            continue;
+                        }
+                        if (curSsid.equals(result.SSID))
+                            mBssids.add(result.BSSID);
+                    }
+                    return HANDLED;
+            }
+            return NOT_HANDLED;
+        }
+
+    }
+
+    class DnsCheckingState extends State {
+        int dnsCheckTries = 0;
+        int dnsCheckSuccesses = 0;
+        String dnsCheckLogStr = "";
+
+        @Override
+        public void enter() {
+            mNumFullDNSchecks++;
+            dnsCheckSuccesses = 0;
+            dnsCheckTries = 0;
+            if (DBG) {
+                Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
+                dnsCheckLogStr = String.format("Dns Check %d.  Pinging %s on ssid [%s]: ",
+                        mNumFullDNSchecks, mDnsPinger.getDns(), mInitialConnInfo.getSSID());
+            }
+
+            sendCheckStepMessage(0);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (msg.what != MESSAGE_CHECK_STEP) {
+                return NOT_HANDLED;
+            }
+            if (msg.arg1 != mNetEventCounter) {
+                Slog.d(WWSM_TAG, "Check step out of sync, ignoring...");
+                return HANDLED;
+            }
+
+            long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
+                    DNS_PING_TIMEOUT_MS);
+
+            dnsCheckTries++;
+            if (pingResponseTime >= 0)
+                dnsCheckSuccesses++;
+
+            if (DBG) {
+                if (pingResponseTime >= 0) {
+                    dnsCheckLogStr += "|" + pingResponseTime;
+                } else {
+                    dnsCheckLogStr += "|x";
+                }
+            }
+
+            if (VDBG) {
+                Slog.v(WWSM_TAG, dnsCheckLogStr);
+            }
+
+            /**
+             * After a full ping count, if we have more responses than this
+             * cutoff, the outcome is success; else it is 'failure'.
+             */
+            double pingResponseCutoff = MIN_DNS_RESPONSE_RATE * NUM_DNS_PINGS;
+            int remainingChecks = NUM_DNS_PINGS - dnsCheckTries;
+
+            /**
+             * Our final success count will be at least this big, so we're
+             * guaranteed to succeed.
+             */
+            if (dnsCheckSuccesses >= pingResponseCutoff) {
+                // DNS CHECKS OK, NOW WALLED GARDEN
+                if (DBG) {
+                    Slog.d(WWSM_TAG, dnsCheckLogStr + "|  SUCCESS");
+                }
+
+                if (!shouldCheckWalledGarden()) {
+                    transitionTo(mOnlineWatchState);
+                    return HANDLED;
+                }
+
+                mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
+                if (isWalledGardenConnection()) {
+                    if (DBG)
+                        Slog.d(WWSM_TAG,
+                                "Walled garden test complete - walled garden detected");
+                    transitionTo(mWalledGardenState);
+                } else {
+                    if (DBG)
+                        Slog.d(WWSM_TAG, "Walled garden test complete - online");
+                    transitionTo(mOnlineWatchState);
+                }
+                return HANDLED;
+            }
+
+            /**
+             * Our final count will be at most the current count plus the
+             * remaining pings - we're guaranteed to fail.
+             */
+            if (remainingChecks + dnsCheckSuccesses < pingResponseCutoff) {
+                if (DBG) {
+                    Slog.d(WWSM_TAG, dnsCheckLogStr + "|  FAILURE");
+                }
+                transitionTo(mDnsCheckFailureState);
+                return HANDLED;
+            }
+
+            // Still in dns check step
+            sendCheckStepMessage(DNS_PING_INTERVAL_MS);
+            return HANDLED;
+        }
+
+        private boolean shouldCheckWalledGarden() {
+            if (!isWalledGardenTestEnabled()) {
+                if (VDBG)
+                    Slog.v(WWSM_TAG, "Skipping walled garden check - disabled");
+                return false;
+            }
+            long waitTime = waitTime(MIN_WALLED_GARDEN_INTERVAL_MS,
+                    mLastWalledGardenCheckTime);
+            if (waitTime > 0) {
+                if (DBG) {
+                    Slog.d(WWSM_TAG, "Skipping walled garden check - wait " +
+                            waitTime + " ms.");
+                }
+                return false;
+            }
+            return true;
+        }
+
+    }
+
+    class OnlineWatchState extends State {
+        /**
+         * Signals a short-wait message is enqueued for the current 'guard' counter
+         */
+        boolean unstableSignalChecks = false;
+
+        /**
+         * The signal is unstable.  We should enqueue a short-wait check, if one is enqueued
+         * already
+         */
+        boolean signalUnstable = false;
+
+        /**
+         * A monotonic counter to ensure that at most one check message will be processed from any
+         * set of check messages currently enqueued.  Avoids duplicate checks when a low-signal
+         * event is observed.
+         */
+        int checkGuard = 0;
+        Long lastCheckTime = null;
+
+        @Override
+        public void enter() {
+            lastCheckTime = SystemClock.elapsedRealtime();
+            signalUnstable = false;
+            checkGuard++;
+            unstableSignalChecks = false;
+            triggerSingleDnsCheck();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_RSSI_CHANGE:
+                    if (msg.arg1 != mNetEventCounter) {
+                        if (DBG) {
+                            Slog.d(WWSM_TAG, "Rssi change message out of sync, ignoring");
+                        }
+                        return HANDLED;
+                    }
+                    int newRssi = msg.arg2;
+                    signalUnstable = !rssiStrengthAboveCutoff(newRssi);
+                    if (VDBG) {
+                        Slog.v(WWSM_TAG, "OnlineWatchState:: new rssi " + newRssi + " --> level " +
+                                WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
+                    }
+
+                    if (signalUnstable && !unstableSignalChecks) {
+                        if (VDBG) {
+                            Slog.v(WWSM_TAG, "Sending triggered check msg");
+                        }
+                        triggerSingleDnsCheck();
+                    }
+                    return HANDLED;
+                case MESSAGE_SINGLE_DNS_CHECK:
+                    if (msg.arg1 != checkGuard) {
+                        if (VDBG) {
+                            Slog.v(WWSM_TAG, "Single check msg out of sync, ignoring.");
+                        }
+                        return HANDLED;
+                    }
+                    lastCheckTime = SystemClock.elapsedRealtime();
+                    long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
+                            DNS_PING_TIMEOUT_MS);
+                    if (responseTime >= 0) {
+                        if (VDBG) {
+                            Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: "
+                                    + responseTime);
+                        }
+
+                        checkGuard++;
+                        unstableSignalChecks = false;
+                        triggerSingleDnsCheck();
+                    } else {
+                        if (DBG) {
+                            Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks.");
+                        }
+                        transitionTo(mDnsCheckingState);
+                    }
+                    return HANDLED;
+            }
+            return NOT_HANDLED;
+        }
+
+        /**
+         * Times a dns check with an interval based on {@link #curSignalStable}
+         */
+        private void triggerSingleDnsCheck() {
+            long waitInterval;
+            if (signalUnstable) {
+                waitInterval = MIN_LOW_SIGNAL_CHECK_INTERVAL_MS;
+                unstableSignalChecks = true;
+            } else {
+                waitInterval = MIN_SINGLE_DNS_CHECK_INTERVAL_MS;
+            }
+            sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
+                    waitTime(waitInterval, lastCheckTime));
+        }
+    }
+
+    class DnsCheckFailureState extends State {
+        @Override
+        public void enter() {
+            obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (msg.what != MESSAGE_HANDLE_BAD_AP) {
+                return NOT_HANDLED;
+            }
+
+            if (msg.arg1 != mNetEventCounter) {
+                if (VDBG) {
+                    Slog.v(WWSM_TAG, "Msg out of sync, ignoring...");
+                }
+                return HANDLED;
+            }
+
+            if (mDisableAPNextFailure || mNumFullDNSchecks >= MAX_CHECKS_PER_SSID) {
+                // TODO : Unban networks if they had low signal ?
+                Slog.i(WWSM_TAG, "Disabling current SSID " + wifiInfoToStr(mInitialConnInfo)
+                        + ".  " +
+                        "numChecks " + mNumFullDNSchecks + ", numAPs " + mBssids.size());
+                mWifiManager.disableNetwork(mInitialConnInfo.getNetworkId());
+                transitionTo(mNotConnectedState);
+            } else {
+                Slog.i(WWSM_TAG, "Blacklisting current BSSID.  " + wifiInfoToStr(mInitialConnInfo) +
+                        "numChecks " + mNumFullDNSchecks + ", numAPs " + mBssids.size());
+
+                mWifiManager.addToBlacklist(mInitialConnInfo.getBSSID());
+                mWifiManager.reassociate();
+                transitionTo(mBlacklistedApState);
+            }
+            return HANDLED;
+        }
+    }
+
+    class WalledGardenState extends State {
+        @Override
+        public void enter() {
+            obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
+                return NOT_HANDLED;
+            }
+
+            if (msg.arg1 != mNetEventCounter) {
+                if (VDBG) {
+                    Slog.v(WWSM_TAG, "WalledGardenState::Msg out of sync, ignoring...");
+                }
+                return HANDLED;
+            }
+            popUpBrowser();
+            transitionTo(mOnlineWatchState);
+            return HANDLED;
+        }
+    }
+
+    class BlacklistedApState extends State {
+        @Override
+        public void enter() {
+            mDisableAPNextFailure = true;
+            sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
+                    BLACKLIST_FOLLOWUP_INTERVAL_MS);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
+                return NOT_HANDLED;
+            }
+
+            if (msg.arg1 != mNetEventCounter) {
+                if (VDBG) {
+                    Slog.v(WWSM_TAG, "BlacklistedApState::Msg out of sync, ignoring...");
+                }
+                return HANDLED;
+            }
+
+            transitionTo(mDnsCheckingState);
+            return HANDLED;
+        }
+    }
+}