8

What's the aim?

Create an Access Point with an Android device (Nexus 7 in my case). The final purpose is to connect a WiFly card to this created network and exchange data between these devices.

Hardware:

  • Nexus 7 with the Android version 4.2.2 rooted with the ROM CyanogenMod 10.1
  • WiFly card: Arduino shield with the same layout as Zigbee cards that use Wifi (the product)

Software:

I understood that the Android version 4.2.2 does not allow to create an Access Point (the service is disabled programmatically). This is why I rooted my device with the ROM from CyanogenMod. This ROM enable this service.

Google has hidden some methods from the class WifiManager. Specifically, the method setWifiApEnabled. This is why I use reflection to call methods on the code below.

The source code is massive! Focus on the method createAccessPoint(). I chose to put the whole source code to help people that want to know how I did all of this.

public class TestAccessPoint extends Activity {

static final String TAG = "AP_TEST";
static final String SSID = "\"Awesome Access Point\"";
static final String PSK = "\"helloworld\"";
String numberOfClientsConnected;
String wifiApEnable;
String wifiApState;
WifiConfiguration wifiApConfig;
WifiManager wifiManager;
WifiConfiguration wifiConfiguration;
BroadcastReceiver receiver;
BroadcastReceiver receiverWifiDisabled;
TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.accesspoint_test);
    textView = (TextView) findViewById(R.id.textView);

    wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    wifiConfiguration = new WifiConfiguration();

    if(wifiManager.isWifiEnabled()) {
        createAccessPoint();
    } else {
        Log.d(TAG, "Set wifi Enable");
        wifiManager.setWifiEnabled(true);
        receiverWifiDisabled = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
                if ( wifiState == WifiManager.WIFI_STATE_ENABLED ) {
                    Log.d(TAG, "Wifi enable");
                    createAccessPoint();
                }
            }
        };
        registerReceiver(receiverWifiDisabled, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
    }

    final Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    textView.setText(
                        wifiApEnable + "\n" + 
                        wifiApState + "\n" +
                        "Nb of clients connected: " + numberOfClientsConnected + "\n" + 
                        "Wifi AP configuration: " + "\n" +
                        wifiApConfig.toString() + "\n" +
                        "WifiManager connection info: " +  "\n" + 
                        wifiManager.getConnectionInfo().toString() +
                        "DHCP state: " + wifiManager.getDhcpInfo().toString()
                    );
                break;
            }
        }
    };

    Thread thread = new Thread(new Runnable() {
        boolean alive = true;
        @Override
        public void run() {
            while(alive) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) { e.printStackTrace(); }

                numberOfClientsConnected = numberOfClientsConnected();
                wifiApEnable = isWifiApEnabled();
                wifiApState = getWifiApState();
                wifiApConfig = getWifiApConfiguration();

                mHandler.sendMessage(mHandler.obtainMessage(1));
            }
        }
    });
    thread.start();
}

@Override 
public void onDestroy() {
    super.onDestroy();
    if(receiver != null) {
        unregisterReceiver(receiver);
    }
    if(receiverWifiDisabled != null) {
        unregisterReceiver(receiverWifiDisabled);
    }
}

protected void createAccessPoint() {
    // Check if the Wifi configuration already exists
    List<WifiConfiguration> list = wifiManager.getConfiguredNetworks();
    int networkID = -1;
    if(list != null){
        for(WifiConfiguration conf : list) {
            Log.d(TAG, "Network ID: " + String.valueOf(conf.networkId) + " ; Network SSID: " + conf.SSID);
            if(conf.SSID.equals(SSID)) {
                Log.d(TAG, "SSID found");
                networkID = conf.networkId;
                wifiConfiguration = conf;
                break;
            }
        } 
    } else
        Log.d(TAG, "List of WifiConfiguration is null");

    // If the configuration exists, remove it to recreate it from scratch
    if(networkID != -1) {
        wifiManager.removeNetwork(networkID);
    }

    // Create a new WiFi configuration
    wifiConfiguration.SSID = SSID;
    wifiConfiguration.preSharedKey = PSK;
    wifiConfiguration.hiddenSSID = false;
    wifiConfiguration.status = WifiConfiguration.Status.ENABLED;

    wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
    wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);

    wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
    wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);

    wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
    wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);

    wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);

    // Catch Enumeration IpAssignment and ProxySettings from WifiConfiguration
    Enum ipAssignment = catchEnumIpAssignmentFromWifiConfiguration();
    Enum proxySettings = catchEnumProxySettingsFromWifiConfiguration();

    // Set IP address, gateway, DNS, etc
    try {
        Log.d(TAG, "Try to set IP, gateway and DNS");
        setIpAssignment(ipAssignment, wifiConfiguration);
        Log.d(TAG, "IpAssignment: Ok");
        setProxySettings(proxySettings, wifiConfiguration);
        Log.d(TAG, "ProxySettings: Ok");
        setIpAddress(InetAddress.getByName("192.168.2.100"), 24, wifiConfiguration);
        Log.d(TAG, "IpAddress: Ok");
        setGateway(InetAddress.getByName("192.168.2.1"), wifiConfiguration);
        Log.d(TAG, "Gateway: Ok");
        setDNS(InetAddress.getByName("192.168.2.1"), wifiConfiguration);
        Log.d(TAG, "DNS: Ok");
    } catch(Exception e) {
        e.printStackTrace();
    }

    // Add this new configuration to the wpa_supplicant file
    networkID = wifiManager.addNetwork(wifiConfiguration);
    if(networkID != -1)
        Log.d(TAG, "Succeed to update the WiFi configuration: " + networkID);
    else
        Log.d(TAG, "Failed to update the WiFi configuration");

    // Save the new configuration on the wpa_supplicant
    if(wifiManager.saveConfiguration())
        Log.d(TAG, "Succeed to save the wpa_supplicant");
    else
        Log.d(TAG, "Failed to save the wpa_supplicant");

    // Set the Wifi disable to be able to start the Access Point
    Log.d(TAG, "Set wifi disable");
    wifiManager.setWifiEnabled(false);

    receiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
            if ( wifiState == WifiManager.WIFI_STATE_DISABLED ) {
                Log.d(TAG, "Wifi disabled");
                // When the Wifi is disable
                // Create the Access Point with the WiFi configuration
                try {
                    Method m = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
                    boolean succeed = (Boolean) m.invoke(wifiManager, wifiConfiguration, true);
                    if(succeed)
                        Log.d(TAG, "Succeed to set wifi AP");
                    else
                        Log.d(TAG, "A problem occured while setting the wifi AP");
                } catch (Exception e) {
                    Log.e(TAG, "Failed to set wifi AP", e);
                }
            }
        }
    };
    registerReceiver(receiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
}

protected String getWifiApState() {
    try {
        Method m3 = wifiManager.getClass().getMethod("getWifiApState");
        return "WiFi Ap State: " + String.valueOf(m3.invoke(wifiManager));
    } catch (NoSuchMethodException e1) {    e1.printStackTrace();
    } catch (IllegalArgumentException e) {  e.printStackTrace();
    } catch (IllegalAccessException e) {    e.printStackTrace();
    } catch (InvocationTargetException e) { e.printStackTrace(); }
    return null;
}

protected WifiConfiguration getWifiApConfiguration() {
    WifiConfiguration wifiConfiguration = null;
    try {
        Method m4 = wifiManager.getClass().getMethod("getWifiApConfiguration");
        wifiConfiguration = (WifiConfiguration) m4.invoke(wifiManager);
    } catch (Exception e) { e.printStackTrace(); }
    return wifiConfiguration;
}

protected String isWifiApEnabled() {
    try {
        Method m = wifiManager.getClass().getMethod("isWifiApEnabled");
        if((Boolean) m.invoke(wifiManager))
            return "WiFiAP enabled";
        else
            return "WiFiAP not enabled";
    } catch (Exception e) { e.printStackTrace(); }
    return null;
}

protected String numberOfClientsConnected() {
    int macCount = 0;
    BufferedReader br = null;
    try {
        br = new BufferedReader(new FileReader("/proc/net/arp"));
        String line;
        while ((line = br.readLine()) != null) {
            String[] splitted = line.split(" +");
            if (splitted != null && splitted.length >= 4) {
                String mac = splitted[3];
                if (mac.matches("..:..:..:..:..:..")) {
                    macCount++;
                } 
            }
        }
    } catch (Exception e) { e.printStackTrace();
    } finally {
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return String.valueOf(macCount);        
}

@SuppressWarnings({ "unchecked", "rawtypes" })
protected Enum catchEnumIpAssignmentFromWifiConfiguration() {
    Enum DHCP = null;
    try {
        Class<Enum> enumIpAssignment = (Class<Enum>) Class.forName("android.net.wifi.WifiConfiguration$IpAssignment");
        DHCP = Enum.valueOf(enumIpAssignment, "DHCP");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return DHCP;
}

@SuppressWarnings({ "unchecked", "rawtypes" })
protected Enum catchEnumProxySettingsFromWifiConfiguration() {
    Enum ProxySet = null;
    try {
        Class<Enum> enumProxySettings = (Class<Enum>) Class.forName("android.net.wifi.WifiConfiguration$ProxySettings");
        ProxySet = Enum.valueOf(enumProxySettings, "NONE");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return ProxySet;
}

public static void setIpAssignment(Object assign , WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
    setEnumField(wifiConf, assign, "ipAssignment");     
}

public static void setProxySettings(Object assign , WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
    setEnumField(wifiConf, assign, "proxySettings");     
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public static void setIpAddress(InetAddress addr, int prefixLength, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException,
        NoSuchMethodException, ClassNotFoundException, InstantiationException, InvocationTargetException {
    Object linkProperties = getField(wifiConf, "linkProperties");
    if(linkProperties == null) return;
    Class laClass = Class.forName("android.net.LinkAddress");
    Constructor laConstructor = laClass.getConstructor(new Class[]{InetAddress.class, int.class});
    Object linkAddress = laConstructor.newInstance(addr, prefixLength);

    ArrayList mLinkAddresses = (ArrayList)getDeclaredField(linkProperties, "mLinkAddresses");
    mLinkAddresses.clear();
    mLinkAddresses.add(linkAddress);     
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public static void setGateway(InetAddress gateway, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, 
        ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException {
    Object linkProperties = getField(wifiConf, "linkProperties");
    if(linkProperties == null)return;
    Class routeInfoClass = Class.forName("android.net.RouteInfo");
    Constructor routeInfoConstructor = routeInfoClass.getConstructor(new Class[]{InetAddress.class});
    Object routeInfo = routeInfoConstructor.newInstance(gateway);

    ArrayList mRoutes = (ArrayList)getDeclaredField(linkProperties, "mRoutes");
    mRoutes.clear();
    mRoutes.add(routeInfo);
}

@SuppressWarnings("unchecked")
public static void setDNS(InetAddress dns, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
    Object linkProperties = getField(wifiConf, "linkProperties");
    if(linkProperties == null)return;

    ArrayList<InetAddress> mDnses = (ArrayList<InetAddress>)getDeclaredField(linkProperties, "mDnses");
    mDnses.clear();
    mDnses.add(dns); 
}

public static Object getField(Object obj, String name)
        throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Field f = obj.getClass().getField(name);
    Object out = f.get(obj);
    return out;
}

public static Object getDeclaredField(Object obj, String name)
        throws SecurityException, NoSuchFieldException,
        IllegalArgumentException, IllegalAccessException {
    Field f = obj.getClass().getDeclaredField(name);
    f.setAccessible(true);
    Object out = f.get(obj);
    return out;
}  

public static void setEnumField(Object obj, Object value, String name)
        throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Field f = obj.getClass().getField(name);
    f.set(obj, value);
}
}

This code works fine on my Nexus 7. It creates an Access Point. My laptop see the network link this: enter image description here

It asks me to enter the WPA key. I need to write it wrapped with quotes whether it does not work ("helloworld").

After that, my laptop is connected to the network but with the software Xirrus I realized that the DHCP module doesn't give any IP address.

Logs:

I get two interesting logs. This one is when I start the application:

E/hostapd(): Configuration file: /data/misc/wifi/hostapd.conf
E/hostapd(): HT (IEEE 802.11n) in 11b mode is not allowed, disabling HT capabilites
I/hostapd(): rfkill: Cannot open RFKILL control device
W/hostapd(): wlan0: Could not connect to kernel driver
E/hostapd(): Using interface wlan0 with hwaddr 02:1a:11:fd:32:58 and ssid "\"Awesome Access Point\""
E/hostapd(): random: Cannot read from /dev/random: Try again
I/hostapd(): random: Only 0/20 bytes of strong random data available from /dev/random
I/hostapd(): random: Allow operation to proceed based on internal entropy

And this one is when I connect and disconnect my laptop from/to the Access Point:

I/hostapd(): wlan0: AP-STA-DISCONNECTED 00:27:10:ca:f0:80
I/hostapd(): wlan0: AP-STA-CONNECTED 00:27:10:ca:f0:80

Questions:

  • If you think that I'm doing the wrong way, could you tell me a better way?
  • Do you know why I didn't get IP address from the DHCP module?
  • Do you know I could get more information/logs from the DHCP module?

Thank you for your support.

Gilles D.
  • 1,019
  • 6
  • 16
David Guyon
  • 2,343
  • 1
  • 26
  • 38
  • Have you had any success with this project? – zerodog Dec 03 '13 at 04:39
  • Unfortunately not really. I decided to change the project structure to make it easier to develop. Now I generate the Wifi from my WiFly card and my Nexus 7 connects to this network thanks to an Android Scala application which I wrote. – David Guyon Dec 03 '13 at 08:46
  • Some notes for others pursuing this: My research so far has led me to that there is no IPV4LL link local address being generated. The android dhcpcd client daemon *should* generate an IPV4LL by default, but doesn't. I tried installing BusyBox and using the zcip (zero configuration ip) utility to generate a link local IPV4LL but get an error regarding wlan0 permission access denied even though it is run as root. – zerodog Dec 04 '13 at 19:00

1 Answers1

0

Unfortunately, this is not possible. Stock version of Android ROM for Nexus 7 simply doesn't have capability for creating WiFi accesspoints (it's turned off by default). The only reasonable way is to root the device and load some custom ROM like CyanogenMod or something like that. CyanogenMod for example, has the capability turned on.

Sorry to disappoint you, but with regular firmware this is simply not possible at all.

  • 1
    Thank you for your answer. I knew it's not possible if the device is not rooted. What I forgot to say is that my Nexus 7 is rooted with the last version of CyanogenMod. I also created an Access Point (I can find the SSID) but the problem is located around the `dhcp` module. When I try to connect to it, I don't get any IP address and if I set the IP address static, the problem is the same. I will update my first post to indicate I'm using CM10 and also where I am with the second try. – David Guyon Mar 27 '13 at 11:31
  • 1
    It is not quite the case that an access point can not be created. I am doing this on a Nexus 10 in much the same way DGeTuX is and I have NOT rooted the device or installed a non-standard ROM. As DGeTuX says, he can indeed connect to the access point. What he can not do is to get the access point to supply a DHCP address. I have reached the same impasse. I have also tried to associate a static address with the access point, but have not been successful. MPP may be correct that an access point can not be made to work. I would like to find out. – SteveJP Apr 23 '13 at 08:29