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 static com.android.bips.discovery.P2pDiscovery.toPrinter; 20 21 import android.net.wifi.p2p.WifiP2pDevice; 22 import android.net.wifi.p2p.WifiP2pInfo; 23 import android.util.Log; 24 25 import com.android.bips.BuiltInPrintService; 26 import com.android.bips.DelayedAction; 27 import com.android.bips.discovery.ConnectionListener; 28 import com.android.bips.discovery.DiscoveredPrinter; 29 import com.android.bips.discovery.Discovery; 30 import com.android.bips.discovery.P2pDiscovery; 31 import com.android.bips.jni.LocalPrinterCapabilities; 32 33 import java.net.Inet4Address; 34 import java.net.NetworkInterface; 35 import java.net.SocketException; 36 import java.net.UnknownHostException; 37 import java.util.Arrays; 38 39 /** 40 * Manage the process of connecting to a P2P device, discovering a printer on the new network, and 41 * verifying its capabilities. 42 */ 43 public class P2pPrinterConnection implements Discovery.Listener, P2pConnectionListener, 44 P2pPeerListener { 45 private static final String TAG = P2pPrinterConnection.class.getSimpleName(); 46 private static final boolean DEBUG = false; 47 private static final int TIMEOUT_DISCOVERY = 15 * 1000; 48 49 private final BuiltInPrintService mService; 50 private final Discovery mMdnsDiscovery; 51 private ConnectionListener mListener; 52 53 private DiscoveredPrinter mPrinter; 54 private WifiP2pDevice mPeer; 55 private NetworkInterface mInterface; 56 private DelayedAction mMdnsDiscoveryTimeout; 57 P2pPrinterConnection(BuiltInPrintService service, ConnectionListener listener)58 private P2pPrinterConnection(BuiltInPrintService service, ConnectionListener listener) { 59 mService = service; 60 mListener = listener; 61 mMdnsDiscovery = mService.getMdnsDiscovery(); 62 } 63 64 /** Create a new connection to a known P2P peer device */ P2pPrinterConnection(BuiltInPrintService service, WifiP2pDevice peer, ConnectionListener listener)65 public P2pPrinterConnection(BuiltInPrintService service, WifiP2pDevice peer, 66 ConnectionListener listener) { 67 this(service, listener); 68 if (DEBUG) Log.d(TAG, "Connecting to " + P2pMonitor.toString(peer)); 69 // Initialize mPrinter to handle onPeerFound callback for re-discover cases 70 mPrinter = toPrinter(peer); 71 connectToPeer(peer); 72 } 73 74 /** Re-discover and create a new connection to a previously discovered P2P device */ P2pPrinterConnection(BuiltInPrintService service, DiscoveredPrinter printer, ConnectionListener listener)75 public P2pPrinterConnection(BuiltInPrintService service, DiscoveredPrinter printer, 76 ConnectionListener listener) { 77 this(service, listener); 78 if (DEBUG) Log.d(TAG, "Connecting to " + printer); 79 if (P2pUtils.isOnConnectedInterface(service, printer)) { 80 WifiP2pDevice peer = service.getP2pMonitor().getConnection().getPeer(); 81 connectToPeer(peer); 82 return; 83 } 84 85 mPrinter = printer; 86 service.getP2pMonitor().discover(this); 87 } 88 connectToPeer(WifiP2pDevice peer)89 private void connectToPeer(WifiP2pDevice peer) { 90 mPeer = peer; 91 mService.getP2pMonitor().connect(mPeer, this, this); 92 } 93 94 @Override onPeerFound(WifiP2pDevice peer)95 public void onPeerFound(WifiP2pDevice peer) { 96 if (mListener == null) { 97 return; 98 } 99 100 String address = mPrinter.path.getHost().replaceAll("-", ":"); 101 102 if (peer.deviceAddress.equals(address)) { 103 mService.getP2pMonitor().stopDiscover(this); 104 // Stop discovery and start validation 105 connectToPeer(peer); 106 } 107 } 108 109 @Override onPeerLost(WifiP2pDevice peer)110 public void onPeerLost(WifiP2pDevice peer) { 111 // Ignored 112 } 113 114 @Override onConnectionOpen(String networkInterface, WifiP2pInfo info)115 public void onConnectionOpen(String networkInterface, WifiP2pInfo info) { 116 if (mListener == null) { 117 return; 118 } 119 120 try { 121 mInterface = NetworkInterface.getByName(networkInterface); 122 } catch (SocketException ignored) { 123 } 124 125 if (mInterface == null) { 126 if (DEBUG) Log.d(TAG, "Failed to get interface from " + networkInterface); 127 mListener.onConnectionComplete(null); 128 close(); 129 return; 130 } 131 132 if (DEBUG) Log.d(TAG, "Connected on network interface " + mInterface); 133 134 // Timeout after a while if MDNS does not find a printer 135 mMdnsDiscoveryTimeout = mService.delay(TIMEOUT_DISCOVERY, () -> { 136 mMdnsDiscovery.stop(this); 137 if (mListener != null) { 138 mListener.onConnectionComplete(null); 139 } 140 close(); 141 }); 142 143 mMdnsDiscovery.start(this); 144 } 145 146 @Override onConnectionClosed()147 public void onConnectionClosed() { 148 if (DEBUG) Log.d(TAG, "closed/failed connection to " + P2pMonitor.toString(mPeer)); 149 if (mListener != null) { 150 mListener.onConnectionComplete(null); 151 } 152 close(); 153 } 154 155 @Override onConnectionDelayed(boolean delayed)156 public void onConnectionDelayed(boolean delayed) { 157 if (mListener == null) { 158 return; 159 } 160 mListener.onConnectionDelayed(delayed); 161 } 162 163 @Override onPrinterFound(DiscoveredPrinter printer)164 public void onPrinterFound(DiscoveredPrinter printer) { 165 if (DEBUG) Log.d(TAG, "onPrinterFound(" + printer + ")"); 166 if (mListener == null) { 167 return; 168 } 169 170 Inet4Address printerAddress; 171 try { 172 printerAddress = (Inet4Address) Inet4Address.getByName(printer.path.getHost()); 173 } catch (UnknownHostException e) { 174 return; 175 } 176 177 if (mInterface != null && P2pUtils.isOnInterface(mInterface, printerAddress)) { 178 // Stop discovery and start capabilities query 179 mMdnsDiscovery.stop(this); 180 mMdnsDiscoveryTimeout.cancel(); 181 mService.getCapabilitiesCache().request(printer, true, capabilities -> 182 onCapabilities(printer, capabilities)); 183 } 184 } 185 onCapabilities(DiscoveredPrinter printer, LocalPrinterCapabilities capabilities)186 private void onCapabilities(DiscoveredPrinter printer, LocalPrinterCapabilities capabilities) { 187 if (mListener == null) { 188 return; 189 } 190 191 if (DEBUG) Log.d(TAG, "Printer " + printer + " caps=" + capabilities); 192 if (capabilities == null) { 193 mListener.onConnectionComplete(null); 194 close(); 195 } else { 196 // Make a copy of the printer bearing its P2P path as primary and discovered path 197 // as secondary 198 DiscoveredPrinter p2pPrinter = new DiscoveredPrinter(printer.uuid, printer.name, 199 Arrays.asList(P2pDiscovery.toPath(mPeer), printer.path), printer.location); 200 mListener.onConnectionComplete(p2pPrinter); 201 } 202 } 203 204 @Override onPrinterLost(DiscoveredPrinter printer)205 public void onPrinterLost(DiscoveredPrinter printer) { 206 } 207 208 /** Close the connection and any intermediate procedures */ close()209 public void close() { 210 if (DEBUG) Log.d(TAG, "close()"); 211 mMdnsDiscovery.stop(this); 212 if (mMdnsDiscoveryTimeout != null) { 213 mMdnsDiscoveryTimeout.cancel(); 214 } 215 mService.getP2pMonitor().stopDiscover(this); 216 mService.getP2pMonitor().stopConnect(this); 217 218 // No further notifications 219 mListener = null; 220 } 221 } 222