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

Commit 31b62322 authored by Irfan Sheriff's avatar Irfan Sheriff
Browse files

Add per network static IP settings

Remove the existing global static IP settings and add support
for per network configuration

Change-Id: I5a6d8b877471b8c8ad07951c96d273893754607f
parent b729dcc8
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -37,6 +37,19 @@ public class DhcpInfo implements Parcelable {
        super();
    }

    /** copy constructor {@hide} */
    public DhcpInfo(DhcpInfo source) {
        if (source != null) {
            ipAddress = source.ipAddress;
            gateway = source.gateway;
            netmask = source.netmask;
            dns1 = source.dns1;
            dns2 = source.dns2;
            serverAddress = source.serverAddress;
            leaseDuration = source.leaseDuration;
        }
    }

    public String toString() {
        StringBuffer str = new StringBuffer();

+1 −0
Original line number Diff line number Diff line
@@ -1128,6 +1128,7 @@ public final class Settings {
         */
        public static final int WIFI_SLEEP_POLICY_NEVER = 2;

        //TODO: deprecate static IP constants
        /**
         * Whether to use static IP and other static network attributes.
         * <p>
+324 −37
Original line number Diff line number Diff line
@@ -19,12 +19,22 @@ package android.net.wifi;
import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
import android.net.DhcpInfo;
import android.net.wifi.WifiConfiguration.IpAssignment;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.Status;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;

/**
@@ -34,9 +44,23 @@ import java.util.List;
 *
 * It deals with the following
 * - Add/update/remove a WifiConfiguration
 *   The configuration contains two types of information.
 *     = IP configuration that is handled by WifiConfigStore and
 *       is saved to disk on any change.
 *     = SSID & security details that is pushed to the supplicant.
 *       supplicant saves these details to the disk on calling
 *       saveConfigCommand().
 *
 *       We have two kinds of APIs exposed:
 *        > public API calls that provide fine grained control
 *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
 *          removeNetwork(). For these calls, the config is not persisted
 *          to the disk. (TODO: deprecate these calls in WifiManager)
 *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
 *          These calls persist the supplicant config to disk.
 * - Maintain a list of configured networks for quick access
 *
 * TODO:
 * - handle static IP per configuration
 * - handle proxy per configuration
 */
class WifiConfigStore {
@@ -44,10 +68,28 @@ class WifiConfigStore {
    private static Context sContext;
    private static final String TAG = "WifiConfigStore";

    private static List<WifiConfiguration> sConfiguredNetworks = new ArrayList<WifiConfiguration>();
    /* configured networks with network id as the key */
    private static HashMap<Integer, WifiConfiguration> sConfiguredNetworks =
            new HashMap<Integer, WifiConfiguration>();

    /* A network id is a unique identifier for a network configured in the
     * supplicant. Network ids are generated when the supplicant reads
     * the configuration file at start and can thus change for networks.
     * We store the IP configuration for networks along with a unique id
     * that is generated from SSID and security type of the network. A mapping
     * from the generated unique id to network id of the network is needed to
     * map supplicant config to IP configuration. */
    private static HashMap<Integer, Integer> sNetworkIds =
            new HashMap<Integer, Integer>();

    /* Tracks the highest priority of configured networks */
    private static int sLastPriority = -1;

    private static final String ipConfigFile = Environment.getDataDirectory() +
            "/misc/wifi/ipconfig.txt";

    private static final int IPCONFIG_FILE_VERSION = 1;

    /**
     * Initialize context, fetch the list of configured networks
     * and enable all stored networks in supplicant.
@@ -66,7 +108,7 @@ class WifiConfigStore {
    static List<WifiConfiguration> getConfiguredNetworks() {
        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
        synchronized (sConfiguredNetworks) {
            for (WifiConfiguration config : sConfiguredNetworks) {
            for(WifiConfiguration config : sConfiguredNetworks.values()) {
                networks.add(config.clone());
            }
        }
@@ -78,14 +120,20 @@ class WifiConfigStore {
     * of configured networks indicates all networks as being enabled
     */
    static void enableAllNetworks() {
        for (WifiConfiguration config : sConfiguredNetworks) {
        synchronized (sConfiguredNetworks) {
            for(WifiConfiguration config : sConfiguredNetworks.values()) {
                if(config != null && config.status == Status.DISABLED) {
                WifiNative.enableNetworkCommand(config.networkId, false);
                    if(WifiNative.enableNetworkCommand(config.networkId, false)) {
                        config.status = Status.ENABLED;
                    } else {
                        Log.e(TAG, "Enable network failed on " + config.networkId);
                    }
                }
            }
        }

        WifiNative.saveConfigCommand();
        updateConfigAndSendChangeBroadcast();
        sendConfigChangeBroadcast();
    }

    /**
@@ -102,7 +150,11 @@ class WifiConfigStore {
    static void selectNetwork(WifiConfiguration config) {
        if (config != null) {
            int netId = addOrUpdateNetworkNative(config);
            if (netId != -1) {
                selectNetwork(netId);
            } else {
                Log.e(TAG, "Failed to update network " + config);
            }
        }
    }

@@ -120,10 +172,12 @@ class WifiConfigStore {
    static void selectNetwork(int netId) {
        // Reset the priority of each network at start or if it goes too high.
        if (sLastPriority == -1 || sLastPriority > 1000000) {
            for (WifiConfiguration conf : sConfiguredNetworks) {
                if (conf.networkId != -1) {
                    conf.priority = 0;
                    addOrUpdateNetworkNative(conf);
            synchronized (sConfiguredNetworks) {
                for(WifiConfiguration config : sConfiguredNetworks.values()) {
                    if (config.networkId != -1) {
                        config.priority = 0;
                        addOrUpdateNetworkNative(config);
                    }
                }
            }
            sLastPriority = 0;
@@ -138,13 +192,10 @@ class WifiConfigStore {
        WifiNative.saveConfigCommand();

        /* Enable the given network while disabling all other networks */
        WifiNative.enableNetworkCommand(netId, true);
        enableNetworkWithoutBroadcast(netId, true);

        /* update the configured networks list but not send a
         * broadcast to avoid a fetch from settings
         * during this temporary disabling of networks
         */
        updateConfiguredNetworks();
       /* Avoid saving the config & sending a broadcast to prevent settings
        * from displaying a disabled list of networks */
    }

    /**
@@ -153,13 +204,17 @@ class WifiConfigStore {
     * @param config WifiConfiguration to be saved
     */
    static void saveNetwork(WifiConfiguration config) {
        boolean newNetwork = (config.networkId == -1);
        int netId = addOrUpdateNetworkNative(config);
        /* enable a new network */
        if (config.networkId < 0) {
        if (newNetwork && netId >= 0) {
            WifiNative.enableNetworkCommand(netId, false);
            synchronized (sConfiguredNetworks) {
                sConfiguredNetworks.get(netId).status = Status.ENABLED;
            }
        }
        WifiNative.saveConfigCommand();
        updateConfigAndSendChangeBroadcast();
        sendConfigChangeBroadcast();
    }

    /**
@@ -168,9 +223,15 @@ class WifiConfigStore {
     * @param netId network to forget
     */
    static void forgetNetwork(int netId) {
        WifiNative.removeNetworkCommand(netId);
        if (WifiNative.removeNetworkCommand(netId)) {
            WifiNative.saveConfigCommand();
        updateConfigAndSendChangeBroadcast();
            synchronized (sConfiguredNetworks) {
                sConfiguredNetworks.remove(netId);
            }
            sendConfigChangeBroadcast();
        } else {
            Log.e(TAG, "Failed to remove network " + netId);
        }
    }

    /**
@@ -183,7 +244,7 @@ class WifiConfigStore {
     */
    static int addOrUpdateNetwork(WifiConfiguration config) {
        int ret = addOrUpdateNetworkNative(config);
        updateConfigAndSendChangeBroadcast();
        sendConfigChangeBroadcast();
        return ret;
    }

@@ -197,7 +258,10 @@ class WifiConfigStore {
     */
    static boolean removeNetwork(int netId) {
        boolean ret = WifiNative.removeNetworkCommand(netId);
        updateConfigAndSendChangeBroadcast();
        synchronized (sConfiguredNetworks) {
            if (ret) sConfiguredNetworks.remove(netId);
        }
        sendConfigChangeBroadcast();
        return ret;
    }

@@ -210,8 +274,28 @@ class WifiConfigStore {
     * @param netId network to be removed
     */
    static boolean enableNetwork(int netId, boolean disableOthers) {
        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
        sendConfigChangeBroadcast();
        return ret;
    }

    static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
        boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
        updateConfigAndSendChangeBroadcast();

        synchronized (sConfiguredNetworks) {
            WifiConfiguration config = sConfiguredNetworks.get(netId);
            if (config != null) config.status = Status.ENABLED;
        }

        if (disableOthers) {
            synchronized (sConfiguredNetworks) {
                for(WifiConfiguration config : sConfiguredNetworks.values()) {
                    if(config != null && config.networkId != netId) {
                        config.status = Status.DISABLED;
                    }
                }
            }
        }
        return ret;
    }

@@ -221,7 +305,11 @@ class WifiConfigStore {
     */
    static boolean disableNetwork(int netId) {
        boolean ret = WifiNative.disableNetworkCommand(netId);
        updateConfigAndSendChangeBroadcast();
        synchronized (sConfiguredNetworks) {
            WifiConfiguration config = sConfiguredNetworks.get(netId);
            if (config != null) config.status = Status.DISABLED;
        }
        sendConfigChangeBroadcast();
        return ret;
    }

@@ -232,8 +320,31 @@ class WifiConfigStore {
        return WifiNative.saveConfigCommand();
    }

    private static void updateConfigAndSendChangeBroadcast() {
        updateConfiguredNetworks();
    /**
     * Fetch the IP configuration for a given network id
     */
    static DhcpInfo getIpConfiguration(int netId) {
        synchronized (sConfiguredNetworks) {
            WifiConfiguration config = sConfiguredNetworks.get(netId);
            if (config != null) return new DhcpInfo(config.ipConfig);
        }
        return null;
    }

    /**
     * Return if the specified network is using static IP
     */
    static boolean isUsingStaticIp(int netId) {
        synchronized (sConfiguredNetworks) {
            WifiConfiguration config = sConfiguredNetworks.get(netId);
            if (config != null && config.ipAssignment == IpAssignment.STATIC) {
                return true;
            }
        }
        return false;
    }

    private static void sendConfigChangeBroadcast() {
        if (!ActivityManagerNative.isSystemReady()) return;
        Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
        sContext.sendBroadcast(intent);
@@ -245,6 +356,7 @@ class WifiConfigStore {

        synchronized (sConfiguredNetworks) {
            sConfiguredNetworks.clear();
            sNetworkIds.clear();

            if (listStr == null)
                return;
@@ -274,9 +386,129 @@ class WifiConfigStore {
                if (config.priority > sLastPriority) {
                    sLastPriority = config.priority;
                }
                sConfiguredNetworks.add(config);
                sConfiguredNetworks.put(config.networkId, config);
                sNetworkIds.put(configKey(config), config.networkId);
            }
        }
        readIpConfigurations();
    }

    private static void writeIpConfigurations() {
        StringBuilder builder = new StringBuilder();
        BufferedWriter out = null;

        builder.append(IPCONFIG_FILE_VERSION);
        builder.append("\n");

        synchronized (sConfiguredNetworks) {
            for(WifiConfiguration config : sConfiguredNetworks.values()) {
                if (config.ipAssignment == WifiConfiguration.IpAssignment.STATIC) {
                    builder.append("id=" + configKey(config));
                    builder.append(":");
                    builder.append("ip=" + config.ipConfig.ipAddress);
                    builder.append(":");
                    builder.append("gateway=" + config.ipConfig.gateway);
                    builder.append(":");
                    builder.append("netmask=" + config.ipConfig.netmask);
                    builder.append(":");
                    builder.append("dns1=" + config.ipConfig.dns1);
                    builder.append(":");
                    builder.append("dns2=" + config.ipConfig.dns2);
                    builder.append("\n");
                }
            }
        }

        try {
            out = new BufferedWriter(new FileWriter(ipConfigFile), builder.length());
            out.write(builder.toString());
        } catch (IOException e) {
            Log.e(TAG, "Error writing data file");
            return;
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {}
            }
        }
    }

    private static void readIpConfigurations() {
        File f = new File(ipConfigFile);
        byte[] buffer;
        FileInputStream s = null;
        try {
            buffer = new byte[(int)f.length()];
            s = new FileInputStream(f);
            s.read(buffer);
        } catch (IOException e) {
            Log.e(TAG, "Error reading data file");
            return;
        } finally {
            if (s != null) {
                try {
                    s.close();
                } catch (Exception e) {}
            }
        }

        String data = new String(buffer);
        if (data == null || data.length() == 0) {
            Log.d(TAG, "IP configuration file empty");
            return;
        }

        String[] parsed = data.split("\n");
        try {
            if (Integer.parseInt(parsed[0]) != IPCONFIG_FILE_VERSION) {
                Log.e(TAG, "Bad version on IP configuration file, ignore read");
                return;
            }

            for (String line : parsed) {
                int hashKey = -1;
                DhcpInfo ipConfig = new DhcpInfo();
                String[] keyVals = line.split(":");

                for (String keyVal : keyVals) {
                    String[] keyValPair = keyVal.split("=");
                    if (keyValPair[0].equals("id")) {
                        hashKey = Integer.parseInt(keyValPair[1]);
                    } else if (keyValPair[0].equals("ip")) {
                        ipConfig.ipAddress = Integer.parseInt(keyValPair[1]);
                    } else if (keyValPair[0].equals("gateway")) {
                        ipConfig.gateway = Integer.parseInt(keyValPair[1]);
                    } else if (keyValPair[0].equals("netmask")) {
                        ipConfig.netmask = Integer.parseInt(keyValPair[1]);
                    } else if (keyValPair[0].equals("dns1")) {
                        ipConfig.dns1 = Integer.parseInt(keyValPair[1]);
                    } else if (keyValPair[0].equals("dns2")) {
                        ipConfig.dns2 = Integer.parseInt(keyValPair[1]);
                    } else {
                        Log.w(TAG, "Ignoring " + keyVal);
                    }
                }

                if (hashKey != -1) {
                    synchronized (sConfiguredNetworks) {
                        WifiConfiguration config = sConfiguredNetworks.get(
                                sNetworkIds.get(hashKey));

                        if (config == null) {
                            Log.e(TAG, "IP configuration found for missing network, ignored");
                        } else {
                            config.ipAssignment = WifiConfiguration.IpAssignment.STATIC;
                            config.ipConfig = ipConfig;
                        }
                    }
                } else {
                    Log.e(TAG,"Missing id while parsing configuration" + line);
                }
            }
        } catch (NumberFormatException e) {
            Log.e(TAG, "Error parsing configuration");
        }
    }

    private static int addOrUpdateNetworkNative(WifiConfiguration config) {
@@ -286,6 +518,7 @@ class WifiConfigStore {
         * refer to an existing configuration.
         */
        int netId = config.networkId;
        boolean updateFailed = true;
        boolean newNetwork = netId == -1;
        // networkId of -1 means we want to create a new network

@@ -457,19 +690,55 @@ class WifiConfigStore {
                    }
                }
            }
            return netId;
            updateFailed = false;
        }

        if (updateFailed) {
            if (newNetwork) {
                WifiNative.removeNetworkCommand(netId);
                Log.d(TAG,
                        "Failed to set a network variable, removed network: "
                        + netId);
            }

            return -1;
        }

        /* An update of the network variables requires reading them
         * back from the supplicant to update sConfiguredNetworks.
         * This is because some of the variables (SSID, wep keys &
         * passphrases) reflect different values when read back than
         * when written. For example, wep key is stored as * irrespective
         * of the value sent to the supplicant
         */
        WifiConfiguration sConfig;
        synchronized (sConfiguredNetworks) {
            sConfig = sConfiguredNetworks.get(netId);
        }
        if (sConfig == null) {
            sConfig = new WifiConfiguration();
            sConfig.networkId = netId;
            synchronized (sConfiguredNetworks) {
                sConfiguredNetworks.put(netId, sConfig);
            }
        }
        readNetworkVariables(sConfig);

        if (config.ipAssignment != IpAssignment.UNASSIGNED) {
            if (newNetwork ||
                    (sConfig.ipAssignment != config.ipAssignment) ||
                    (sConfig.ipConfig.ipAddress != config.ipConfig.ipAddress) ||
                    (sConfig.ipConfig.gateway != config.ipConfig.gateway) ||
                    (sConfig.ipConfig.netmask != config.ipConfig.netmask) ||
                    (sConfig.ipConfig.dns1 != config.ipConfig.dns1) ||
                    (sConfig.ipConfig.dns2 != config.ipConfig.dns2)) {
                sConfig.ipAssignment = config.ipAssignment;
                sConfig.ipConfig = config.ipConfig;
                writeIpConfigurations();
            }
        }
        return netId;
    }

    /**
     * Read the variables from the supplicant daemon that are needed to
     * fill in the WifiConfiguration object.
@@ -669,6 +938,24 @@ class WifiConfigStore {
        return -1;
    }

    /* Returns a unique for a given configuration */
    private static int configKey(WifiConfiguration config) {
        String key;

        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
        } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
        } else if (config.wepKeys[0] != null) {
            key = config.SSID + "WEP";
        } else {
            key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
        }

        return key.hashCode();
    }

    static String dump() {
        StringBuffer sb = new StringBuffer();
        String LS = System.getProperty("line.separator");
+43 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.net.wifi;

import android.net.DhcpInfo;
import android.os.Parcelable;
import android.os.Parcel;

@@ -294,6 +295,22 @@ public class WifiConfiguration implements Parcelable {
     */
    public BitSet allowedGroupCiphers;

    /**
     * @hide
     */
    public enum IpAssignment {
        STATIC,
        DHCP,
        UNASSIGNED
    }
    /**
     * @hide
     */
    public IpAssignment ipAssignment;
    /**
     * @hide
     */
    public DhcpInfo ipConfig;

    public WifiConfiguration() {
        networkId = -1;
@@ -312,6 +329,8 @@ public class WifiConfiguration implements Parcelable {
        for (EnterpriseField field : enterpriseFields) {
            field.setValue(null);
        }
        ipAssignment = IpAssignment.UNASSIGNED;
        ipConfig = new DhcpInfo();
    }

    public String toString() {
@@ -393,6 +412,11 @@ public class WifiConfiguration implements Parcelable {
            if (value != null) sbuf.append(value);
        }
        sbuf.append('\n');
        if (ipAssignment == IpAssignment.STATIC) {
            sbuf.append(" ").append("Static IP configuration:").append('\n');
            sbuf.append(" ").append(ipConfig);
        }
        sbuf.append('\n');
        return sbuf.toString();
    }

@@ -461,6 +485,8 @@ public class WifiConfiguration implements Parcelable {
        for (int i = 0; i < enterpriseFields.length; i++) {
            config.enterpriseFields[i].setValue(enterpriseFields[i].value());
        }
        config.ipAssignment = ipAssignment;
        config.ipConfig = new DhcpInfo(ipConfig);
        return config;
    }

@@ -486,6 +512,14 @@ public class WifiConfiguration implements Parcelable {
        for (EnterpriseField field : enterpriseFields) {
            dest.writeString(field.value());
        }
        dest.writeString(ipAssignment.name());
        dest.writeInt(ipConfig.ipAddress);
        dest.writeInt(ipConfig.netmask);
        dest.writeInt(ipConfig.gateway);
        dest.writeInt(ipConfig.dns1);
        dest.writeInt(ipConfig.dns2);
        dest.writeInt(ipConfig.serverAddress);
        dest.writeInt(ipConfig.leaseDuration);
    }

    /** Implement the Parcelable interface {@hide} */
@@ -512,6 +546,15 @@ public class WifiConfiguration implements Parcelable {
                for (EnterpriseField field : config.enterpriseFields) {
                    field.setValue(in.readString());
                }

                config.ipAssignment = IpAssignment.valueOf(in.readString());
                config.ipConfig.ipAddress = in.readInt();
                config.ipConfig.netmask = in.readInt();
                config.ipConfig.gateway = in.readInt();
                config.ipConfig.dns1 = in.readInt();
                config.ipConfig.dns2 = in.readInt();
                config.ipConfig.serverAddress = in.readInt();
                config.ipConfig.leaseDuration = in.readInt();
                return config;
            }

+40 −161

File changed.

Preview size limit exceeded, changes collapsed.