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