• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.cts;
18 
19 import static android.print.test.Utils.eventually;
20 import static android.print.test.Utils.runOnMainThread;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.Mockito.inOrder;
27 
28 import android.print.PrintAttributes;
29 import android.print.PrintAttributes.Margins;
30 import android.print.PrintAttributes.MediaSize;
31 import android.print.PrintAttributes.Resolution;
32 import android.print.PrintDocumentAdapter;
33 import android.print.PrinterCapabilitiesInfo;
34 import android.print.PrinterId;
35 import android.print.PrinterInfo;
36 import android.print.test.BasePrintTest;
37 import android.print.test.services.FirstPrintService;
38 import android.print.test.services.PrintServiceCallbacks;
39 import android.print.test.services.PrinterDiscoverySessionCallbacks;
40 import android.print.test.services.SecondPrintService;
41 import android.print.test.services.StubbablePrinterDiscoverySession;
42 import android.printservice.PrintJob;
43 import android.printservice.PrinterDiscoverySession;
44 import android.support.test.uiautomator.UiObject;
45 import android.support.test.uiautomator.UiSelector;
46 
47 import androidx.annotation.NonNull;
48 import androidx.test.runner.AndroidJUnit4;
49 
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.InOrder;
54 import org.mockito.exceptions.verification.VerificationInOrderFailure;
55 
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.List;
59 
60 /**
61  * This test verifies that the system respects the {@link PrinterDiscoverySession}
62  * contract is respected.
63  */
64 @RunWith(AndroidJUnit4.class)
65 public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest {
66     private static final String FIRST_PRINTER_LOCAL_ID = "first_printer";
67     private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
68 
69     private static StubbablePrinterDiscoverySession sSession;
70 
71     @Before
clearPrintSpoolerState()72     public void clearPrintSpoolerState() throws Exception {
73         clearPrintSpoolerData();
74     }
75 
76     /**
77      * Add a printer to {@#sSession}.
78      *
79      * @param localId The id of the printer to add
80      * @param hasCapabilities If the printer has capabilities
81      */
addPrinter(@onNull String localId, boolean hasCapabilities)82     private void addPrinter(@NonNull String localId, boolean hasCapabilities) {
83         // Add the first printer.
84         PrinterId firstPrinterId = sSession.getService().generatePrinterId(
85                 localId);
86 
87         PrinterInfo.Builder printer = new PrinterInfo.Builder(firstPrinterId,
88                 localId, PrinterInfo.STATUS_IDLE);
89 
90         if (hasCapabilities) {
91             printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(firstPrinterId)
92                     .setMinMargins(new Margins(200, 200, 200, 200))
93                     .addMediaSize(MediaSize.ISO_A0, true)
94                     .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
95                     .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
96                             PrintAttributes.COLOR_MODE_COLOR)
97                     .build());
98         }
99 
100         sSession.addPrinters(Collections.singletonList(printer.build()));
101     }
102 
103     /**
104      * Make {@code localPrinterId} the default printer. This requires a full print workflow.
105      *
106      * As a side-effect also approved the print service.
107      *
108      * @param localPrinterId The printer to make default
109      */
makeDefaultPrinter(String localPrinterId)110     private void makeDefaultPrinter(String localPrinterId) throws Throwable {
111         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
112 
113         print(adapter);
114         waitForWriteAdapterCallback(1);
115 
116         runOnMainThread(() -> addPrinter(localPrinterId, true));
117         selectPrinter(localPrinterId);
118         waitForWriteAdapterCallback(2);
119 
120         clickPrintButton();
121 
122         eventually(() -> {
123             answerPrintServicesWarning(true);
124 
125             waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
126         }, OPERATION_TIMEOUT_MILLIS * 2);
127 
128         resetCounters();
129     }
130 
131     /**
132      * Select a printer in the all printers activity
133      *
134      * @param printerName The name of the printer to select
135      */
selectInAllPrintersActivity(@onNull String printerName)136     private void selectInAllPrintersActivity(@NonNull String printerName) throws Exception {
137         while (true) {
138             UiObject printerItem = getUiDevice().findObject(
139                     new UiSelector().text(printerName));
140 
141             if (printerItem.isEnabled()) {
142                 printerItem.click();
143                 break;
144             } else {
145                 Thread.sleep(100);
146             }
147         }
148     }
149 
150     @Test
defaultPrinterBecomesAvailableWhileInBackground()151     public void defaultPrinterBecomesAvailableWhileInBackground() throws Throwable {
152         // Create the session callbacks that we will be checking.
153         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
154                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
155                     sSession =
156                             ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
157 
158                     onPrinterDiscoverySessionCreateCalled();
159                     return null;
160                 }, null, null, null, null, null, invocation -> {
161                     onPrinterDiscoverySessionDestroyCalled();
162                     return null;
163                 });
164 
165         // Create the service callbacks for the first print service.
166         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
167                 invocation -> firstSessionCallbacks, null, null);
168 
169         // Configure the print services.
170         FirstPrintService.setCallbacks(firstServiceCallbacks);
171         SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
172 
173         makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID);
174 
175         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
176         print(adapter);
177         waitForPrinterDiscoverySessionCreateCallbackCalled();
178 
179         waitForPrinterUnavailable();
180 
181         selectPrinter("All printers…");
182         // Let all printers activity start
183         Thread.sleep(500);
184 
185         // Add printer
186         runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true));
187 
188         // Select printer once available (this returns to main print activity)
189         selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID);
190 
191         // Wait for preview to load and finish print
192         waitForWriteAdapterCallback(1);
193 
194         eventually(() -> {
195             clickPrintButton();
196             waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
197         }, OPERATION_TIMEOUT_MILLIS * 2);
198     }
199 
200     @Test
defaultPrinterBecomesUsableWhileInBackground()201     public void defaultPrinterBecomesUsableWhileInBackground() throws Throwable {
202         // Create the session callbacks that we will be checking.
203         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
204                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
205                     sSession =
206                             ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
207 
208                     onPrinterDiscoverySessionCreateCalled();
209                     return null;
210                 }, null, null, null, null, null, invocation -> {
211                     onPrinterDiscoverySessionDestroyCalled();
212                     return null;
213                 });
214 
215         // Create the service callbacks for the first print service.
216         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
217                 invocation -> firstSessionCallbacks, null, null);
218 
219         // Configure the print services.
220         FirstPrintService.setCallbacks(firstServiceCallbacks);
221         SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
222 
223         makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID);
224 
225         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
226         print(adapter);
227         waitForPrinterDiscoverySessionCreateCallbackCalled();
228 
229         // Add printer but do not enable it (capabilities == null)
230         runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, false));
231         waitForPrinterUnavailable();
232 
233         selectPrinter("All printers…");
234         // Let all printers activity start
235         Thread.sleep(500);
236 
237         // Enable printer
238         runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true));
239 
240         // Select printer once available (this returns to main print activity)
241         selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID);
242 
243         // Wait for preview to load and finish print
244         waitForWriteAdapterCallback(1);
245 
246         eventually(() -> {
247             clickPrintButton();
248             waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
249         }, OPERATION_TIMEOUT_MILLIS * 2);
250     }
251 
252     @Test
normalLifecycle()253     public void normalLifecycle() throws Throwable {
254         // Create the session callbacks that we will be checking.
255         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
256                 createFirstMockPrinterDiscoverySessionCallbacks();
257 
258         // Create the service callbacks for the first print service.
259         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
260                 invocation -> firstSessionCallbacks,
261                 invocation -> {
262                     PrintJob printJob = (PrintJob) invocation.getArguments()[0];
263                     // We pretend the job is handled immediately.
264                     printJob.complete();
265                     return null;
266                 }, null);
267 
268         // Configure the print services.
269         FirstPrintService.setCallbacks(firstServiceCallbacks);
270         SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
271 
272         // Create a print adapter that respects the print contract.
273         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
274 
275         // Start printing.
276         print(adapter);
277 
278         // Wait for write of the first page.
279         waitForWriteAdapterCallback(1);
280 
281         runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
282         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
283 
284         // Select the first printer.
285         selectPrinter(FIRST_PRINTER_LOCAL_ID);
286 
287         eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
288                 sSession.getTrackedPrinters().get(0).getLocalId())));
289         runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted()));
290         runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
291 
292         // Wait for layout as the printer has different capabilities.
293         waitForLayoutAdapterCallbackCount(2);
294 
295         // Select the second printer (same capabilities as the other
296         // one so no layout should happen).
297         selectPrinter(SECOND_PRINTER_LOCAL_ID);
298 
299         eventually(() -> runOnMainThread(() -> assertEquals(SECOND_PRINTER_LOCAL_ID,
300                 sSession.getTrackedPrinters().get(0).getLocalId())));
301         runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
302 
303         // While the printer discovery session is still alive store the
304         // ids of printers as we want to make some assertions about them
305         // but only the print service can create printer ids which means
306         // that we need to get the created ones.
307         PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
308                 FIRST_PRINTER_LOCAL_ID);
309         PrinterId secondPrinterId = getAddedPrinterIdForLocalId(
310                 SECOND_PRINTER_LOCAL_ID);
311         assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId);
312         assertNotNull("Coundn't find printer:" + SECOND_PRINTER_LOCAL_ID, secondPrinterId);
313 
314         // Click the print button.
315         clickPrintButton();
316 
317         eventually(() -> {
318             // Answer the dialog for the print service cloud warning
319             answerPrintServicesWarning(true);
320 
321             // Wait for all print jobs to be handled after which the session destroyed.
322             waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
323         }, OPERATION_TIMEOUT_MILLIS * 2);
324 
325         runOnMainThread(() -> assertTrue(sSession.isDestroyed()));
326         runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted()));
327         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
328 
329         // Verify the expected calls.
330         InOrder inOrder = inOrder(firstSessionCallbacks);
331 
332         // We start discovery as the print dialog was up.
333         List<PrinterId> emptyPrinterIdList = Collections.emptyList();
334         inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
335                 emptyPrinterIdList);
336 
337         // We selected the first printer and now it should be tracked.
338         inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
339                 firstPrinterId);
340 
341         // We selected the second printer so the first should not be tracked.
342         inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
343                 firstPrinterId);
344 
345         // We selected the second printer and now it should be tracked.
346         inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
347                 secondPrinterId);
348 
349         // The print dialog went away so we first stop the printer tracking...
350         inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
351                 secondPrinterId);
352 
353         // ... next we stop printer discovery...
354         inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
355 
356         // ... last the session is destroyed.
357         inOrder.verify(firstSessionCallbacks).onDestroy();
358     }
359 
360     @Test
cancelPrintServicesAlertDialog()361     public void cancelPrintServicesAlertDialog() throws Throwable {
362         // Create the session callbacks that we will be checking.
363         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
364                 createFirstMockPrinterDiscoverySessionCallbacks();
365 
366         // Create the service callbacks for the first print service.
367         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
368                 invocation -> firstSessionCallbacks,
369                 invocation -> {
370                     PrintJob printJob = (PrintJob) invocation.getArguments()[0];
371                     // We pretend the job is handled immediately.
372                     printJob.complete();
373                     return null;
374                 }, null);
375 
376         // Configure the print services.
377         FirstPrintService.setCallbacks(firstServiceCallbacks);
378         SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
379 
380         // Create a print adapter that respects the print contract.
381         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
382 
383         // Start printing.
384         print(adapter);
385 
386         // Wait for write of the first page.
387         waitForWriteAdapterCallback(1);
388 
389         runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
390         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
391 
392         // Select the first printer.
393         selectPrinter(FIRST_PRINTER_LOCAL_ID);
394 
395         eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
396                 sSession.getTrackedPrinters().get(0).getLocalId())));
397         runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted()));
398         runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
399 
400         // While the printer discovery session is still alive store the
401         // ids of printers as we want to make some assertions about them
402         // but only the print service can create printer ids which means
403         // that we need to get the created ones.
404         PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
405                 FIRST_PRINTER_LOCAL_ID);
406         assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId);
407 
408         // Click the print button.
409         clickPrintButton();
410 
411         // Cancel the dialog for the print service cloud warning
412         answerPrintServicesWarning(false);
413 
414         // Click the print button again.
415         clickPrintButton();
416 
417         // Answer the dialog for the print service cloud warning
418         answerPrintServicesWarning(true);
419 
420         // Wait for all print jobs to be handled after which the session destroyed.
421         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
422 
423         runOnMainThread(() -> assertTrue(sSession.isDestroyed()));
424         runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted()));
425         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
426 
427         // Verify the expected calls.
428         InOrder inOrder = inOrder(firstSessionCallbacks);
429 
430         // We start discovery as the print dialog was up.
431         List<PrinterId> emptyPrinterIdList = Collections.emptyList();
432         inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
433                 emptyPrinterIdList);
434 
435         // We selected the first printer and now it should be tracked.
436         inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
437                 firstPrinterId);
438 
439         // We selected the second printer so the first should not be tracked.
440         inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
441                 firstPrinterId);
442 
443         // ... next we stop printer discovery...
444         inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
445 
446         // ... last the session is destroyed.
447         inOrder.verify(firstSessionCallbacks).onDestroy();
448     }
449 
450     @Test
startPrinterDiscoveryWithHistoricalPrinters()451     public void startPrinterDiscoveryWithHistoricalPrinters() throws Throwable {
452         // Create the session callbacks that we will be checking.
453         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
454                 createFirstMockPrinterDiscoverySessionCallbacks();
455 
456         // Create the service callbacks for the first print service.
457         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
458                 invocation -> firstSessionCallbacks,
459                 invocation -> {
460                     PrintJob printJob = (PrintJob) invocation.getArguments()[0];
461                     // We pretend the job is handled immediately.
462                     printJob.complete();
463                     return null;
464                 }, null);
465 
466         // Configure the print services.
467         FirstPrintService.setCallbacks(firstServiceCallbacks);
468         SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
469 
470         // Create a print adapter that respects the print contract.
471         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
472 
473         // Start printing.
474         print(adapter);
475 
476         // Wait for write of the first page.
477         waitForWriteAdapterCallback(1);
478 
479         runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
480         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
481 
482         // Select the first printer.
483         selectPrinter(FIRST_PRINTER_LOCAL_ID);
484 
485         eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
486                 sSession.getTrackedPrinters().get(0).getLocalId())));
487         runOnMainThread(() -> assertTrue(sSession.isPrinterDiscoveryStarted()));
488         runOnMainThread(() -> assertEquals(1, sSession.getTrackedPrinters().size()));
489 
490         // Wait for a layout to finish - first layout was for the
491         // PDF printer, second for the first printer in preview mode.
492         waitForLayoutAdapterCallbackCount(2);
493 
494         // While the printer discovery session is still alive store the
495         // ids of printer as we want to make some assertions about it
496         // but only the print service can create printer ids which means
497         // that we need to get the created one.
498         PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
499                 FIRST_PRINTER_LOCAL_ID);
500 
501         // Click the print button.
502         clickPrintButton();
503 
504         eventually(() -> {
505             // Answer the dialog for the print service cloud warning
506             answerPrintServicesWarning(true);
507 
508             // Wait for the print to complete.
509             waitForAdapterFinishCallbackCalled();
510         }, OPERATION_TIMEOUT_MILLIS * 2);
511 
512         // Now print again as we want to confirm that the start
513         // printer discovery passes in the priority list.
514         print(adapter);
515 
516         // Wait for a layout to finish - first layout was for the
517         // PDF printer, second for the first printer in preview mode,
518         // the third for the first printer in non-preview mode, and
519         // now a fourth for the PDF printer as we are printing again.
520         waitForLayoutAdapterCallbackCount(4);
521 
522         // Cancel the printing.
523         getUiDevice().pressBack(); // wakes up the device.
524         getUiDevice().pressBack();
525 
526         // Wait for all print jobs to be handled after which the is session destroyed.
527         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
528 
529         runOnMainThread(() -> assertTrue(sSession.isDestroyed()));
530         runOnMainThread(() -> assertFalse(sSession.isPrinterDiscoveryStarted()));
531         runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
532 
533         // Verify the expected calls.
534         InOrder inOrder = inOrder(firstSessionCallbacks);
535 
536         // We start discovery with no printer history.
537         List<PrinterId> priorityList = new ArrayList<>();
538         inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
539                 priorityList);
540 
541         // We selected the first printer and now it should be tracked.
542         inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
543                 firstPrinterId);
544 
545         // We confirmed print so the first should not be tracked.
546         inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
547                 firstPrinterId);
548 
549         // This is tricky. It is possible that the print activity was not
550         // destroyed (the platform delays destruction at convenient time as
551         // an optimization) and we get the same instance which means that
552         // the discovery session may not have been destroyed. We try the
553         // case with the activity being destroyed and if this fails the
554         // case with the activity brought to front.
555         priorityList.add(firstPrinterId);
556         try {
557             inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(priorityList);
558         } catch (VerificationInOrderFailure error) {
559             inOrder.verify(firstSessionCallbacks).onValidatePrinters(priorityList);
560         }
561 
562         // The system selects the highest ranked historical printer.
563         inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
564                 firstPrinterId);
565 
566         // We canceled print so the first should not be tracked.
567         inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
568                 firstPrinterId);
569 
570 
571         // Discovery is always stopped before the session is always destroyed.
572         inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
573 
574         // ...last the session is destroyed.
575         inOrder.verify(firstSessionCallbacks).onDestroy();
576     }
577 
578     @Test
addRemovePrinters()579     public void addRemovePrinters() throws Throwable {
580         StubbablePrinterDiscoverySession[] session = new StubbablePrinterDiscoverySession[1];
581 
582         // Create the session callbacks that we will be checking.
583         final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
584                 createMockPrinterDiscoverySessionCallbacks(invocation -> {
585                     session[0] = ((PrinterDiscoverySessionCallbacks)
586                             invocation.getMock()).getSession();
587 
588                     onPrinterDiscoverySessionCreateCalled();
589                     return null;
590                 }, null, null, null, null, null, invocation -> {
591                     onPrinterDiscoverySessionDestroyCalled();
592                     return null;
593                 });
594 
595         // Create the service callbacks for the first print service.
596         PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
597                 invocation -> firstSessionCallbacks, null, null);
598 
599         // Configure the print services.
600         FirstPrintService.setCallbacks(firstServiceCallbacks);
601         SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
602 
603         print(createDefaultPrintDocumentAdapter(1));
604 
605         waitForPrinterDiscoverySessionCreateCallbackCalled();
606 
607         runOnMainThread(() -> assertEquals(0, session[0].getPrinters().size()));
608 
609         PrinterId[] printerIds = new PrinterId[3];
610         runOnMainThread(() -> {
611             printerIds[0] = session[0].getService().generatePrinterId("0");
612             printerIds[1] = session[0].getService().generatePrinterId("1");
613             printerIds[2] = session[0].getService().generatePrinterId("2");
614         });
615 
616         PrinterInfo printer1 = (new PrinterInfo.Builder(printerIds[0], "0",
617                 PrinterInfo.STATUS_IDLE)).build();
618 
619         PrinterInfo printer2 = (new PrinterInfo.Builder(printerIds[1], "1",
620                 PrinterInfo.STATUS_IDLE)).build();
621 
622         PrinterInfo printer3 = (new PrinterInfo.Builder(printerIds[2], "2",
623                 PrinterInfo.STATUS_IDLE)).build();
624 
625         ArrayList<PrinterInfo> printers = new ArrayList<>();
626         printers.add(printer1);
627         runOnMainThread(() -> session[0].addPrinters(printers));
628         eventually(() -> runOnMainThread(() -> assertEquals(1, session[0].getPrinters().size())));
629 
630         printers.add(printer2);
631         printers.add(printer3);
632         runOnMainThread(() -> session[0].addPrinters(printers));
633         eventually(() -> runOnMainThread(() -> assertEquals(3, session[0].getPrinters().size())));
634 
635         ArrayList<PrinterId> printerIdsToRemove = new ArrayList<>();
636         printerIdsToRemove.add(printer1.getId());
637         runOnMainThread(() -> session[0].removePrinters(printerIdsToRemove));
638         eventually(() -> runOnMainThread(() -> assertEquals(2, session[0].getPrinters().size())));
639 
640         printerIdsToRemove.add(printer2.getId());
641         printerIdsToRemove.add(printer3.getId());
642         runOnMainThread(() -> session[0].removePrinters(printerIdsToRemove));
643         eventually(() -> runOnMainThread(() -> assertEquals(0, session[0].getPrinters().size())));
644 
645         getUiDevice().pressBack();
646 
647         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
648     }
649 
getAddedPrinterIdForLocalId(String printerLocalId)650     private PrinterId getAddedPrinterIdForLocalId(String printerLocalId) throws Throwable {
651         final List<PrinterInfo> reportedPrinters = new ArrayList<>();
652         runOnMainThread(() -> {
653             // Grab the printer ids as only the service can create such.
654             reportedPrinters.addAll(sSession.getPrinters());
655         });
656 
657         final int reportedPrinterCount = reportedPrinters.size();
658         for (int i = 0; i < reportedPrinterCount; i++) {
659             PrinterInfo reportedPrinter = reportedPrinters.get(i);
660             String localId = reportedPrinter.getId().getLocalId();
661             if (printerLocalId.equals(localId)) {
662                 return reportedPrinter.getId();
663             }
664         }
665 
666         return null;
667     }
668 
createSecondMockPrintServiceCallbacks()669     private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
670         return createMockPrintServiceCallbacks(null, null, null);
671     }
672 
createFirstMockPrinterDiscoverySessionCallbacks()673     private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
674         return createMockPrinterDiscoverySessionCallbacks(invocation -> {
675             // Get the session.
676             sSession = ((PrinterDiscoverySessionCallbacks)
677                     invocation.getMock()).getSession();
678 
679             assertTrue(sSession.isPrinterDiscoveryStarted());
680 
681             addPrinter(FIRST_PRINTER_LOCAL_ID, false);
682             addPrinter(SECOND_PRINTER_LOCAL_ID, false);
683 
684             return null;
685         }, invocation -> {
686             assertFalse(sSession.isPrinterDiscoveryStarted());
687             return null;
688         }, null, invocation -> {
689             // Get the session.
690             StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
691                     invocation.getMock()).getSession();
692 
693             PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0];
694             List<PrinterInfo> reportedPrinters = session.getPrinters();
695 
696             // We should be tracking a printer that we added.
697             PrinterInfo trackedPrinter = null;
698             final int reportedPrinterCount = reportedPrinters.size();
699             for (int i = 0; i < reportedPrinterCount; i++) {
700                 PrinterInfo reportedPrinter = reportedPrinters.get(i);
701                 if (reportedPrinter.getId().equals(trackedPrinterId)) {
702                     trackedPrinter = reportedPrinter;
703                     break;
704                 }
705             }
706             assertNotNull("Can track only added printers", trackedPrinter);
707 
708             assertTrue(sSession.getTrackedPrinters().contains(trackedPrinter.getId()));
709             assertEquals(1, sSession.getTrackedPrinters().size());
710 
711             // If the printer does not have capabilities reported add them.
712             if (trackedPrinter.getCapabilities() == null) {
713 
714                 // Add the capabilities to emulate lazy discovery.
715                 // Same for each printer is fine for what we test.
716                 PrinterCapabilitiesInfo capabilities =
717                         new PrinterCapabilitiesInfo.Builder(trackedPrinterId)
718                                 .setMinMargins(new Margins(200, 200, 200, 200))
719                                 .addMediaSize(MediaSize.ISO_A4, true)
720                                 .addMediaSize(MediaSize.ISO_A5, false)
721                                 .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
722                                 .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
723                                         PrintAttributes.COLOR_MODE_COLOR)
724                                 .build();
725                 PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter)
726                         .setCapabilities(capabilities)
727                         .build();
728 
729                 // Update the printer.
730                 List<PrinterInfo> printers = new ArrayList<>();
731                 printers.add(updatedPrinter);
732                 session.addPrinters(printers);
733             }
734 
735             return null;
736         }, null, null, invocation -> {
737             assertTrue(sSession.isDestroyed());
738 
739             // Take a note onDestroy was called.
740             onPrinterDiscoverySessionDestroyCalled();
741             return null;
742         });
743     }
744 }
745