PrinterDiscoverySession.java revision 798bed6cc7d273e72b0253288605db9cd2b57740
1/* 2 * Copyright (C) 2013 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 17package android.printservice; 18 19import android.content.Context; 20import android.os.Handler; 21import android.os.Looper; 22import android.os.Message; 23import android.os.RemoteException; 24import android.print.IPrinterDiscoverySessionController; 25import android.print.IPrinterDiscoverySessionObserver; 26import android.print.PrinterId; 27import android.print.PrinterInfo; 28import android.util.Log; 29 30import java.lang.ref.WeakReference; 31import java.util.List; 32 33/** 34 * This class encapsulates the interaction between a print service and the 35 * system during printer discovery. During printer discovery you are responsible 36 * for adding discovered printers, removing already added printers that 37 * disappeared, and updating already added printers. 38 * <p> 39 * The opening of the session is announced by a call to {@link 40 * PrinterDiscoverySession#onOpen(List)} at which point you should start printer 41 * discovery. The closing of the session is announced by a call to {@link 42 * PrinterDiscoverySession#onClose()} at which point you should stop printer 43 * discovery. Discovered printers are added by invoking {@link 44 * PrinterDiscoverySession#addPrinters(List)}. Added printers that disappeared 45 * are removed by invoking {@link PrinterDiscoverySession#removePrinters(List)}. 46 * Added printers whose properties or capabilities changed are updated through 47 * a call to {@link PrinterDiscoverySession#updatePrinters(List)}. 48 * </p> 49 * <p> 50 * The system will make a call to 51 * {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you 52 * need to update a given printer. It is possible that you add a printer without 53 * specifying its capabilities. This enables you to avoid querying all 54 * discovered printers for their capabilities, rather querying the capabilities 55 * of a printer only if necessary. For example, the system will require that you 56 * update a printer if it gets selected by the user. If you did not report the 57 * printer capabilities when adding it, you must do so after the system requests 58 * a printer update. Otherwise, the printer will be ignored. 59 * </p> 60 * <p> 61 * During printer discovery all printers that are known to your print service 62 * have to be added. The system does not retain any printers from previous 63 * sessions. 64 * </p> 65 */ 66public abstract class PrinterDiscoverySession { 67 private static final String LOG_TAG = "PrinterDiscoverySession"; 68 69 private static int sIdCounter = 0; 70 71 private final Object mLock = new Object(); 72 73 private final Handler mHandler; 74 75 private final int mId; 76 77 private IPrinterDiscoverySessionController mController; 78 79 private IPrinterDiscoverySessionObserver mObserver; 80 81 /** 82 * Constructor. 83 * 84 * @param context A context instance. 85 */ 86 public PrinterDiscoverySession(Context context) { 87 mId = sIdCounter++; 88 mHandler = new SessionHandler(context.getMainLooper()); 89 mController = new PrinterDiscoverySessionController(this); 90 } 91 92 void setObserver(IPrinterDiscoverySessionObserver observer) { 93 synchronized (mLock) { 94 mObserver = observer; 95 try { 96 mObserver.setController(mController); 97 } catch (RemoteException re) { 98 Log.e(LOG_TAG, "Error setting session controller", re); 99 } 100 } 101 } 102 103 int getId() { 104 return mId; 105 } 106 107 /** 108 * Adds discovered printers. Adding an already added printer has no effect. 109 * Removed printers can be added again. You can call this method multiple 110 * times during printer discovery. 111 * <p> 112 * <strong>Note: </strong> Calling this method when the session is closed, 113 * which is if {@link #isClosed()} returns true, will throw an {@link 114 * IllegalStateException}. 115 * </p> 116 * 117 * @param printers The printers to add. 118 * 119 * @see #removePrinters(List) 120 * @see #updatePrinters(List) 121 * @see #isClosed() 122 */ 123 public final void addPrinters(List<PrinterInfo> printers) { 124 final IPrinterDiscoverySessionObserver observer; 125 synchronized (mLock) { 126 throwIfClosedLocked(); 127 observer = mObserver; 128 } 129 if (observer != null) { 130 try { 131 observer.onPrintersAdded(printers); 132 } catch (RemoteException re) { 133 Log.e(LOG_TAG, "Error adding printers", re); 134 } 135 } 136 } 137 138 /** 139 * Removes added printers. Removing an already removed or never added 140 * printer has no effect. Removed printers can be added again. You 141 * can call this method multiple times during printer discovery. 142 * <p> 143 * <strong>Note: </strong> Calling this method when the session is closed, 144 * which is if {@link #isClosed()} returns true, will throw an {@link 145 * IllegalStateException}. 146 * </p> 147 * 148 * @param printerIds The ids of the removed printers. 149 * 150 * @see #addPrinters(List) 151 * @see #updatePrinters(List) 152 * @see #isClosed() 153 */ 154 public final void removePrinters(List<PrinterId> printerIds) { 155 final IPrinterDiscoverySessionObserver observer; 156 synchronized (mLock) { 157 throwIfClosedLocked(); 158 observer = mObserver; 159 } 160 if (observer != null) { 161 try { 162 observer.onPrintersRemoved(printerIds); 163 } catch (RemoteException re) { 164 Log.e(LOG_TAG, "Error removing printers", re); 165 } 166 } 167 } 168 169 /** 170 * Updates added printers. Updating a printer that was not added or that 171 * was removed has no effect. You can call this method multiple times 172 * during printer discovery. 173 * <p> 174 * <strong>Note: </strong> Calling this method when the session is closed, 175 * which is if {@link #isClosed()} returns true, will throw an {@link 176 * IllegalStateException}. 177 * </p> 178 * 179 * @param printers The printers to update. 180 * 181 * @see #addPrinters(List) 182 * @see #removePrinters(List) 183 * @see #isClosed() 184 */ 185 public final void updatePrinters(List<PrinterInfo> printers) { 186 final IPrinterDiscoverySessionObserver observer; 187 synchronized (mLock) { 188 throwIfClosedLocked(); 189 observer = mObserver; 190 } 191 if (observer != null) { 192 try { 193 observer.onPrintersUpdated(printers); 194 } catch (RemoteException re) { 195 Log.e(LOG_TAG, "Error updating printers", re); 196 } 197 } 198 } 199 200 /** 201 * Callback notifying you that the session is open and you should start 202 * printer discovery. Discovered printers should be added via calling 203 * {@link #addPrinters(List)}. Added printers that disappeared should be 204 * removed via calling {@link #removePrinters(List)}. Added printers whose 205 * properties or capabilities changes should be updated via calling {@link 206 * #updatePrinters(List)}. When the session is closed you will receive a 207 * call to {@link #onClose()}. 208 * <p> 209 * During printer discovery all printers that are known to your print 210 * service have to be added. The system does not retain any printers from 211 * previous sessions. 212 * </p> 213 * <p> 214 * <strong>Note: </strong>You are also given a list of printers whose 215 * availability has to be checked first. For example, these printers could 216 * be the user's favorite ones, therefore they have to be verified first. 217 * </p> 218 * 219 * @see #onClose() 220 * @see #isClosed() 221 * @see #addPrinters(List) 222 * @see #removePrinters(List) 223 * @see #updatePrinters(List) 224 */ 225 public abstract void onOpen(List<PrinterId> priorityList); 226 227 /** 228 * Callback notifying you that the session is closed and you should stop 229 * printer discovery. After the session is closed and any attempt to call 230 * any of its methods will throw an exception. Whether a session is closed 231 * can be checked by calling {@link #isClosed()}. Once the session is closed 232 * it will never be opened again. 233 * 234 * @see #onOpen(List) 235 * @see #isClosed() 236 * @see #addPrinters(List) 237 * @see #removePrinters(List) 238 * @see #updatePrinters(List) 239 */ 240 public abstract void onClose(); 241 242 /** 243 * Requests that you update a printer. You are responsible for updating 244 * the printer by also reporting its capabilities via calling {@link 245 * #updatePrinters(List)}. 246 * <p> 247 * <strong>Note: </strong> A printer can be initially added without its 248 * capabilities to avoid polling printers that the user will not select. 249 * However, after this method is called you are expected to update the 250 * printer <strong>including</strong> its capabilities. Otherwise, the 251 * printer will be ignored. 252 * <p> 253 * A scenario when you may be requested to update a printer is if the user 254 * selects it and the system has to present print options UI based on the 255 * printer's capabilities. 256 * </p> 257 * 258 * @param printerId The printer id. 259 * 260 * @see #updatePrinters(List) 261 * @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo) 262 * PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo) 263 */ 264 public abstract void onRequestPrinterUpdate(PrinterId printerId); 265 266 /** 267 * Gets whether this session is closed. 268 * 269 * @return Whether the session is closed. 270 */ 271 public final boolean isClosed() { 272 synchronized (mLock) { 273 return (mController == null && mObserver == null); 274 } 275 } 276 277 void close() { 278 synchronized (mLock) { 279 throwIfClosedLocked(); 280 mController = null; 281 mObserver = null; 282 } 283 } 284 285 private void throwIfClosedLocked() { 286 if (isClosed()) { 287 throw new IllegalStateException("Session is closed"); 288 } 289 } 290 291 private final class SessionHandler extends Handler { 292 public static final int MSG_OPEN = 1; 293 public static final int MSG_CLOSE = 2; 294 public static final int MSG_REQUEST_PRINTER_UPDATE = 3; 295 296 public SessionHandler(Looper looper) { 297 super(looper, null, true); 298 } 299 300 @Override 301 @SuppressWarnings("unchecked") 302 public void handleMessage(Message message) { 303 switch (message.what) { 304 case MSG_OPEN: { 305 List<PrinterId> priorityList = (List<PrinterId>) message.obj; 306 onOpen(priorityList); 307 } break; 308 309 case MSG_CLOSE: { 310 onClose(); 311 close(); 312 } break; 313 314 case MSG_REQUEST_PRINTER_UPDATE: { 315 PrinterId printerId = (PrinterId) message.obj; 316 onRequestPrinterUpdate(printerId); 317 } break; 318 } 319 } 320 } 321 322 private static final class PrinterDiscoverySessionController extends 323 IPrinterDiscoverySessionController.Stub { 324 private final WeakReference<PrinterDiscoverySession> mWeakSession; 325 326 public PrinterDiscoverySessionController(PrinterDiscoverySession session) { 327 mWeakSession = new WeakReference<PrinterDiscoverySession>(session); 328 } 329 330 @Override 331 public void open(List<PrinterId> priorityList) { 332 PrinterDiscoverySession session = mWeakSession.get(); 333 if (session != null) { 334 session.mHandler.obtainMessage(SessionHandler.MSG_OPEN, 335 priorityList).sendToTarget(); 336 } 337 } 338 339 @Override 340 public void close() { 341 PrinterDiscoverySession session = mWeakSession.get(); 342 if (session != null) { 343 session.mHandler.sendEmptyMessage(SessionHandler.MSG_CLOSE); 344 } 345 } 346 347 @Override 348 public void requestPrinterUpdate(PrinterId printerId) { 349 PrinterDiscoverySession session = mWeakSession.get(); 350 if (session != null) { 351 session.mHandler.obtainMessage( 352 SessionHandler.MSG_REQUEST_PRINTER_UPDATE, 353 printerId).sendToTarget(); 354 } 355 } 356 }; 357} 358