Android平台Wifi_Direct使用

        Wifi_Direct是目前设备间最快的无线数据连接方式,速度可以达到40Mb/s。Google从Android 4.0(ICS)开始支持Wifi_Direct,而三星则更早些就在它自己的设备上支持了Wifi_Direct。几年来,Wifi_Direct的发展一直不温不火,但是目前市面上支持Wifi_Direct的设备并不是很多。
        从目前接触过得设备来看,三星I9100的Wifi_Direct功能其实使用了Wifi的硬件,所以,它在使用Wifi_Direct功能时,无法使用wifi;nexus7、Padfone infinite(A80)则有独立的硬件来支持Wifi_Direct,所以,在使用Wifi_Direct功能的时候,Wifi仍旧可用。

          Android framework提供了一个android.net.wifi.p2p包来提供对于Wifi_Direct的支持,其中包含了7个class和9个interface。其中WifiP2pManager为最核心的class,其他的class和interface都为它所用。

          使用Wifi_P2p需要的Permission有两个:

public static final String ACCESS_WIFI_STATE
Added in API level 1
Allows applications to access information about Wi-Fi networks
Constant Value: "android.permission.ACCESS_WIFI_STATE"


public static final String CHANGE_WIFI_STATE
Added in API level 1
Allows applications to change Wi-Fi connectivity state
Constant Value: "android.permission.CHANGE_WIFI_STATE"

        Wifi_Direct的大致配对流程如下:

        1. WifiP2pManager.discoverPeers()开始扫描设备
        2. 获取扫描到的设备,选择其中一个设备进行连接配对WifiP2pManager.connect
        3. 配对成功后,根据WifiP2pInfo.isGroupOwner和WifiP2pInfo.groupOwnerAddress进行连接。

        流程图如下:



        个人认为Wifi_Direct配对需要注意的问题:
        1. Setting中启用/关闭WifiP2p按钮,应该是和Wifi的启用/关闭按钮放在一起了(其实,有些设备的实现中,Wifip2p使用的就是wifi的硬件),所以使用WifiP2p功能需要开启Wifi。
        2. Setting中BlueTooth有一个“让自己可见”的按钮,而Wifi_Direct没有这样的设置,仅提供了一个启动scan的按钮。本人尚未明确在未启动scan的情况下,设备对于其他wifi_direct是否是可见的,但是可以明确scan中的wifi_direct设备对其他设备来说是可见的。所以,建议需要进行配对的两台Wifi_Direct设备都进行scan。
        3. 配对成功的前提条件是:进行配对的两台设备都必须能够扫描到对方。所以,两台设备都进行scan操作的根本原因在这里。
        4. 开发者无法决定GroupOwner是哪台设备,但是可以通过WifiP2pConfig.groupOwnerIntent参数进行建议。

        从测试的结果来说,Wifi_Direct的表现受具体设备的影响很大,配对的速度也有较大差异,从10秒到2分钟甚至更久。大概的来说,nexus7成功的概率较高,个人感觉可以达到70%的成功率,Padfone infinite(A80)的成功率在50%以下。
         
         为了兼容传统的Wifi设备,Wifi_Direct其实还存在另一种使用方式,暂且称为兼容模式。兼容模式的特点在于,只需要担任GroupOwner的设备支持Wifi_Direct,而其他设备只需要支持传统的Wifi就可以了(个人觉得其实这种使用模式很像Android的便携热点功能)。
         操作流程为:
         1. 支持Wifi_Direct的设备创建group,WifiP2pManager.createGroup(),成为GroupOwner。
         2.  其他设备扫描Wifi_Direct设备创建group后产生的Wifi热点并连接即可。

         兼容模式存在的一个问题是:因为作为group member的设备是使用Wifi硬件接入到group中,所以会导致member进行wifi 热点切换以及网络中断,可能对正在进行的网络操作造成影响,而group owner则不存在这个问题。另外,而WifiP2p配对的使用方式,WifiP2p和Wifi可以独立运作,相互不受影响。
         
          但是,兼容模式因为省去了扫描和配对的过程,所以建立连接的成功率明显提升,并且建立连接的速度要快不少(具体时间比较随机)。
          从个人的使用感觉来讲,这WifiP2p这套API接口高度的异步化,API都需要以回调的方式获取操作结果(包内interface比较多的原因就在于此)。更加麻烦的是,几个关键API(例如WifiP2pManager.connect)的回调获取到的结果仅仅是执行是否开始,真正的结果还得注册broadcast receiver,通过监听广播来获得,才能进行下一步操作。异步的设计提高了代码的逻辑复杂度。

         使用NFC来实现WifiP2p的连接:
         1. 使用NFC将owner设备创建的group的SSID和密码传递给member设备
         2. owner开始监听指定端口,等待member的连接
         3. member接收到nfc传递过来的数据后,根据SSID和密码连接到group
         4. 连接成功以后,过去owner设备的ip地址(获取gateway ip即可),连接到owner的指定端口


          常见问题:
          1. WifiP2p相关的广播有哪些,各自有哪些参数?

WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:当WifiP2p扫描开始或者停止时,触发该广播
该广播包含一个int型extra, key为WifiP2pManager.EXTRA_DISCOVERY_STATE,其值为WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED或者WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED.

WifiP2pManager.WIFI_P2P_STATE_CHANGED_ATIONIC:当WifiP2p状态发生变化时触发(如果WifiP2p可用,那么当BroadcastReceiverregister时,也会收到该广播)
该广播包含一个int型extra,key为WifiP2pManager.EXTRA_WIFI_STATE,其值为WifiP2pManager.WIFI_P2P_STATE_ENABLED或者WifiP2pManager.WIFI_P2P_STATE_DISABLED。

WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:当设备的WifiP2p状态发生变化时触发广播(如果WifiP2p可用,那么当BroadcastReceiverregister时,也会收到该广播)
该广播包含一个类型为WifiP2pDevice的extra,key为WifiP2pManager.EXTRA_WIFI_P2P_DEVICE.

WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:当WifiP2p扫描时,发现device列表发生变化时,触发该广播
该广播不含extra,开发者应该接收到此广播后,调用WifiP2pManager.requestPeers()函数查询当前设别列表。

WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:当WifiP2p的group发生变化时,触发该广播。
该广播包含两个extra:
key:WifiP2pManager.EXTRA_NETWORK_INFO,其值为NetworkInfo类型。
key:WifiP2pManager.EXTRA_P2P_INFO,其值为WifiP2pInfo类型。
PS:这里的WifiP2p group发生变化包含如下情况:
1. 建立group
2. member加入到group
3. member退出group
4. 关闭group

        2. 如何获得WifiP2pGroupInfo,它有什么用?
WifiP2pManager.requestGroupInfo()函数,可以获取GroupInfo,较为有用的api有:
1. GroupInfo.getClientList()可以获得连接到group的member列表
2. GroupInfo.getNetWorkName()可以获得group的wifi热点名称(SSID)
3. GroupInfo.getPassphrase() 可以获得连接到wifi 热点的密码

        3. 如何获得WifiP2pInfo?
可以从WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION广播中的extra中获取
也可以从WifiP2pManager.requestConnectionInfo()函数获取。

        4. 如何防止配对产生的提示框?
在不修改framework的情况下,本人暂时为找到可行的方案。
这个提示狂是由系统提供的,具体表现视设备而定。nexus只在第一次配对的时候弹出,而A80每一次配对都会弹出。
但是,使用兼容模式使用Wifi_Direct是没有提示框的。

         5. 如何实现wifi热点的连接?
 经过测试,在A80上,如下代码可以实现连接到热点。
            // build a wifi config
            final WifiConfiguration config = new WifiConfiguration();
            config.allowedAuthAlgorithms.clear();
            config.allowedPairwiseCiphers.clear();
            config.allowedGroupCiphers.clear();
            config.allowedKeyManagement.clear();
            config.allowedProtocols.clear();

            config.SSID = "\"" + ssid + "\"";//设定ssid
            config.preSharedKey = "\"" + pw + "\"";//设定密码
            config.hiddenSSID = false;
            config.status = WifiConfiguration.Status.ENABLED;
            config.priority = 1;
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            config.allowedPairwiseCiphers.set(3);
            config.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
            config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);

            // connect to ap
            int id = WifiManager.addNetwork(config);
            config.networkId = id;
            if (id != -1 && mWifiManager.enableNetwork(id, true)) {
                ....
            }

        6. 如何通过代码开启便携热点?(这个问题和Wifi_Direct无关,但是可以让不支持Wifi_Direct的Android设备获得类似模拟模式的效果,可能和快牙的实现方式相似)

正常情况下,开启便携热点的API因为hide隐藏的关系,无法被apk调用,仅原生app可以调用。但是用java的反射机制可以让普通app也能调用这个api,实现如下:
  1. // wifi热点开关  
  2.     public boolean setWifiApEnabled(boolean enabled) {  
  3.         if (enabled) { // disable WiFi in any case  
  4.             //wifi和热点不能同时打开,所以打开热点的时候需要关闭wifi  
  5.             wifiManager.setWifiEnabled(false);  
  6.         }  
  7.         try {  
  8.             //热点的配置类  
  9.             WifiConfiguration apConfig = new WifiConfiguration();  
  10.             //配置热点的名称(可以在名字后面加点随机数什么的)  
  11.             apConfig.SSID = "YRCCONNECTION";  
  12.             //配置热点的密码  
  13.             apConfig.preSharedKey="12122112";  
  14.                 //通过反射调用设置热点  
  15.             Method method = wifiManager.getClass().getMethod(  
  16.                     "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);  
  17.             //返回热点打开状态  
  18.             return (Boolean) method.invoke(wifiManager, apConfig, enabled);  
  19.         } catch (Exception e) {  
  20.             return false;  
  21.         }  
  22.     }  
以上代码拷贝自:http://blog.csdn.net/luoboo525/article/details/7883998


        7. WifiP2pManager.discovePeers仅仅返回附近有哪些设备开启了wifi p2p,而app的实际使用场景,往往希望寻找可以提供某些特定服务的设备。例如同一房间内,有A,B,C,D四台设备开启了wifi p2p,而A设备和B设备都安装了app1,C设备和D设备都安装了app2,使用者希望A设备能和B设备配对连接,而C设备与D设备连接,运行在A设备上的app1如何识别它应该连接的是B设备,而非C、D设备呢?

        为了支持更加个性化的设备发现,WifiP2pManager支持UPNP和DNS两种方式的设备(服务?)发现。
        App可以通过WifiP2pManager.addLocalService来向周边的设备广播自己支持哪些服务。
        也可以通过如下步骤实现发现这些服务:
        1. 通过WifiP2pManager.addServiceRequest()添加服务请求
        2. 通过WifiP2pManager.discoverService()开始服务发现
        3. 通过WifiP2pManager.setDnsSdResponseListener()或者WifiP2pManager.setUpnpServiceResponseListener()监听服务内容。




没有更多推荐了,返回首页