Skip to content

Instantly share code, notes, and snippets.

@brucetoo
Created April 27, 2018 03:23
Show Gist options
  • Save brucetoo/f57a6a5b56151d1d9d5cd7974a0b8d76 to your computer and use it in GitHub Desktop.
Save brucetoo/f57a6a5b56151d1d9d5cd7974a0b8d76 to your computer and use it in GitHub Desktop.
WifiP2p helper class
package com.brucetoo.wifip2p;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.util.Log;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Bruce Too
* On 27/04/2018.
* At 10:21
* Use example:
* 1. Init assistant by {@link WifiP2PAssistant#getInstance(Context)}
*
* 2. Register broadcaster and check if P2P is enable by {@link WifiP2PAssistant#enable()}
* unregister by {@link WifiP2PAssistant#disable()}
*
* 3. Create GO(let the caller device be GO) by {@link WifiP2PAssistant#createGroup()}
* remove by {@link WifiP2PAssistant#removeGroup()}
*
* 4. Discover peer devices by {@link WifiP2PAssistant#discoverPeers()}
* cancel operation by {@link WifiP2PAssistant#cancelDiscoverPeers()}
*
* 5. Connect one single Peer by {@link WifiP2PAssistant#connect(WifiP2pDevice)}
*
* 6. Register key lifecycle event callback by {@link WifiP2PAssistant#setCallback(WifiP2PAssistantCallback)}
*
* More apis to see the detail below..
*/
public class WifiP2PAssistant {
/**
* Key process event callback listener
*/
public interface WifiP2PAssistantCallback {
void onWifiP2PEvent(Event event);
}
private static final String TAG = WifiP2PAssistant.class.getSimpleName();
private static WifiP2PAssistant sWifiP2PAssistant = null;
private final List<WifiP2pDevice> mCurrentPeers = new ArrayList<WifiP2pDevice>();
private Context mContext = null;
private boolean mIsWifiP2pEnabled = false;
private final IntentFilter mIntentFilter;
private final WifiP2pManager.Channel mWifiP2pChannel;
private final WifiP2pManager mWifiP2pManager;
private WifiP2pBroadcastReceiver mReceiver;
private final WifiP2PConnectionInfoListener mConnectionListener;
private final WifiP2PPeerListListener mPeerListListener;
private final WifiP2PGroupInfoListener mGroupInfoListener;
private int mFailureReason = WifiP2pManager.ERROR;
private ConnectStatus mConnectStatus = ConnectStatus.NOT_CONNECTED;
private Event mLastEvent = null;
private String mDeviceMacAddress = "";
private String mDeviceName = "";
private InetAddress mGroupOwnerAddress = null;
private String mGroupOwnerMacAddress = "";
private String mGroupOwnerName = "";
private String mPassphrase = "";
private boolean mGroupFormed = false;
// tracks the number of clients, must be thread safe
private int clients = 0;
private WifiP2PAssistantCallback mEventCallback = null;
/**
* Key lifecycle event enum
*/
public enum Event {
DISCOVERING_PEERS,
PEERS_AVAILABLE,
GROUP_CREATED,
CONNECTING,
CONNECTED_AS_PEER,
CONNECTED_AS_GROUP_OWNER,
DISCONNECTED,
CONNECTION_INFO_AVAILABLE,
ERROR
}
/**
* P2P Connect Status
*/
public enum ConnectStatus {
NOT_CONNECTED,
CONNECTING,
CONNECTED,
GROUP_OWNER,
ERROR
}
public synchronized static WifiP2PAssistant getInstance(Context context) {
if (sWifiP2PAssistant == null) sWifiP2PAssistant = new WifiP2PAssistant(context);
return sWifiP2PAssistant;
}
private WifiP2PAssistant(@NonNull Context context) {
this.mContext = context;
// Set up the intent filter for wifi P2P
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
mWifiP2pManager = (WifiP2pManager) context.getSystemService(Context.WIFI_P2P_SERVICE);
if (mWifiP2pManager == null) {
throw new RuntimeException("Wifi P2P Manager is null");
}
mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);
mReceiver = new WifiP2pBroadcastReceiver();
mConnectionListener = new WifiP2PConnectionInfoListener();
mPeerListListener = new WifiP2PPeerListListener();
mGroupInfoListener = new WifiP2PGroupInfoListener();
}
/*
* Maintains the list of wifi p2p peers available
*/
private class WifiP2PPeerListListener implements WifiP2pManager.PeerListListener {
/*
* mEventCallback method, called by Android when the peer list changes
*/
@Override
public void onPeersAvailable(WifiP2pDeviceList peerList) {
mCurrentPeers.clear();
mCurrentPeers.addAll(peerList.getDeviceList());
Log.v(TAG, "Wifi P2P peers found: " + mCurrentPeers.size());
for (WifiP2pDevice peer : mCurrentPeers) {
// deviceAddress is the MAC address, deviceName is the human readable name
String s = " peer: " + peer.deviceAddress + " " + peer.deviceName;
Log.v(TAG, s);
}
fireEvent(Event.PEERS_AVAILABLE);
}
}
/*
* Updates when this device connects
*/
private class WifiP2PConnectionInfoListener implements WifiP2pManager.ConnectionInfoListener {
@Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
if (mWifiP2pManager == null) return;
//when the connection state changes, request group info to find GO
mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, mGroupInfoListener);
mGroupOwnerAddress = info.groupOwnerAddress;
Log.v(TAG, "Group owners address: " + mGroupOwnerAddress.toString());
if (info.groupFormed && info.isGroupOwner) {
Log.v(TAG, "Wifi P2P group formed, this device is the group owner (GO)");
mConnectStatus = ConnectStatus.GROUP_OWNER;
fireEvent(Event.CONNECTED_AS_GROUP_OWNER);
} else if (info.groupFormed) {
Log.v(TAG, "Wifi P2P group formed, this device is a client");
mConnectStatus = ConnectStatus.CONNECTED;
fireEvent(Event.CONNECTED_AS_PEER);
} else {
Log.v(TAG, "Wifi P2P group NOT formed, ERROR: " + info.toString());
mFailureReason = WifiP2pManager.ERROR; // there is no error code for this
mConnectStatus = ConnectStatus.ERROR;
fireEvent(Event.ERROR);
}
}
}
private class WifiP2PGroupInfoListener implements WifiP2pManager.GroupInfoListener {
@Override
public void onGroupInfoAvailable(WifiP2pGroup group) {
if (group == null) return;
if (group.isGroupOwner()) {
mGroupOwnerMacAddress = mDeviceMacAddress;
mGroupOwnerName = mDeviceName;
} else {
WifiP2pDevice go = group.getOwner();
mGroupOwnerMacAddress = go.deviceAddress;
mGroupOwnerName = go.deviceName;
}
mPassphrase = group.getPassphrase();
// make sure passphrase isn't null
mPassphrase = (mPassphrase != null) ? mPassphrase : "";
Log.v(TAG, "Wifi P2P connection information available");
fireEvent(Event.CONNECTION_INFO_AVAILABLE);
}
}
private class WifiP2pBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
mIsWifiP2pEnabled = (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED);
Log.v(TAG, "Wifi P2P state - enabled: " + mIsWifiP2pEnabled);
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
Log.v(TAG, "Wifi P2P peers changed");
if (mWifiP2pManager == null) return;
mWifiP2pManager.requestPeers(mWifiP2pChannel, mPeerListListener);
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
WifiP2pInfo wifip2pinfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
Log.v(TAG, "Wifi P2P connection changed - connected: " + networkInfo.isConnected());
if (mWifiP2pManager == null) return;
if (networkInfo.isConnected()) {
mWifiP2pManager.requestConnectionInfo(mWifiP2pChannel, mConnectionListener);
mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
} else {
mConnectStatus = ConnectStatus.NOT_CONNECTED;
if (!mGroupFormed) {
discoverPeers();
}
// if we were previously connected, notify that we are now disconnected
if (isConnected()) {
fireEvent(Event.DISCONNECTED);
}
mGroupFormed = wifip2pinfo.groupFormed;
}
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
Log.v(TAG, "Wifi P2P this device changed");
WifiP2pDevice wifiP2pDevice = (WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
mDeviceName = wifiP2pDevice.deviceName;
mDeviceMacAddress = wifiP2pDevice.deviceAddress;
Log.v(TAG, "Wifi P2P device information: " + mDeviceName + " " + mDeviceMacAddress);
}
}
}
public synchronized void enable() {
clients += 1;
Log.v(TAG, "There are " + clients + " Wifi P2P Assistant Clients (+)");
if (clients == 1) {
Log.v(TAG, "Enabling Wifi P2P Assistant");
if (mReceiver == null) mReceiver = new WifiP2pBroadcastReceiver();
mContext.registerReceiver(mReceiver, mIntentFilter);
}
}
public synchronized void disable() {
clients -= 1;
Log.v(TAG, "There are " + clients + " Wifi P2P Assistant Clients (-)");
if (clients == 0) {
Log.v(TAG, "Disabling Wifi P2P Assistant");
mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
mWifiP2pManager.cancelConnect(mWifiP2pChannel, null);
try {
mContext.unregisterReceiver(mReceiver);
} catch (IllegalArgumentException e) {
// disable() was called, but enable() was never called; ignore
}
mLastEvent = null;
}
}
public synchronized boolean isEnabled() {
return (clients > 0);
}
public ConnectStatus getConnectStatus() {
return mConnectStatus;
}
public List<WifiP2pDevice> getPeersList() {
return new ArrayList<WifiP2pDevice>(mCurrentPeers);
}
public WifiP2PAssistantCallback getCallback() {
return mEventCallback;
}
public void setCallback(WifiP2PAssistantCallback callback) {
this.mEventCallback = callback;
}
/**
* Get the device mac address
*
* @return mac address
*/
public String getDeviceMacAddress() {
return mDeviceMacAddress;
}
/**
* Get the device name,if want to set device name,you need
* reflect call {@link WifiP2pManager#setDeviceName(WifiP2pManager.Channel c, String devName, ActionListener listener)}
*
* @return device name
*/
public String getDeviceName() {
return mDeviceName;
}
/**
* Get the IP address of the group owner
*
* @return ip address
*/
public InetAddress getGroupOwnerAddress() {
return mGroupOwnerAddress;
}
/**
* Get the group owners mac address
*
* @return mac address
*/
public String getGroupOwnerMacAddress() {
return mGroupOwnerMacAddress;
}
/**
* Get the group owners device name
*
* @return device name
*/
public String getGroupOwnerName() {
return mGroupOwnerName;
}
/**
* Return the passphrase for this network; only valid if this device is the group owner
*
* @return the passphrase to this device
*/
public String getPassphrase() {
return mPassphrase;
}
public boolean isWifiP2pEnabled() {
return mIsWifiP2pEnabled;
}
/**
* Returns true if connected, or group owner
*
* @return true if connected, otherwise false
*/
public boolean isConnected() {
return (mConnectStatus == ConnectStatus.CONNECTED
|| mConnectStatus == ConnectStatus.GROUP_OWNER);
}
/**
* Returns true if this device is the group owner
*
* @return true if group owner, otherwise false
*/
public boolean isGroupOwner() {
return (mConnectStatus == ConnectStatus.GROUP_OWNER);
}
/**
* Discover Wifi P2P peers
*/
public void discoverPeers() {
mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
fireEvent(Event.DISCOVERING_PEERS);
Log.v(TAG, "Wifi P2P discovering peers");
}
@Override
public void onFailure(int reason) {
String reasonStr = failureReasonToString(reason);
mFailureReason = reason;
Log.v(TAG, "Wifi P2P failure while trying to discover peers - reason: " + reasonStr);
fireEvent(Event.ERROR);
}
});
}
/**
* Cancel discover Wifi P2P peers request
*/
public void cancelDiscoverPeers() {
Log.v(TAG, "Wifi P2P stop discovering peers");
mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
}
/**
* Create a Wifi P2P group
* <p>
* Will receive a Event.GROUP_CREATED if the group is created. If there is an
* error creating group Event.ERROR will be sent. If group already exists, no
* event will be sent. However, a Event.CONNECTED_AS_GROUP_OWNER should be
* received.
*/
public void createGroup() {
mWifiP2pManager.createGroup(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
mConnectStatus = ConnectStatus.GROUP_OWNER;
fireEvent(Event.GROUP_CREATED);
Log.v(TAG, "Wifi P2P created group");
}
@Override
public void onFailure(int reason) {
if (reason == WifiP2pManager.BUSY) {
// most likely group is already created
Log.v(TAG, "Wifi P2P cannot create group, does group already exist?");
} else {
String reasonStr = failureReasonToString(reason);
mFailureReason = reason;
Log.v(TAG, "Wifi P2P failure while trying to create group - reason: " + reasonStr);
mConnectStatus = ConnectStatus.ERROR;
fireEvent(Event.ERROR);
}
}
});
}
/**
* Remove a Wifi P2P group
*/
public void removeGroup() {
mWifiP2pManager.removeGroup(mWifiP2pChannel, null);
}
public void connect(WifiP2pDevice peer) {
if (mConnectStatus == ConnectStatus.CONNECTING || mConnectStatus == ConnectStatus.CONNECTED) {
Log.v(TAG, "WifiP2P connection request to " + peer.deviceAddress + " ignored, already connected");
return;
}
Log.v(TAG, "WifiP2P connecting to " + peer.deviceAddress);
mConnectStatus = ConnectStatus.CONNECTING;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = peer.deviceAddress;
config.wps.setup = WpsInfo.PBC;
config.groupOwnerIntent = 1;
mWifiP2pManager.connect(mWifiP2pChannel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.v(TAG, "WifiP2P connect started");
fireEvent(Event.CONNECTING);
}
@Override
public void onFailure(int reason) {
String reasonStr = failureReasonToString(reason);
mFailureReason = reason;
Log.v(TAG, "WifiP2P connect cannot start - reason: " + reasonStr);
fireEvent(Event.ERROR);
}
});
}
public String getFailureReason() {
return failureReasonToString(mFailureReason);
}
public static String failureReasonToString(int reason) {
switch (reason) {
case WifiP2pManager.P2P_UNSUPPORTED:
return "P2P_UNSUPPORTED";
case WifiP2pManager.ERROR:
return "ERROR";
case WifiP2pManager.BUSY:
return "BUSY";
default:
return "UNKNOWN (reason " + reason + ")";
}
}
private void fireEvent(Event event) {
// don't send duplicate events
if (mLastEvent == event && mLastEvent != Event.PEERS_AVAILABLE) return;
mLastEvent = event;
if (mEventCallback != null) mEventCallback.onWifiP2PEvent(event);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment