1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bips.p2p; 18 19 import android.content.Context; 20 import android.net.wifi.p2p.WifiP2pDevice; 21 import android.net.wifi.p2p.WifiP2pInfo; 22 import android.net.wifi.p2p.WifiP2pManager; 23 import android.util.Log; 24 25 import com.android.bips.BuiltInPrintService; 26 27 /** 28 * Globally manage P2P discovery and connectivity 29 */ 30 public class P2pMonitor { 31 private static final String TAG = P2pMonitor.class.getSimpleName(); 32 private static final boolean DEBUG = false; 33 34 private final BuiltInPrintService mService; 35 private final WifiP2pManager mP2pManager; 36 private P2pDiscoveryProcedure mPeerDiscovery; 37 private P2pConnectionProcedure mConnection; 38 private String mConnectedInterface; 39 P2pMonitor(BuiltInPrintService service)40 public P2pMonitor(BuiltInPrintService service) { 41 mService = service; 42 mP2pManager = (WifiP2pManager) mService.getSystemService(Context.WIFI_P2P_SERVICE); 43 } 44 45 /** Return a printable String form of a {@link WifiP2pDevice} */ toString(WifiP2pDevice device)46 public static String toString(WifiP2pDevice device) { 47 if (device == null) { 48 return "null"; 49 } else { 50 return device.deviceName + " " + device.deviceAddress + ", status=" 51 + statusString(device.status); 52 } 53 } 54 statusString(int status)55 private static String statusString(int status) { 56 switch (status) { 57 case WifiP2pDevice.AVAILABLE: 58 return "available"; 59 case WifiP2pDevice.CONNECTED: 60 return "connected"; 61 case WifiP2pDevice.FAILED: 62 return "failed"; 63 case WifiP2pDevice.INVITED: 64 return "invited"; 65 case WifiP2pDevice.UNAVAILABLE: 66 return "unavailable"; 67 default: 68 return "unknown"; 69 } 70 } 71 72 /** 73 * Start a discovery of Wi-Fi Direct peers until all requests are closed 74 */ discover(P2pPeerListener listener)75 public void discover(P2pPeerListener listener) { 76 if (DEBUG) Log.d(TAG, "discover()"); 77 78 if (mP2pManager == null) { 79 return; 80 } 81 if (mPeerDiscovery == null) { 82 mPeerDiscovery = new P2pDiscoveryProcedure(mService, mP2pManager, listener); 83 } else { 84 mPeerDiscovery.addListener(listener); 85 } 86 } 87 88 /** 89 * Remove the request to discover having the same listener. When all outstanding requests are 90 * removed, discovery itself is stopped. 91 */ stopDiscover(P2pPeerListener listener)92 public void stopDiscover(P2pPeerListener listener) { 93 if (DEBUG) Log.d(TAG, "stopDiscover"); 94 if (mPeerDiscovery != null) { 95 mPeerDiscovery.removeListener(listener); 96 if (mPeerDiscovery.getListeners().isEmpty()) { 97 mPeerDiscovery.cancel(); 98 mPeerDiscovery = null; 99 } 100 } 101 } 102 103 /** 104 * Request connection to a peer (which may already be connected) at least until stopped. Keeps 105 * the current connection open as long as it might be useful. 106 */ connect(WifiP2pDevice peer, P2pConnectionListener listener, P2pPeerListener discoveryListener)107 public void connect(WifiP2pDevice peer, P2pConnectionListener listener, 108 P2pPeerListener discoveryListener) { 109 if (DEBUG) Log.d(TAG, "connect(" + toString(peer) + ")"); 110 111 boolean isP2pAlreadyConnected = false; 112 113 if (mP2pManager == null) { 114 // Device has no P2P support so indicate failure 115 mService.getMainHandler().post(listener::onConnectionClosed); 116 return; 117 } 118 119 // Check for competing connection 120 if (mConnection != null && !peer.deviceAddress.equals(mConnection.getPeer() 121 .deviceAddress)) { 122 if (mConnection.getListenerCount() == 1) { 123 // The only listener is our internal one, so close this connection to make room 124 mConnection.close(); 125 mConnection = null; 126 isP2pAlreadyConnected = true; 127 // Restarting p2p discovery and re-initiating the p2p connection after a delay of 128 // 1 second makes subsequent WFD(p2p) connection possible 129 // more info - https://issuetracker.google.com/issues/298540041 130 mService.delay(1000, () -> { 131 stopDiscover(discoveryListener); 132 discover(discoveryListener); 133 }); 134 } else { 135 // Cannot open connection 136 mService.getMainHandler().post(listener::onConnectionClosed); 137 return; 138 } 139 } 140 141 // If connected to other device, bail out and wait for connect(..) to be called again after 142 // re-discovery 143 if (isP2pAlreadyConnected) { 144 return; 145 } 146 147 // Check for existing connection to the same device. 148 if (mConnection == null) { 149 // Create a new connection request with our internal listener 150 mConnection = new P2pConnectionProcedure(mService, mP2pManager, peer, 151 new P2pConnectionListener() { 152 @Override 153 public void onConnectionOpen(String networkInterface, WifiP2pInfo info) { 154 mConnectedInterface = networkInterface; 155 } 156 157 @Override 158 public void onConnectionClosed() { 159 mConnectedInterface = null; 160 } 161 162 @Override 163 public void onConnectionDelayed(boolean delayed) { 164 } 165 }); 166 } 167 mConnection.addListener(listener); 168 } 169 170 /** 171 * Give up on the connection request associated with a listener. The connection will stay 172 * open as long as other requests exist. 173 */ stopConnect(P2pConnectionListener listener)174 void stopConnect(P2pConnectionListener listener) { 175 if (mConnection == null || !mConnection.hasListener(listener)) { 176 return; 177 } 178 179 if (DEBUG) Log.d(TAG, "stopConnect " + toString(mConnection.getPeer())); 180 mConnection.removeListener(listener); 181 182 // If current connection attempt is incomplete and no longer required, close it. 183 if (mConnection.getListenerCount() == 1 && mConnectedInterface == null) { 184 if (DEBUG) Log.d(TAG, "Abandoning connection request"); 185 mConnection.close(); 186 mConnection = null; 187 } 188 } 189 190 /** Return the current connection procedure, if any */ getConnection()191 P2pConnectionProcedure getConnection() { 192 return mConnection; 193 } 194 195 /** Return the current connected interface, if any */ getConnectedInterface()196 public String getConnectedInterface() { 197 return mConnectedInterface; 198 } 199 200 /** Forcibly stops all connections/discoveries in progress, if any */ stopAll()201 public void stopAll() { 202 if (mConnection != null) { 203 mConnection.close(); 204 mConnection = null; 205 mConnectedInterface = null; 206 } 207 if (mPeerDiscovery != null) { 208 mPeerDiscovery.cancel(); 209 mPeerDiscovery = null; 210 } 211 } 212 } 213