| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.bips.p2p; |
| |
| import android.net.wifi.p2p.WifiP2pDevice; |
| import android.net.wifi.p2p.WifiP2pInfo; |
| import android.util.Log; |
| |
| import com.android.bips.BuiltInPrintService; |
| import com.android.bips.DelayedAction; |
| import com.android.bips.discovery.ConnectionListener; |
| import com.android.bips.discovery.DiscoveredPrinter; |
| import com.android.bips.discovery.Discovery; |
| import com.android.bips.discovery.P2pDiscovery; |
| import com.android.bips.jni.LocalPrinterCapabilities; |
| |
| import java.net.Inet4Address; |
| import java.net.NetworkInterface; |
| import java.net.SocketException; |
| import java.net.UnknownHostException; |
| |
| /** |
| * Manage the process of connecting to a P2P device, discovering a printer on the new network, and |
| * verifying its capabilities. |
| */ |
| public class P2pPrinterConnection implements Discovery.Listener, P2pConnectionListener, |
| P2pPeerListener { |
| private static final String TAG = P2pPrinterConnection.class.getSimpleName(); |
| private static final boolean DEBUG = false; |
| private static final int TIMEOUT_DISCOVERY = 15 * 1000; |
| |
| private final BuiltInPrintService mService; |
| private final Discovery mMdnsDiscovery; |
| private ConnectionListener mListener; |
| |
| private DiscoveredPrinter mPrinter; |
| private WifiP2pDevice mPeer; |
| private NetworkInterface mInterface; |
| private DelayedAction mMdnsDiscoveryTimeout; |
| |
| private P2pPrinterConnection(BuiltInPrintService service, ConnectionListener listener) { |
| mService = service; |
| mListener = listener; |
| mMdnsDiscovery = mService.getMdnsDiscovery(); |
| } |
| |
| /** Create a new connection to a known P2P peer device */ |
| public P2pPrinterConnection(BuiltInPrintService service, WifiP2pDevice peer, |
| ConnectionListener listener) { |
| this(service, listener); |
| if (DEBUG) Log.d(TAG, "Connecting to " + P2pMonitor.toString(peer)); |
| connectToPeer(peer); |
| } |
| |
| /** Re-discover and create a new connection to a previously discovered P2P device */ |
| public P2pPrinterConnection(BuiltInPrintService service, DiscoveredPrinter printer, |
| ConnectionListener listener) { |
| this(service, listener); |
| if (DEBUG) Log.d(TAG, "Connecting to " + printer); |
| if (P2pUtils.isOnConnectedInterface(service, printer)) { |
| WifiP2pDevice peer = service.getP2pMonitor().getConnection().getPeer(); |
| connectToPeer(peer); |
| return; |
| } |
| |
| mPrinter = printer; |
| service.getP2pMonitor().discover(this); |
| } |
| |
| private void connectToPeer(WifiP2pDevice peer) { |
| mPeer = peer; |
| mService.getP2pMonitor().connect(mPeer, this); |
| } |
| |
| @Override |
| public void onPeerFound(WifiP2pDevice peer) { |
| if (mListener == null) { |
| return; |
| } |
| |
| String address = mPrinter.path.getHost().replaceAll("-", ":"); |
| |
| if (peer.deviceAddress.equals(address)) { |
| mService.getP2pMonitor().stopDiscover(this); |
| // Stop discovery and start validation |
| connectToPeer(peer); |
| } |
| } |
| |
| @Override |
| public void onPeerLost(WifiP2pDevice peer) { |
| // Ignored |
| } |
| |
| @Override |
| public void onConnectionOpen(String networkInterface, WifiP2pInfo info) { |
| if (mListener == null) { |
| return; |
| } |
| |
| try { |
| mInterface = NetworkInterface.getByName(networkInterface); |
| } catch (SocketException ignored) { |
| } |
| |
| if (mInterface == null) { |
| if (DEBUG) Log.d(TAG, "Failed to get interface from " + networkInterface); |
| mListener.onConnectionComplete(null); |
| close(); |
| return; |
| } |
| |
| if (DEBUG) Log.d(TAG, "Connected on network interface " + mInterface); |
| |
| // Timeout after a while if MDNS does not find a printer |
| mMdnsDiscoveryTimeout = mService.delay(TIMEOUT_DISCOVERY, () -> { |
| mMdnsDiscovery.stop(this); |
| if (mListener != null) { |
| mListener.onConnectionComplete(null); |
| } |
| close(); |
| }); |
| |
| mMdnsDiscovery.start(this); |
| } |
| |
| @Override |
| public void onConnectionClosed() { |
| if (DEBUG) Log.d(TAG, "closed/failed connection to " + P2pMonitor.toString(mPeer)); |
| if (mListener != null) { |
| mListener.onConnectionComplete(null); |
| } |
| close(); |
| } |
| |
| @Override |
| public void onConnectionDelayed(boolean delayed) { |
| if (mListener == null) { |
| return; |
| } |
| mListener.onConnectionDelayed(delayed); |
| } |
| |
| @Override |
| public void onPrinterFound(DiscoveredPrinter printer) { |
| if (DEBUG) Log.d(TAG, "onPrinterFound(" + printer + ")"); |
| if (mListener == null) { |
| return; |
| } |
| |
| Inet4Address printerAddress; |
| try { |
| printerAddress = (Inet4Address) Inet4Address.getByName(printer.path.getHost()); |
| } catch (UnknownHostException e) { |
| return; |
| } |
| |
| if (mInterface != null && P2pUtils.isOnInterface(mInterface, printerAddress)) { |
| // Stop discovery and start capabilities query |
| mMdnsDiscovery.stop(this); |
| mMdnsDiscoveryTimeout.cancel(); |
| mService.getCapabilitiesCache().request(printer, true, capabilities -> |
| onCapabilities(printer, capabilities)); |
| } |
| } |
| |
| private void onCapabilities(DiscoveredPrinter printer, LocalPrinterCapabilities capabilities) { |
| if (mListener == null) { |
| return; |
| } |
| |
| if (DEBUG) Log.d(TAG, "Printer " + printer + " caps=" + capabilities); |
| if (capabilities == null) { |
| mListener.onConnectionComplete(null); |
| close(); |
| } else { |
| // Make a copy of the printer bearing its P2P path |
| DiscoveredPrinter p2pPrinter = new DiscoveredPrinter(printer.uuid, printer.name, |
| P2pDiscovery.toPath(mPeer), printer.location); |
| mListener.onConnectionComplete(p2pPrinter); |
| } |
| } |
| |
| @Override |
| public void onPrinterLost(DiscoveredPrinter printer) { |
| } |
| |
| /** Close the connection and any intermediate procedures */ |
| public void close() { |
| if (DEBUG) Log.d(TAG, "close()"); |
| mMdnsDiscovery.stop(this); |
| if (mMdnsDiscoveryTimeout != null) { |
| mMdnsDiscoveryTimeout.cancel(); |
| } |
| mService.getP2pMonitor().stopDiscover(this); |
| mService.getP2pMonitor().stopConnect(this); |
| |
| // No further notifications |
| mListener = null; |
| } |
| } |