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