Merge "Revert "Fix bug #8194572 Chrome SIGBUS'es on launch around TextLayoutCacheKey"" into jb-mr2-dev
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 6fe358c..80df568 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -13,7 +13,9 @@
 #include <cutils/process_name.h>
 #include <cutils/memory.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <sys/personality.h>
 
+#include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 
@@ -128,8 +130,31 @@
     strlcpy(const_cast<char *>(argv0), newArgv0, strlen(argv0));
 }
 
-int main(int argc, const char* const argv[])
+int main(int argc, char* const argv[])
 {
+#ifdef __arm__
+    /*
+     * b/7188322 - Temporarily revert to the compat memory layout
+     * to avoid breaking third party apps.
+     *
+     * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
+     *
+     * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
+     * changes the kernel mapping from bottom up to top-down.
+     * This breaks some programs which improperly embed
+     * an out of date copy of Android's linker.
+     */
+    if (getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) {
+        int current = personality(0xFFFFFFFF);
+        if ((current & ADDR_COMPAT_LAYOUT) == 0) {
+            personality(current | ADDR_COMPAT_LAYOUT);
+            setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1);
+            execv("/system/bin/app_process", argv);
+            return -1;
+        }
+    }
+#endif
+
     // These are global variables in ProcessState.cpp
     mArgC = argc;
     mArgV = argv;
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 6aac723..f8b7a0c 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -18,9 +18,11 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentFilter;
 import android.content.BroadcastReceiver;
+import android.content.res.Resources;
 import android.database.SQLException;
 import android.os.Bundle;
 import android.os.Handler;
@@ -44,6 +46,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import com.android.internal.R;
 import com.google.android.collect.Maps;
 
 /**
@@ -1777,8 +1780,11 @@
                                     };
                                     // have many accounts, launch the chooser
                                     Intent intent = new Intent();
-                                    intent.setClassName("android",
-                                            "android.accounts.ChooseAccountActivity");
+                                    ComponentName componentName = ComponentName.unflattenFromString(
+                                            Resources.getSystem().getString(
+                                                    R.string.config_chooseAccountActivity));
+                                    intent.setClassName(componentName.getPackageName(),
+                                            componentName.getClassName());
                                     intent.putExtra(KEY_ACCOUNTS, accounts);
                                     intent.putExtra(KEY_ACCOUNT_MANAGER_RESPONSE,
                                             new AccountManagerResponse(chooseResponse));
@@ -1934,7 +1940,10 @@
             String[] addAccountRequiredFeatures,
             Bundle addAccountOptions) {
         Intent intent = new Intent();
-        intent.setClassName("android", "android.accounts.ChooseTypeAndAccountActivity");
+        ComponentName componentName = ComponentName.unflattenFromString(
+                Resources.getSystem().getString(R.string.config_chooseTypeAndAccountActivity));
+        intent.setClassName(componentName.getPackageName(),
+                componentName.getClassName());
         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST,
                 allowableAccounts);
         intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 3ba4f26..43c2392 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -373,4 +373,14 @@
     public void setDependencyMet(boolean met) {
         // not supported on this network
     }
+
+    @Override
+    public void addStackedLink(LinkProperties link) {
+        mLinkProperties.addStackedLink(link);
+    }
+
+    @Override
+    public void removeStackedLink(LinkProperties link) {
+        mLinkProperties.removeStackedLink(link);
+    }
 }
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index d59c7b8..1128230 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -54,7 +54,7 @@
         implements HandlerCaller.Callback {
     private static final String TAG = "InputMethodWrapper";
     private static final boolean DEBUG = false;
-    
+
     private static final int DO_DUMP = 1;
     private static final int DO_ATTACH_TOKEN = 10;
     private static final int DO_SET_INPUT_CONTEXT = 20;
@@ -284,12 +284,6 @@
                 flags, resultReceiver));
     }
 
-    @Override
-    public void removeSoftInputMessages() {
-        mCaller.removeMessages(DO_SHOW_SOFT_INPUT);
-        mCaller.removeMessages(DO_HIDE_SOFT_INPUT);
-    }
-
     public void changeInputMethodSubtype(InputMethodSubtype subtype) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
                 subtype));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 288ceff..5a9cde1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -39,7 +39,6 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
-import android.util.Slog;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -49,6 +48,7 @@
 import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.WindowManager.BadTokenException;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
@@ -352,7 +352,6 @@
          * Take care of attaching the given window token provided by the system.
          */
         public void attachToken(IBinder token) {
-            Slog.i(TAG, "attachToken: Existing token=" + mToken + " new token=" + token);
             if (mToken == null) {
                 mToken = token;
                 mWindow.setToken(token);
@@ -419,11 +418,16 @@
          * Handle a request by the system to show the soft input area.
          */
         public void showSoftInput(int flags, ResultReceiver resultReceiver) {
-            if (true || DEBUG) Slog.v(TAG, "showSoftInput()");
+            if (DEBUG) Log.v(TAG, "showSoftInput()");
             boolean wasVis = isInputViewShown();
             mShowInputFlags = 0;
             if (onShowInputRequested(flags, false)) {
-                showWindow(true);
+                try {
+                    showWindow(true);
+                } catch (BadTokenException e) {
+                    if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
+                    mWindowVisible = false;
+                }
             }
             // If user uses hard keyboard, IME button should always be shown.
             boolean showing = onEvaluateInputViewShown();
@@ -1390,7 +1394,7 @@
     }
     
     public void showWindow(boolean showInput) {
-        if (true || DEBUG) Slog.v(TAG, "Showing window: showInput=" + showInput
+        if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
                 + " mShowInputRequested=" + mShowInputRequested
                 + " mWindowAdded=" + mWindowAdded
                 + " mWindowCreated=" + mWindowCreated
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index 4b60f07..a554611 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -155,4 +155,14 @@
     public void setDependencyMet(boolean met) {
         // Base tracker doesn't handle dependencies
     }
+
+    @Override
+    public void addStackedLink(LinkProperties link) {
+        mLinkProperties.addStackedLink(link);
+    }
+
+    @Override
+    public void removeStackedLink(LinkProperties link) {
+        mLinkProperties.removeStackedLink(link);
+    }
 }
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 518dd4b..4194c9d 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -50,7 +50,7 @@
 public class DhcpStateMachine extends StateMachine {
 
     private static final String TAG = "DhcpStateMachine";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
 
 
     /* A StateMachine that controls the DhcpStateMachine */
@@ -359,7 +359,7 @@
         } else if (dhcpAction == DhcpAction.RENEW) {
             if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
             success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpResults);
-            dhcpResults.updateFromDhcpRequest(mDhcpResults);
+            if (success) dhcpResults.updateFromDhcpRequest(mDhcpResults);
         }
         if (success) {
             if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index 39440c2..db8f0bc 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -203,6 +203,16 @@
         // not supported on this network
     }
 
+    @Override
+    public void addStackedLink(LinkProperties link) {
+        mLinkProperties.addStackedLink(link);
+    }
+
+    @Override
+    public void removeStackedLink(LinkProperties link) {
+        mLinkProperties.removeStackedLink(link);
+    }
+
     static private void log(String s) {
         Slog.d(TAG, s);
     }
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 8947162..b744a47 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -407,4 +407,14 @@
     public void setDependencyMet(boolean met) {
         // not supported on this network
     }
+
+    @Override
+    public void addStackedLink(LinkProperties link) {
+        mLinkProperties.addStackedLink(link);
+    }
+
+    @Override
+    public void removeStackedLink(LinkProperties link) {
+        mLinkProperties.removeStackedLink(link);
+    }
 }
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 9292e5f..7044d39 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -23,10 +23,13 @@
 import android.util.Log;
 
 import java.net.InetAddress;
+import java.net.Inet4Address;
+
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Hashtable;
 
 /**
  * Describes the properties of a network link.
@@ -48,10 +51,15 @@
  * don't care which is used.  The gateways will be
  * selected based on the destination address and the
  * source address has no relavence.
+ *
+ * Links can also be stacked on top of each other.
+ * This can be used, for example, to represent a tunnel
+ * interface that runs on top of a physical interface.
+ *
  * @hide
  */
 public class LinkProperties implements Parcelable {
-
+    // The interface described by the network link.
     private String mIfaceName;
     private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
     private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
@@ -60,6 +68,11 @@
     private ProxyProperties mHttpProxy;
     public boolean mLogMe;
 
+    // Stores the properties of links that are "stacked" above this link.
+    // Indexed by interface name to allow modification and to prevent duplicates being added.
+    private Hashtable<String, LinkProperties> mStackedLinks =
+        new Hashtable<String, LinkProperties>();
+
     public static class CompareResult<T> {
         public Collection<T> removed = new ArrayList<T>();
         public Collection<T> added = new ArrayList<T>();
@@ -90,6 +103,9 @@
             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
             mHttpProxy = (source.getHttpProxy() == null)  ?
                     null : new ProxyProperties(source.getHttpProxy());
+            for (LinkProperties l: source.mStackedLinks.values()) {
+                addStackedLink(l);
+            }
         }
     }
 
@@ -165,10 +181,25 @@
         }
     }
 
+    /**
+     * Returns all the routes on this link.
+     */
     public Collection<RouteInfo> getRoutes() {
         return Collections.unmodifiableCollection(mRoutes);
     }
 
+    /**
+     * Returns all the routes on this link and all the links stacked above it.
+     */
+    public Collection<RouteInfo> getAllRoutes() {
+        Collection<RouteInfo> routes = new ArrayList();
+        routes.addAll(mRoutes);
+        for (LinkProperties stacked: mStackedLinks.values()) {
+            routes.addAll(stacked.getAllRoutes());
+        }
+        return routes;
+    }
+
     public void setHttpProxy(ProxyProperties proxy) {
         mHttpProxy = proxy;
     }
@@ -176,6 +207,46 @@
         return mHttpProxy;
     }
 
+    /**
+     * Adds a stacked link.
+     *
+     * If there is already a stacked link with the same interfacename as link,
+     * that link is replaced with link. Otherwise, link is added to the list
+     * of stacked links. If link is null, nothing changes.
+     *
+     * @param link The link to add.
+     */
+    public void addStackedLink(LinkProperties link) {
+        if (link != null && link.getInterfaceName() != null) {
+            mStackedLinks.put(link.getInterfaceName(), link);
+        }
+    }
+
+    /**
+     * Removes a stacked link.
+     *
+     * If there a stacked link with the same interfacename as link, it is
+     * removed. Otherwise, nothing changes.
+     *
+     * @param link The link to add.
+     */
+    public void removeStackedLink(LinkProperties link) {
+        if (link != null && link.getInterfaceName() != null) {
+            mStackedLinks.remove(link.getInterfaceName());
+        }
+    }
+
+    /**
+     * Returns all the links stacked on top of this link.
+     */
+    public Collection<LinkProperties> getStackedLinks() {
+        Collection<LinkProperties> stacked = new ArrayList<LinkProperties>();
+        for (LinkProperties link : mStackedLinks.values()) {
+          stacked.add(new LinkProperties(link));
+        }
+        return Collections.unmodifiableCollection(stacked);
+    }
+
     public void clear() {
         if (mLogMe) {
             Log.d("LinkProperties", "clear from " + mIfaceName);
@@ -190,6 +261,7 @@
         mDomains = null;
         mRoutes.clear();
         mHttpProxy = null;
+        mStackedLinks.clear();
     }
 
     /**
@@ -219,7 +291,29 @@
         routes += "] ";
         String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
 
-        return ifaceName + linkAddresses + routes + dns + domainName + proxy;
+        String stacked = "";
+        if (mStackedLinks.values().size() > 0) {
+            stacked += " Stacked: [";
+            for (LinkProperties link: mStackedLinks.values()) {
+                stacked += " [" + link.toString() + " ],";
+            }
+            stacked += "] ";
+        }
+        return ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked;
+    }
+
+    /**
+     * Returns true if this link has an IPv4 address.
+     *
+     * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+     */
+    public boolean hasIPv4Address() {
+        for (LinkAddress address : mLinkAddresses) {
+          if (address.getAddress() instanceof Inet4Address) {
+            return true;
+          }
+        }
+        return false;
     }
 
     /**
@@ -286,6 +380,26 @@
                     getHttpProxy().equals(target.getHttpProxy());
     }
 
+    /**
+     * Compares this {@code LinkProperties} stacked links against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     */
+    public boolean isIdenticalStackedLinks(LinkProperties target) {
+        if (!mStackedLinks.keys().equals(target.mStackedLinks.keys())) {
+            return false;
+        }
+        for (LinkProperties stacked : mStackedLinks.values()) {
+            // Hashtable values can never be null.
+            String iface = stacked.getInterfaceName();
+            if (!stacked.equals(target.mStackedLinks.get(iface))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Override
     /**
      * Compares this {@code LinkProperties} instance against the target
@@ -298,6 +412,10 @@
      * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
      * 2. Worst case performance is O(n^2).
      *
+     * This method does not check that stacked interfaces are equal, because
+     * stacked interfaces are not so much a property of the link as a
+     * description of connections between links.
+     *
      * @param obj the object to be tested for equality.
      * @return {@code true} if both objects are equal, {@code false} otherwise.
      */
@@ -312,7 +430,8 @@
                 isIdenticalAddresses(target) &&
                 isIdenticalDnses(target) &&
                 isIdenticalRoutes(target) &&
-                isIdenticalHttpProxy(target);
+                isIdenticalHttpProxy(target) &&
+                isIdenticalStackedLinks(target);
     }
 
     /**
@@ -394,10 +513,10 @@
          */
         CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
 
-        result.removed = new ArrayList<RouteInfo>(mRoutes);
+        result.removed = getAllRoutes();
         result.added.clear();
         if (target != null) {
-            for (RouteInfo r : target.getRoutes()) {
+            for (RouteInfo r : target.getAllRoutes()) {
                 if (! result.removed.remove(r)) {
                     result.added.add(r);
                 }
@@ -419,7 +538,8 @@
                 + mDnses.size() * 37
                 + ((null == mDomains) ? 0 : mDomains.hashCode())
                 + mRoutes.size() * 41
-                + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
+                + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode())
+                + mStackedLinks.hashCode() * 47);
     }
 
     /**
@@ -449,6 +569,8 @@
         } else {
             dest.writeByte((byte)0);
         }
+        ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values());
+        dest.writeList(stackedLinks);
     }
 
     /**
@@ -481,6 +603,11 @@
                 if (in.readByte() == 1) {
                     netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
                 }
+                ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
+                in.readList(stackedLinks, LinkProperties.class.getClassLoader());
+                for (LinkProperties stackedLink: stackedLinks) {
+                    netProp.addStackedLink(stackedLink);
+                }
                 return netProp;
             }
 
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index b35d61c..faf739b 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -522,6 +522,16 @@
     }
 
     @Override
+    public void addStackedLink(LinkProperties link) {
+        mLinkProperties.addStackedLink(link);
+    }
+
+    @Override
+    public void removeStackedLink(LinkProperties link) {
+        mLinkProperties.removeStackedLink(link);
+    }
+
+    @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
         final PrintWriter pw = new PrintWriter(writer);
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 0a0c1e0..b22159c 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -197,4 +197,14 @@
      * An external dependency has been met/unmet
      */
     public void setDependencyMet(boolean met);
+
+    /**
+     * Informs the state tracker that another interface is stacked on top of it.
+     **/
+    public void addStackedLink(LinkProperties link);
+
+    /**
+     * Informs the state tracker that a stacked interface has been removed.
+     **/
+    public void removeStackedLink(LinkProperties link);
 }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 2bec1c1..9666d9a 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import android.util.Log;
+
 import java.io.BufferedInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -25,6 +27,8 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
@@ -34,6 +38,8 @@
  * @hide
  */
 public class FileUtils {
+    private static final String TAG = "FileUtils";
+
     public static final int S_IRWXU = 00700;
     public static final int S_IRUSR = 00400;
     public static final int S_IWUSR = 00200;
@@ -161,7 +167,8 @@
             } else if (max < 0) {  // "tail" mode: keep the last N
                 int len;
                 boolean rolled = false;
-                byte[] last = null, data = null;
+                byte[] last = null;
+                byte[] data = null;
                 do {
                     if (last != null) rolled = true;
                     byte[] tmp = last; last = data; data = tmp;
@@ -237,4 +244,40 @@
             }
         }
     }
+
+    /**
+     * Delete older files in a directory until only those matching the given
+     * constraints remain.
+     *
+     * @param minCount Always keep at least this many files.
+     * @param minAge Always keep files younger than this age.
+     */
+    public static void deleteOlderFiles(File dir, int minCount, long minAge) {
+        if (minCount < 0 || minAge < 0) {
+            throw new IllegalArgumentException("Constraints must be positive or 0");
+        }
+
+        final File[] files = dir.listFiles();
+        if (files == null) return;
+
+        // Sort with newest files first
+        Arrays.sort(files, new Comparator<File>() {
+            @Override
+            public int compare(File lhs, File rhs) {
+                return (int) (rhs.lastModified() - lhs.lastModified());
+            }
+        });
+
+        // Keep at least minCount files
+        for (int i = minCount; i < files.length; i++) {
+            final File file = files[i];
+
+            // Keep files newer than minAge
+            final long age = System.currentTimeMillis() - file.lastModified();
+            if (age > minAge) {
+                Log.d(TAG, "Deleting old file " + file);
+                file.delete();
+            }
+        }
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 266d0d3..d251ca2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4784,6 +4784,13 @@
        public static final String WIFI_ON = "wifi_on";
 
        /**
+        * Setting to allow scans to be enabled even wifi is turned off for connectivity.
+        * @hide
+        */
+       public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
+                "wifi_scan_always_enabled";
+
+       /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
         * the user turns off tethering.
@@ -5345,6 +5352,7 @@
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
             WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
             WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
+            WIFI_SCAN_ALWAYS_AVAILABLE,
             WIFI_NUM_OPEN_NETWORKS_KEPT,
             EMERGENCY_TONE,
             CALL_AUTO_RETRY,
diff --git a/core/java/android/text/bidi/BidiFormatter.java b/core/java/android/text/bidi/BidiFormatter.java
index 370cbf7..c5a77a6 100644
--- a/core/java/android/text/bidi/BidiFormatter.java
+++ b/core/java/android/text/bidi/BidiFormatter.java
@@ -172,9 +172,9 @@
      * A class for building a BidiFormatter with non-default options.
      */
     public static final class Builder {
-        private boolean isRtlContext;
-        private int flags;
-        private TextDirectionHeuristic textDirectionHeuristic;
+        private boolean mIsRtlContext;
+        private int mFlags;
+        private TextDirectionHeuristic mTextDirectionHeuristic;
 
         /**
          * Constructor.
@@ -208,9 +208,9 @@
          * @param isRtlContext Whether the context is RTL or not.
          */
         private void initialize(boolean isRtlContext) {
-            this.isRtlContext = isRtlContext;
-            textDirectionHeuristic = DEFAULT_TEXT_DIRECTION_HEURISTIC;
-            this.flags = DEFAULT_FLAGS;
+            mIsRtlContext = isRtlContext;
+            mTextDirectionHeuristic = DEFAULT_TEXT_DIRECTION_HEURISTIC;
+            mFlags = DEFAULT_FLAGS;
         }
 
         /**
@@ -219,9 +219,9 @@
          */
         public Builder stereoReset(boolean stereoReset) {
             if (stereoReset) {
-                flags |= FLAG_STEREO_RESET;
+                mFlags |= FLAG_STEREO_RESET;
             } else {
-                flags &= ~FLAG_STEREO_RESET;
+                mFlags &= ~FLAG_STEREO_RESET;
             }
             return this;
         }
@@ -234,7 +234,7 @@
          * @return the builder itself.
          */
         public Builder setTextDirectionHeuristic(TextDirectionHeuristic heuristic) {
-            this.textDirectionHeuristic = heuristic;
+            mTextDirectionHeuristic = heuristic;
             return this;
         }
 
@@ -246,11 +246,11 @@
          * @return A BidiFormatter with the specified options.
          */
         public BidiFormatter build() {
-            if (flags == DEFAULT_FLAGS &&
-                    textDirectionHeuristic == DEFAULT_TEXT_DIRECTION_HEURISTIC) {
-                return getDefaultInstanceFromContext(isRtlContext);
+            if (mFlags == DEFAULT_FLAGS &&
+                    mTextDirectionHeuristic == DEFAULT_TEXT_DIRECTION_HEURISTIC) {
+                return getDefaultInstanceFromContext(mIsRtlContext);
             }
-            return new BidiFormatter(isRtlContext, flags, textDirectionHeuristic);
+            return new BidiFormatter(mIsRtlContext, mFlags, mTextDirectionHeuristic);
         }
     }
 
@@ -268,9 +268,9 @@
             DEFAULT_FLAGS,
             DEFAULT_TEXT_DIRECTION_HEURISTIC);
 
-    private final boolean isRtlContext;
-    private final int flags;
-    private final TextDirectionHeuristic defaultTextDirectionHeuristic;
+    private final boolean mIsRtlContext;
+    private final int mFlags;
+    private final TextDirectionHeuristic mDefaultTextDirectionHeuristic;
 
     /**
      * Factory for creating an instance of BidiFormatter given the context directionality.
@@ -296,16 +296,16 @@
      * @param heuristic The default text direction heuristic.
      */
     private BidiFormatter(boolean isRtlContext, int flags, TextDirectionHeuristic heuristic) {
-        this.isRtlContext = isRtlContext;
-        this.flags = flags;
-        this.defaultTextDirectionHeuristic = heuristic;
+        mIsRtlContext = isRtlContext;
+        mFlags = flags;
+        mDefaultTextDirectionHeuristic = heuristic;
     }
 
     /**
      * @return Whether the context directionality is RTL
      */
     public boolean isRtlContext() {
-        return isRtlContext;
+        return mIsRtlContext;
     }
 
     /**
@@ -313,7 +313,7 @@
      * bidi-wrapped, not just after it.
      */
     public boolean getStereoReset() {
-        return (flags & FLAG_STEREO_RESET) != 0;
+        return (mFlags & FLAG_STEREO_RESET) != 0;
     }
 
     /**
@@ -384,7 +384,7 @@
      *     context; else, the empty string.
      */
     public String dirAttr(boolean isRtl) {
-        return (isRtl != isRtlContext) ? (isRtl ? DIR_RTL_STRING :  DIR_LTR_STRING) : EMPTY_STRING;
+        return (isRtl != mIsRtlContext) ? (isRtl ? DIR_RTL_STRING :  DIR_LTR_STRING) : EMPTY_STRING;
     }
 
     /**
@@ -401,7 +401,7 @@
      *     else, the empty string.
      */
     public String markAfter(String str) {
-        return markAfter(str, defaultTextDirectionHeuristic);
+        return markAfter(str, mDefaultTextDirectionHeuristic);
     }
 
     /**
@@ -417,10 +417,10 @@
     public String markAfter(String str, TextDirectionHeuristic heuristic) {
         final boolean isRtl = heuristic.isRtl(str, 0, str.length());
         // getExitDir() is called only if needed (short-circuit).
-        if (!isRtlContext && (isRtl || getExitDir(str) == DIR_RTL)) {
+        if (!mIsRtlContext && (isRtl || getExitDir(str) == DIR_RTL)) {
             return LRM_STRING;
         }
-        if (isRtlContext && (!isRtl || getExitDir(str) == DIR_LTR)) {
+        if (mIsRtlContext && (!isRtl || getExitDir(str) == DIR_LTR)) {
             return RLM_STRING;
         }
         return EMPTY_STRING;
@@ -440,7 +440,7 @@
      *     else, the empty string.
      */
     public String markBefore(String str) {
-        return markBefore(str, defaultTextDirectionHeuristic);
+        return markBefore(str, mDefaultTextDirectionHeuristic);
     }
 
     /**
@@ -456,10 +456,10 @@
     public String markBefore(String str, TextDirectionHeuristic heuristic) {
         final boolean isRtl = heuristic.isRtl(str, 0, str.length());
         // getEntryDir() is called only if needed (short-circuit).
-        if (!isRtlContext && (isRtl || getEntryDir(str) == DIR_RTL)) {
+        if (!mIsRtlContext && (isRtl || getEntryDir(str) == DIR_RTL)) {
             return LRM_STRING;
         }
-        if (isRtlContext && (!isRtl || getEntryDir(str) == DIR_LTR)) {
+        if (mIsRtlContext && (!isRtl || getEntryDir(str) == DIR_LTR)) {
             return RLM_STRING;
         }
         return EMPTY_STRING;
@@ -470,7 +470,7 @@
      * directionality, RLM for RTL context directionality).
      */
     public String mark() {
-        return isRtlContext ? RLM_STRING : LRM_STRING;
+        return mIsRtlContext ? RLM_STRING : LRM_STRING;
     }
 
     /**
@@ -478,7 +478,7 @@
      * returns "left".
      */
     public String startEdge() {
-        return isRtlContext  ? RIGHT : LEFT;
+        return mIsRtlContext ? RIGHT : LEFT;
     }
 
     /**
@@ -486,7 +486,7 @@
      * returns "right".
      */
     public String endEdge() {
-        return isRtlContext ? LEFT : RIGHT;
+        return mIsRtlContext ? LEFT : RIGHT;
     }
 
     /**
@@ -497,7 +497,7 @@
      *          false.
      */
     public boolean isRtl(String str) {
-        return defaultTextDirectionHeuristic.isRtl(str, 0, str.length());
+        return mDefaultTextDirectionHeuristic.isRtl(str, 0, str.length());
     }
 
     /**
@@ -536,7 +536,7 @@
             result.append(markBefore(origStr,
                     isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
         }
-        if (isRtl != isRtlContext) {
+        if (isRtl != mIsRtlContext) {
             result.append("<span ").append(dirAttr(isRtl)).append('>').append(str).append("</span>");
         } else {
             result.append(str);
@@ -570,7 +570,7 @@
      * @return Input string after applying the above processing.
      */
     public String spanWrap(String str, boolean isolate) {
-        return spanWrap(str, defaultTextDirectionHeuristic, isolate);
+        return spanWrap(str, mDefaultTextDirectionHeuristic, isolate);
     }
 
     /**
@@ -581,7 +581,7 @@
      * @return Input string after applying the above processing.
      */
     public String spanWrap(String str) {
-        return spanWrap(str, defaultTextDirectionHeuristic, true /* isolate */);
+        return spanWrap(str, mDefaultTextDirectionHeuristic, true /* isolate */);
     }
 
     /**
@@ -620,7 +620,7 @@
             result.append(markBefore(str,
                     isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR));
         }
-        if (isRtl != isRtlContext) {
+        if (isRtl != mIsRtlContext) {
             result.append(isRtl ? RLE : LRE);
             result.append(str);
             result.append(PDF);
@@ -656,7 +656,7 @@
      * @return Input string after applying the above processing.
      */
     public String unicodeWrap(String str, boolean isolate) {
-        return unicodeWrap(str, defaultTextDirectionHeuristic, isolate);
+        return unicodeWrap(str, mDefaultTextDirectionHeuristic, isolate);
     }
 
     /**
@@ -667,7 +667,7 @@
      * @return Input string after applying the above processing.
      */
     public String unicodeWrap(String str) {
-        return unicodeWrap(str, defaultTextDirectionHeuristic, true /* isolate */);
+        return unicodeWrap(str, mDefaultTextDirectionHeuristic, true /* isolate */);
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a937882..8808af0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1947,20 +1947,16 @@
             // Would not normally trigger another layout, so just let it pass through as usual
             return true;
         }
+        if (!mLayoutRequesters.contains(view)) {
+            mLayoutRequesters.add(view);
+        }
         if (!mHandlingLayoutInLayoutRequest) {
-            if (!mLayoutRequesters.contains(view)) {
-                mLayoutRequesters.add(view);
-            }
+            // Let the request proceed normally; it will be processed in a second layout pass
+            // if necessary
             return true;
         } else {
-            Log.w("View", "requestLayout() called by " + view + " during second layout pass: " +
-                    "posting to next frame");
-            view.post(new Runnable() {
-                @Override
-                public void run() {
-                    view.requestLayout();
-                }
-            });
+            // Don't let the request proceed during the second layout pass.
+            // It will post to the next frame instead.
             return false;
         }
     }
@@ -1988,59 +1984,50 @@
                 // If no layout-request flags are set on the requesting views, there is no problem.
                 // If some requests are still pending, then we need to clear those flags and do
                 // a full request/measure/layout pass to handle this situation.
-
-                // Check state of layout flags for all requesters
-                ArrayList<View> mValidLayoutRequesters = null;
-                for (int i = 0; i < numViewsRequestingLayout; ++i) {
-                    View view = mLayoutRequesters.get(i);
-                    if ((view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) == View.PFLAG_FORCE_LAYOUT) {
-                        while (view != null && view.mAttachInfo != null && view.mParent != null &&
-                                (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) {
-                            if ((view.mViewFlags & View.VISIBILITY_MASK) != View.GONE) {
-                                // Only trigger new requests for non-GONE views
-                                Log.w(TAG, "requestLayout() improperly called during " +
-                                        "layout: running second layout pass for " + view);
-                                if (mValidLayoutRequesters == null) {
-                                    mValidLayoutRequesters = new ArrayList<View>();
-                                }
-                                mValidLayoutRequesters.add(view);
-                                break;
-                            }
-                            if (view.mParent instanceof View) {
-                                view = (View) view.mParent;
-                            } else {
-                                view = null;
-                            }
-                        }
-                    }
-                }
-                if (mValidLayoutRequesters != null) {
-                    // Clear flags throughout hierarchy, walking up from each flagged requester
-                    for (int i = 0; i < numViewsRequestingLayout; ++i) {
-                        View view = mLayoutRequesters.get(i);
-                        while (view != null &&
-                                (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) {
-                            view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
-                            if (view.mParent instanceof View) {
-                                view = (View) view.mParent;
-                            } else {
-                                view = null;
-                            }
-                        }
-                    }
-                    // Process fresh layout requests, then measure and layout
+                ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
+                        false);
+                if (validLayoutRequesters != null) {
+                    // Set this flag to indicate that any further requests are happening during
+                    // the second pass, which may result in posting those requests to the next
+                    // frame instead
                     mHandlingLayoutInLayoutRequest = true;
-                    int numValidRequests = mValidLayoutRequesters.size();
+
+                    // Process fresh layout requests, then measure and layout
+                    int numValidRequests = validLayoutRequesters.size();
                     for (int i = 0; i < numValidRequests; ++i) {
-                        mValidLayoutRequesters.get(i).requestLayout();
+                        final View view = validLayoutRequesters.get(i);
+                        Log.w("View", "requestLayout() improperly called by " + view +
+                                " during layout: running second layout pass");
+                        view.requestLayout();
                     }
                     measureHierarchy(host, lp, mView.getContext().getResources(),
                             desiredWindowWidth, desiredWindowHeight);
                     mInLayout = true;
                     host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
+
                     mHandlingLayoutInLayoutRequest = false;
+
+                    // Check the valid requests again, this time without checking/clearing the
+                    // layout flags, since requests happening during the second pass get noop'd
+                    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
+                    if (validLayoutRequesters != null) {
+                        final ArrayList<View> finalRequesters = validLayoutRequesters;
+                        // Post second-pass requests to the next frame
+                        getRunQueue().post(new Runnable() {
+                            @Override
+                            public void run() {
+                                int numValidRequests = finalRequesters.size();
+                                for (int i = 0; i < numValidRequests; ++i) {
+                                    final View view = finalRequesters.get(i);
+                                    Log.w("View", "requestLayout() improperly called by " + view +
+                                            " during second layout pass: posting in next frame");
+                                    view.requestLayout();
+                                }
+                            }
+                        });
+                    }
                 }
-                mLayoutRequesters.clear();
+
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
@@ -2048,6 +2035,68 @@
         mInLayout = false;
     }
 
+    /**
+     * This method is called during layout when there have been calls to requestLayout() during
+     * layout. It walks through the list of views that requested layout to determine which ones
+     * still need it, based on visibility in the hierarchy and whether they have already been
+     * handled (as is usually the case with ListView children).
+     *
+     * @param layoutRequesters The list of views that requested layout during layout
+     * @param secondLayoutRequests Whether the requests were issued during the second layout pass.
+     * If so, the FORCE_LAYOUT flag was not set on requesters.
+     * @return A list of the actual views that still need to be laid out.
+     */
+    private ArrayList<View> getValidLayoutRequesters(ArrayList<View> layoutRequesters,
+            boolean secondLayoutRequests) {
+
+        int numViewsRequestingLayout = layoutRequesters.size();
+        ArrayList<View> validLayoutRequesters = null;
+        for (int i = 0; i < numViewsRequestingLayout; ++i) {
+            View view = layoutRequesters.get(i);
+            if (view != null && view.mAttachInfo != null && view.mParent != null &&
+                    (secondLayoutRequests || (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) ==
+                            View.PFLAG_FORCE_LAYOUT)) {
+                boolean gone = false;
+                View parent = view;
+                // Only trigger new requests for views in a non-GONE hierarchy
+                while (parent != null) {
+                    if ((parent.mViewFlags & View.VISIBILITY_MASK) == View.GONE) {
+                        gone = true;
+                        break;
+                    }
+                    if (parent.mParent instanceof View) {
+                        parent = (View) parent.mParent;
+                    } else {
+                        parent = null;
+                    }
+                }
+                if (!gone) {
+                    if (validLayoutRequesters == null) {
+                        validLayoutRequesters = new ArrayList<View>();
+                    }
+                    validLayoutRequesters.add(view);
+                }
+            }
+        }
+        if (!secondLayoutRequests) {
+            // If we're checking the layout flags, then we need to clean them up also
+            for (int i = 0; i < numViewsRequestingLayout; ++i) {
+                View view = layoutRequesters.get(i);
+                while (view != null &&
+                        (view.mPrivateFlags & View.PFLAG_FORCE_LAYOUT) != 0) {
+                    view.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
+                    if (view.mParent instanceof View) {
+                        view = (View) view.mParent;
+                    } else {
+                        view = null;
+                    }
+                }
+            }
+        }
+        layoutRequesters.clear();
+        return validLayoutRequesters;
+    }
+
     public void requestTransparentRegion(View child) {
         // the test below should not fail unless someone is messing with us
         checkThread();
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 7c2b1b5..91b109e 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -45,6 +45,7 @@
     public static final int BASE_WIFI_P2P_SERVICE                                   = 0x00023000;
     public static final int BASE_WIFI_MONITOR                                       = 0x00024000;
     public static final int BASE_WIFI_MANAGER                                       = 0x00025000;
+    public static final int BASE_WIFI_CONTROLLER                                    = 0x00026000;
     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/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index e547f23..2cdd579 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1549,6 +1549,24 @@
      *
      * @param what  is assigned to Message.what
      * @param arg1  is assigned to Message.arg1
+     * @return  A Message object from the global pool
+     */
+    public final Message obtainMessage(int what, int arg1) {
+        // use this obtain so we don't match the obtain(h, what, Object) method
+        return Message.obtain(mSmHandler, what, arg1, 0);
+    }
+
+    /**
+     * Get a message and set Message.target state machine handler,
+     * what, arg1 and arg2
+     *
+     * Note: The handler can be null if the state machine has quit,
+     * which means target will be null and may cause a AndroidRuntimeException
+     * in MessageQueue#enqueMessage if sent directly or if sent using
+     * StateMachine#sendMessage the message will just be ignored.
+     *
+     * @param what  is assigned to Message.what
+     * @param arg1  is assigned to Message.arg1
      * @param arg2  is assigned to Message.arg2
      * @return  A Message object from the global pool
      */
@@ -1606,6 +1624,32 @@
      *
      * Message is ignored if state machine has quit.
      */
+    public final void sendMessage(int what, int arg1) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessage(obtainMessage(what, arg1));
+    }
+
+    /**
+     * Enqueue a message to this state machine.
+     *
+     * Message is ignored if state machine has quit.
+     */
+    public final void sendMessage(int what, int arg1, int arg2) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessage(obtainMessage(what, arg1, arg2));
+    }
+
+    /**
+     * Enqueue a message to this state machine.
+     *
+     * Message is ignored if state machine has quit.
+     */
     public final void sendMessage(int what, int arg1, int arg2, Object obj) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
@@ -1658,6 +1702,32 @@
      *
      * Message is ignored if state machine has quit.
      */
+    public final void sendMessageDelayed(int what, int arg1, long delayMillis) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessageDelayed(obtainMessage(what, arg1), delayMillis);
+    }
+
+    /**
+     * Enqueue a message to this state machine after a delay.
+     *
+     * Message is ignored if state machine has quit.
+     */
+    public final void sendMessageDelayed(int what, int arg1, int arg2, long delayMillis) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessageDelayed(obtainMessage(what, arg1, arg2), delayMillis);
+    }
+
+    /**
+     * Enqueue a message to this state machine after a delay.
+     *
+     * Message is ignored if state machine has quit.
+     */
     public final void sendMessageDelayed(int what, int arg1, int arg2, Object obj,
             long delayMillis) {
         // mSmHandler can be null if the state machine has quit.
@@ -1686,6 +1756,20 @@
      *
      * Message is ignored if state machine has quit.
      */
+    protected final void sendMessageAtFrontOfQueue(int what) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessageAtFrontOfQueue(obtainMessage(what));
+    }
+
+    /**
+     * Enqueue a message to the front of the queue for this state machine.
+     * Protected, may only be called by instances of StateMachine.
+     *
+     * Message is ignored if state machine has quit.
+     */
     protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
@@ -1700,12 +1784,27 @@
      *
      * Message is ignored if state machine has quit.
      */
-    protected final void sendMessageAtFrontOfQueue(int what) {
+    protected final void sendMessageAtFrontOfQueue(int what, int arg1) {
         // mSmHandler can be null if the state machine has quit.
         SmHandler smh = mSmHandler;
         if (smh == null) return;
 
-        smh.sendMessageAtFrontOfQueue(obtainMessage(what));
+        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1));
+    }
+
+
+    /**
+     * Enqueue a message to the front of the queue for this state machine.
+     * Protected, may only be called by instances of StateMachine.
+     *
+     * Message is ignored if state machine has quit.
+     */
+    protected final void sendMessageAtFrontOfQueue(int what, int arg1, int arg2) {
+        // mSmHandler can be null if the state machine has quit.
+        SmHandler smh = mSmHandler;
+        if (smh == null) return;
+
+        smh.sendMessageAtFrontOfQueue(obtainMessage(what, arg1, arg2));
     }
 
     /**
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index c2a7fc7..5db860b 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -33,28 +33,26 @@
  * Service).
  * {@hide}
  */
-interface IInputMethod {
-    oneway void attachToken(IBinder token);
-    
-    oneway void bindInput(in InputBinding binding);
-    
-    oneway void unbindInput();
+oneway interface IInputMethod {
+    void attachToken(IBinder token);
 
-    oneway void startInput(in IInputContext inputContext, in EditorInfo attribute);
+    void bindInput(in InputBinding binding);
 
-    oneway void restartInput(in IInputContext inputContext, in EditorInfo attribute);
+    void unbindInput();
 
-    oneway void createSession(IInputMethodCallback callback);
-    
-    oneway void setSessionEnabled(IInputMethodSession session, boolean enabled);
-    
-    oneway void revokeSession(IInputMethodSession session);
-    
-    oneway void showSoftInput(int flags, in ResultReceiver resultReceiver);
-    
-    oneway void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+    void startInput(in IInputContext inputContext, in EditorInfo attribute);
 
-    void removeSoftInputMessages();
+    void restartInput(in IInputContext inputContext, in EditorInfo attribute);
 
-    oneway void changeInputMethodSubtype(in InputMethodSubtype subtype);
+    void createSession(IInputMethodCallback callback);
+
+    void setSessionEnabled(IInputMethodSession session, boolean enabled);
+
+    void revokeSession(IInputMethodSession session);
+
+    void showSoftInput(int flags, in ResultReceiver resultReceiver);
+
+    void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+
+    void changeInputMethodSubtype(in InputMethodSubtype subtype);
 }
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index f5f22b2..faae11e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -136,6 +136,10 @@
         result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
                 dns, server, &lease, vendorInfo, domains);
     }
+    if (result != 0) {
+        ALOGD("dhcp_do_request failed");
+    }
+
     env->ReleaseStringUTFChars(ifname, nameStr);
     if (result == 0) {
         env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.clear);
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ccdddd8..6a8407f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1031,4 +1031,14 @@
 
     <!-- Flag indicating if the speed up audio on mt call code should be executed -->
     <bool name="config_speed_up_audio_on_mt_calls">false</bool>
+
+    <!-- Class name of the framework account picker activity.
+         Can be customized for other product types -->
+    <string name="config_chooseAccountActivity"
+            >android/android.accounts.ChooseAccountActivity</string>
+    <!-- Class name of the account type and account picker activity.
+         Can be customized for other product types -->
+    <string name="config_chooseTypeAndAccountActivity"
+            >android/android.accounts.ChooseTypeAndAccountActivity</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 140ff70..d57d56a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -863,6 +863,9 @@
   <java-symbol type="string" name="media_route_status_available" />
   <java-symbol type="string" name="media_route_status_not_available" />
   <java-symbol type="string" name="owner_name" />
+  <java-symbol type="string" name="config_chooseAccountActivity" />
+  <java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
+
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index fffaa00..274ac6b 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -22,6 +22,7 @@
 import junit.framework.TestCase;
 
 import java.net.InetAddress;
+import java.util.ArrayList;
 
 public class LinkPropertiesTest extends TestCase {
     private static String ADDRV4 = "75.208.6.1";
@@ -256,4 +257,30 @@
         assertEquals(3, lp.compareRoutes(lp2).added.size());
         assertEquals(3, lp.compareRoutes(lp2).removed.size());
     }
+
+    @SmallTest
+    public void testStackedInterfaces() {
+        LinkProperties rmnet0 = new LinkProperties();
+        rmnet0.setInterfaceName("rmnet0");
+
+        LinkProperties clat4 = new LinkProperties();
+        clat4.setInterfaceName("clat4");
+
+        assertEquals(0, rmnet0.getStackedLinks().size());
+        rmnet0.addStackedLink(clat4);
+        assertEquals(1, rmnet0.getStackedLinks().size());
+        rmnet0.addStackedLink(clat4);
+        assertEquals(1, rmnet0.getStackedLinks().size());
+        assertEquals(0, clat4.getStackedLinks().size());
+
+        // Modify an item in the returned collection to see what happens.
+        for (LinkProperties link : rmnet0.getStackedLinks()) {
+            if (link.getInterfaceName().equals("clat4")) {
+               link.setInterfaceName("newname");
+            }
+        }
+        for (LinkProperties link : rmnet0.getStackedLinks()) {
+            assertFalse("newname".equals(link.getInterfaceName()));
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 4d0b892..0f2b803 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -16,61 +16,65 @@
 
 package android.os;
 
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+
 import android.content.Context;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import com.google.android.collect.Sets;
+
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
+import java.util.Arrays;
+import java.util.HashSet;
 
+import libcore.io.IoUtils;
+
+@MediumTest
 public class FileUtilsTest extends AndroidTestCase {
     private static final String TEST_DATA =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
+    private File mDir;
     private File mTestFile;
     private File mCopyFile;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        File testDir = getContext().getDir("testing", Context.MODE_PRIVATE);
-        mTestFile = new File(testDir, "test.file");
-        mCopyFile = new File(testDir, "copy.file");
-        FileWriter writer = new FileWriter(mTestFile);
-        try {
-            writer.write(TEST_DATA, 0, TEST_DATA.length());
-        } finally {
-            writer.close();
-        }
+        mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
+        mTestFile = new File(mDir, "test.file");
+        mCopyFile = new File(mDir, "copy.file");
     }
 
     @Override
     protected void tearDown() throws Exception {
-        if (mTestFile.exists()) mTestFile.delete();
-        if (mCopyFile.exists()) mCopyFile.delete();
+        IoUtils.deleteContents(mDir);
     }
 
     // TODO: test setPermissions(), getPermissions()
 
-    @MediumTest
     public void testCopyFile() throws Exception {
+        stageFile(mTestFile, TEST_DATA);
         assertFalse(mCopyFile.exists());
         FileUtils.copyFile(mTestFile, mCopyFile);
         assertTrue(mCopyFile.exists());
         assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
-    @MediumTest
     public void testCopyToFile() throws Exception {
         final String s = "Foo Bar";
         assertFalse(mCopyFile.exists());
-        FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile);        assertTrue(mCopyFile.exists());
+        FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile);
+        assertTrue(mCopyFile.exists());
         assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
-    @MediumTest
     public void testIsFilenameSafe() throws Exception {
         assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
         assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -78,8 +82,9 @@
         assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
     }
 
-    @MediumTest
     public void testReadTextFile() throws Exception {
+        stageFile(mTestFile, TEST_DATA);
+
         assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
 
         assertEquals("ABCDE", FileUtils.readTextFile(mTestFile, 5, null));
@@ -97,8 +102,8 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
     }
 
-    @MediumTest
     public void testReadTextFileWithZeroLengthFile() throws Exception {
+        stageFile(mTestFile, TEST_DATA);
         new FileOutputStream(mTestFile).close();  // Zero out the file
         assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
         assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
@@ -106,4 +111,81 @@
         assertEquals("", FileUtils.readTextFile(mTestFile, -1, "<>"));
         assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
     }
+
+    public void testDeleteOlderEmptyDir() throws Exception {
+        FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
+        assertDirContents();
+    }
+
+    public void testDeleteOlderTypical() throws Exception {
+        touch("file1", HOUR_IN_MILLIS);
+        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        FileUtils.deleteOlderFiles(mDir, 3, DAY_IN_MILLIS);
+        assertDirContents("file1", "file2", "file3");
+    }
+
+    public void testDeleteOlderInFuture() throws Exception {
+        touch("file1", -HOUR_IN_MILLIS);
+        touch("file2", HOUR_IN_MILLIS);
+        touch("file3", WEEK_IN_MILLIS);
+        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
+        assertDirContents("file1", "file2");
+
+        touch("file1", -HOUR_IN_MILLIS);
+        touch("file2", HOUR_IN_MILLIS);
+        touch("file3", WEEK_IN_MILLIS);
+        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
+        assertDirContents("file1", "file2");
+    }
+
+    public void testDeleteOlderOnlyAge() throws Exception {
+        touch("file1", HOUR_IN_MILLIS);
+        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
+        assertDirContents("file1");
+    }
+
+    public void testDeleteOlderOnlyCount() throws Exception {
+        touch("file1", HOUR_IN_MILLIS);
+        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
+        FileUtils.deleteOlderFiles(mDir, 2, 0);
+        assertDirContents("file1", "file2");
+    }
+
+    private void touch(String name, long age) throws Exception {
+        final File file = new File(mDir, name);
+        file.createNewFile();
+        file.setLastModified(System.currentTimeMillis() - age);
+    }
+
+    private void stageFile(File file, String data) throws Exception {
+        FileWriter writer = new FileWriter(file);
+        try {
+            writer.write(data, 0, data.length());
+        } finally {
+            writer.close();
+        }
+    }
+
+    private void assertDirContents(String... expected) {
+        final HashSet<String> expectedSet = Sets.newHashSet(expected);
+        String[] actual = mDir.list();
+        if (actual == null) actual = new String[0];
+
+        assertEquals(
+                "Expected " + Arrays.toString(expected) + " but actual " + Arrays.toString(actual),
+                expected.length, actual.length);
+        for (String actualFile : actual) {
+            assertTrue("Unexpected actual file " + actualFile, expectedSet.contains(actualFile));
+        }
+    }
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 312c252..efa8089 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -120,14 +120,14 @@
     oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
     void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
 
-    oneway void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
+           void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
     oneway void unregisterMediaButtonIntent(in PendingIntent pi,  in ComponentName c);
 
     oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
     oneway void unregisterMediaButtonEventReceiverForCalls();
 
-    int registerRemoteControlClient(in PendingIntent mediaIntent,
-           in IRemoteControlClient rcClient, in String callingPackageName);
+           int registerRemoteControlClient(in PendingIntent mediaIntent,
+               in IRemoteControlClient rcClient, in String callingPackageName);
     oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
            in IRemoteControlClient rcClient);
 
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index dfd0e94..c3cc1e6 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -276,7 +276,7 @@
      */
     public void release() {
         if (mState == MUXER_STATE_STARTED) {
-            throw new IllegalStateException("Can't release when muxer is started");
+            stop();
         }
         if (mNativeObject != 0) {
             nativeRelease(mNativeObject);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 5c74552..fa60ca6 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -43,7 +43,7 @@
     public static int mBitRate = profile.videoBitRate;
     public static boolean mRemoveVideo = true;
     public static int mDuration = 60 * 1000; // 60 seconds
-    public static int mTimeLapseDuration = 180 * 1000; // 3 minutes
+    public static int mTimeLapseDuration = 15 * 60 * 1000; // 15 minutes
     public static double mCaptureRate = 0.5; // 2 sec timelapse interval
 
     @Override
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 6eb9891..199f179 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -50,16 +50,10 @@
     private Camera mCamera;
 
     private static final int CAMERA_ID = 0;
-    private static final int NUMBER_OF_CAMERA_STRESS_LOOPS = 100;
-    private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100;
-    private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50;
-    private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200;
-    private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 25;
-    private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 5* 1000; // 5 seconds
+    private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 1;
+    private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 30 * 1000; // 30 seconds
     private static final int USE_TEST_RUNNER_PROFILE = -1;
     private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
-    private static final long WAIT_TIME_CAMERA_TEST = 3 * 1000; // 3 seconds
-    private static final long WAIT_TIME_RECORDER_TEST = 6 * 1000; // 6 seconds
     private static final String OUTPUT_FILE_EXT = ".3gp";
     private static final String MEDIA_STRESS_OUTPUT = "mediaStressOutput.txt";
 
@@ -150,158 +144,6 @@
         }
     }
 
-    //Test case for stressing the camera preview.
-    @LargeTest
-    public void testStressCamera() throws Exception {
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        Log.v(TAG, "Camera start preview stress test");
-        mOutput.write("Total number of loops:" + NUMBER_OF_CAMERA_STRESS_LOOPS + "\n");
-        try {
-            Log.v(TAG, "Start preview");
-            mOutput.write("No of loop: ");
-
-            for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++) {
-                runOnLooper(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCamera = Camera.open(CAMERA_ID);
-                    }
-                });
-                mCamera.setErrorCallback(mCameraErrorCallback);
-                mCamera.setPreviewDisplay(mSurfaceHolder);
-                mCamera.startPreview();
-                Thread.sleep(WAIT_TIME_CAMERA_TEST);
-                mCamera.stopPreview();
-                mCamera.release();
-                if (i == 0) {
-                    mOutput.write(i + 1);
-                } else {
-                    mOutput.write(String.format(", %d", (i + 1)));
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, e.toString());
-            fail("Camera startup preview stress test");
-        }
-    }
-
-    //Test case for stressing the camera preview.
-    @LargeTest
-    public void testStressRecorder() throws Exception {
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        Log.v(TAG, "H263 video record: reset after prepare Stress test");
-        mOutput.write("Total number of loops:" + NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
-        try {
-            mOutput.write("No of loop: ");
-            Log.v(TAG, "Start preview");
-            for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++) {
-                runOnLooper(new Runnable() {
-                    @Override
-                    public void run() {
-                        mRecorder = new MediaRecorder();
-                    }
-                });
-                Log.v(TAG, "counter = " + i);
-                String fileName = String.format("%s/temp%d%s",
-                        Environment.getExternalStorageDirectory(),
-                        i, OUTPUT_FILE_EXT);
-
-                Log.v(TAG, fileName);
-                mRecorder.setOnErrorListener(mRecorderErrorCallback);
-                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
-                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
-                mRecorder.setOutputFile(fileName);
-                mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
-                mRecorder.setVideoSize(176,144);
-                Log.v(TAG, "setEncoder");
-                mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
-                mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-                Log.v(TAG, "setPreview");
-                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
-                Log.v(TAG, "prepare");
-                mRecorder.prepare();
-                Log.v(TAG, "before release");
-                Thread.sleep(WAIT_TIME_RECORDER_TEST);
-                mRecorder.reset();
-                mRecorder.release();
-                if (i == 0) {
-                    mOutput.write(i + 1);
-                } else {
-                    mOutput.write(String.format(", %d", (i + 1)));
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, e.toString());
-            fail("H263 video recording stress test");
-        }
-    }
-
-    //Stress test case for switching camera and video recorder preview.
-    @LargeTest
-    public void testStressCameraSwitchRecorder() throws Exception {
-        SurfaceHolder mSurfaceHolder;
-        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
-        Log.v(TAG, "Camera and video recorder preview switching");
-        mOutput.write("Total number of loops: " +
-                NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n");
-        try {
-            Log.v(TAG, "Start preview");
-            mOutput.write("No of loop: ");
-            for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++) {
-                runOnLooper(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCamera = Camera.open(CAMERA_ID);
-                    }
-                });
-                mCamera.setErrorCallback(mCameraErrorCallback);
-                mCamera.setPreviewDisplay(mSurfaceHolder);
-                mCamera.startPreview();
-                Thread.sleep(WAIT_TIME_CAMERA_TEST);
-                mCamera.stopPreview();
-                mCamera.release();
-                mCamera = null;
-                Log.v(TAG, "release camera");
-                String fileName = String.format("%s/temp%d%s",
-                        Environment.getExternalStorageDirectory(),
-                        i, OUTPUT_FILE_EXT);
-                Log.v(TAG, fileName);
-                runOnLooper(new Runnable() {
-                    @Override
-                    public void run() {
-                        mRecorder = new MediaRecorder();
-                    }
-                });
-                mRecorder.setOnErrorListener(mRecorderErrorCallback);
-                mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
-                mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
-                mRecorder.setOutputFile(fileName);
-                mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
-                mRecorder.setVideoSize(176,144);
-                Log.v(TAG, "Media recorder setEncoder");
-                mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
-                Log.v(TAG, "mediaRecorder setPreview");
-                mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
-                Log.v(TAG, "prepare");
-                mRecorder.prepare();
-                Log.v(TAG, "before release");
-                Thread.sleep(WAIT_TIME_CAMERA_TEST);
-                mRecorder.release();
-                Log.v(TAG, "release video recorder");
-                if (i == 0) {
-                    mOutput.write(i + 1);
-                } else {
-                    mOutput.write(String.format(", %d", (i + 1)));
-                }
-            }
-        } catch (Exception e) {
-            Log.e(TAG, e.toString());
-            fail("Camera and recorder switch mode");
-        }
-    }
-
     public void validateRecordedVideo(String recordedFile) {
         try {
             MediaPlayer mp = new MediaPlayer();
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index 3b1ebf4..de04909 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -29,17 +29,16 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.FileUtils;
 import android.os.SystemProperties;
 import android.support.v4.content.FileProvider;
-import android.util.Log;
+import android.text.format.DateUtils;
 import android.util.Patterns;
 
 import com.google.android.collect.Lists;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
 
 /**
  * Receiver that handles finished bugreports, usually by attaching them to an
@@ -54,10 +53,15 @@
     private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
 
     /**
-     * Number of bugreports to retain before deleting the oldest; 4 reports and
-     * 4 screenshots are roughly 17MB of disk space.
+     * Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are
+     * roughly 17MB of disk space.
      */
-    private static final int NUM_OLD_FILES = 8;
+    private static final int MIN_KEEP_COUNT = 8;
+
+    /**
+     * Always keep bugreports taken in the last week.
+     */
+    private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS;
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -94,7 +98,8 @@
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                deleteOlderFiles(bugreportFile.getParentFile(), NUM_OLD_FILES);
+                FileUtils.deleteOlderFiles(
+                        bugreportFile.getParentFile(), MIN_KEEP_COUNT, MIN_KEEP_AGE);
                 result.finish();
                 return null;
             }
@@ -164,28 +169,6 @@
         return foundAccount;
     }
 
-    /**
-     * Delete the oldest files in given directory until only the requested
-     * number remain.
-     */
-    private static void deleteOlderFiles(File dir, int retainNum) {
-        final File[] files = dir.listFiles();
-        if (files == null) return;
-
-        Arrays.sort(files, new ModifiedComparator());
-        for (int i = retainNum; i < files.length; i++) {
-            Log.d(TAG, "Deleting old file " + files[i]);
-            files[i].delete();
-        }
-    }
-
-    private static class ModifiedComparator implements Comparator<File> {
-        @Override
-        public int compare(File lhs, File rhs) {
-            return (int) (rhs.lastModified() - lhs.lastModified());
-        }
-    }
-
     private static File getFileExtra(Intent intent, String key) {
         final String path = intent.getStringExtra(key);
         if (path != null) {
@@ -194,5 +177,4 @@
             return null;
         }
     }
-
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 06f06b5..78d7caa 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -310,9 +310,7 @@
         mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
         mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
 
-        if (!(mContext instanceof Activity)) {
-            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
-        }
+        setBackButtonEnabled(false);
 
         addDefaultWidgets();
 
@@ -329,6 +327,13 @@
         updateSecurityViews();
     }
 
+    private void setBackButtonEnabled(boolean enabled) {
+        if (mContext instanceof Activity) return;  // always enabled in activity mode
+        setSystemUiVisibility(enabled ?
+                getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_BACK :
+                getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
+    }
+
     private boolean shouldEnableAddWidget() {
         return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
     }
@@ -907,6 +912,10 @@
             // Discard current runnable if we're switching back to the selector view
             setOnDismissAction(null);
         }
+        if (securityMode == SecurityMode.Account && !mLockPatternUtils.isPermanentlyLocked()) {
+            // we're showing account as a backup, provide a way to get back to primary
+            setBackButtonEnabled(true);
+        }
         mCurrentSecuritySelection = securityMode;
     }
 
@@ -1579,6 +1588,12 @@
     }
 
     public boolean handleBackKey() {
+        if (mCurrentSecuritySelection == SecurityMode.Account) {
+            // go back to primary screen and re-disable back
+            setBackButtonEnabled(false);
+            showPrimarySecurityScreen(false /*turningOff*/);
+            return true;
+        }
         if (mCurrentSecuritySelection != SecurityMode.None) {
             mCallback.dismiss(false);
             return true;
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c83a919..0b3df35 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1430,11 +1430,11 @@
 
     private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
             boolean toDefaultTable) {
-        String iface = lp.getInterfaceName();
-        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), addr);
+        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr);
         if (bestRoute == null) {
-            bestRoute = RouteInfo.makeHostRoute(addr, iface);
+            bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName());
         } else {
+            String iface = bestRoute.getInterface();
             if (bestRoute.getGateway().equals(addr)) {
                 // if there is no better route, add the implied hostroute for our gateway
                 bestRoute = RouteInfo.makeHostRoute(addr, iface);
@@ -1449,9 +1449,8 @@
 
     private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
             boolean toDefaultTable) {
-        String ifaceName = lp.getInterfaceName();
-        if ((ifaceName == null) || (lp == null) || (r == null)) {
-            if (DBG) log("modifyRoute got unexpected null: " + ifaceName + ", " + lp + ", " + r);
+        if ((lp == null) || (r == null)) {
+            if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r);
             return false;
         }
 
@@ -1460,8 +1459,14 @@
             return false;
         }
 
+        String ifaceName = r.getInterface();
+        if(ifaceName == null) {
+            loge("Error modifying route - no interface name");
+            return false;
+        }
+
         if (r.isHostRoute() == false) {
-            RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), r.getGateway());
+            RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), r.getGateway());
             if (bestRoute != null) {
                 if (bestRoute.getGateway().equals(r.getGateway())) {
                     // if there is no better route, add the implied hostroute for our gateway
@@ -2300,7 +2305,7 @@
             routeDiff = curLp.compareRoutes(newLp);
             dnsDiff = curLp.compareDnses(newLp);
         } else if (newLp != null) {
-            routeDiff.added = newLp.getRoutes();
+            routeDiff.added = newLp.getAllRoutes();
             dnsDiff.added = newLp.getDnses();
         }
 
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 14841af..f6c9d82 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1237,24 +1237,13 @@
     public void onServiceConnected(ComponentName name, IBinder service) {
         synchronized (mMethodMap) {
             if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
-                IInputMethod prevMethod = mCurMethod;
                 mCurMethod = IInputMethod.Stub.asInterface(service);
                 if (mCurToken == null) {
                     Slog.w(TAG, "Service connected without a token!");
                     unbindCurrentMethodLocked(false, false);
                     return;
                 }
-                // Remove messages relating to the previous service. Otherwise WindowManagerService
-                // will throw a BadTokenException because the old token is being removed.
-                if (prevMethod != null) {
-                    try {
-                        prevMethod.removeSoftInputMessages();
-                    } catch (RemoteException e) {
-                    }
-                }
-                mCaller.removeMessages(MSG_SHOW_SOFT_INPUT);
-                mCaller.removeMessages(MSG_HIDE_SOFT_INPUT);
-                if (true || DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
+                if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
                 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                         MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
                 if (mCurClient != null) {
@@ -1700,7 +1689,7 @@
                     }
                 }
 
-                if (true || DEBUG) Slog.v(TAG, "Client requesting input be shown");
+                if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
                 return showCurrentInputLocked(flags, resultReceiver);
             }
         } finally {
@@ -1724,8 +1713,7 @@
 
         boolean res = false;
         if (mCurMethod != null) {
-            if (true ||DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken,
-                    new RuntimeException("here").fillInStackTrace());
+            if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
                     MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
                     resultReceiver));
@@ -1797,13 +1785,11 @@
     boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mShowExplicitlyRequested || mShowForced)) {
-            if (true ||DEBUG) Slog.v(TAG,
-                    "Not hiding: explicit show not cancelled by non-explicit hide");
+            if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
             return false;
         }
         if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
-            if (true ||DEBUG) Slog.v(TAG,
-                    "Not hiding: forced show not cancelled by not-always hide");
+            if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
             return false;
         }
         boolean res;
@@ -2314,7 +2300,7 @@
             case MSG_SHOW_SOFT_INPUT:
                 args = (SomeArgs)msg.obj;
                 try {
-                    if (true || DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
+                    if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
                             + msg.arg1 + ", " + args.arg2 + ")");
                     ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2);
                 } catch (RemoteException e) {
@@ -2324,7 +2310,7 @@
             case MSG_HIDE_SOFT_INPUT:
                 args = (SomeArgs)msg.obj;
                 try {
-                    if (true || DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
+                    if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
                             + args.arg2 + ")");
                     ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
                 } catch (RemoteException e) {
@@ -2334,7 +2320,7 @@
             case MSG_ATTACH_TOKEN:
                 args = (SomeArgs)msg.obj;
                 try {
-                    if (true || DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
+                    if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
                     ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
                 } catch (RemoteException e) {
                 }
diff --git a/services/java/com/android/server/wifi/README.txt b/services/java/com/android/server/wifi/README.txt
new file mode 100644
index 0000000..c03bff5
--- /dev/null
+++ b/services/java/com/android/server/wifi/README.txt
@@ -0,0 +1,12 @@
+WifiService: Implements the IWifiManager 3rd party API. The API and the device state information (screen on/off, battery state, sleep policy) go as input into the WifiController which tracks high level states as to whether STA or AP mode is operational and controls the WifiStateMachine to handle bringup and shut down.
+
+WifiController: Acts as a controller to the WifiStateMachine based on various inputs (API and device state). Runs on the same thread created in WifiService.
+
+WifiSettingsStore: Tracks the various settings (wifi toggle, airplane toggle, tethering toggle, scan mode toggle) and provides API to figure if wifi should be turned on or off.
+
+WifiTrafficPoller: Polls traffic on wifi and notifies apps listening on it.
+
+WifiNotificationController: Controls whether the open network notification is displayed or not based on the scan results.
+
+WifiStateMachine: Tracks the various states on STA and AP connectivity and handles bring up and shut down.
+
diff --git a/services/java/com/android/server/wifi/WifiController.java b/services/java/com/android/server/wifi/WifiController.java
new file mode 100644
index 0000000..4d7c434
--- /dev/null
+++ b/services/java/com/android/server/wifi/WifiController.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import static android.net.wifi.WifiManager.WIFI_MODE_FULL;
+import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF;
+import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY;
+import android.net.wifi.WifiStateMachine;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.server.wifi.WifiService.LockList;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+class WifiController extends StateMachine {
+    private static final String TAG = "WifiController";
+    private static final boolean DBG = false;
+    private Context mContext;
+    private boolean mScreenOff;
+    private boolean mDeviceIdle;
+    private int mPluggedType;
+    private int mStayAwakeConditions;
+    private long mIdleMillis;
+    private int mSleepPolicy;
+
+    private AlarmManager mAlarmManager;
+    private PendingIntent mIdleIntent;
+    private static final int IDLE_REQUEST = 0;
+
+    /**
+     * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
+     * Settings.Global value is not present. This timeout value is chosen as
+     * the approximate point at which the battery drain caused by Wi-Fi
+     * being enabled but not active exceeds the battery drain caused by
+     * re-establishing a connection to the mobile data network.
+     */
+    private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
+
+    NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+
+    private static final String ACTION_DEVICE_IDLE =
+            "com.android.server.WifiManager.action.DEVICE_IDLE";
+
+    /* References to values tracked in WifiService */
+    final WifiStateMachine mWifiStateMachine;
+    final WifiSettingsStore mSettingsStore;
+    final LockList mLocks;
+
+    /**
+     * Temporary for computing UIDS that are responsible for starting WIFI.
+     * Protected by mWifiStateTracker lock.
+     */
+    private final WorkSource mTmpWorkSource = new WorkSource();
+
+    private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
+
+    static final int CMD_EMERGENCY_MODE_CHANGED     = BASE + 1;
+    static final int CMD_SCREEN_ON                  = BASE + 2;
+    static final int CMD_SCREEN_OFF                 = BASE + 3;
+    static final int CMD_BATTERY_CHANGED            = BASE + 4;
+    static final int CMD_DEVICE_IDLE                = BASE + 5;
+    static final int CMD_LOCKS_CHANGED              = BASE + 6;
+    static final int CMD_SCAN_ALWAYS_MODE_CHANGED   = BASE + 7;
+    static final int CMD_WIFI_TOGGLED               = BASE + 8;
+    static final int CMD_AIRPLANE_TOGGLED           = BASE + 9;
+    static final int CMD_SET_AP                     = BASE + 10;
+
+    private DefaultState mDefaultState = new DefaultState();
+    private StaEnabledState mStaEnabledState = new StaEnabledState();
+    private ApStaDisabledState mApStaDisabledState = new ApStaDisabledState();
+    private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState();
+    private ApEnabledState mApEnabledState = new ApEnabledState();
+    private DeviceActiveState mDeviceActiveState = new DeviceActiveState();
+    private DeviceInactiveState mDeviceInactiveState = new DeviceInactiveState();
+    private ScanOnlyLockHeldState mScanOnlyLockHeldState = new ScanOnlyLockHeldState();
+    private FullLockHeldState mFullLockHeldState = new FullLockHeldState();
+    private FullHighPerfLockHeldState mFullHighPerfLockHeldState = new FullHighPerfLockHeldState();
+    private NoLockHeldState mNoLockHeldState = new NoLockHeldState();
+    private EcmState mEcmState = new EcmState();
+
+    WifiController(Context context, WifiService service, Looper looper) {
+        super(TAG, looper);
+        mContext = context;
+        mWifiStateMachine = service.mWifiStateMachine;
+        mSettingsStore = service.mSettingsStore;
+        mLocks = service.mLocks;
+
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
+        mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
+
+        addState(mDefaultState);
+            addState(mApStaDisabledState, mDefaultState);
+            addState(mStaEnabledState, mDefaultState);
+                addState(mDeviceActiveState, mStaEnabledState);
+                addState(mDeviceInactiveState, mStaEnabledState);
+                    addState(mScanOnlyLockHeldState, mDeviceInactiveState);
+                    addState(mFullLockHeldState, mDeviceInactiveState);
+                    addState(mFullHighPerfLockHeldState, mDeviceInactiveState);
+                    addState(mNoLockHeldState, mDeviceInactiveState);
+            addState(mStaDisabledWithScanState, mDefaultState);
+            addState(mApEnabledState, mDefaultState);
+            addState(mEcmState, mDefaultState);
+        setInitialState(mApStaDisabledState);
+        setLogRecSize(25);
+        setLogOnlyTransitions(true);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_DEVICE_IDLE);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        String action = intent.getAction();
+                        if (action.equals(ACTION_DEVICE_IDLE)) {
+                            sendMessage(CMD_DEVICE_IDLE);
+                        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+                                    WifiManager.EXTRA_NETWORK_INFO);
+                        }
+                    }
+                },
+                new IntentFilter(filter));
+
+        initializeAndRegisterForSettingsChange(looper);
+    }
+
+    private void initializeAndRegisterForSettingsChange(Looper looper) {
+        Handler handler = new Handler(looper);
+        readStayAwakeConditions();
+        registerForStayAwakeModeChange(handler);
+        readWifiIdleTime();
+        registerForWifiIdleTimeChange(handler);
+        readStayAwakeConditions();
+        registerForWifiSleepPolicyChange(handler);
+    }
+
+    private void readStayAwakeConditions() {
+        mStayAwakeConditions = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+    }
+
+    private void readWifiIdleTime() {
+        mIdleMillis = Settings.Global.getLong(mContext.getContentResolver(),
+                Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
+    }
+
+    private void readWifiSleepPolicy() {
+        mSleepPolicy = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_SLEEP_POLICY,
+                Settings.Global.WIFI_SLEEP_POLICY_NEVER);
+    }
+
+    /**
+     * Observes settings changes to scan always mode.
+     */
+    private void registerForStayAwakeModeChange(Handler handler) {
+        ContentObserver contentObserver = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                readStayAwakeConditions();
+            }
+        };
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
+                false, contentObserver);
+    }
+
+    /**
+     * Observes settings changes to scan always mode.
+     */
+    private void registerForWifiIdleTimeChange(Handler handler) {
+        ContentObserver contentObserver = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                readWifiIdleTime();
+            }
+        };
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS),
+                false, contentObserver);
+    }
+
+    /**
+     * Observes changes to wifi sleep policy
+     */
+    private void registerForWifiSleepPolicyChange(Handler handler) {
+        ContentObserver contentObserver = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                readWifiSleepPolicy();
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY),
+                false, contentObserver);
+    }
+
+    /**
+     * Determines whether the Wi-Fi chipset should stay awake or be put to
+     * sleep. Looks at the setting for the sleep policy and the current
+     * conditions.
+     *
+     * @see #shouldDeviceStayAwake(int)
+     */
+    private boolean shouldWifiStayAwake(int pluggedType) {
+        if (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) {
+            // Never sleep
+            return true;
+        } else if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
+                (pluggedType != 0)) {
+            // Never sleep while plugged, and we're plugged
+            return true;
+        } else {
+            // Default
+            return shouldDeviceStayAwake(pluggedType);
+        }
+    }
+
+    /**
+     * Determine whether the bit value corresponding to {@code pluggedType} is set in
+     * the bit string mStayAwakeConditions. This determines whether the device should
+     * stay awake based on the current plugged type.
+     *
+     * @param pluggedType the type of plug (USB, AC, or none) for which the check is
+     * being made
+     * @return {@code true} if {@code pluggedType} indicates that the device is
+     * supposed to stay awake, {@code false} otherwise.
+     */
+    private boolean shouldDeviceStayAwake(int pluggedType) {
+        return (mStayAwakeConditions & pluggedType) != 0;
+    }
+
+    private void updateBatteryWorkSource() {
+        mTmpWorkSource.clear();
+        if (mDeviceIdle) {
+            mLocks.updateWorkSource(mTmpWorkSource);
+        }
+        mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
+    }
+
+    class DefaultState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_SCREEN_ON:
+                    mAlarmManager.cancel(mIdleIntent);
+                    mScreenOff = false;
+                    mDeviceIdle = false;
+                    updateBatteryWorkSource();
+                    break;
+                case CMD_SCREEN_OFF:
+                    mScreenOff = true;
+                    /*
+                    * Set a timer to put Wi-Fi to sleep, but only if the screen is off
+                    * AND the "stay on while plugged in" setting doesn't match the
+                    * current power conditions (i.e, not plugged in, plugged in to USB,
+                    * or plugged in to AC).
+                    */
+                    if (!shouldWifiStayAwake(mPluggedType)) {
+                        //Delayed shutdown if wifi is connected
+                        if (mNetworkInfo.getDetailedState() ==
+                                NetworkInfo.DetailedState.CONNECTED) {
+                            if (DBG) Slog.d(TAG, "set idle timer: " + mIdleMillis + " ms");
+                            mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+                                    System.currentTimeMillis() + mIdleMillis, mIdleIntent);
+                        } else {
+                            sendMessage(CMD_DEVICE_IDLE);
+                        }
+                    }
+                    break;
+                case CMD_DEVICE_IDLE:
+                    mDeviceIdle = true;
+                    updateBatteryWorkSource();
+                    break;
+                case CMD_BATTERY_CHANGED:
+                    /*
+                    * Set a timer to put Wi-Fi to sleep, but only if the screen is off
+                    * AND we are transitioning from a state in which the device was supposed
+                    * to stay awake to a state in which it is not supposed to stay awake.
+                    * If "stay awake" state is not changing, we do nothing, to avoid resetting
+                    * the already-set timer.
+                    */
+                    int pluggedType = msg.arg1;
+                    if (DBG) Slog.d(TAG, "battery changed pluggedType: " + pluggedType);
+                    if (mScreenOff && shouldWifiStayAwake(mPluggedType) &&
+                            !shouldWifiStayAwake(pluggedType)) {
+                        long triggerTime = System.currentTimeMillis() + mIdleMillis;
+                        if (DBG) Slog.d(TAG, "set idle timer for " + mIdleMillis + "ms");
+                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
+                    }
+
+                    mPluggedType = pluggedType;
+                    break;
+                case CMD_SET_AP:
+                case CMD_SCAN_ALWAYS_MODE_CHANGED:
+                case CMD_LOCKS_CHANGED:
+                case CMD_WIFI_TOGGLED:
+                case CMD_AIRPLANE_TOGGLED:
+                case CMD_EMERGENCY_MODE_CHANGED:
+                    break;
+                default:
+                    throw new RuntimeException("WifiController.handleMessage " + msg.what);
+            }
+            return HANDLED;
+        }
+
+    }
+
+    class ApStaDisabledState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setSupplicantRunning(false);
+        }
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_WIFI_TOGGLED:
+                case CMD_AIRPLANE_TOGGLED:
+                    if (mSettingsStore.isWifiToggleEnabled()) {
+                        if (mDeviceIdle == false) {
+                            transitionTo(mDeviceActiveState);
+                        } else {
+                            checkLocksAndTransitionWhenDeviceIdle();
+                        }
+                    }
+                    break;
+                case CMD_SCAN_ALWAYS_MODE_CHANGED:
+                    if (mSettingsStore.isScanAlwaysAvailable()) {
+                        transitionTo(mStaDisabledWithScanState);
+                    }
+                    break;
+                case CMD_SET_AP:
+                    if (msg.arg1 == 1) {
+                        mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,
+                                true);
+                        transitionTo(mApEnabledState);
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+    }
+
+    class StaEnabledState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setSupplicantRunning(true);
+        }
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_WIFI_TOGGLED:
+                    if (! mSettingsStore.isWifiToggleEnabled()) {
+                        if (mSettingsStore.isScanAlwaysAvailable()) {
+                            transitionTo(mStaDisabledWithScanState);
+                        } else {
+                            transitionTo(mApStaDisabledState);
+                        }
+                    }
+                    break;
+                case CMD_AIRPLANE_TOGGLED:
+                    /* When wi-fi is turned off due to airplane,
+                    * disable entirely (including scan)
+                    */
+                    if (! mSettingsStore.isWifiToggleEnabled()) {
+                        transitionTo(mApStaDisabledState);
+                    }
+                    break;
+                case CMD_EMERGENCY_MODE_CHANGED:
+                    if (msg.arg1 == 1) {
+                        transitionTo(mEcmState);
+                        break;
+                    }
+                default:
+                    return NOT_HANDLED;
+
+            }
+            return HANDLED;
+        }
+    }
+
+    class StaDisabledWithScanState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setSupplicantRunning(true);
+            mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
+            mWifiStateMachine.setDriverStart(true);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_WIFI_TOGGLED:
+                    if (mSettingsStore.isWifiToggleEnabled()) {
+                        if (mDeviceIdle == false) {
+                            transitionTo(mDeviceActiveState);
+                        } else {
+                            checkLocksAndTransitionWhenDeviceIdle();
+                        }
+                    }
+                    break;
+                case CMD_AIRPLANE_TOGGLED:
+                    if (mSettingsStore.isAirplaneModeOn() &&
+                            ! mSettingsStore.isWifiToggleEnabled()) {
+                        transitionTo(mApStaDisabledState);
+                    }
+                case CMD_SCAN_ALWAYS_MODE_CHANGED:
+                    if (! mSettingsStore.isScanAlwaysAvailable()) {
+                        transitionTo(mApStaDisabledState);
+                    }
+                    break;
+                case CMD_SET_AP:
+                    // Before starting tethering, turn off supplicant for scan mode
+                    if (msg.arg1 == 1) {
+                        deferMessage(msg);
+                        transitionTo(mApStaDisabledState);
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    class ApEnabledState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_AIRPLANE_TOGGLED:
+                    if (mSettingsStore.isAirplaneModeOn()) {
+                        mWifiStateMachine.setHostApRunning(null, false);
+                        transitionTo(mApStaDisabledState);
+                    }
+                    break;
+                case CMD_SET_AP:
+                    if (msg.arg1 == 0) {
+                        mWifiStateMachine.setHostApRunning(null, false);
+                        transitionTo(mApStaDisabledState);
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    class EcmState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setSupplicantRunning(false);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (msg.what == CMD_EMERGENCY_MODE_CHANGED && msg.arg1 == 0) {
+                if (mSettingsStore.isWifiToggleEnabled()) {
+                    if (mDeviceIdle == false) {
+                        transitionTo(mDeviceActiveState);
+                    } else {
+                        checkLocksAndTransitionWhenDeviceIdle();
+                    }
+                } else if (mSettingsStore.isScanAlwaysAvailable()) {
+                    transitionTo(mStaDisabledWithScanState);
+                } else {
+                    transitionTo(mApStaDisabledState);
+                }
+                return HANDLED;
+            } else {
+                return NOT_HANDLED;
+            }
+        }
+    }
+
+    /* Parent: StaEnabledState */
+    class DeviceActiveState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+            mWifiStateMachine.setDriverStart(true);
+            mWifiStateMachine.setHighPerfModeEnabled(false);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (msg.what == CMD_DEVICE_IDLE) {
+                checkLocksAndTransitionWhenDeviceIdle();
+                // We let default state handle the rest of work
+            }
+            return NOT_HANDLED;
+        }
+    }
+
+    /* Parent: StaEnabledState */
+    class DeviceInactiveState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_LOCKS_CHANGED:
+                    checkLocksAndTransitionWhenDeviceIdle();
+                    updateBatteryWorkSource();
+                    return HANDLED;
+                case CMD_SCREEN_ON:
+                    transitionTo(mDeviceActiveState);
+                    // More work in default state
+                    return NOT_HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a scan only lock. */
+    class ScanOnlyLockHeldState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
+            mWifiStateMachine.setDriverStart(true);
+        }
+    }
+
+    /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a full lock. */
+    class FullLockHeldState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+            mWifiStateMachine.setDriverStart(true);
+            mWifiStateMachine.setHighPerfModeEnabled(false);
+        }
+    }
+
+    /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a high perf lock. */
+    class FullHighPerfLockHeldState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+            mWifiStateMachine.setDriverStart(true);
+            mWifiStateMachine.setHighPerfModeEnabled(true);
+        }
+    }
+
+    /* Parent: DeviceInactiveState. Device is inactive and no app is holding a wifi lock. */
+    class NoLockHeldState extends State {
+        @Override
+        public void enter() {
+            mWifiStateMachine.setDriverStart(false);
+        }
+    }
+
+    private void checkLocksAndTransitionWhenDeviceIdle() {
+        if (mLocks.hasLocks()) {
+            switch (mLocks.getStrongestLockMode()) {
+                case WIFI_MODE_FULL:
+                    transitionTo(mFullLockHeldState);
+                    break;
+                case WIFI_MODE_FULL_HIGH_PERF:
+                    transitionTo(mFullHighPerfLockHeldState);
+                    break;
+                case WIFI_MODE_SCAN_ONLY:
+                    transitionTo(mScanOnlyLockHeldState);
+                    break;
+                default:
+                    loge("Illegal lock " + mLocks.getStrongestLockMode());
+            }
+        } else {
+            if (mSettingsStore.isScanAlwaysAvailable()) {
+                transitionTo(mScanOnlyLockHeldState);
+            } else {
+                transitionTo(mNoLockHeldState);
+            }
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+
+        pw.println("mScreenOff " + mScreenOff);
+        pw.println("mDeviceIdle " + mDeviceIdle);
+        pw.println("mPluggedType " + mPluggedType);
+        pw.println("mIdleMillis " + mIdleMillis);
+        pw.println("mSleepPolicy " + mSleepPolicy);
+    }
+}
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index d675822..bc6bdaf 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -17,15 +17,14 @@
 package com.android.server.wifi;
 
 import android.app.ActivityManager;
-import android.app.AlarmManager;
 import android.app.AppOpsManager;
-import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
@@ -33,12 +32,9 @@
 import android.net.wifi.WifiStateMachine;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiWatchdogStateMachine;
-import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.os.Binder;
@@ -63,39 +59,35 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import com.android.internal.R;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.AsyncChannel;
 import com.android.server.am.BatteryStatsService;
-import com.android.internal.R;
-
+import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
+import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
+import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED;
+import static com.android.server.wifi.WifiController.CMD_LOCKS_CHANGED;
+import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED;
+import static com.android.server.wifi.WifiController.CMD_SCREEN_OFF;
+import static com.android.server.wifi.WifiController.CMD_SCREEN_ON;
+import static com.android.server.wifi.WifiController.CMD_SET_AP;
+import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
 /**
  * WifiService handles remote WiFi operation requests by implementing
  * the IWifiManager interface.
  *
  * @hide
  */
-//TODO: Clean up multiple locks and implement WifiService
-// as a SM to track soft AP/client/adhoc bring up based
-// on device idle state, airplane mode and boot.
-
 public final class WifiService extends IWifiManager.Stub {
     private static final String TAG = "WifiService";
     private static final boolean DBG = false;
 
-    private final WifiStateMachine mWifiStateMachine;
+    final WifiStateMachine mWifiStateMachine;
 
     private final Context mContext;
 
-    private AlarmManager mAlarmManager;
-    private PendingIntent mIdleIntent;
-    private static final int IDLE_REQUEST = 0;
-    private boolean mScreenOff;
-    private boolean mDeviceIdle;
-    private boolean mEmergencyCallbackMode = false;
-    private int mPluggedType;
-
-    private final LockList mLocks = new LockList();
+    final LockList mLocks = new LockList();
     // some wifi lock statistics
     private int mFullHighPerfLocksAcquired;
     private int mFullHighPerfLocksReleased;
@@ -119,19 +111,7 @@
     /* Polls traffic stats and notifies clients */
     private WifiTrafficPoller mTrafficPoller;
     /* Tracks the persisted states for wi-fi & airplane mode */
-    private WifiSettingsStore mSettingsStore;
-
-    /**
-     * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
-     * Settings.Global value is not present. This timeout value is chosen as
-     * the approximate point at which the battery drain caused by Wi-Fi
-     * being enabled but not active exceeds the battery drain caused by
-     * re-establishing a connection to the mobile data network.
-     */
-    private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
-
-    private static final String ACTION_DEVICE_IDLE =
-            "com.android.server.WifiManager.action.DEVICE_IDLE";
+    final WifiSettingsStore mSettingsStore;
 
     /* The work source (UID) that triggered the current WIFI scan, synchronized
      * on this */
@@ -139,8 +119,6 @@
 
     private boolean mIsReceiverRegistered = false;
 
-    NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
-
     /**
      * Asynchronous channel to WifiStateMachine
      */
@@ -195,7 +173,7 @@
                     break;
                 }
                 default: {
-                    Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
+                    Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
                     break;
                 }
             }
@@ -243,11 +221,6 @@
     }
     WifiStateMachineHandler mWifiStateMachineHandler;
 
-    /**
-     * Temporary for computing UIDS that are responsible for starting WIFI.
-     * Protected by mWifiStateTracker lock.
-     */
-    private final WorkSource mTmpWorkSource = new WorkSource();
     private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
 
     public WifiService(Context context) {
@@ -260,20 +233,24 @@
         mBatteryStats = BatteryStatsService.getService();
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
 
-        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
-        Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
-        mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
-
         mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
         mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);
         mSettingsStore = new WifiSettingsStore(mContext);
 
+        HandlerThread wifiThread = new HandlerThread("WifiService");
+        wifiThread.start();
+        mClientHandler = new ClientHandler(wifiThread.getLooper());
+        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
+        mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
+        mWifiController.start();
+
+        registerForScanModeChange();
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
                         if (mSettingsStore.handleAirplaneModeToggled()) {
-                            updateWifiState();
+                            mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED);
                         }
                     }
                 },
@@ -289,13 +266,10 @@
                         }
                     }
                 }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
-
-        HandlerThread wifiThread = new HandlerThread("WifiService");
-        wifiThread.start();
-        mClientHandler = new ClientHandler(wifiThread.getLooper());
-        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
     }
 
+    private WifiController mWifiController;
+
     /** Tell battery stats about a new WIFI scan */
     private void noteScanStart() {
         WorkSource scanWorkSource = null;
@@ -342,7 +316,7 @@
      */
     public void checkAndStartWifi() {
         /* Check if wi-fi needs to be enabled */
-        boolean wifiEnabled = mSettingsStore.shouldWifiBeEnabled();
+        boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled();
         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
                 (wifiEnabled ? "enabled" : "disabled"));
 
@@ -430,11 +404,7 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        if (enable) {
-            reportStartWorkSource();
-        }
-
-        mWifiStateMachine.setWifiEnabled(enable);
+        mWifiController.sendMessage(CMD_WIFI_TOGGLED);
 
         if (enable) {
             if (!mIsReceiverRegistered) {
@@ -470,7 +440,7 @@
      */
     public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
         enforceChangePermission();
-        mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
+        mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
     }
 
     /**
@@ -507,6 +477,26 @@
     }
 
     /**
+     * @param enable {@code true} to enable, {@code false} to disable.
+     * @return {@code true} if the enable/disable operation was
+     *         started or is already in the queue.
+     */
+    public boolean isScanningAlwaysAvailable() {
+        // TODO: implement
+        return true;
+    }
+
+    /**
+     * @param enable {@code true} to enable, {@code false} to disable.
+     * @return {@code true} if the enable/disable operation was
+     *         started or is already in the queue.
+     */
+    public void setScanningAlwaysAvailable(boolean enable) {
+        // TODO: implement
+    }
+
+
+    /**
      * see {@link android.net.wifi.WifiManager#disconnect()}
      */
     public void disconnect() {
@@ -777,7 +767,7 @@
          * of WifiLock & device idle status unless wifi enabled status is toggled
          */
 
-        mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode);
+        mWifiStateMachine.setDriverStart(true);
         mWifiStateMachine.reconnectCommand();
     }
 
@@ -796,7 +786,7 @@
          * TODO: if a stop is issued, wifi is brought up only by startWifi
          * unless wifi enabled status is toggled
          */
-        mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
+        mWifiStateMachine.setDriverStart(false);
     }
 
     /**
@@ -848,175 +838,39 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-
-            long idleMillis =
-                Settings.Global.getLong(mContext.getContentResolver(),
-                                        Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
-            int stayAwakeConditions =
-                Settings.Global.getInt(mContext.getContentResolver(),
-                                       Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                if (DBG) {
-                    Slog.d(TAG, "ACTION_SCREEN_ON");
-                }
-                mAlarmManager.cancel(mIdleIntent);
-                mScreenOff = false;
-                setDeviceIdleAndUpdateWifi(false);
+                mWifiController.sendMessage(CMD_SCREEN_ON);
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                if (DBG) {
-                    Slog.d(TAG, "ACTION_SCREEN_OFF");
-                }
-                mScreenOff = true;
-                /*
-                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
-                 * AND the "stay on while plugged in" setting doesn't match the
-                 * current power conditions (i.e, not plugged in, plugged in to USB,
-                 * or plugged in to AC).
-                 */
-                if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
-                    //Delayed shutdown if wifi is connected
-                    if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
-                        if (DBG) Slog.d(TAG, "setting ACTION_DEVICE_IDLE: " + idleMillis + " ms");
-                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
-                                + idleMillis, mIdleIntent);
-                    } else {
-                        setDeviceIdleAndUpdateWifi(true);
-                    }
-                }
-            } else if (action.equals(ACTION_DEVICE_IDLE)) {
-                setDeviceIdleAndUpdateWifi(true);
+                mWifiController.sendMessage(CMD_SCREEN_OFF);
             } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                /*
-                 * Set a timer to put Wi-Fi to sleep, but only if the screen is off
-                 * AND we are transitioning from a state in which the device was supposed
-                 * to stay awake to a state in which it is not supposed to stay awake.
-                 * If "stay awake" state is not changing, we do nothing, to avoid resetting
-                 * the already-set timer.
-                 */
                 int pluggedType = intent.getIntExtra("plugged", 0);
-                if (DBG) {
-                    Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
-                }
-                if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
-                        !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
-                    long triggerTime = System.currentTimeMillis() + idleMillis;
-                    if (DBG) {
-                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
-                    }
-                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
-                }
-
-                mPluggedType = pluggedType;
+                mWifiController.sendMessage(CMD_BATTERY_CHANGED, pluggedType, 0, null);
             } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
                 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
                         BluetoothAdapter.STATE_DISCONNECTED);
                 mWifiStateMachine.sendBluetoothAdapterStateChange(state);
             } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
-                mEmergencyCallbackMode = intent.getBooleanExtra("phoneinECMState", false);
-                updateWifiState();
+                boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
+                mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
             }
         }
-
-        /**
-         * Determines whether the Wi-Fi chipset should stay awake or be put to
-         * sleep. Looks at the setting for the sleep policy and the current
-         * conditions.
-         *
-         * @see #shouldDeviceStayAwake(int, int)
-         */
-        private boolean shouldWifiStayAwake(int stayAwakeConditions, int pluggedType) {
-            //Never sleep as long as the user has not changed the settings
-            int wifiSleepPolicy = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.WIFI_SLEEP_POLICY,
-                    Settings.Global.WIFI_SLEEP_POLICY_NEVER);
-
-            if (wifiSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) {
-                // Never sleep
-                return true;
-            } else if ((wifiSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
-                    (pluggedType != 0)) {
-                // Never sleep while plugged, and we're plugged
-                return true;
-            } else {
-                // Default
-                return shouldDeviceStayAwake(stayAwakeConditions, pluggedType);
-            }
-        }
-
-        /**
-         * Determine whether the bit value corresponding to {@code pluggedType} is set in
-         * the bit string {@code stayAwakeConditions}. Because a {@code pluggedType} value
-         * of {@code 0} isn't really a plugged type, but rather an indication that the
-         * device isn't plugged in at all, there is no bit value corresponding to a
-         * {@code pluggedType} value of {@code 0}. That is why we shift by
-         * {@code pluggedType - 1} instead of by {@code pluggedType}.
-         * @param stayAwakeConditions a bit string specifying which "plugged types" should
-         * keep the device (and hence Wi-Fi) awake.
-         * @param pluggedType the type of plug (USB, AC, or none) for which the check is
-         * being made
-         * @return {@code true} if {@code pluggedType} indicates that the device is
-         * supposed to stay awake, {@code false} otherwise.
-         */
-        private boolean shouldDeviceStayAwake(int stayAwakeConditions, int pluggedType) {
-            return (stayAwakeConditions & pluggedType) != 0;
-        }
     };
 
-    private void setDeviceIdleAndUpdateWifi(boolean deviceIdle) {
-        mDeviceIdle = deviceIdle;
-        reportStartWorkSource();
-        updateWifiState();
-    }
-
-    private synchronized void reportStartWorkSource() {
-        mTmpWorkSource.clear();
-        if (mDeviceIdle) {
-            for (int i=0; i<mLocks.mList.size(); i++) {
-                mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
+    /**
+     * Observes settings changes to scan always mode.
+     */
+    private void registerForScanModeChange() {
+        ContentObserver contentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                mSettingsStore.handleWifiScanAlwaysAvailableToggled();
+                mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
             }
-        }
-        mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
-    }
+        };
 
-    private void updateWifiState() {
-        boolean lockHeld = mLocks.hasLocks();
-        int strongestLockMode = WifiManager.WIFI_MODE_FULL;
-        boolean wifiShouldBeStarted;
-
-        if (mEmergencyCallbackMode) {
-            wifiShouldBeStarted = false;
-        } else {
-            wifiShouldBeStarted = !mDeviceIdle || lockHeld;
-        }
-
-        if (lockHeld) {
-            strongestLockMode = mLocks.getStrongestLockMode();
-        }
-        /* If device is not idle, lockmode cannot be scan only */
-        if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
-            strongestLockMode = WifiManager.WIFI_MODE_FULL;
-        }
-
-        /* Disable tethering when airplane mode is enabled */
-        if (mSettingsStore.isAirplaneModeOn()) {
-            mWifiStateMachine.setWifiApEnabled(null, false);
-        }
-
-        if (mSettingsStore.shouldWifiBeEnabled()) {
-            if (wifiShouldBeStarted) {
-                reportStartWorkSource();
-                mWifiStateMachine.setWifiEnabled(true);
-                mWifiStateMachine.setScanOnlyMode(
-                        strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
-                mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode);
-                mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
-                        == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
-            } else {
-                mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode);
-            }
-        } else {
-            mWifiStateMachine.setWifiEnabled(false);
-        }
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
+                false, contentObserver);
     }
 
     private void registerForBroadcasts() {
@@ -1024,7 +878,7 @@
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        intentFilter.addAction(ACTION_DEVICE_IDLE);
+        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
         intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
         mContext.registerReceiver(mReceiver, intentFilter);
@@ -1043,12 +897,9 @@
         pw.println("Stay-awake conditions: " +
                 Settings.Global.getInt(mContext.getContentResolver(),
                                        Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
-        pw.println("mScreenOff " + mScreenOff);
-        pw.println("mDeviceIdle " + mDeviceIdle);
-        pw.println("mPluggedType " + mPluggedType);
-        pw.println("mEmergencyCallbackMode " + mEmergencyCallbackMode);
         pw.println("mMulticastEnabled " + mMulticastEnabled);
         pw.println("mMulticastDisabled " + mMulticastDisabled);
+        mWifiController.dump(fd, pw, args);
         mSettingsStore.dump(fd, pw, args);
         mNotificationController.dump(fd, pw, args);
         mTrafficPoller.dump(fd, pw, args);
@@ -1099,18 +950,18 @@
         }
     }
 
-    private class LockList {
+    class LockList {
         private List<WifiLock> mList;
 
         private LockList() {
             mList = new ArrayList<WifiLock>();
         }
 
-        private synchronized boolean hasLocks() {
+        synchronized boolean hasLocks() {
             return !mList.isEmpty();
         }
 
-        private synchronized int getStrongestLockMode() {
+        synchronized int getStrongestLockMode() {
             if (mList.isEmpty()) {
                 return WifiManager.WIFI_MODE_FULL;
             }
@@ -1126,6 +977,12 @@
             return WifiManager.WIFI_MODE_SCAN_ONLY;
         }
 
+        synchronized void updateWorkSource(WorkSource ws) {
+            for (int i = 0; i < mLocks.mList.size(); i++) {
+                ws.add(mLocks.mList.get(i).mWorkSource);
+            }
+        }
+
         private void addLock(WifiLock lock) {
             if (findLockByBinder(lock.mBinder) < 0) {
                 mList.add(lock);
@@ -1145,9 +1002,10 @@
 
         private int findLockByBinder(IBinder binder) {
             int size = mList.size();
-            for (int i = size - 1; i >= 0; i--)
+            for (int i = size - 1; i >= 0; i--) {
                 if (mList.get(i).mBinder == binder)
                     return i;
+            }
             return -1;
         }
 
@@ -1231,12 +1089,7 @@
                 ++mScanLocksAcquired;
                 break;
             }
-
-            // Be aggressive about adding new locks into the accounted state...
-            // we want to over-report rather than under-report.
-            reportStartWorkSource();
-
-            updateWifiState();
+            mWifiController.sendMessage(CMD_LOCKS_CHANGED);
             return true;
         } catch (RemoteException e) {
             return false;
@@ -1303,11 +1156,8 @@
                         ++mScanLocksReleased;
                         break;
                 }
+                mWifiController.sendMessage(CMD_LOCKS_CHANGED);
             }
-
-            // TODO - should this only happen if you hadLock?
-            updateWifiState();
-
         } catch (RemoteException e) {
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/java/com/android/server/wifi/WifiSettingsStore.java b/services/java/com/android/server/wifi/WifiSettingsStore.java
index d7c8752..3ff8061 100644
--- a/services/java/com/android/server/wifi/WifiSettingsStore.java
+++ b/services/java/com/android/server/wifi/WifiSettingsStore.java
@@ -37,7 +37,11 @@
     private int mPersistWifiState = WIFI_DISABLED;
     /* Tracks current airplane mode state */
     private boolean mAirplaneModeOn = false;
-    /* Tracks whether wifi is enabled from WifiStateMachine's perspective */
+
+    /* Tracks the setting of scan being available even when wi-fi is turned off
+     */
+    private boolean mScanAlwaysAvailable;
+
     private final Context mContext;
 
     /* Tracks if we have checked the saved wi-fi state after boot */
@@ -47,9 +51,10 @@
         mContext = context;
         mAirplaneModeOn = getPersistedAirplaneModeOn();
         mPersistWifiState = getPersistedWifiState();
+        mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
     }
 
-    synchronized boolean shouldWifiBeEnabled() {
+    synchronized boolean isWifiToggleEnabled() {
         if (!mCheckSavedStateAtBoot) {
             mCheckSavedStateAtBoot = true;
             if (testAndClearWifiSavedState()) return true;
@@ -70,6 +75,10 @@
        return mAirplaneModeOn;
     }
 
+    synchronized boolean isScanAlwaysAvailable() {
+        return mScanAlwaysAvailable;
+    }
+
     synchronized boolean handleWifiToggled(boolean wifiEnabled) {
         // Can Wi-Fi be toggled in airplane mode ?
         if (mAirplaneModeOn && !isAirplaneToggleable()) {
@@ -114,6 +123,10 @@
         return true;
     }
 
+    synchronized void handleWifiScanAlwaysAvailableToggled() {
+        mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
+    }
+
     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("mPersistWifiState " + mPersistWifiState);
         pw.println("mAirplaneModeOn " + mAirplaneModeOn);
@@ -175,4 +188,10 @@
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
     }
+
+    private boolean getPersistedScanAlwaysAvailable() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
+                0) == 1;
+    }
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index bef5824..e0684fb 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -71,6 +71,10 @@
 
     DhcpInfo getDhcpInfo();
 
+    boolean isScanningAlwaysAvailable();
+
+    void setScanningAlwaysAvailable(boolean enable);
+
     boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws);
 
     void updateWifiLockWorkSource(IBinder lock, in WorkSource ws);
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index eb2f74c..2385c24 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -395,6 +395,23 @@
         return ret;
     }
 
+    void disableAllNetworks() {
+        boolean networkDisabled = false;
+        for(WifiConfiguration config : mConfiguredNetworks.values()) {
+            if(config != null && config.status != Status.DISABLED) {
+                if(mWifiNative.disableNetwork(config.networkId)) {
+                    networkDisabled = true;
+                    config.status = Status.DISABLED;
+                } else {
+                    loge("Disable network failed on " + config.networkId);
+                }
+            }
+        }
+
+        if (networkDisabled) {
+            sendConfiguredNetworksChangedBroadcast();
+        }
+    }
     /**
      * Disable a network. Note that there is no saveConfig operation.
      * @param netId network to be disabled
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index ed5d22c..8cdfe03 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -134,7 +134,12 @@
     private boolean mEnableBackgroundScan = false;
     private int mRssiPollToken = 0;
     private int mReconnectCount = 0;
-    private boolean mIsScanMode = false;
+    /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE
+    * In CONNECT_MODE, the STA can scan and connect to an access point
+    * In SCAN_ONLY_MODE, the STA can only scan for access points
+    * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off
+    */
+    private int mOperationalMode = CONNECT_MODE;
     private boolean mScanResultIsPending = false;
     /* Tracks if state machine has received any screen state change broadcast yet.
      * We can miss one of these at boot.
@@ -285,8 +290,8 @@
     /* Supplicant commands after driver start*/
     /* Initiate a scan */
     static final int CMD_START_SCAN                       = BASE + 71;
-    /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
-    static final int CMD_SET_SCAN_MODE                    = BASE + 72;
+    /* Set operational mode. CONNECT, SCAN ONLY, SCAN_ONLY with Wi-Fi off mode */
+    static final int CMD_SET_OPERATIONAL_MODE             = BASE + 72;
     /* Disconnect from a network */
     static final int CMD_DISCONNECT                       = BASE + 73;
     /* Reconnect to a network */
@@ -342,16 +347,13 @@
     public static final int CMD_DISABLE_P2P_REQ           = BASE + 132;
     public static final int CMD_DISABLE_P2P_RSP           = BASE + 133;
 
-    private static final int CONNECT_MODE   = 1;
-    private static final int SCAN_ONLY_MODE = 2;
+    public static final int CONNECT_MODE                   = 1;
+    public static final int SCAN_ONLY_MODE                 = 2;
+    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE   = 3;
 
     private static final int SUCCESS = 1;
     private static final int FAILURE = -1;
 
-    /* Phone in emergency call back mode */
-    private static final int IN_ECM_STATE = 1;
-    private static final int NOT_IN_ECM_STATE = 0;
-
     /**
      * The maximum number of times we will retry a connection to an access point
      * for which we have failed in acquiring an IP address from DHCP. A value of
@@ -496,9 +498,6 @@
      */
     private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
 
-    private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
-    private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
-
     private static final int SCAN_REQUEST = 0;
     private static final String ACTION_START_SCAN =
         "com.android.server.WifiManager.action.START_SCAN";
@@ -623,7 +622,7 @@
                     @Override
                     public void onReceive(Context context, Intent intent) {
                        int counter = intent.getIntExtra(DELAYED_STOP_COUNTER, 0);
-                       sendMessage(obtainMessage(CMD_DELAYED_STOP_DRIVER, counter, 0));
+                       sendMessage(CMD_DELAYED_STOP_DRIVER, counter, 0);
                     }
                 },
                 new IntentFilter(ACTION_DELAYED_DRIVER_STOP));
@@ -714,8 +713,7 @@
     /**
      * TODO: doc
      */
-    public void setWifiEnabled(boolean enable) {
-        mLastEnableUid.set(Binder.getCallingUid());
+    public void setSupplicantRunning(boolean enable) {
         if (enable) {
             sendMessage(CMD_START_SUPPLICANT);
         } else {
@@ -726,10 +724,9 @@
     /**
      * TODO: doc
      */
-    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
-        mLastApEnableUid.set(Binder.getCallingUid());
+    public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
         if (enable) {
-            sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
+            sendMessage(CMD_START_AP, wifiConfig);
         } else {
             sendMessage(CMD_STOP_AP);
         }
@@ -818,27 +815,23 @@
     /**
      * TODO: doc
      */
-    public void setDriverStart(boolean enable, boolean ecm) {
+    public void setDriverStart(boolean enable) {
         if (enable) {
             sendMessage(CMD_START_DRIVER);
         } else {
-            sendMessage(obtainMessage(CMD_STOP_DRIVER, ecm ? IN_ECM_STATE : NOT_IN_ECM_STATE, 0));
+            sendMessage(CMD_STOP_DRIVER);
         }
     }
 
     public void captivePortalCheckComplete() {
-        sendMessage(obtainMessage(CMD_CAPTIVE_CHECK_COMPLETE));
+        sendMessage(CMD_CAPTIVE_CHECK_COMPLETE);
     }
 
     /**
      * TODO: doc
      */
-    public void setScanOnlyMode(boolean enable) {
-        if (enable) {
-            sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
-        } else {
-            sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
-        }
+    public void setOperationalMode(int mode) {
+        sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
     }
 
     /**
@@ -941,7 +934,7 @@
      * @param bssid BSSID of the network
      */
     public void addToBlacklist(String bssid) {
-        sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
+        sendMessage(CMD_BLACKLIST_NETWORK, bssid);
     }
 
     /**
@@ -949,15 +942,15 @@
      *
      */
     public void clearBlacklist() {
-        sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
+        sendMessage(CMD_CLEAR_BLACKLIST);
     }
 
     public void enableRssiPolling(boolean enabled) {
-       sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+       sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
     }
 
     public void enableBackgroundScanCommand(boolean enabled) {
-       sendMessage(obtainMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0));
+       sendMessage(CMD_ENABLE_BACKGROUND_SCAN, enabled ? 1 : 0, 0);
     }
 
     public void enableAllNetworks() {
@@ -969,7 +962,7 @@
      */
     public void startFilteringMulticastV4Packets() {
         mFilteringMulticastV4Packets.set(true);
-        sendMessage(obtainMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0));
+        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V4, 0);
     }
 
     /**
@@ -977,21 +970,21 @@
      */
     public void stopFilteringMulticastV4Packets() {
         mFilteringMulticastV4Packets.set(false);
-        sendMessage(obtainMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0));
+        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V4, 0);
     }
 
     /**
      * Start filtering Multicast v4 packets
      */
     public void startFilteringMulticastV6Packets() {
-        sendMessage(obtainMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0));
+        sendMessage(CMD_START_PACKET_FILTERING, MULTICAST_V6, 0);
     }
 
     /**
      * Stop filtering Multicast v4 packets
      */
     public void stopFilteringMulticastV6Packets() {
-        sendMessage(obtainMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0));
+        sendMessage(CMD_STOP_PACKET_FILTERING, MULTICAST_V6, 0);
     }
 
     /**
@@ -1001,7 +994,7 @@
      * @param enable true if enable, false otherwise
      */
     public void setHighPerfModeEnabled(boolean enable) {
-        sendMessage(obtainMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0));
+        sendMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0);
     }
 
     /**
@@ -1015,7 +1008,7 @@
                     Settings.Global.WIFI_COUNTRY_CODE,
                     countryCode);
         }
-        sendMessage(obtainMessage(CMD_SET_COUNTRY_CODE, countryCode));
+        sendMessage(CMD_SET_COUNTRY_CODE, countryCode);
     }
 
     /**
@@ -1029,7 +1022,7 @@
                     Settings.Global.WIFI_FREQUENCY_BAND,
                     band);
         }
-        sendMessage(obtainMessage(CMD_SET_FREQUENCY_BAND, band, 0));
+        sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
     }
 
     /**
@@ -1050,7 +1043,7 @@
      * Send a message indicating bluetooth adapter connection state changed
      */
     public void sendBluetoothAdapterStateChange(int state) {
-        sendMessage(obtainMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0));
+        sendMessage(CMD_BLUETOOTH_ADAPTER_STATE_CHANGE, state, 0);
     }
 
     /**
@@ -1114,7 +1107,7 @@
         pw.println("mLastBssid " + mLastBssid);
         pw.println("mLastNetworkId " + mLastNetworkId);
         pw.println("mReconnectCount " + mReconnectCount);
-        pw.println("mIsScanMode " + mIsScanMode);
+        pw.println("mOperationalMode " + mOperationalMode);
         pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
         pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
         pw.println("Supplicant status " + mWifiNative.status());
@@ -1136,11 +1129,11 @@
         if (screenOn) enableAllNetworks();
         if (mUserWantsSuspendOpt.get()) {
             if (screenOn) {
-                sendMessage(obtainMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0));
+                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 0, 0);
             } else {
                 //Allow 2s for suspend optimizations to be set
                 mSuspendWakeLock.acquire(2000);
-                sendMessage(obtainMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0));
+                sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, 0);
             }
         }
         mScreenBroadcastReceived.set(true);
@@ -1914,7 +1907,7 @@
                 case WifiMonitor.WPS_OVERLAP_EVENT:
                 case CMD_BLACKLIST_NETWORK:
                 case CMD_CLEAR_BLACKLIST:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_RSSI_POLL:
@@ -1929,6 +1922,7 @@
                 case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
                 case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
                 case CMD_NO_NETWORKS_PERIODIC_SCAN:
+                case CMD_DISABLE_P2P_RSP:
                     break;
                 case DhcpStateMachine.CMD_ON_QUIT:
                     mDhcpStateMachine = null;
@@ -1942,8 +1936,8 @@
                     }
                     break;
                 case WifiMonitor.DRIVER_HUNG_EVENT:
-                    setWifiEnabled(false);
-                    setWifiEnabled(true);
+                    setSupplicantRunning(false);
+                    setSupplicantRunning(true);
                     break;
                 case WifiManager.CONNECT_NETWORK:
                     replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
@@ -2143,7 +2137,7 @@
                 case CMD_STOP_AP:
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
@@ -2160,8 +2154,6 @@
     class SupplicantStartedState extends State {
         @Override
         public void enter() {
-            /* Initialize for connect mode operation at start */
-            mIsScanMode = false;
             /* Wifi is available as long as we have a connection to supplicant */
             mNetworkInfo.setIsAvailable(true);
 
@@ -2176,7 +2168,6 @@
         }
         @Override
         public boolean processMessage(Message message) {
-            WifiConfiguration config;
             switch(message.what) {
                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
                     if (mP2pSupported) {
@@ -2206,87 +2197,13 @@
                     boolean ok = mWifiNative.ping();
                     replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
-                case CMD_ADD_OR_UPDATE_NETWORK:
-                    config = (WifiConfiguration) message.obj;
-                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
-                            mWifiConfigStore.addOrUpdateNetwork(config));
-                    break;
-                case CMD_REMOVE_NETWORK:
-                    ok = mWifiConfigStore.removeNetwork(message.arg1);
-                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
-                    break;
-                case CMD_ENABLE_NETWORK:
-                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
-                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
-                    break;
-                case CMD_ENABLE_ALL_NETWORKS:
-                    long time =  android.os.SystemClock.elapsedRealtime();
-                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
-                        mWifiConfigStore.enableAllNetworks();
-                        mLastEnableAllNetworksTime = time;
-                    }
-                    break;
-                case WifiManager.DISABLE_NETWORK:
-                    if (mWifiConfigStore.disableNetwork(message.arg1,
-                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
-                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
-                    } else {
-                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
-                                WifiManager.ERROR);
-                    }
-                    break;
-                case CMD_BLACKLIST_NETWORK:
-                    mWifiNative.addToBlacklist((String)message.obj);
-                    break;
-                case CMD_CLEAR_BLACKLIST:
-                    mWifiNative.clearBlacklist();
-                    break;
-                case CMD_SAVE_CONFIG:
-                    ok = mWifiConfigStore.saveConfig();
-                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
-
-                    // Inform the backup manager about a data change
-                    IBackupManager ibm = IBackupManager.Stub.asInterface(
-                            ServiceManager.getService(Context.BACKUP_SERVICE));
-                    if (ibm != null) {
-                        try {
-                            ibm.dataChanged("com.android.providers.settings");
-                        } catch (Exception e) {
-                            // Try again later
-                        }
-                    }
-                    break;
-                case CMD_GET_CONFIGURED_NETWORKS:
-                    replyToMessage(message, message.what,
-                            mWifiConfigStore.getConfiguredNetworks());
-                    break;
                     /* Cannot start soft AP while in client mode */
                 case CMD_START_AP:
                     loge("Failed to start soft AP with a running supplicant");
                     setWifiApState(WIFI_AP_STATE_FAILED);
                     break;
-                case CMD_SET_SCAN_MODE:
-                    mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
-                    break;
-                case WifiManager.SAVE_NETWORK:
-                    config = (WifiConfiguration) message.obj;
-                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
-                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
-                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
-                    } else {
-                        loge("Failed to save network");
-                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
-                                WifiManager.ERROR);
-                    }
-                    break;
-                case WifiManager.FORGET_NETWORK:
-                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
-                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
-                    } else {
-                        loge("Failed to forget network");
-                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
-                                WifiManager.ERROR);
-                    }
+                case CMD_SET_OPERATIONAL_MODE:
+                    mOperationalMode = message.arg1;
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2344,7 +2261,7 @@
                 case CMD_STOP_AP:
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
@@ -2452,7 +2369,7 @@
                 mWifiNative.stopFilteringMulticastV4Packets();
             }
 
-            if (mIsScanMode) {
+            if (mOperationalMode != CONNECT_MODE) {
                 mWifiNative.disconnect();
                 transitionTo(mScanModeState);
             } else {
@@ -2510,8 +2427,8 @@
                 case CMD_STOP_DRIVER:
                     int mode = message.arg1;
 
-                    /* Already doing a delayed stop && not in ecm state */
-                    if (mInDelayedStop && mode != IN_ECM_STATE) {
+                    /* Already doing a delayed stop */
+                    if (mInDelayedStop) {
                         if (DBG) log("Already in delayed stop");
                         break;
                     }
@@ -2519,20 +2436,15 @@
                     mDelayedStopCounter++;
                     if (DBG) log("Delayed stop message " + mDelayedStopCounter);
 
-                    if (mode == IN_ECM_STATE) {
-                        /* send a shut down immediately */
-                        sendMessage(obtainMessage(CMD_DELAYED_STOP_DRIVER, mDelayedStopCounter, 0));
-                    } else {
-                        /* send regular delayed shut down */
-                        Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
-                        driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
-                        mDriverStopIntent = PendingIntent.getBroadcast(mContext,
-                                DRIVER_STOP_REQUEST, driverStopIntent,
-                                PendingIntent.FLAG_UPDATE_CURRENT);
+                    /* send regular delayed shut down */
+                    Intent driverStopIntent = new Intent(ACTION_DELAYED_DRIVER_STOP, null);
+                    driverStopIntent.putExtra(DELAYED_STOP_COUNTER, mDelayedStopCounter);
+                    mDriverStopIntent = PendingIntent.getBroadcast(mContext,
+                            DRIVER_STOP_REQUEST, driverStopIntent,
+                            PendingIntent.FLAG_UPDATE_CURRENT);
 
-                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
-                                + mDriverStopDelayMs, mDriverStopIntent);
-                    }
+                    mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+                            + mDriverStopDelayMs, mDriverStopIntent);
                     break;
                 case CMD_START_DRIVER:
                     if (mInDelayedStop) {
@@ -2638,7 +2550,7 @@
                 case CMD_STOP_AP:
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
@@ -2713,30 +2625,42 @@
     }
 
     class ScanModeState extends State {
+        private int mLastOperationMode;
+        @Override
+        public void enter() {
+            mWifiConfigStore.disableAllNetworks();
+            mLastOperationMode = mOperationalMode;
+            if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
+                mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
+                setWifiState(WIFI_STATE_DISABLED);
+            }
+        }
+        @Override
+        public void exit() {
+            if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
+                setWifiState(WIFI_STATE_ENABLED);
+                mWifiP2pChannel.sendMessage(CMD_ENABLE_P2P);
+            }
+            mWifiConfigStore.enableAllNetworks();
+            mWifiNative.reconnect();
+        }
         @Override
         public boolean processMessage(Message message) {
             switch(message.what) {
-                case CMD_SET_SCAN_MODE:
-                    if (message.arg1 == SCAN_ONLY_MODE) {
-                        /* Ignore */
-                        return HANDLED;
-                    } else {
-                        mWifiNative.reconnect();
-                        mIsScanMode = false;
+                case CMD_SET_OPERATIONAL_MODE:
+                    if (message.arg1 == CONNECT_MODE) {
+                        mOperationalMode = CONNECT_MODE;
                         transitionTo(mDisconnectedState);
+                    } else {
+                        // Nothing to do
+                        return HANDLED;
                     }
                     break;
+                // Handle scan. All the connection related commands are
+                // handled only in ConnectModeState
                 case CMD_START_SCAN:
                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
                     break;
-                    /* Ignore */
-                case CMD_DISCONNECT:
-                case CMD_RECONNECT:
-                case CMD_REASSOCIATE:
-                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
-                case WifiMonitor.NETWORK_CONNECTION_EVENT:
-                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
-                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -2747,7 +2671,8 @@
     class ConnectModeState extends State {
         @Override
         public boolean processMessage(Message message) {
-            StateChangeResult stateChangeResult;
+            WifiConfiguration config;
+            boolean ok;
             switch(message.what) {
                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
                     mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
@@ -2787,6 +2712,60 @@
                         mTemporarilyDisconnectWifi = false;
                     }
                     break;
+                case CMD_ADD_OR_UPDATE_NETWORK:
+                    config = (WifiConfiguration) message.obj;
+                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
+                            mWifiConfigStore.addOrUpdateNetwork(config));
+                    break;
+                case CMD_REMOVE_NETWORK:
+                    ok = mWifiConfigStore.removeNetwork(message.arg1);
+                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    break;
+                case CMD_ENABLE_NETWORK:
+                    ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
+                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    break;
+                case CMD_ENABLE_ALL_NETWORKS:
+                    long time =  android.os.SystemClock.elapsedRealtime();
+                    if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
+                        mWifiConfigStore.enableAllNetworks();
+                        mLastEnableAllNetworksTime = time;
+                    }
+                    break;
+                case WifiManager.DISABLE_NETWORK:
+                    if (mWifiConfigStore.disableNetwork(message.arg1,
+                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
+                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
+                    break;
+                case CMD_BLACKLIST_NETWORK:
+                    mWifiNative.addToBlacklist((String)message.obj);
+                    break;
+                case CMD_CLEAR_BLACKLIST:
+                    mWifiNative.clearBlacklist();
+                    break;
+                case CMD_SAVE_CONFIG:
+                    ok = mWifiConfigStore.saveConfig();
+                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
+
+                    // Inform the backup manager about a data change
+                    IBackupManager ibm = IBackupManager.Stub.asInterface(
+                            ServiceManager.getService(Context.BACKUP_SERVICE));
+                    if (ibm != null) {
+                        try {
+                            ibm.dataChanged("com.android.providers.settings");
+                        } catch (Exception e) {
+                            // Try again later
+                        }
+                    }
+                    break;
+                case CMD_GET_CONFIGURED_NETWORKS:
+                    replyToMessage(message, message.what,
+                            mWifiConfigStore.getConfiguredNetworks());
+                    break;
                     /* Do a redundant disconnect without transition */
                 case CMD_DISCONNECT:
                     mWifiNative.disconnect();
@@ -2804,7 +2783,7 @@
                      * For an existing network, a network id is passed
                      */
                     int netId = message.arg1;
-                    WifiConfiguration config = (WifiConfiguration) message.obj;
+                    config = (WifiConfiguration) message.obj;
 
                     /* Save the network config */
                     if (config != null) {
@@ -2826,26 +2805,46 @@
                         break;
                     }
                     break;
+                case WifiManager.SAVE_NETWORK:
+                    config = (WifiConfiguration) message.obj;
+                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
+                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
+                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+                    } else {
+                        loge("Failed to save network");
+                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
+                    break;
+                case WifiManager.FORGET_NETWORK:
+                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
+                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
+                    } else {
+                        loge("Failed to forget network");
+                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
+                    break;
                 case WifiManager.START_WPS:
                     WpsInfo wpsInfo = (WpsInfo) message.obj;
-                    WpsResult result;
+                    WpsResult wpsResult;
                     switch (wpsInfo.setup) {
                         case WpsInfo.PBC:
-                            result = mWifiConfigStore.startWpsPbc(wpsInfo);
+                            wpsResult = mWifiConfigStore.startWpsPbc(wpsInfo);
                             break;
                         case WpsInfo.KEYPAD:
-                            result = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
+                            wpsResult = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
                             break;
                         case WpsInfo.DISPLAY:
-                            result = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
+                            wpsResult = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
                             break;
                         default:
-                            result = new WpsResult(Status.FAILURE);
+                            wpsResult = new WpsResult(Status.FAILURE);
                             loge("Invalid setup for WPS");
                             break;
                     }
-                    if (result.status == Status.SUCCESS) {
-                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, result);
+                    if (wpsResult.status == Status.SUCCESS) {
+                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
                         transitionTo(mWpsRunningState);
                     } else {
                         loge("Failed to start WPS with config " + wpsInfo.toString());
@@ -2881,7 +2880,7 @@
         public void enter() {
             mRssiPollToken++;
             if (mEnableRssiPolling) {
-                sendMessage(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0));
+                sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
             }
         }
 
@@ -2915,8 +2914,8 @@
                         transitionTo(mDisconnectingState);
                     }
                     break;
-                case CMD_SET_SCAN_MODE:
-                    if (message.arg1 == SCAN_ONLY_MODE) {
+                case CMD_SET_OPERATIONAL_MODE:
+                    if (message.arg1 != CONNECT_MODE) {
                         sendMessage(CMD_DISCONNECT);
                         deferMessage(message);
                     }
@@ -3152,8 +3151,8 @@
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                case CMD_SET_SCAN_MODE:
-                    if (message.arg1 == SCAN_ONLY_MODE) {
+                case CMD_SET_OPERATIONAL_MODE:
+                    if (message.arg1 != CONNECT_MODE) {
                         deferMessage(message);
                     }
                     break;
@@ -3259,11 +3258,9 @@
                                 ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
                     ret = NOT_HANDLED;
                     break;
-                case CMD_SET_SCAN_MODE:
-                    if (message.arg1 == SCAN_ONLY_MODE) {
-                        //Supplicant disconnect to prevent further connects
-                        mWifiNative.disconnect();
-                        mIsScanMode = true;
+                case CMD_SET_OPERATIONAL_MODE:
+                    if (message.arg1 != CONNECT_MODE) {
+                        mOperationalMode = message.arg1;
                         transitionTo(mScanModeState);
                     }
                     break;
@@ -3396,7 +3393,7 @@
                  * or put the state machine out of connect mode
                  */
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case WifiManager.CONNECT_NETWORK:
                 case CMD_ENABLE_NETWORK:
                 case CMD_RECONNECT:
@@ -3459,7 +3456,7 @@
                 case CMD_STOP_AP:
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
@@ -3547,7 +3544,8 @@
                     if (message.arg1 == mTetherToken) {
                         loge("Failed to get tether update, shutdown soft access point");
                         transitionTo(mSoftApStartedState);
-                        sendMessage(CMD_STOP_AP);
+                        // Needs to be first thing handled
+                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
                     }
                     break;
                 case CMD_START_SUPPLICANT:
@@ -3556,7 +3554,7 @@
                 case CMD_STOP_AP:
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
@@ -3578,7 +3576,7 @@
                     TetherStateChange stateChange = (TetherStateChange) message.obj;
                     if (!isWifiTethered(stateChange.active)) {
                         loge("Tethering reports wifi as untethered!, shut down soft Ap");
-                        setWifiApEnabled(null, false);
+                        setHostApRunning(null, false);
                     }
                     return HANDLED;
                 case CMD_STOP_AP:
@@ -3612,13 +3610,15 @@
                     if (isWifiTethered(stateChange.active)) break;
 
                     transitionTo(mSoftApStartedState);
-                    sendMessage(CMD_STOP_AP);
+                    // Needs to be first thing handled
+                    sendMessageAtFrontOfQueue(CMD_STOP_AP);
                     break;
                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
                     if (message.arg1 == mTetherToken) {
                         loge("Failed to get tether update, force stop access point");
                         transitionTo(mSoftApStartedState);
-                        sendMessage(CMD_STOP_AP);
+                        // Needs to be first thing handled
+                        sendMessageAtFrontOfQueue(CMD_STOP_AP);
                     }
                     break;
                 case CMD_START_SUPPLICANT:
@@ -3627,7 +3627,7 @@
                 case CMD_STOP_AP:
                 case CMD_START_DRIVER:
                 case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
+                case CMD_SET_OPERATIONAL_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 55ea34f..81d2e11 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -252,4 +252,14 @@
     public void setDependencyMet(boolean met) {
         // not supported on this network
     }
+
+    @Override
+    public void addStackedLink(LinkProperties link) {
+        mLinkProperties.addStackedLink(link);
+    }
+
+    @Override
+    public void removeStackedLink(LinkProperties link) {
+        mLinkProperties.removeStackedLink(link);
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 53e6b51..eb47a4a 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -587,8 +587,9 @@
                     break;
 
                 case EVENT_WIFI_RADIO_STATE_CHANGE:
-                    if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING)
+                    if (msg.arg1 == WifiManager.WIFI_STATE_DISABLING) {
                         transitionTo(mNotConnectedState);
+                    }
                     break;
 
                 default: