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.
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.
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
Thank you for your support.
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.