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:
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.