Home | History | Annotate | Download | only in cts
      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
     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      */
     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      */
    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      */
    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
    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
    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
    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
    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
    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
    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 
    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 
    669     private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
    670         return createMockPrintServiceCallbacks(null, null, null);
    671     }
    672 
    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