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