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