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