• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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