1. /**
    2. * Created by Bruce Too
    3. * On 27/04/2018.
    4. * At 10:21
    5. * Use example:
    6. * 1. Init assistant by {@link WifiP2PAssistant#getInstance(Context)}
    7. *
    8. * 2. Register broadcaster and check if P2P is enable by {@link WifiP2PAssistant#enable()}
    9. * unregister by {@link WifiP2PAssistant#disable()}
    10. *
    11. * 3. Create GO(let the caller device be GO) by {@link WifiP2PAssistant#createGroup()}
    12. * remove by {@link WifiP2PAssistant#removeGroup()}
    13. *
    14. * 4. Discover peer devices by {@link WifiP2PAssistant#discoverPeers()}
    15. * cancel operation by {@link WifiP2PAssistant#cancelDiscoverPeers()}
    16. *
    17. * 5. Connect one single Peer by {@link WifiP2PAssistant#connect(WifiP2pDevice)}
    18. *
    19. * 6. Register key lifecycle event callback by {@link WifiP2PAssistant#setCallback(WifiP2PAssistantCallback)}
    20. *
    21. * More apis to see the detail below..
    22. */
    23. public class WifiP2PAssistant {
    24. /**
    25. * Key process event callback listener
    26. */
    27. public interface WifiP2PAssistantCallback {
    28. void onWifiP2PEvent(Event event);
    29. }
    30. private static final String TAG = WifiP2PAssistant.class.getSimpleName();
    31. private static WifiP2PAssistant sWifiP2PAssistant = null;
    32. private final List<WifiP2pDevice> mCurrentPeers = new ArrayList<WifiP2pDevice>();
    33. private Context mContext = null;
    34. private boolean mIsWifiP2pEnabled = false;
    35. private final IntentFilter mIntentFilter;
    36. private final WifiP2pManager.Channel mWifiP2pChannel;
    37. private final WifiP2pManager mWifiP2pManager;
    38. private WifiP2pBroadcastReceiver mReceiver;
    39. private final WifiP2PConnectionInfoListener mConnectionListener;
    40. private final WifiP2PPeerListListener mPeerListListener;
    41. private final WifiP2PGroupInfoListener mGroupInfoListener;
    42. private int mFailureReason = WifiP2pManager.ERROR;
    43. private ConnectStatus mConnectStatus = ConnectStatus.NOT_CONNECTED;
    44. private Event mLastEvent = null;
    45. private String mDeviceMacAddress = "";
    46. private String mDeviceName = "";
    47. private InetAddress mGroupOwnerAddress = null;
    48. private String mGroupOwnerMacAddress = "";
    49. private String mGroupOwnerName = "";
    50. private String mPassphrase = "";
    51. private boolean mGroupFormed = false;
    52. // tracks the number of clients, must be thread safe
    53. private int clients = 0;
    54. private WifiP2PAssistantCallback mEventCallback = null;
    55. /**
    56. * Key lifecycle event enum
    57. */
    58. public enum Event {
    59. DISCOVERING_PEERS,
    60. PEERS_AVAILABLE,
    61. GROUP_CREATED,
    62. CONNECTING,
    63. CONNECTED_AS_PEER,
    64. CONNECTED_AS_GROUP_OWNER,
    65. DISCONNECTED,
    66. CONNECTION_INFO_AVAILABLE,
    67. ERROR
    68. }
    69. /**
    70. * P2P Connect Status
    71. */
    72. public enum ConnectStatus {
    73. NOT_CONNECTED,
    74. CONNECTING,
    75. CONNECTED,
    76. GROUP_OWNER,
    77. ERROR
    78. }
    79. public synchronized static WifiP2PAssistant getInstance(Context context) {
    80. if (sWifiP2PAssistant == null) sWifiP2PAssistant = new WifiP2PAssistant(context);
    81. return sWifiP2PAssistant;
    82. }
    83. private WifiP2PAssistant(@NonNull Context context) {
    84. this.mContext = context;
    85. // Set up the intent filter for wifi P2P
    86. mIntentFilter = new IntentFilter();
    87. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    88. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    89. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    90. mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
    91. mWifiP2pManager = (WifiP2pManager) context.getSystemService(Context.WIFI_P2P_SERVICE);
    92. if (mWifiP2pManager == null) {
    93. throw new RuntimeException("Wifi P2P Manager is null");
    94. }
    95. mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);
    96. mReceiver = new WifiP2pBroadcastReceiver();
    97. mConnectionListener = new WifiP2PConnectionInfoListener();
    98. mPeerListListener = new WifiP2PPeerListListener();
    99. mGroupInfoListener = new WifiP2PGroupInfoListener();
    100. }
    101. /*
    102. * Maintains the list of wifi p2p peers available
    103. */
    104. private class WifiP2PPeerListListener implements WifiP2pManager.PeerListListener {
    105. /*
    106. * mEventCallback method, called by Android when the peer list changes
    107. */
    108. @Override
    109. public void onPeersAvailable(WifiP2pDeviceList peerList) {
    110. mCurrentPeers.clear();
    111. mCurrentPeers.addAll(peerList.getDeviceList());
    112. Log.v(TAG, "Wifi P2P peers found: " + mCurrentPeers.size());
    113. for (WifiP2pDevice peer : mCurrentPeers) {
    114. // deviceAddress is the MAC address, deviceName is the human readable name
    115. String s = " peer: " + peer.deviceAddress + " " + peer.deviceName;
    116. Log.v(TAG, s);
    117. }
    118. fireEvent(Event.PEERS_AVAILABLE);
    119. }
    120. }
    121. /*
    122. * Updates when this device connects
    123. */
    124. private class WifiP2PConnectionInfoListener implements WifiP2pManager.ConnectionInfoListener {
    125. @Override
    126. public void onConnectionInfoAvailable(final WifiP2pInfo info) {
    127. if (mWifiP2pManager == null) return;
    128. //when the connection state changes, request group info to find GO
    129. mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, mGroupInfoListener);
    130. mGroupOwnerAddress = info.groupOwnerAddress;
    131. Log.v(TAG, "Group owners address: " + mGroupOwnerAddress.toString());
    132. if (info.groupFormed && info.isGroupOwner) {
    133. Log.v(TAG, "Wifi P2P group formed, this device is the group owner (GO)");
    134. mConnectStatus = ConnectStatus.GROUP_OWNER;
    135. fireEvent(Event.CONNECTED_AS_GROUP_OWNER);
    136. } else if (info.groupFormed) {
    137. Log.v(TAG, "Wifi P2P group formed, this device is a client");
    138. mConnectStatus = ConnectStatus.CONNECTED;
    139. fireEvent(Event.CONNECTED_AS_PEER);
    140. } else {
    141. Log.v(TAG, "Wifi P2P group NOT formed, ERROR: " + info.toString());
    142. mFailureReason = WifiP2pManager.ERROR; // there is no error code for this
    143. mConnectStatus = ConnectStatus.ERROR;
    144. fireEvent(Event.ERROR);
    145. }
    146. }
    147. }
    148. private class WifiP2PGroupInfoListener implements WifiP2pManager.GroupInfoListener {
    149. @Override
    150. public void onGroupInfoAvailable(WifiP2pGroup group) {
    151. if (group == null) return;
    152. if (group.isGroupOwner()) {
    153. mGroupOwnerMacAddress = mDeviceMacAddress;
    154. mGroupOwnerName = mDeviceName;
    155. } else {
    156. WifiP2pDevice go = group.getOwner();
    157. mGroupOwnerMacAddress = go.deviceAddress;
    158. mGroupOwnerName = go.deviceName;
    159. }
    160. mPassphrase = group.getPassphrase();
    161. // make sure passphrase isn't null
    162. mPassphrase = (mPassphrase != null) ? mPassphrase : "";
    163. Log.v(TAG, "Wifi P2P connection information available");
    164. fireEvent(Event.CONNECTION_INFO_AVAILABLE);
    165. }
    166. }
    167. private class WifiP2pBroadcastReceiver extends BroadcastReceiver {
    168. @Override
    169. public void onReceive(Context context, Intent intent) {
    170. String action = intent.getAction();
    171. if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
    172. int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
    173. mIsWifiP2pEnabled = (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED);
    174. Log.v(TAG, "Wifi P2P state - enabled: " + mIsWifiP2pEnabled);
    175. } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
    176. Log.v(TAG, "Wifi P2P peers changed");
    177. if (mWifiP2pManager == null) return;
    178. mWifiP2pManager.requestPeers(mWifiP2pChannel, mPeerListListener);
    179. } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
    180. NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
    181. WifiP2pInfo wifip2pinfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
    182. Log.v(TAG, "Wifi P2P connection changed - connected: " + networkInfo.isConnected());
    183. if (mWifiP2pManager == null) return;
    184. if (networkInfo.isConnected()) {
    185. mWifiP2pManager.requestConnectionInfo(mWifiP2pChannel, mConnectionListener);
    186. mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
    187. } else {
    188. mConnectStatus = ConnectStatus.NOT_CONNECTED;
    189. if (!mGroupFormed) {
    190. discoverPeers();
    191. }
    192. // if we were previously connected, notify that we are now disconnected
    193. if (isConnected()) {
    194. fireEvent(Event.DISCONNECTED);
    195. }
    196. mGroupFormed = wifip2pinfo.groupFormed;
    197. }
    198. } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
    199. Log.v(TAG, "Wifi P2P this device changed");
    200. WifiP2pDevice wifiP2pDevice = (WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
    201. mDeviceName = wifiP2pDevice.deviceName;
    202. mDeviceMacAddress = wifiP2pDevice.deviceAddress;
    203. Log.v(TAG, "Wifi P2P device information: " + mDeviceName + " " + mDeviceMacAddress);
    204. }
    205. }
    206. }
    207. public synchronized void enable() {
    208. clients += 1;
    209. Log.v(TAG, "There are " + clients + " Wifi P2P Assistant Clients (+)");
    210. if (clients == 1) {
    211. Log.v(TAG, "Enabling Wifi P2P Assistant");
    212. if (mReceiver == null) mReceiver = new WifiP2pBroadcastReceiver();
    213. mContext.registerReceiver(mReceiver, mIntentFilter);
    214. }
    215. }
    216. public synchronized void disable() {
    217. clients -= 1;
    218. Log.v(TAG, "There are " + clients + " Wifi P2P Assistant Clients (-)");
    219. if (clients == 0) {
    220. Log.v(TAG, "Disabling Wifi P2P Assistant");
    221. mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
    222. mWifiP2pManager.cancelConnect(mWifiP2pChannel, null);
    223. try {
    224. mContext.unregisterReceiver(mReceiver);
    225. } catch (IllegalArgumentException e) {
    226. // disable() was called, but enable() was never called; ignore
    227. }
    228. mLastEvent = null;
    229. }
    230. }
    231. public synchronized boolean isEnabled() {
    232. return (clients > 0);
    233. }
    234. public ConnectStatus getConnectStatus() {
    235. return mConnectStatus;
    236. }
    237. public List<WifiP2pDevice> getPeersList() {
    238. return new ArrayList<WifiP2pDevice>(mCurrentPeers);
    239. }
    240. public WifiP2PAssistantCallback getCallback() {
    241. return mEventCallback;
    242. }
    243. public void setCallback(WifiP2PAssistantCallback callback) {
    244. this.mEventCallback = callback;
    245. }
    246. /**
    247. * Get the device mac address
    248. *
    249. * @return mac address
    250. */
    251. public String getDeviceMacAddress() {
    252. return mDeviceMacAddress;
    253. }
    254. /**
    255. * Get the device name,if want to set device name,you need
    256. * reflect call {@link WifiP2pManager#setDeviceName(WifiP2pManager.Channel c, String devName, ActionListener listener)}
    257. *
    258. * @return device name
    259. */
    260. public String getDeviceName() {
    261. return mDeviceName;
    262. }
    263. /**
    264. * Get the IP address of the group owner
    265. *
    266. * @return ip address
    267. */
    268. public InetAddress getGroupOwnerAddress() {
    269. return mGroupOwnerAddress;
    270. }
    271. /**
    272. * Get the group owners mac address
    273. *
    274. * @return mac address
    275. */
    276. public String getGroupOwnerMacAddress() {
    277. return mGroupOwnerMacAddress;
    278. }
    279. /**
    280. * Get the group owners device name
    281. *
    282. * @return device name
    283. */
    284. public String getGroupOwnerName() {
    285. return mGroupOwnerName;
    286. }
    287. /**
    288. * Return the passphrase for this network; only valid if this device is the group owner
    289. *
    290. * @return the passphrase to this device
    291. */
    292. public String getPassphrase() {
    293. return mPassphrase;
    294. }
    295. public boolean isWifiP2pEnabled() {
    296. return mIsWifiP2pEnabled;
    297. }
    298. /**
    299. * Returns true if connected, or group owner
    300. *
    301. * @return true if connected, otherwise false
    302. */
    303. public boolean isConnected() {
    304. return (mConnectStatus == ConnectStatus.CONNECTED
    305. || mConnectStatus == ConnectStatus.GROUP_OWNER);
    306. }
    307. /**
    308. * Returns true if this device is the group owner
    309. *
    310. * @return true if group owner, otherwise false
    311. */
    312. public boolean isGroupOwner() {
    313. return (mConnectStatus == ConnectStatus.GROUP_OWNER);
    314. }
    315. /**
    316. * Discover Wifi P2P peers
    317. */
    318. public void discoverPeers() {
    319. mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
    320. @Override
    321. public void onSuccess() {
    322. fireEvent(Event.DISCOVERING_PEERS);
    323. Log.v(TAG, "Wifi P2P discovering peers");
    324. }
    325. @Override
    326. public void onFailure(int reason) {
    327. String reasonStr = failureReasonToString(reason);
    328. mFailureReason = reason;
    329. Log.v(TAG, "Wifi P2P failure while trying to discover peers - reason: " + reasonStr);
    330. fireEvent(Event.ERROR);
    331. }
    332. });
    333. }
    334. /**
    335. * Cancel discover Wifi P2P peers request
    336. */
    337. public void cancelDiscoverPeers() {
    338. Log.v(TAG, "Wifi P2P stop discovering peers");
    339. mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
    340. }
    341. /**
    342. * Create a Wifi P2P group
    343. * <p>
    344. * Will receive a Event.GROUP_CREATED if the group is created. If there is an
    345. * error creating group Event.ERROR will be sent. If group already exists, no
    346. * event will be sent. However, a Event.CONNECTED_AS_GROUP_OWNER should be
    347. * received.
    348. */
    349. public void createGroup() {
    350. mWifiP2pManager.createGroup(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
    351. @Override
    352. public void onSuccess() {
    353. mConnectStatus = ConnectStatus.GROUP_OWNER;
    354. fireEvent(Event.GROUP_CREATED);
    355. Log.v(TAG, "Wifi P2P created group");
    356. }
    357. @Override
    358. public void onFailure(int reason) {
    359. if (reason == WifiP2pManager.BUSY) {
    360. // most likely group is already created
    361. Log.v(TAG, "Wifi P2P cannot create group, does group already exist?");
    362. } else {
    363. String reasonStr = failureReasonToString(reason);
    364. mFailureReason = reason;
    365. Log.v(TAG, "Wifi P2P failure while trying to create group - reason: " + reasonStr);
    366. mConnectStatus = ConnectStatus.ERROR;
    367. fireEvent(Event.ERROR);
    368. }
    369. }
    370. });
    371. }
    372. /**
    373. * Remove a Wifi P2P group
    374. */
    375. public void removeGroup() {
    376. mWifiP2pManager.removeGroup(mWifiP2pChannel, null);
    377. }
    378. public void connect(WifiP2pDevice peer) {
    379. if (mConnectStatus == ConnectStatus.CONNECTING || mConnectStatus == ConnectStatus.CONNECTED) {
    380. Log.v(TAG, "WifiP2P connection request to " + peer.deviceAddress + " ignored, already connected");
    381. return;
    382. }
    383. Log.v(TAG, "WifiP2P connecting to " + peer.deviceAddress);
    384. mConnectStatus = ConnectStatus.CONNECTING;
    385. WifiP2pConfig config = new WifiP2pConfig();
    386. config.deviceAddress = peer.deviceAddress;
    387. config.wps.setup = WpsInfo.PBC;
    388. config.groupOwnerIntent = 1;
    389. mWifiP2pManager.connect(mWifiP2pChannel, config, new WifiP2pManager.ActionListener() {
    390. @Override
    391. public void onSuccess() {
    392. Log.v(TAG, "WifiP2P connect started");
    393. fireEvent(Event.CONNECTING);
    394. }
    395. @Override
    396. public void onFailure(int reason) {
    397. String reasonStr = failureReasonToString(reason);
    398. mFailureReason = reason;
    399. Log.v(TAG, "WifiP2P connect cannot start - reason: " + reasonStr);
    400. fireEvent(Event.ERROR);
    401. }
    402. });
    403. }
    404. public String getFailureReason() {
    405. return failureReasonToString(mFailureReason);
    406. }
    407. public static String failureReasonToString(int reason) {
    408. switch (reason) {
    409. case WifiP2pManager.P2P_UNSUPPORTED:
    410. return "P2P_UNSUPPORTED";
    411. case WifiP2pManager.ERROR:
    412. return "ERROR";
    413. case WifiP2pManager.BUSY:
    414. return "BUSY";
    415. default:
    416. return "UNKNOWN (reason " + reason + ")";
    417. }
    418. }
    419. private void fireEvent(Event event) {
    420. // don't send duplicate events
    421. if (mLastEvent == event && mLastEvent != Event.PEERS_AVAILABLE) return;
    422. mLastEvent = event;
    423. if (mEventCallback != null) mEventCallback.onWifiP2PEvent(event);
    424. }
    425. }