PrinterDiscoverySession.java revision c335eb411503154cf475903eb6c5c67575769112
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.print; 18 19import android.content.Context; 20import android.content.pm.ParceledListSlice; 21import android.os.Handler; 22import android.os.Looper; 23import android.os.Message; 24import android.os.RemoteException; 25import android.util.ArrayMap; 26import android.util.Log; 27 28import java.lang.ref.WeakReference; 29import java.util.ArrayList; 30import java.util.Collections; 31import java.util.List; 32 33/** 34 * @hide 35 */ 36public final class PrinterDiscoverySession { 37 38 private static final String LOG_TAG ="PrinterDiscoverySession"; 39 40 private static final int MSG_PRINTERS_ADDED = 1; 41 private static final int MSG_PRINTERS_REMOVED = 2; 42 43 private final ArrayMap<PrinterId, PrinterInfo> mPrinters = 44 new ArrayMap<PrinterId, PrinterInfo>(); 45 46 private final IPrintManager mPrintManager; 47 48 private final int mUserId; 49 50 private final Handler mHandler; 51 52 private IPrinterDiscoveryObserver mObserver; 53 54 private OnPrintersChangeListener mListener; 55 56 private boolean mIsPrinterDiscoveryStarted; 57 58 public static interface OnPrintersChangeListener { 59 public void onPrintersChanged(); 60 } 61 62 PrinterDiscoverySession(IPrintManager printManager, Context context, int userId) { 63 mPrintManager = printManager; 64 mUserId = userId; 65 mHandler = new SessionHandler(context.getMainLooper()); 66 mObserver = new PrinterDiscoveryObserver(this); 67 try { 68 mPrintManager.createPrinterDiscoverySession(mObserver, mUserId); 69 } catch (RemoteException re) { 70 Log.e(LOG_TAG, "Error creating printer discovery session", re); 71 } 72 } 73 74 public final void startPrinterDisovery(List<PrinterId> priorityList) { 75 if (isDestroyed()) { 76 Log.w(LOG_TAG, "Ignoring start printers dsicovery - session destroyed"); 77 return; 78 } 79 if (!mIsPrinterDiscoveryStarted) { 80 mIsPrinterDiscoveryStarted = true; 81 try { 82 mPrintManager.startPrinterDiscovery(mObserver, priorityList, mUserId); 83 } catch (RemoteException re) { 84 Log.e(LOG_TAG, "Error starting printer discovery", re); 85 } 86 } 87 } 88 89 public final void stopPrinterDiscovery() { 90 if (isDestroyed()) { 91 Log.w(LOG_TAG, "Ignoring stop printers discovery - session destroyed"); 92 return; 93 } 94 if (mIsPrinterDiscoveryStarted) { 95 mIsPrinterDiscoveryStarted = false; 96 try { 97 mPrintManager.stopPrinterDiscovery(mObserver, mUserId); 98 } catch (RemoteException re) { 99 Log.e(LOG_TAG, "Error stopping printer discovery", re); 100 } 101 } 102 } 103 104 public final void startPrinterStateTracking(PrinterId printerId) { 105 if (isDestroyed()) { 106 Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed"); 107 return; 108 } 109 try { 110 mPrintManager.startPrinterStateTracking(printerId, mUserId); 111 } catch (RemoteException re) { 112 Log.e(LOG_TAG, "Error starting printer state tracking", re); 113 } 114 } 115 116 public final void stopPrinterStateTracking(PrinterId printerId) { 117 if (isDestroyed()) { 118 Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed"); 119 return; 120 } 121 try { 122 mPrintManager.stopPrinterStateTracking(printerId, mUserId); 123 } catch (RemoteException re) { 124 Log.e(LOG_TAG, "Error stoping printer state tracking", re); 125 } 126 } 127 128 public final void validatePrinters(List<PrinterId> printerIds) { 129 if (isDestroyed()) { 130 Log.w(LOG_TAG, "Ignoring validate printers - session destroyed"); 131 return; 132 } 133 try { 134 mPrintManager.validatePrinters(printerIds, mUserId); 135 } catch (RemoteException re) { 136 Log.e(LOG_TAG, "Error validating printers", re); 137 } 138 } 139 140 public final void destroy() { 141 if (isDestroyed()) { 142 Log.w(LOG_TAG, "Ignoring destroy - session destroyed"); 143 } 144 destroyNoCheck(); 145 } 146 147 public final List<PrinterInfo> getPrinters() { 148 if (isDestroyed()) { 149 Log.w(LOG_TAG, "Ignoring get printers - session destroyed"); 150 return Collections.emptyList(); 151 } 152 return new ArrayList<PrinterInfo>(mPrinters.values()); 153 } 154 155 public final boolean isDestroyed() { 156 throwIfNotCalledOnMainThread(); 157 return isDestroyedNoCheck(); 158 } 159 160 public final boolean isPrinterDiscoveryStarted() { 161 throwIfNotCalledOnMainThread(); 162 return mIsPrinterDiscoveryStarted; 163 } 164 165 public final void setOnPrintersChangeListener(OnPrintersChangeListener listener) { 166 throwIfNotCalledOnMainThread(); 167 mListener = listener; 168 } 169 170 @Override 171 protected final void finalize() throws Throwable { 172 if (!isDestroyedNoCheck()) { 173 Log.e(LOG_TAG, "Destroying leaked printer discovery session"); 174 destroyNoCheck(); 175 } 176 super.finalize(); 177 } 178 179 private boolean isDestroyedNoCheck() { 180 return (mObserver == null); 181 } 182 183 private void destroyNoCheck() { 184 stopPrinterDiscovery(); 185 try { 186 mPrintManager.destroyPrinterDiscoverySession(mObserver, mUserId); 187 } catch (RemoteException re) { 188 Log.e(LOG_TAG, "Error destroying printer discovery session", re); 189 } finally { 190 mObserver = null; 191 mPrinters.clear(); 192 } 193 } 194 195 private void handlePrintersAdded(List<PrinterInfo> addedPrinters) { 196 if (isDestroyed()) { 197 return; 198 } 199 200 // No old printers - do not bother keeping their position. 201 if (mPrinters.isEmpty()) { 202 final int printerCount = addedPrinters.size(); 203 for (int i = 0; i < printerCount; i++) { 204 PrinterInfo printer = addedPrinters.get(i); 205 mPrinters.put(printer.getId(), printer); 206 } 207 notifyOnPrintersChanged(); 208 return; 209 } 210 211 // Add the printers to a map. 212 ArrayMap<PrinterId, PrinterInfo> addedPrintersMap = 213 new ArrayMap<PrinterId, PrinterInfo>(); 214 final int printerCount = addedPrinters.size(); 215 for (int i = 0; i < printerCount; i++) { 216 PrinterInfo printer = addedPrinters.get(i); 217 addedPrintersMap.put(printer.getId(), printer); 218 } 219 220 // Update printers we already have. 221 final int oldPrinterCount = mPrinters.size(); 222 for (int i = 0; i < oldPrinterCount; i++) { 223 PrinterId oldPrinterId = mPrinters.keyAt(i); 224 PrinterInfo updatedPrinter = addedPrintersMap.remove(oldPrinterId); 225 if (updatedPrinter != null) { 226 mPrinters.put(oldPrinterId, updatedPrinter); 227 } 228 } 229 230 // Add the new printers, i.e. what is left. 231 mPrinters.putAll(addedPrintersMap); 232 233 // Announce the change. 234 notifyOnPrintersChanged(); 235 } 236 237 private void handlePrintersRemoved(List<PrinterId> printerIds) { 238 if (isDestroyed()) { 239 return; 240 } 241 boolean printersChanged = false; 242 final int removedPrinterIdCount = printerIds.size(); 243 for (int i = 0; i < removedPrinterIdCount; i++) { 244 PrinterId removedPrinterId = printerIds.get(i); 245 if (mPrinters.remove(removedPrinterId) != null) { 246 printersChanged = true; 247 } 248 } 249 if (printersChanged) { 250 notifyOnPrintersChanged(); 251 } 252 } 253 254 private void notifyOnPrintersChanged() { 255 if (mListener != null) { 256 mListener.onPrintersChanged(); 257 } 258 } 259 260 private static void throwIfNotCalledOnMainThread() { 261 if (!Looper.getMainLooper().isCurrentThread()) { 262 throw new IllegalAccessError("must be called from the main thread"); 263 } 264 } 265 266 private final class SessionHandler extends Handler { 267 268 public SessionHandler(Looper looper) { 269 super(looper, null, false); 270 } 271 272 @Override 273 @SuppressWarnings("unchecked") 274 public void handleMessage(Message message) { 275 switch (message.what) { 276 case MSG_PRINTERS_ADDED: { 277 List<PrinterInfo> printers = (List<PrinterInfo>) message.obj; 278 handlePrintersAdded(printers); 279 } break; 280 281 case MSG_PRINTERS_REMOVED: { 282 List<PrinterId> printerIds = (List<PrinterId>) message.obj; 283 handlePrintersRemoved(printerIds); 284 } break; 285 } 286 } 287 } 288 289 private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { 290 291 private final WeakReference<PrinterDiscoverySession> mWeakSession; 292 293 public PrinterDiscoveryObserver(PrinterDiscoverySession session) { 294 mWeakSession = new WeakReference<PrinterDiscoverySession>(session); 295 } 296 297 @Override 298 @SuppressWarnings("rawtypes") 299 public void onPrintersAdded(ParceledListSlice printers) { 300 PrinterDiscoverySession session = mWeakSession.get(); 301 if (session != null) { 302 session.mHandler.obtainMessage(MSG_PRINTERS_ADDED, 303 printers.getList()).sendToTarget(); 304 } 305 } 306 307 @Override 308 @SuppressWarnings("rawtypes") 309 public void onPrintersRemoved(ParceledListSlice printerIds) { 310 PrinterDiscoverySession session = mWeakSession.get(); 311 if (session != null) { 312 session.mHandler.obtainMessage(MSG_PRINTERS_REMOVED, 313 printerIds.getList()).sendToTarget(); 314 } 315 } 316 } 317} 318