1 /*
2  * Copyright (C) 2016 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;
18 
19 import android.net.Uri;
20 import android.print.PrinterCapabilitiesInfo;
21 import android.print.PrinterId;
22 import android.print.PrinterInfo;
23 import android.util.Log;
24 import android.widget.Toast;
25 
26 import com.android.bips.discovery.ConnectionListener;
27 import com.android.bips.discovery.DiscoveredPrinter;
28 import com.android.bips.ipp.CapabilitiesCache;
29 import com.android.bips.jni.LocalPrinterCapabilities;
30 import com.android.bips.p2p.P2pPrinterConnection;
31 import com.android.bips.p2p.P2pUtils;
32 
33 import java.net.InetAddress;
34 import java.util.Collections;
35 
36 /**
37  * A session-specific printer record. Encapsulates logic for getting the latest printer
38  * capabilities as necessary.
39  */
40 class LocalPrinter implements CapabilitiesCache.OnLocalPrinterCapabilities {
41     private static final String TAG = LocalPrinter.class.getSimpleName();
42     private static final boolean DEBUG = false;
43 
44     private final BuiltInPrintService mPrintService;
45     private final LocalDiscoverySession mSession;
46     private final PrinterId mPrinterId;
47     private long mLastSeenTime = System.currentTimeMillis();
48     private boolean mFound = true;
49     private boolean mTracking = false;
50     private LocalPrinterCapabilities mCapabilities;
51     private DiscoveredPrinter mDiscoveredPrinter;
52     private P2pPrinterConnection mTrackingConnection;
53 
LocalPrinter(BuiltInPrintService printService, LocalDiscoverySession session, DiscoveredPrinter discoveredPrinter)54     LocalPrinter(BuiltInPrintService printService, LocalDiscoverySession session,
55             DiscoveredPrinter discoveredPrinter) {
56         mPrintService = printService;
57         mSession = session;
58         mDiscoveredPrinter = discoveredPrinter;
59         mPrinterId = discoveredPrinter.getId(printService);
60     }
61 
62     /** Return the address of the printer or {@code null} if not known */
getAddress()63     public InetAddress getAddress() {
64         if (mCapabilities != null) {
65             return mCapabilities.inetAddress;
66         }
67         return null;
68     }
69 
70     /** Return true if this printer should be aged out */
isExpired()71     boolean isExpired() {
72         return !mFound && (System.currentTimeMillis() - mLastSeenTime)
73                 > LocalDiscoverySession.PRINTER_EXPIRATION_MILLIS;
74     }
75 
76     /** Return capabilities or null if not present */
getCapabilities()77     LocalPrinterCapabilities getCapabilities() {
78         return mCapabilities;
79     }
80 
81     /** Create a PrinterInfo from this record or null if not possible */
createPrinterInfo(boolean knownGood)82     PrinterInfo createPrinterInfo(boolean knownGood) {
83         if (mCapabilities == null) {
84             if (P2pUtils.isP2p(mDiscoveredPrinter)) {
85                 // Allow user to select a P2P to establish a connection
86                 PrinterInfo.Builder builder = new PrinterInfo.Builder(
87                         mPrinterId, mDiscoveredPrinter.name,
88                         PrinterInfo.STATUS_IDLE)
89                         .setIconResourceId(R.drawable.ic_printer)
90                         .setDescription(mPrintService.getDescription(mDiscoveredPrinter));
91                 return builder.build();
92             } else if (!knownGood) {
93                 // Ignore unknown LAN printers with no caps
94                 return null;
95             }
96         } else if (!mCapabilities.isSupported) {
97             // Fail out if capabilities indicate not-supported.
98             return null;
99         }
100 
101         // Get the most recently discovered version of this printer
102         DiscoveredPrinter printer = mPrintService.getDiscovery()
103                 .getPrinter(mDiscoveredPrinter.getUri());
104         if (printer == null) {
105             return null;
106         }
107 
108         boolean idle = mFound && mCapabilities != null;
109         PrinterInfo.Builder builder = new PrinterInfo.Builder(
110                 mPrinterId, printer.name,
111                 idle ? PrinterInfo.STATUS_IDLE : PrinterInfo.STATUS_UNAVAILABLE)
112                 .setIconResourceId(R.drawable.ic_printer)
113                 .setDescription(mPrintService.getDescription(mDiscoveredPrinter));
114 
115         if (mCapabilities != null) {
116             // Add capabilities if we have them
117             PrinterCapabilitiesInfo.Builder capabilitiesBuilder =
118                     new PrinterCapabilitiesInfo.Builder(mPrinterId);
119             mCapabilities.buildCapabilities(mPrintService, capabilitiesBuilder);
120             builder.setCapabilities(capabilitiesBuilder.build());
121         }
122 
123         return builder.build();
124     }
125 
126     @Override
onCapabilities(LocalPrinterCapabilities capabilities)127     public void onCapabilities(LocalPrinterCapabilities capabilities) {
128         if (mSession.isDestroyed() || !mSession.isKnown(mPrinterId)) {
129             return;
130         }
131 
132         if (capabilities == null) {
133             if (DEBUG) Log.d(TAG, "No capabilities so removing printer " + this);
134             mSession.removePrinters(Collections.singletonList(mPrinterId));
135         } else {
136             mCapabilities = capabilities;
137             mSession.handlePrinter(this);
138         }
139     }
140 
getPrinterId()141     PrinterId getPrinterId() {
142         return mPrinterId;
143     }
144 
145     /** Return true if the printer is in a "found" state according to discoveries */
isFound()146     boolean isFound() {
147         return mFound;
148     }
149 
150     /**
151      * Indicate the printer was found and gather capabilities if we don't have them
152      */
found(DiscoveredPrinter printer)153     void found(DiscoveredPrinter printer) {
154         mDiscoveredPrinter = printer;
155         mLastSeenTime = System.currentTimeMillis();
156         mFound = true;
157 
158         // Check for cached capabilities
159         LocalPrinterCapabilities capabilities = mPrintService.getCapabilitiesCache()
160                 .get(mDiscoveredPrinter);
161 
162         if (capabilities != null) {
163             // Report current capabilities
164             onCapabilities(capabilities);
165         } else {
166             // Announce printer and fetch capabilities immediately if possible
167             mSession.handlePrinter(this);
168             if (!P2pUtils.isP2p(mDiscoveredPrinter)) {
169                 mPrintService.getCapabilitiesCache().request(mDiscoveredPrinter,
170                         mSession.isPriority(mPrinterId), this);
171             } else if (mTracking) {
172                 startTracking();
173             }
174         }
175     }
176 
177     /**
178      * Begin tracking (getting latest capabilities) for this printer
179      */
track()180     public void track() {
181         if (DEBUG) Log.d(TAG, "track " + mDiscoveredPrinter);
182         startTracking();
183     }
184 
startTracking()185     private void startTracking() {
186         mTracking = true;
187         if (mTrackingConnection != null) {
188             return;
189         }
190 
191         // For any P2P printer, obtain a connection
192         if (P2pUtils.isP2p(mDiscoveredPrinter)
193                 || P2pUtils.isOnConnectedInterface(mPrintService, mDiscoveredPrinter)) {
194             ConnectionListener listener = new ConnectionListener() {
195                 @Override
196                 public void onConnectionComplete(DiscoveredPrinter printer) {
197                     if (DEBUG) Log.d(TAG, "connection complete " + printer);
198                     if (printer == null) {
199                         mTrackingConnection = null;
200                     }
201                 }
202 
203                 @Override
204                 public void onConnectionDelayed(boolean delayed) {
205                     if (DEBUG) Log.d(TAG, "connection delayed=" + delayed);
206                     if (delayed) {
207                         Toast.makeText(mPrintService, R.string.connect_hint_text,
208                                 Toast.LENGTH_LONG).show();
209                     }
210                 }
211             };
212             mTrackingConnection = new P2pPrinterConnection(mPrintService,
213                     mDiscoveredPrinter, listener);
214         }
215     }
216 
217     /**
218      * Stop tracking this printer
219      */
stopTracking()220     void stopTracking() {
221         if (mTrackingConnection != null) {
222             mTrackingConnection.close();
223             mTrackingConnection = null;
224         }
225         mTracking = false;
226     }
227 
228     /**
229      * Mark this printer as not found (will eventually expire)
230      */
notFound()231     void notFound() {
232         mFound = false;
233         mLastSeenTime = System.currentTimeMillis();
234     }
235 
236     /** Return the UUID for this printer if it is known */
getUuid()237     public Uri getUuid() {
238         return mDiscoveredPrinter.uuid;
239     }
240 
241     @Override
toString()242     public String toString() {
243         return mDiscoveredPrinter.toString();
244     }
245 }
246