使用Nexus 7创建一个接入点

9

目标是什么?

使用Android设备(我的例子是Nexus 7)创建一个访问点。最终目的是将WiFly卡连接到这个创建的网络并在这些设备之间交换数据。

硬件:

  • 安卓版本为4.2.2的Nexus 7,已root,使用CyanogenMod 10.1 ROM
  • WiFly卡:使用与使用Wifi的Zigbee卡相同布局的Arduino shield (产品)

软件:

我了解到,Android版本4.2.2不允许创建访问点(此服务被程序禁用)。这就是为什么我用CyanogenMod的ROM root我的设备。该ROM启用了此服务。

谷歌已经从WifiManager类中隐藏了一些方法。特别是方法setWifiApEnabled。这就是为什么我使用反射调用下面代码中的方法。

源代码非常庞大!请关注方法createAccessPoint()。我选择放置整个源代码以帮助想要了解我如何完成所有操作的人们。

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);
}
}

这段代码在我的Nexus 7上运行良好,它创建了一个访问点。我的笔记本电脑看到网络链接如下图所示:enter image description here 它要求我输入WPA密钥。我需要用引号括起来写入,否则它不起作用("helloworld")。
之后,我的笔记本电脑连接到了网络,但是使用软件Xirrus,我发现DHCP模块没有分配任何IP地址。
日志:
我得到了两个有趣的日志。这是我启动应用程序时的一个:
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

这是当我将笔记本电脑连接/断开与接入点时的情况:

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

问题:

  • 如果您认为我做错了,能否告诉我更好的方法?
  • 您知道我为什么没有从DHCP模块获取IP地址吗?
  • 您知道我如何从DHCP模块获取更多信息/日志吗?

感谢您的支持。


这个项目你有没有取得成功呢? - zerodog
不幸的是,我决定改变项目结构以便更容易开发。现在,我从我的WiFly卡生成Wifi,并且由于我编写的Android Scala应用程序,我的Nexus 7连接到这个网络。 - David Guyon
一些给其他人的提示:到目前为止,我的研究表明没有生成IPV4LL本地链接地址。Android dhcpcd客户端守护程序默认情况下应该生成一个IPV4LL,但实际上并没有。我尝试安装BusyBox并使用zcip(零配置IP)实用程序来生成本地链接IPV4LL,但出现了有关wlan0权限访问被拒绝的错误,即使以root身份运行也是如此。 - zerodog
1个回答

0

很遗憾,这是不可能的。Nexus 7的原版Android ROM没有创建WiFi接入点的功能(默认情况下已关闭)。唯一合理的方法是对设备进行root并加载一些自定义ROM,例如CyanogenMod之类的ROM。例如,CyanogenMod已经开启了此功能。

很抱歉让您失望,但使用常规固件根本不可能实现此功能。


1
谢谢你的回答。我知道如果设备没有root权限是不可能的。我忘了说我的Nexus 7已经root过了,而且使用的是最新版本的CyanogenMod。我还创建了一个访问点(可以找到SSID),但问题出在dhcp模块上。当我尝试连接时,我没有得到任何IP地址,如果我设置静态IP地址,问题仍然存在。我将更新我的第一篇帖子,以指示我正在使用CM10以及我在第二次尝试中的位置。 - David Guyon
1
并不是说不能创建访问点。我正在 Nexus 10 上以与 DGeTuX 相同的方式进行操作,而且我没有对设备进行 root 或安装非标准 ROM。正如 DGeTuX 所说,他确实可以连接到访问点。但他无法获得访问点提供的 DHCP 地址。我也遇到了同样的问题。我还尝试将静态地址与访问点关联,但没有成功。MPP 可能正确,即访问点无法正常工作。我想找出答案。 - SteveJP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接