Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c249c2cc authored by Vinit Deshapnde's avatar Vinit Deshapnde Committed by Robert Greenwalt
Browse files

Use a single socket to communicate with supplicant

This helps to prepare for future updates from external sources.

Bug: 9298955

Change-Id: I4c63ad5fc1ea3564aab38cfce955de19bad75c0c
(cherry picked from commit fb40801ed8c217ae01082fb1cbd0c30bbf5532ac)
parent f6971c80
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -141,7 +141,7 @@ static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstr
                dns, server, &lease, vendorInfo, domains, mtu);
    }
    if (result != 0) {
        ALOGD("dhcp_do_request failed");
        ALOGD("dhcp_do_request failed : %s (%s)", nameStr, renew ? "renew" : "new");
    }

    env->ReleaseStringUTFChars(ifname, nameStr);
+26 −40
Original line number Diff line number Diff line
@@ -33,11 +33,11 @@ namespace android {

static jint DBG = false;

static int doCommand(const char *ifname, char *cmd, char *replybuf, int replybuflen)
static int doCommand(char *cmd, char *replybuf, int replybuflen)
{
    size_t reply_len = replybuflen - 1;

    if (::wifi_command(ifname, cmd, BUF_SIZE, replybuf, &reply_len) != 0)
    if (::wifi_command(cmd, replybuf, &reply_len) != 0)
        return -1;
    else {
        // Strip off trailing newline
@@ -49,7 +49,7 @@ static int doCommand(const char *ifname, char *cmd, char *replybuf, int replybuf
    }
}

static jint doIntCommand(const char *ifname, const char* fmt, ...)
static jint doIntCommand(const char* fmt, ...)
{
    char buf[BUF_SIZE];
    va_list args;
@@ -60,13 +60,13 @@ static jint doIntCommand(const char *ifname, const char* fmt, ...)
        return -1;
    }
    char reply[BUF_SIZE];
    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {
    if (doCommand(buf, reply, sizeof(reply)) != 0) {
        return -1;
    }
    return static_cast<jint>(atoi(reply));
}

static jboolean doBooleanCommand(const char *ifname, const char* expect, const char* fmt, ...)
static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
{
    char buf[BUF_SIZE];
    va_list args;
@@ -77,14 +77,14 @@ static jboolean doBooleanCommand(const char *ifname, const char* expect, const c
        return JNI_FALSE;
    }
    char reply[BUF_SIZE];
    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {
    if (doCommand(buf, reply, sizeof(reply)) != 0) {
        return JNI_FALSE;
    }
    return (strcmp(reply, expect) == 0);
}

// Send a command to the supplicant, and return the reply as a String
static jstring doStringCommand(JNIEnv* env, const char *ifname, const char* fmt, ...) {
static jstring doStringCommand(JNIEnv* env, const char* fmt, ...) {
    char buf[BUF_SIZE];
    va_list args;
    va_start(args, fmt);
@@ -94,7 +94,7 @@ static jstring doStringCommand(JNIEnv* env, const char *ifname, const char* fmt,
        return NULL;
    }
    char reply[4096];
    if (doCommand(ifname, buf, reply, sizeof(reply)) != 0) {
    if (doCommand(buf, reply, sizeof(reply)) != 0) {
        return NULL;
    }
    // TODO: why not just NewStringUTF?
@@ -127,23 +127,20 @@ static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject, jboolean p
    return (jboolean)(::wifi_stop_supplicant(p2pSupported) == 0);
}

static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface)
static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject)
{
    ScopedUtfChars ifname(env, jIface);
    return (jboolean)(::wifi_connect_to_supplicant(ifname.c_str()) == 0);
    return (jboolean)(::wifi_connect_to_supplicant() == 0);
}

static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject, jstring jIface)
static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject)
{
    ScopedUtfChars ifname(env, jIface);
    ::wifi_close_supplicant_connection(ifname.c_str());
    ::wifi_close_supplicant_connection();
}

static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject, jstring jIface)
static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject)
{
    char buf[EVENT_BUF_SIZE];
    ScopedUtfChars ifname(env, jIface);
    int nread = ::wifi_wait_for_event(ifname.c_str(), buf, sizeof buf);
    int nread = ::wifi_wait_for_event(buf, sizeof buf);
    if (nread > 0) {
        return env->NewStringUTF(buf);
    } else {
@@ -151,43 +148,36 @@ static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject, jstring jIfac
    }
}

static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jobject, jstring jIface,
        jstring jCommand)
static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jobject, jstring jCommand)
{
    ScopedUtfChars ifname(env, jIface);
    ScopedUtfChars command(env, jCommand);

    if (command.c_str() == NULL) {
        return JNI_FALSE;
    }
    if (DBG) ALOGD("doBoolean: %s", command.c_str());
    return doBooleanCommand(ifname.c_str(), "OK", "%s", command.c_str());
    return doBooleanCommand("OK", "%s", command.c_str());
}

static jint android_net_wifi_doIntCommand(JNIEnv* env, jobject, jstring jIface,
        jstring jCommand)
static jint android_net_wifi_doIntCommand(JNIEnv* env, jobject, jstring jCommand)
{
    ScopedUtfChars ifname(env, jIface);
    ScopedUtfChars command(env, jCommand);

    if (command.c_str() == NULL) {
        return -1;
    }
    if (DBG) ALOGD("doInt: %s", command.c_str());
    return doIntCommand(ifname.c_str(), "%s", command.c_str());
    return doIntCommand("%s", command.c_str());
}

static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring jIface,
        jstring jCommand)
static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring jCommand)
{
    ScopedUtfChars ifname(env, jIface);

    ScopedUtfChars command(env, jCommand);
    if (command.c_str() == NULL) {
        return NULL;
    }
    if (DBG) ALOGD("doString: %s", command.c_str());
    return doStringCommand(env, ifname.c_str(), "%s", command.c_str());
    return doStringCommand(env, "%s", command.c_str());
}


@@ -205,17 +195,13 @@ static JNINativeMethod gWifiMethods[] = {
    { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
    { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
    { "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },
    { "connectToSupplicant", "(Ljava/lang/String;)Z",
            (void *)android_net_wifi_connectToSupplicant },
    { "closeSupplicantConnection", "(Ljava/lang/String;)V",
    { "connectToSupplicantNative", "()Z", (void *)android_net_wifi_connectToSupplicant },
    { "closeSupplicantConnectionNative", "()V",
            (void *)android_net_wifi_closeSupplicantConnection },
    { "waitForEvent", "(Ljava/lang/String;)Ljava/lang/String;",
            (void*) android_net_wifi_waitForEvent },
    { "doBooleanCommand", "(Ljava/lang/String;Ljava/lang/String;)Z",
            (void*) android_net_wifi_doBooleanCommand },
    { "doIntCommand", "(Ljava/lang/String;Ljava/lang/String;)I",
            (void*) android_net_wifi_doIntCommand },
    { "doStringCommand", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
    { "waitForEventNative", "()Ljava/lang/String;", (void*)android_net_wifi_waitForEvent },
    { "doBooleanCommandNative", "(Ljava/lang/String;)Z", (void*)android_net_wifi_doBooleanCommand },
    { "doIntCommandNative", "(Ljava/lang/String;)I", (void*)android_net_wifi_doIntCommand },
    { "doStringCommandNative", "(Ljava/lang/String;)Ljava/lang/String;",
            (void*) android_net_wifi_doStringCommand },
};

+328 −178
Original line number Diff line number Diff line
@@ -32,7 +32,10 @@ import android.util.Log;
import com.android.internal.util.Protocol;
import com.android.internal.util.StateMachine;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

@@ -44,6 +47,7 @@ import java.util.regex.Matcher;
 */
public class WifiMonitor {

    private static final boolean DBG = false;
    private static final String TAG = "WifiMonitor";

    /** Events we receive from the supplicant daemon */
@@ -279,9 +283,6 @@ public class WifiMonitor {
    /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
    private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";

    private final StateMachine mStateMachine;
    private final WifiNative mWifiNative;

    /* Supplicant events reported to a state machine */
    private static final int BASE = Protocol.BASE_WIFI_MONITOR;

@@ -346,49 +347,222 @@ public class WifiMonitor {
     */
    private static final String WPA_RECV_ERROR_STR = "recv error";

    /**
     * Tracks consecutive receive errors
     */
    private int mRecvErrors = 0;

    /**
     * Max errors before we close supplicant connection
     */
    private static final int MAX_RECV_ERRORS    = 10;

    private final String mInterfaceName;
    private final WifiNative mWifiNative;
    private final StateMachine mWifiStateMachine;
    private boolean mMonitoring;

    public WifiMonitor(StateMachine wifiStateMachine, WifiNative wifiNative) {
        mStateMachine = wifiStateMachine;
        if (DBG) Log.d(TAG, "Creating WifiMonitor");
        mWifiNative = wifiNative;
        mInterfaceName = wifiNative.mInterfaceName;
        mWifiStateMachine = wifiStateMachine;
        mMonitoring = false;

        WifiMonitorSingleton.getMonitor().registerInterfaceMonitor(mInterfaceName, this);
    }

    public void startMonitoring() {
        new MonitorThread().start();
        WifiMonitorSingleton.getMonitor().startMonitoring(mInterfaceName);
    }

    class MonitorThread extends Thread {
        public MonitorThread() {
            super("WifiMonitor");
    public void stopMonitoring() {
        WifiMonitorSingleton.getMonitor().stopMonitoring(mInterfaceName);
    }

        public void run() {
    public void stopSupplicant() {
        WifiMonitorSingleton.getMonitor().stopSupplicant();
    }

            if (connectToSupplicant()) {
                // Send a message indicating that it is now possible to send commands
                // to the supplicant
                mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
            } else {
                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
    public void killSupplicant(boolean p2pSupported) {
        WifiMonitorSingleton.getMonitor().killSupplicant(p2pSupported);
    }

    private static class WifiMonitorSingleton {
        private static Object sSingletonLock = new Object();
        private static WifiMonitorSingleton sWifiMonitorSingleton = null;
        private HashMap<String, WifiMonitor> mIfaceMap = new HashMap<String, WifiMonitor>();
        private boolean mConnected = false;
        private WifiNative mWifiNative;

        private WifiMonitorSingleton() {
        }

        static WifiMonitorSingleton getMonitor() {
            if (DBG) Log.d(TAG, "WifiMonitorSingleton gotten");
            synchronized (sSingletonLock) {
                if (sWifiMonitorSingleton == null) {
                    if (DBG) Log.d(TAG, "WifiMonitorSingleton created");
                    sWifiMonitorSingleton = new WifiMonitorSingleton();
                }
            }
            return sWifiMonitorSingleton;
        }

        public synchronized void startMonitoring(String iface) {
            WifiMonitor m = mIfaceMap.get(iface);
            if (m == null) {
                Log.e(TAG, "startMonitor called with unknown iface=" + iface);
                return;
            }

            Log.d(TAG, "startMonitoring(" + iface + ") with mConnected = " + mConnected);

            if (mConnected) {
                m.mMonitoring = true;
                m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT);
            } else {
                if (DBG) Log.d(TAG, "connecting to supplicant");
                int connectTries = 0;
                while (true) {
                    if (mWifiNative.connectToSupplicant()) {
                        m.mMonitoring = true;
                        m.mWifiStateMachine.sendMessage(SUP_CONNECTION_EVENT);
                        new MonitorThread(mWifiNative, this).start();
                        mConnected = true;
                        break;
                    }
                    if (connectTries++ < 5) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ignore) {
                        }
                    } else {
                        mIfaceMap.remove(iface);
                        m.mWifiStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
                        break;
                    }
                }
            }
        }

        public synchronized void stopMonitoring(String iface) {
            WifiMonitor m = mIfaceMap.get(iface);
            if (DBG) Log.d(TAG, "stopMonitoring(" + iface + ") = " + m.mWifiStateMachine);
            m.mMonitoring = false;
            m.mWifiStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
        }

        public synchronized void registerInterfaceMonitor(String iface, WifiMonitor m) {
            if (DBG) Log.d(TAG, "registerInterface(" + iface + "+" + m.mWifiStateMachine + ")");
            mIfaceMap.put(iface, m);
            if (mWifiNative == null) {
                mWifiNative = m.mWifiNative;
            }
        }

        public synchronized void unregisterInterfaceMonitor(String iface) {
            // REVIEW: When should we call this? If this isn't called, then WifiMonitor
            // objects will remain in the mIfaceMap; and won't ever get deleted

            WifiMonitor m = mIfaceMap.remove(iface);
            if (DBG) Log.d(TAG, "unregisterInterface(" + iface + "+" + m.mWifiStateMachine + ")");
        }

        public synchronized void stopSupplicant() {
            mWifiNative.stopSupplicant();
        }

        public synchronized void killSupplicant(boolean p2pSupported) {
            mWifiNative.killSupplicant(p2pSupported);
            mConnected = false;
            Iterator<Map.Entry<String, WifiMonitor>> it = mIfaceMap.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, WifiMonitor> e = it.next();
                WifiMonitor m = e.getValue();
                m.mMonitoring = false;
            }
        }

        private synchronized WifiMonitor getMonitor(String iface) {
            return mIfaceMap.get(iface);
        }
    }

    private static class MonitorThread extends Thread {
        private final WifiNative mWifiNative;
        private final WifiMonitorSingleton mWifiMonitorSingleton;
        private int mRecvErrors = 0;
        private StateMachine mStateMachine = null;

        public MonitorThread(WifiNative wifiNative, WifiMonitorSingleton wifiMonitorSingleton) {
            super("WifiMonitor");
            mWifiNative = wifiNative;
            mWifiMonitorSingleton = wifiMonitorSingleton;
        }

        public void run() {
            //noinspection InfiniteLoopStatement
            for (;;) {
                String eventStr = mWifiNative.waitForEvent();

                // Skip logging the common but mostly uninteresting scan-results event
                if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
                if (DBG && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
                    Log.d(TAG, "Event [" + eventStr + "]");
                }

                WifiMonitor m = null;
                mStateMachine = null;

                if (eventStr.startsWith("IFNAME=")) {
                    int space = eventStr.indexOf(' ');
                    if (space != -1) {
                        String iface = eventStr.substring(7,space);
                        m = mWifiMonitorSingleton.getMonitor(iface);
                        if (m != null) {
                            if (m.mMonitoring) {
                                mStateMachine = m.mWifiStateMachine;
                                eventStr = eventStr.substring(space + 1);
                            }
                            else {
                                if (DBG) Log.d(TAG, "Dropping event because monitor (" + iface +
                                        ") is stopped");
                                continue;
                            }
                        }
                        else {
                            eventStr = eventStr.substring(space + 1);
                        }
                    }
                }

                if (mStateMachine != null) {
                    if (dispatchEvent(eventStr)) {
                        break;
                    }
                } else {
                    if (DBG) Log.d(TAG, "Sending to all monitors because there's no interface id");
                    boolean done = false;
                    Iterator<Map.Entry<String, WifiMonitor>> it =
                            mWifiMonitorSingleton.mIfaceMap.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry<String, WifiMonitor> e = it.next();
                        m = e.getValue();
                        mStateMachine = m.mWifiStateMachine;
                        if (dispatchEvent(eventStr)) {
                            done = true;
                        }
                    }

                    if (done) {
                        // After this thread terminates, we'll no longer
                        // be connected to the supplicant
                        if (DBG) Log.d(TAG, "Disconnecting from the supplicant, no more events");
                        mWifiMonitorSingleton.mConnected = false;
                        break;
                    }
                }
            }
        }

        /* @return true if the event was supplicant disconnection */
        private boolean dispatchEvent(String eventStr) {

            if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
                if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
                        0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
@@ -406,7 +580,10 @@ public class WifiMonitor {
                } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
                    handleHostApEvents(eventStr);
                }
                    continue;
                else {
                    if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
                }
                return false;
            }

            String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
@@ -414,8 +591,8 @@ public class WifiMonitor {
            if (nameEnd != -1)
                eventName = eventName.substring(0, nameEnd);
            if (eventName.length() == 0) {
                    if (false) Log.i(TAG, "Received wpa_supplicant event with empty event name");
                    continue;
                if (DBG) Log.i(TAG, "Received wpa_supplicant event with empty event name");
                return false;
            }
            /*
             * Map event name into event enum
@@ -468,17 +645,17 @@ public class WifiMonitor {
                 */
                if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
                    if (++mRecvErrors > MAX_RECV_ERRORS) {
                            if (false) {
                        if (DBG) {
                            Log.d(TAG, "too many recv errors, closing connection");
                        }
                    } else {
                            continue;
                        return false;
                    }
                }

                // notify and exit
                mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
                    break;
                return true;
            } else if (event == EAP_FAILURE) {
                if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
                    mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
@@ -489,22 +666,6 @@ public class WifiMonitor {
                handleEvent(event, eventData);
            }
            mRecvErrors = 0;
            }
        }

        private boolean connectToSupplicant() {
            int connectTries = 0;

            while (true) {
                if (mWifiNative.connectToSupplicant()) {
                    return true;
                }
                if (connectTries++ < 5) {
                    nap(1);
                } else {
                    break;
                }
            }
            return false;
        }

@@ -723,7 +884,6 @@ public class WifiMonitor {
            }
            notifySupplicantStateChange(networkId, wifiSsid, BSSID, newSupplicantState);
        }
    }

        private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
            String BSSID = null;
@@ -731,7 +891,7 @@ public class WifiMonitor {
            if (newState == NetworkInfo.DetailedState.CONNECTED) {
                Matcher match = mConnectedEventPattern.matcher(data);
                if (!match.find()) {
                if (false) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
                    if (DBG) Log.d(TAG, "Could not find BSSID in CONNECTED event string");
                } else {
                    BSSID = match.group(1);
                    try {
@@ -740,9 +900,9 @@ public class WifiMonitor {
                        networkId = -1;
                    }
                }
        }
                notifyNetworkStateChange(newState, BSSID, networkId);
            }
        }

        /**
         * Send the state machine a notification that the state of Wifi connectivity
@@ -779,15 +939,5 @@ public class WifiMonitor {
            mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
                    new StateChangeResult(networkId, wifiSsid, BSSID, newState)));
        }

    /**
     * Sleep for a period of time.
     * @param secs the number of seconds to sleep
     */
    private static void nap(int secs) {
        try {
            Thread.sleep(secs * 1000);
        } catch (InterruptedException ignore) {
        }
    }
}
+28 −25

File changed.

Preview size limit exceeded, changes collapsed.

+6 −8
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.LruCache;
import android.text.TextUtils;

@@ -543,7 +544,6 @@ public class WifiStateMachine extends StateMachine {

    public WifiStateMachine(Context context, String wlanInterface) {
        super("WifiStateMachine");

        mContext = context;
        mInterfaceName = wlanInterface;

@@ -888,6 +888,7 @@ public class WifiStateMachine extends StateMachine {
     * TODO: doc
     */
    public void setOperationalMode(int mode) {
        if (DBG) log("setting operational mode to " + String.valueOf(mode));
        sendMessage(CMD_SET_OPERATIONAL_MODE, mode, 0);
    }

@@ -1756,8 +1757,7 @@ public class WifiStateMachine extends StateMachine {
        /* Socket connection can be lost when we do a graceful shutdown
        * or when the driver is hung. Ensure supplicant is stopped here.
        */
        mWifiNative.killSupplicant(mP2pSupported);
        mWifiNative.closeSupplicantConnection();
        mWifiMonitor.killSupplicant(mP2pSupported);
        sendSupplicantConnectionChangedBroadcast(false);
        setWifiState(WIFI_STATE_DISABLED);
    }
@@ -2139,7 +2139,7 @@ public class WifiStateMachine extends StateMachine {
                        * Avoids issues with drivers that do not handle interface down
                        * on a running supplicant properly.
                        */
                        mWifiNative.killSupplicant(mP2pSupported);
                        mWifiMonitor.killSupplicant(mP2pSupported);
                        if(mWifiNative.startSupplicant(mP2pSupported)) {
                            setWifiState(WIFI_STATE_ENABLING);
                            if (DBG) log("Supplicant start successful");
@@ -2222,7 +2222,7 @@ public class WifiStateMachine extends StateMachine {
                case WifiMonitor.SUP_DISCONNECTION_EVENT:
                    if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
                        loge("Failed to setup control channel, restart supplicant");
                        mWifiNative.killSupplicant(mP2pSupported);
                        mWifiMonitor.killSupplicant(mP2pSupported);
                        transitionTo(mInitialState);
                        sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                    } else {
@@ -2329,9 +2329,7 @@ public class WifiStateMachine extends StateMachine {
            }

            if (DBG) log("stopping supplicant");
            if (!mWifiNative.stopSupplicant()) {
                loge("Failed to stop supplicant");
            }
            mWifiMonitor.stopSupplicant();

            /* Send ourselves a delayed message to indicate failure after a wait time */
            sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
Loading