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