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