1080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney/* 2080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * Copyright (C) 2017 The Android Open Source Project 3080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * 4080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * Licensed under the Apache License, Version 2.0 (the "License"); 5080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * you may not use this file except in compliance with the License. 6080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * You may obtain a copy of the License at 7080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * 8080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * http://www.apache.org/licenses/LICENSE-2.0 9080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * 10080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * Unless required by applicable law or agreed to in writing, software 11080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * distributed under the License is distributed on an "AS IS" BASIS, 12080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * See the License for the specific language governing permissions and 14080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * limitations under the License. 15080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney */ 16080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 17080c9c93d627b0b56bcfab83748915f92956930bGlade Divineypackage com.android.bips.p2p; 18080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 19080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport android.net.wifi.p2p.WifiP2pDevice; 20080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport android.net.wifi.p2p.WifiP2pInfo; 21080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport android.util.Log; 22080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 23080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.BuiltInPrintService; 24080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.DelayedAction; 25080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.discovery.ConnectionListener; 26080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.discovery.DiscoveredPrinter; 27080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.discovery.Discovery; 28080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.discovery.P2pDiscovery; 29080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport com.android.bips.jni.LocalPrinterCapabilities; 30080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 31080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport java.net.Inet4Address; 32080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport java.net.NetworkInterface; 33080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport java.net.SocketException; 34080c9c93d627b0b56bcfab83748915f92956930bGlade Divineyimport java.net.UnknownHostException; 35080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 36080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney/** 37080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * Manage the process of connecting to a P2P device, discovering a printer on the new network, and 38080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney * verifying its capabilities. 39080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney */ 40080c9c93d627b0b56bcfab83748915f92956930bGlade Divineypublic class P2pPrinterConnection implements Discovery.Listener, P2pConnectionListener, 41080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney P2pPeerListener { 42080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private static final String TAG = P2pPrinterConnection.class.getSimpleName(); 43080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private static final boolean DEBUG = false; 44080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private static final int TIMEOUT_DISCOVERY = 15 * 1000; 45080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 46080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private final BuiltInPrintService mService; 47080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private final Discovery mMdnsDiscovery; 48080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private ConnectionListener mListener; 49080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 50080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private DiscoveredPrinter mPrinter; 51080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private WifiP2pDevice mPeer; 52080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private NetworkInterface mInterface; 53080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private DelayedAction mMdnsDiscoveryTimeout; 54080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 55080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private P2pPrinterConnection(BuiltInPrintService service, ConnectionListener listener) { 56080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mService = service; 57080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener = listener; 58080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscovery = mService.getMdnsDiscovery(); 59080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 60080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 61080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney /** Create a new connection to a known P2P peer device */ 62080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public P2pPrinterConnection(BuiltInPrintService service, WifiP2pDevice peer, 63080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney ConnectionListener listener) { 64080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney this(service, listener); 65080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "Connecting to " + P2pMonitor.toString(peer)); 66080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney connectToPeer(peer); 67080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 68080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 69080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney /** Re-discover and create a new connection to a previously discovered P2P device */ 70080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public P2pPrinterConnection(BuiltInPrintService service, DiscoveredPrinter printer, 71080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney ConnectionListener listener) { 72080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney this(service, listener); 73080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "Connecting to " + printer); 74080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (P2pUtils.isOnConnectedInterface(service, printer)) { 75080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney WifiP2pDevice peer = service.getP2pMonitor().getConnection().getPeer(); 76080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney connectToPeer(peer); 77080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 78080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 79080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 80080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mPrinter = printer; 81080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney service.getP2pMonitor().discover(this); 82080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 83080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 84080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private void connectToPeer(WifiP2pDevice peer) { 85080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mPeer = peer; 86080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mService.getP2pMonitor().connect(mPeer, this); 87080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 88080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 89080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 90080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onPeerFound(WifiP2pDevice peer) { 91080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener == null) { 92080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 93080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 94080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 95080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney String address = mPrinter.path.getHost().replaceAll("-", ":"); 96080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 97080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (peer.deviceAddress.equals(address)) { 98080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mService.getP2pMonitor().stopDiscover(this); 99080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney // Stop discovery and start validation 100080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney connectToPeer(peer); 101080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 102080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 103080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 104080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 105080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onPeerLost(WifiP2pDevice peer) { 106080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney // Ignored 107080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 108080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 109080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 110080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onConnectionOpen(String networkInterface, WifiP2pInfo info) { 111080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener == null) { 112080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 113080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 114080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 115080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney try { 116080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mInterface = NetworkInterface.getByName(networkInterface); 117080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } catch (SocketException ignored) { 118080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 119080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 120080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mInterface == null) { 121080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "Failed to get interface from " + networkInterface); 122080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener.onConnectionComplete(null); 123080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney close(); 124080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 125080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 126080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 127080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "Connected on network interface " + mInterface); 128080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 129080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney // Timeout after a while if MDNS does not find a printer 130080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscoveryTimeout = mService.delay(TIMEOUT_DISCOVERY, () -> { 131080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscovery.stop(this); 132080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener != null) { 133080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener.onConnectionComplete(null); 134080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 135080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney close(); 136080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney }); 137080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 138080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscovery.start(this); 139080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 140080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 141080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 142080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onConnectionClosed() { 143080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "closed/failed connection to " + P2pMonitor.toString(mPeer)); 144080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener != null) { 145080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener.onConnectionComplete(null); 146080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 147080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney close(); 148080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 149080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 150080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 151080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onConnectionDelayed(boolean delayed) { 152080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener == null) { 153080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 154080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 155080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener.onConnectionDelayed(delayed); 156080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 157080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 158080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 159080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onPrinterFound(DiscoveredPrinter printer) { 160080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "onPrinterFound(" + printer + ")"); 161080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener == null) { 162080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 163080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 164080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 165080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney Inet4Address printerAddress; 166080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney try { 167080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney printerAddress = (Inet4Address) Inet4Address.getByName(printer.path.getHost()); 168080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } catch (UnknownHostException e) { 169080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 170080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 171080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 172080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mInterface != null && P2pUtils.isOnInterface(mInterface, printerAddress)) { 173080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney // Stop discovery and start capabilities query 174080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscovery.stop(this); 175080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscoveryTimeout.cancel(); 176080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mService.getCapabilitiesCache().request(printer, true, capabilities -> 177080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney onCapabilities(printer, capabilities)); 178080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 179080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 180080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 181080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney private void onCapabilities(DiscoveredPrinter printer, LocalPrinterCapabilities capabilities) { 182080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mListener == null) { 183080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney return; 184080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 185080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 186080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "Printer " + printer + " caps=" + capabilities); 187080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (capabilities == null) { 188080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener.onConnectionComplete(null); 189080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney close(); 190080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } else { 191080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney // Make a copy of the printer bearing its P2P path 192080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney DiscoveredPrinter p2pPrinter = new DiscoveredPrinter(printer.uuid, printer.name, 193080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney P2pDiscovery.toPath(mPeer), printer.location); 194080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener.onConnectionComplete(p2pPrinter); 195080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 196080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 197080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 198080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney @Override 199080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void onPrinterLost(DiscoveredPrinter printer) { 200080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 201080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 202080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney /** Close the connection and any intermediate procedures */ 203080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney public void close() { 204080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (DEBUG) Log.d(TAG, "close()"); 205080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscovery.stop(this); 206080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney if (mMdnsDiscoveryTimeout != null) { 207080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mMdnsDiscoveryTimeout.cancel(); 208080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 209080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mService.getP2pMonitor().stopDiscover(this); 210080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mService.getP2pMonitor().stopConnect(this); 211080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney 212080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney // No further notifications 213080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney mListener = null; 214080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney } 215080c9c93d627b0b56bcfab83748915f92956930bGlade Diviney} 216