前言
Wifi p2p是Android4.0以上用于用户之间端对端传输文件的协议,其不耗流量,且传输效率比蓝牙传输高的多。其操作流程如下:
- 点击进入Wifi p2p设置页,华为手机一般在WLAN页的列表中可见,OPPO手机一般在其他无线连接页的列表中。
- 搜索附近可连接的p2p Devices。其搜索结果会以Preference的形式添加到列表中。
- 在可用的Devices列表中点击发起邀请或接受另一Device发起的邀请。
Wifi p2p主要通过WifiP2pManager来完成。
mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
WifiP2pManager
其api主要有:
- initialize,此方法必须先行被调用,用于向WifiP2p框架注册我们的应用,其方法返回值WifiP2pManager.channel决定了p2p功能是否可用。
- requestPeers,获取当前可用的peers(WifiP2pDevice)列表,在其PeerListListener中会已参数形式给出可用列表。
- discoverPeers,在p2p连接性发生变化时,比如第一次建立连接及发生disconnect时可触发此方法去初始化Peers操作。其与requestPeers不同之处在与调用时机不同,requestPeers方法主要在WIFI_P2P_PEERS_CHANGED_ACTION广播时调用。discoverPeers失败的原因可能有:ERROR(value=0,内部错误)、P2P_UNSUPPORTED(此设备不支持p2p功能)、BUSY(p2p框架繁忙中)。
- stopPeerDiscovery,停止扫描p2p设备。
- connect,通过构建Config与指定的WifiP2pDevice设备建联。
- setDeviceName,此为@hide的api。设置本机p2p显示名称。
- removeGroup,移除当前的p2p组,意为取消与相关p2p设备的连接,此方法为主动发起连接方调用。
- cancelConnect,取消连接,此方法为受邀请方调用。
- requestPersistentGroupInfo,请求曾经连接成功的p2p组。
- deletePersistentGroup,从系统设置里边删除曾经保存的组。
WifiP2pSettings
- onActivityCreated
其中监听了p2p很多状态的改变。
//状态变化,主要指disable/enable变化
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
//可连接列表的变化监听,此为被动式监听,也可通过requestPeers主动获取peers列表
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
//Wifi p2p框架功能连接性变化的监听
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
//本机设备信息发生变化
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
//搜索变化,如搜索开始或结束的监听
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
//被保存的曾经连接成功的group的变化
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
以下是广播接收者中对应action的处理。
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//状态变化,主要指disable/enable变化
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
//从intent中获取p2p状态
mWifiP2pEnabled = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
WifiP2pManager.WIFI_P2P_STATE_DISABLED) == WifiP2pManager.WIFI_P2P_STATE_ENABLED;
//此处对应的是enable/disable的checkbox的变化
handleP2pStateChanged();
//可连接列表的变化监听
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
//可直接通过intent获取Peers
mPeers = (WifiP2pDeviceList) intent.getParcelableExtra(
WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
handlePeersChanged();
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
if (mWifiP2pManager == null) return;
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
WifiP2pManager.EXTRA_NETWORK_INFO);
WifiP2pInfo wifip2pinfo = (WifiP2pInfo) intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_INFO);
//当前p2p已经处于连接状态
if (networkInfo.isConnected()) {
if (DBG) Log.d(TAG, "Connected");
} else if (mLastGroupFormed != true) {//如果当前p2p组还没有构建
//start a search when we are disconnected
//but not on group removed broadcast event
//如果还没有构建group,此处就进行discoverPeers操作
startSearch();
}
mLastGroupFormed = wifip2pinfo.groupFormed;
//设备信息变化
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
mThisDevice = (WifiP2pDevice) intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
if (DBG) Log.d(TAG, "Update device info: " + mThisDevice);
//更新本机当前p2p的设备信息,如deviceName
updateDevicePref();
//搜索变化,如搜索开始或结束
} else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,
WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
if (DBG) Log.d(TAG, "Discovery state changed: " + discoveryState);
//如果是搜索开始则把按钮置为true
if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
updateSearchMenu(true);
} else {
updateSearchMenu(false);
}
} else if (WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION.equals(action)) {
if (mWifiP2pManager != null) {
mWifiP2pManager.requestPersistentGroupInfo(mChannel, WifiP2pSettings.this);
}
}
}
};
- onResume
在页面中出现时发起请求Peers,此处会在PeerListListener的回调onPeersAvailable中体现出来。
@Override
public void onResume() {
super.onResume();
getActivity().registerReceiver(mReceiver, mIntentFilter);
if (mWifiP2pManager != null) {
//请求获取可连接的设备
mWifiP2pManager.requestPeers(mChannel, WifiP2pSettings.this);
}
}
- onPause
对应的,在页面消失时会暂停请求Peers。
@Override
public void onPause() {
super.onPause();
if (mWifiP2pManager != null) {
//停止扫描可连接设备信息
mWifiP2pManager.stopPeerDiscovery(mChannel, null);
}
getActivity().unregisterReceiver(mReceiver);
}
- 主动扫描
如果用户在进入Activity后需要主动点击进行p2p设备扫描,则需调用以下方法。
private void startSearch() {
if (mWifiP2pManager != null && !mWifiP2pSearching) {
mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
public void onSuccess() {
}
public void onFailure(int reason) {
if (DBG) Log.d(TAG, " discover fail " + reason);
}
});
}
}
在调用WifiP2pManager的discoverPeers方法后,最终会调用到WifiP2pServiceImpl中内部类P2pEnableState的processMessage去,在此方法中,首先清理掉之前的discovery请求,然后根据超时策略去进行p2pFind即设备扫描,如果p2pFind成功,则会发送PeersChanged广播与discovery成功,如失败则发送discovery失败广播。
@Override
public boolean processMessage(Message message) {
if (DBG) logd(getName() + message.toString());
switch (message.what) {
...
case WifiP2pManager.DISCOVER_PEERS:
if (mDiscoveryBlocked) {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.BUSY);
break;
}
// do not send service discovery request while normal find operation.
clearSupplicantServiceRequest();
if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
sendP2pDiscoveryChangedBroadcast(true);
} else {
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.ERROR);
}
break;
...
case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
WifiP2pDevice device = (WifiP2pDevice) message.obj;
if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
mPeers.updateSupplicantDetails(device);
sendPeersChangedBroadcast();
break;
...
default:
return NOT_HANDLED;
}
return HANDLED;
}
- 弹框事件处理。
主要分为以下三种类型的弹框事件处理:
- 取消连接。取消连接分为两种类型的取消,主动方取消连接还是受邀请方取消连接,此两种分别对应不同的调用方式(在本文开始介绍WifiP2pManager的api时有谈及)removeGroup以及cancelConnect。
- 重命名本机p2p设备名称。通过WifiP2pManager的setDeviceName传入需要修改的名称即可,其可能修改失败。底层通过SettingsProvider的Settings进行值的保存。同时设置成功会发送广播WIFI_P2P_THIS_DEVICE_CHANGED_ACTION。
private boolean setAndPersistDeviceName(String devName) { if (devName == null) return false; if (!mWifiNative.setDeviceName(devName)) { loge("Failed to set device name " + devName); return false; } mThisDevice.deviceName = devName; mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName); Settings.Global.putString(mContext.getContentResolver(), Settings.Global.WIFI_P2P_DEVICE_NAME, devName); sendThisDeviceChangedBroadcast(); return true; }
- 删除group组。调用deletePersistentGroup删除成功连接过的group。
- 用于连接的WifiP2pConfig的配置
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;
int forceWps = SystemProperties.getInt("wifidirect.wps", -1);
if (forceWps != -1) {
config.wps.setup = forceWps;
} else {
if (mSelectedWifiPeer.device.wpsPbcSupported()) {
config.wps.setup = WpsInfo.PBC;
} else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {
config.wps.setup = WpsInfo.KEYPAD;
} else {
config.wps.setup = WpsInfo.DISPLAY;
}
}
mWifiP2pManager.connect(mChannel, config,
new WifiP2pManager.ActionListener() {
public void onSuccess() {
if (DBG) Log.d(TAG, " connect success");
}
public void onFailure(int reason) {
Log.e(TAG, " connect fail " + reason);
Toast.makeText(getActivity(),
R.string.wifi_p2p_failed_connect_message,
Toast.LENGTH_SHORT).show();
}
});