/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.print; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; /** * @hide */ public final class PrinterDiscoverySession { private static final String LOG_TAG ="PrinterDiscoverySession"; private static final int MSG_PRINTERS_ADDED = 1; private static final int MSG_PRINTERS_REMOVED = 2; private final LinkedHashMap<PrinterId, PrinterInfo> mPrinters = new LinkedHashMap<PrinterId, PrinterInfo>(); private final IPrintManager mPrintManager; private final int mUserId; private final Handler mHandler; private IPrinterDiscoveryObserver mObserver; private OnPrintersChangeListener mListener; private boolean mIsPrinterDiscoveryStarted; public static interface OnPrintersChangeListener { public void onPrintersChanged(); } PrinterDiscoverySession(IPrintManager printManager, Context context, int userId) { mPrintManager = printManager; mUserId = userId; mHandler = new SessionHandler(context.getMainLooper()); mObserver = new PrinterDiscoveryObserver(this); try { mPrintManager.createPrinterDiscoverySession(mObserver, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error creating printer discovery session", re); } } public final void startPrinterDiscovery(@Nullable List<PrinterId> priorityList) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printers discovery - session destroyed"); return; } if (!mIsPrinterDiscoveryStarted) { mIsPrinterDiscoveryStarted = true; try { mPrintManager.startPrinterDiscovery(mObserver, priorityList, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error starting printer discovery", re); } } } public final void stopPrinterDiscovery() { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring stop printers discovery - session destroyed"); return; } if (mIsPrinterDiscoveryStarted) { mIsPrinterDiscoveryStarted = false; try { mPrintManager.stopPrinterDiscovery(mObserver, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error stopping printer discovery", re); } } } public final void startPrinterStateTracking(@NonNull PrinterId printerId) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed"); return; } try { mPrintManager.startPrinterStateTracking(printerId, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error starting printer state tracking", re); } } public final void stopPrinterStateTracking(@NonNull PrinterId printerId) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed"); return; } try { mPrintManager.stopPrinterStateTracking(printerId, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error stopping printer state tracking", re); } } public final void validatePrinters(List<PrinterId> printerIds) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring validate printers - session destroyed"); return; } try { mPrintManager.validatePrinters(printerIds, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error validating printers", re); } } public final void destroy() { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring destroy - session destroyed"); } destroyNoCheck(); } public final List<PrinterInfo> getPrinters() { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring get printers - session destroyed"); return Collections.emptyList(); } return new ArrayList<PrinterInfo>(mPrinters.values()); } public final boolean isDestroyed() { throwIfNotCalledOnMainThread(); return isDestroyedNoCheck(); } public final boolean isPrinterDiscoveryStarted() { throwIfNotCalledOnMainThread(); return mIsPrinterDiscoveryStarted; } public final void setOnPrintersChangeListener(OnPrintersChangeListener listener) { throwIfNotCalledOnMainThread(); mListener = listener; } @Override protected final void finalize() throws Throwable { if (!isDestroyedNoCheck()) { Log.e(LOG_TAG, "Destroying leaked printer discovery session"); destroyNoCheck(); } super.finalize(); } private boolean isDestroyedNoCheck() { return (mObserver == null); } private void destroyNoCheck() { stopPrinterDiscovery(); try { mPrintManager.destroyPrinterDiscoverySession(mObserver, mUserId); } catch (RemoteException re) { Log.e(LOG_TAG, "Error destroying printer discovery session", re); } finally { mObserver = null; mPrinters.clear(); } } private void handlePrintersAdded(List<PrinterInfo> addedPrinters) { if (isDestroyed()) { return; } // No old printers - do not bother keeping their position. if (mPrinters.isEmpty()) { final int printerCount = addedPrinters.size(); for (int i = 0; i < printerCount; i++) { PrinterInfo printer = addedPrinters.get(i); mPrinters.put(printer.getId(), printer); } notifyOnPrintersChanged(); return; } // Add the printers to a map. ArrayMap<PrinterId, PrinterInfo> addedPrintersMap = new ArrayMap<PrinterId, PrinterInfo>(); final int printerCount = addedPrinters.size(); for (int i = 0; i < printerCount; i++) { PrinterInfo printer = addedPrinters.get(i); addedPrintersMap.put(printer.getId(), printer); } // Update printers we already have. for (PrinterId oldPrinterId : mPrinters.keySet()) { PrinterInfo updatedPrinter = addedPrintersMap.remove(oldPrinterId); if (updatedPrinter != null) { mPrinters.put(oldPrinterId, updatedPrinter); } } // Add the new printers, i.e. what is left. mPrinters.putAll(addedPrintersMap); // Announce the change. notifyOnPrintersChanged(); } private void handlePrintersRemoved(List<PrinterId> printerIds) { if (isDestroyed()) { return; } boolean printersChanged = false; final int removedPrinterIdCount = printerIds.size(); for (int i = 0; i < removedPrinterIdCount; i++) { PrinterId removedPrinterId = printerIds.get(i); if (mPrinters.remove(removedPrinterId) != null) { printersChanged = true; } } if (printersChanged) { notifyOnPrintersChanged(); } } private void notifyOnPrintersChanged() { if (mListener != null) { mListener.onPrintersChanged(); } } private static void throwIfNotCalledOnMainThread() { if (!Looper.getMainLooper().isCurrentThread()) { throw new IllegalAccessError("must be called from the main thread"); } } private final class SessionHandler extends Handler { public SessionHandler(Looper looper) { super(looper, null, false); } @Override @SuppressWarnings("unchecked") public void handleMessage(Message message) { switch (message.what) { case MSG_PRINTERS_ADDED: { List<PrinterInfo> printers = (List<PrinterInfo>) message.obj; handlePrintersAdded(printers); } break; case MSG_PRINTERS_REMOVED: { List<PrinterId> printerIds = (List<PrinterId>) message.obj; handlePrintersRemoved(printerIds); } break; } } } public static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub { private final WeakReference<PrinterDiscoverySession> mWeakSession; public PrinterDiscoveryObserver(PrinterDiscoverySession session) { mWeakSession = new WeakReference<PrinterDiscoverySession>(session); } @Override @SuppressWarnings("rawtypes") public void onPrintersAdded(ParceledListSlice printers) { PrinterDiscoverySession session = mWeakSession.get(); if (session != null) { session.mHandler.obtainMessage(MSG_PRINTERS_ADDED, printers.getList()).sendToTarget(); } } @Override @SuppressWarnings("rawtypes") public void onPrintersRemoved(ParceledListSlice printerIds) { PrinterDiscoverySession session = mWeakSession.get(); if (session != null) { session.mHandler.obtainMessage(MSG_PRINTERS_REMOVED, printerIds.getList()).sendToTarget(); } } } }