Home | History | Annotate | Download | only in p2p
      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 
     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 */
     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 */
     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 
     84     private void connectToPeer(WifiP2pDevice peer) {
     85         mPeer = peer;
     86         mService.getP2pMonitor().connect(mPeer, this);
     87     }
     88 
     89     @Override
     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
    105     public void onPeerLost(WifiP2pDevice peer) {
    106         // Ignored
    107     }
    108 
    109     @Override
    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
    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
    151     public void onConnectionDelayed(boolean delayed) {
    152         if (mListener == null) {
    153             return;
    154         }
    155         mListener.onConnectionDelayed(delayed);
    156     }
    157 
    158     @Override
    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 
    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
    199     public void onPrinterLost(DiscoveredPrinter printer) {
    200     }
    201 
    202     /** Close the connection and any intermediate procedures */
    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