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

Commit 9575a1be authored by Irfan Sheriff's avatar Irfan Sheriff
Browse files

Convert soft ap config store to state machine

- Create thread when needed
- asynchronous interaction with wifi state machine

Bug: 5587391
Change-Id: Ie6d01b5a6b398a3fa12c384920a473e242443131
parent 19c86cac
Loading
Loading
Loading
Loading
+1 −6
Original line number Diff line number Diff line
@@ -619,12 +619,7 @@ public class WifiService extends IWifiManager.Stub {
     */
    public WifiConfiguration getWifiApConfiguration() {
        enforceAccessPermission();
        if (mWifiStateMachineChannel != null) {
            return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
        } else {
            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
            return null;
        }
        return mWifiStateMachine.syncGetWifiApConfiguration();
    }

    /**
+138 −107
Original line number Diff line number Diff line
@@ -19,11 +19,16 @@ package android.net.wifi;
import android.content.Context;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.Environment;
import android.os.Message;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;

import com.android.internal.util.AsyncChannel;
import com.android.internal.R;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
@@ -34,16 +39,13 @@ import java.io.IOException;
import java.net.InetAddress;
import java.util.UUID;

import com.android.internal.R;


/**
 * Provides API to the WifiStateMachine for doing read/write access
 * to soft access point configuration
 */
class WifiApConfigStore {
class WifiApConfigStore extends StateMachine {

    private static Context sContext;
    private Context mContext;
    private static final String TAG = "WifiApConfigStore";

    private static final String AP_CONFIG_FILE = Environment.getDataDirectory() +
@@ -51,87 +53,91 @@ class WifiApConfigStore {

    private static final int AP_CONFIG_FILE_VERSION = 1;

    private static WifiConfiguration sApConfig = new WifiConfiguration();
    private static final Object sApConfigLock = new Object();

    private static FileReadWriteHandler sFileReadWriteHandler;
    private static final int READ_AP_CONFIG               = 1;
    private static final int WRITE_AP_CONFIG              = 2;

    static void initialize(Context context) {
        sContext = context;
    private State mDefaultState = new DefaultState();
    private State mInactiveState = new InactiveState();
    private State mActiveState = new ActiveState();

        /* File operations happen on a seperate thread */
        HandlerThread configThread = new HandlerThread("WifiApConfigStore");
        configThread.start();
        sFileReadWriteHandler = new FileReadWriteHandler(configThread.getLooper());
        Message.obtain(sFileReadWriteHandler, READ_AP_CONFIG).sendToTarget();
    }
    private WifiConfiguration mWifiApConfig = null;
    private AsyncChannel mReplyChannel = new AsyncChannel();

    WifiApConfigStore(Context context, Handler target) {
        super(TAG, target.getLooper());

    static void setApConfiguration(WifiConfiguration config) {
        synchronized (sApConfigLock) {
            sApConfig = config;
        }
        Message.obtain(sFileReadWriteHandler, WRITE_AP_CONFIG, new WifiConfiguration(config))
            .sendToTarget();
    }
        mContext = context;
        addState(mDefaultState);
            addState(mInactiveState, mDefaultState);
            addState(mActiveState, mDefaultState);

    static WifiConfiguration getApConfiguration() {
        synchronized (sApConfigLock) {
            return new WifiConfiguration(sApConfig);
        }
        setInitialState(mInactiveState);
    }

    /**
     * File read/write handler
     */
    private static class FileReadWriteHandler extends Handler {

        public FileReadWriteHandler(android.os.Looper looper) {
            super(looper);
    public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) {
        WifiApConfigStore s = new WifiApConfigStore(context, target);
        s.start();
        return s;
    }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case WRITE_AP_CONFIG:
                    writeApConfiguration((WifiConfiguration) msg.obj);
    class DefaultState extends State {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case WifiStateMachine.CMD_SET_AP_CONFIG:
                case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
                    Log.e(TAG, "Unexpected message: " + message);
                    break;
                case READ_AP_CONFIG:
                    readApConfiguration();
                case WifiStateMachine.CMD_REQUEST_AP_CONFIG:
                    mReplyChannel.replyToMessage(message,
                            WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);
                    break;
                default:
                    Log.e(TAG, "Unknown command in FileReadWriteHandler: " + msg);
                    Log.e(TAG, "Failed to handle " + message);
                    break;
            }
            return HANDLED;
        }
    }

        private static void writeApConfiguration(final WifiConfiguration config) {
            DataOutputStream out = null;
            try {
                out = new DataOutputStream(new BufferedOutputStream(
                            new FileOutputStream(AP_CONFIG_FILE)));
    class InactiveState extends State {
        public boolean processMessage(Message message) {
            switch (message.what) {
                case WifiStateMachine.CMD_SET_AP_CONFIG:
                    mWifiApConfig = (WifiConfiguration) message.obj;
                    transitionTo(mActiveState);
                    break;
                default:
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }

                out.writeInt(AP_CONFIG_FILE_VERSION);
                out.writeUTF(config.SSID);
                int authType = config.getAuthType();
                out.writeInt(authType);
                if(authType != KeyMgmt.NONE) {
                    out.writeUTF(config.preSharedKey);
    class ActiveState extends State {
        public void enter() {
            new Thread(new Runnable() {
                public void run() {
                    writeApConfiguration(mWifiApConfig);
                    sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED);
                }
            } catch (IOException e) {
                Log.e(TAG, "Error writing hotspot configuration" + e);
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {}
            }).start();
        }

        public boolean processMessage(Message message) {
            switch (message.what) {
                //TODO: have feedback to the user when we do this
                //to indicate the write is currently in progress
                case WifiStateMachine.CMD_SET_AP_CONFIG:
                    deferMessage(message);
                    break;
                case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
                    transitionTo(mInactiveState);
                    break;
                default:
                    return NOT_HANDLED;
            }
            return HANDLED;
        }
    }

        private static void readApConfiguration() {
    void loadApConfiguration() {
        DataInputStream in = null;
        try {
            WifiConfiguration config = new WifiConfiguration();
@@ -150,9 +156,7 @@ class WifiApConfigStore {
            if (authType != KeyMgmt.NONE) {
                config.preSharedKey = in.readUTF();
            }
                synchronized (sApConfigLock) {
                    sApConfig = config;
                }
            mWifiApConfig = config;
        } catch (IOException ignore) {
            setDefaultApConfiguration();
        } finally {
@@ -164,18 +168,45 @@ class WifiApConfigStore {
        }
    }

    Messenger getMessenger() {
        return new Messenger(getHandler());
    }

    private void writeApConfiguration(final WifiConfiguration config) {
        DataOutputStream out = null;
        try {
            out = new DataOutputStream(new BufferedOutputStream(
                        new FileOutputStream(AP_CONFIG_FILE)));

            out.writeInt(AP_CONFIG_FILE_VERSION);
            out.writeUTF(config.SSID);
            int authType = config.getAuthType();
            out.writeInt(authType);
            if(authType != KeyMgmt.NONE) {
                out.writeUTF(config.preSharedKey);
            }
        } catch (IOException e) {
            Log.e(TAG, "Error writing hotspot configuration" + e);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {}
            }
        }
    }

    /* Generate a default WPA2 based configuration with a random password.
       We are changing the Wifi Ap configuration storage from secure settings to a
       flat file accessible only by the system. A WPA2 based default configuration
       will keep the device secure after the update */
        private static void setDefaultApConfiguration() {
    private void setDefaultApConfiguration() {
        WifiConfiguration config = new WifiConfiguration();
            config.SSID = sContext.getString(R.string.wifi_tether_configure_ssid_default);
        config.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
        config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
        String randomUUID = UUID.randomUUID().toString();
        //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
        config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13);
            setApConfiguration(config);
        }
        sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG, config);
    }
}
+62 −47
Original line number Diff line number Diff line
@@ -184,6 +184,7 @@ public class WifiStateMachine extends StateMachine {
    private WifiP2pManager mWifiP2pManager;
    //Used to initiate a connection with WifiP2pService
    private AsyncChannel mWifiP2pChannel = new AsyncChannel();
    private AsyncChannel mWifiApConfigChannel = new AsyncChannel();

    // Event log tags (must be in sync with event-log-tags)
    private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
@@ -233,12 +234,16 @@ public class WifiStateMachine extends StateMachine {
    static final int CMD_STOP_AP                          = BASE + 24;
    /* Set the soft access point configuration */
    static final int CMD_SET_AP_CONFIG                    = BASE + 25;
    /* Get the soft access point configuration */
    static final int CMD_GET_AP_CONFIG                    = BASE + 26;
    /* Soft access point configuration set completed */
    static final int CMD_SET_AP_CONFIG_COMPLETED          = BASE + 26;
    /* Request the soft access point configuration */
    static final int CMD_REQUEST_AP_CONFIG                = BASE + 27;
    /* Response to access point configuration request */
    static final int CMD_RESPONSE_AP_CONFIG               = BASE + 28;
    /* Set configuration on tether interface */
    static final int CMD_TETHER_INTERFACE                 = BASE + 27;
    static final int CMD_TETHER_INTERFACE                 = BASE + 29;

    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 28;
    static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE   = BASE + 30;

    /* Supplicant commands */
    /* Is supplicant alive ? */
@@ -530,6 +535,11 @@ public class WifiStateMachine extends StateMachine {
        mWpsStateMachine = new WpsStateMachine(context, this, getHandler());
        mLinkProperties = new LinkProperties();

        WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
                context, getHandler());
        wifiApConfigStore.loadApConfiguration();
        mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger());

        mNetworkInfo.setIsAvailable(false);
        mLinkProperties.clear();
        mLastBssid = null;
@@ -659,11 +669,11 @@ public class WifiStateMachine extends StateMachine {
    }

    public void setWifiApConfiguration(WifiConfiguration config) {
        sendMessage(obtainMessage(CMD_SET_AP_CONFIG, config));
        mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
    }

    public WifiConfiguration syncGetWifiApConfiguration(AsyncChannel channel) {
        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_AP_CONFIG);
    public WifiConfiguration syncGetWifiApConfiguration() {
        Message resultMsg = mWifiApConfigChannel.sendMessageSynchronously(CMD_REQUEST_AP_CONFIG);
        WifiConfiguration ret = (WifiConfiguration) resultMsg.obj;
        resultMsg.recycle();
        return ret;
@@ -1714,12 +1724,10 @@ public class WifiStateMachine extends StateMachine {
     * TODO: Add control channel setup through hostapd that allows changing config
     * on a running daemon
     */
    private boolean startSoftApWithConfig(WifiConfiguration config) {
        if (config == null) {
            config = WifiApConfigStore.getApConfiguration();
        } else {
            WifiApConfigStore.setApConfiguration(config);
        }
    private void startSoftApWithConfig(final WifiConfiguration config) {
        // start hostapd on a seperate thread
        new Thread(new Runnable() {
            public void run() {
                try {
                    mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
                } catch (Exception e) {
@@ -1729,10 +1737,14 @@ public class WifiStateMachine extends StateMachine {
                        mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
                    } catch (Exception e1) {
                        loge("Exception in softap re-start " + e1);
                return false;
                        sendMessage(CMD_START_AP_FAILURE);
                        return;
                    }
                }
        return true;
                if (DBG) log("Soft AP start successful");
                sendMessage(CMD_START_AP_SUCCESS);
            }
        }).start();
    }

    /********************************************************
@@ -1775,13 +1787,6 @@ public class WifiStateMachine extends StateMachine {
                case CMD_ENABLE_BACKGROUND_SCAN:
                    mEnableBackgroundScan = (message.arg1 == 1);
                    break;
                case CMD_SET_AP_CONFIG:
                    WifiApConfigStore.setApConfiguration((WifiConfiguration) message.obj);
                    break;
                case CMD_GET_AP_CONFIG:
                    WifiConfiguration config = WifiApConfigStore.getApConfiguration();
                    mReplyChannel.replyToMessage(message, message.what, config);
                    break;
                    /* Discard */
                case CMD_LOAD_DRIVER:
                case CMD_UNLOAD_DRIVER:
@@ -1823,6 +1828,11 @@ public class WifiStateMachine extends StateMachine {
                case CMD_ENABLE_ALL_NETWORKS:
                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                /* Handled by WifiApConfigStore */
                case CMD_SET_AP_CONFIG:
                case CMD_SET_AP_CONFIG_COMPLETED:
                case CMD_REQUEST_AP_CONFIG:
                case CMD_RESPONSE_AP_CONFIG:
                    break;
                case WifiMonitor.DRIVER_HUNG_EVENT:
                    setWifiEnabled(false);
@@ -1856,8 +1866,6 @@ public class WifiStateMachine extends StateMachine {
            // 50021 wifi_state_changed (custom|1|5)
            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());

            WifiApConfigStore.initialize(mContext);

            if (WifiNative.isDriverLoaded()) {
                transitionTo(mDriverLoadedState);
            }
@@ -3243,21 +3251,19 @@ public class WifiStateMachine extends StateMachine {
            if (DBG) log(getName() + "\n");
            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());

            final Message message = Message.obtain(getCurrentMessage());
            final Message message = getCurrentMessage();
            if (message.what == CMD_START_AP) {
                final WifiConfiguration config = (WifiConfiguration) message.obj;

            // start hostapd on a seperate thread
            new Thread(new Runnable() {
                public void run() {
                    if (startSoftApWithConfig(config)) {
                        if (DBG) log("Soft AP start successful");
                        sendMessage(CMD_START_AP_SUCCESS);
                if (config == null) {
                    mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
                } else {
                        loge("Soft AP start failed");
                        sendMessage(CMD_START_AP_FAILURE);
                    mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
                    startSoftApWithConfig(config);
                }
            } else {
                throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
            }
            }).start();
        }
        @Override
        public boolean processMessage(Message message) {
@@ -3282,6 +3288,15 @@ public class WifiStateMachine extends StateMachine {
                case WifiP2pService.P2P_ENABLE_PENDING:
                    deferMessage(message);
                    break;
                case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
                    WifiConfiguration config = (WifiConfiguration) message.obj;
                    if (config != null) {
                        startSoftApWithConfig(config);
                    } else {
                        loge("Softap config is null!");
                        sendMessage(CMD_START_AP_FAILURE);
                    }
                    break;
                case CMD_START_AP_SUCCESS:
                    setWifiApState(WIFI_AP_STATE_ENABLED);
                    transitionTo(mSoftApStartedState);