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 org.mockito.Mockito.inOrder; 20 21 import android.os.ParcelFileDescriptor; 22 import android.print.PageRange; 23 import android.print.PrintAttributes; 24 import android.print.PrintAttributes.Margins; 25 import android.print.PrintAttributes.MediaSize; 26 import android.print.PrintAttributes.Resolution; 27 import android.print.PrintDocumentAdapter; 28 import android.print.PrintDocumentAdapter.LayoutResultCallback; 29 import android.print.PrintDocumentAdapter.WriteResultCallback; 30 import android.print.PrintDocumentInfo; 31 import android.print.PrinterCapabilitiesInfo; 32 import android.print.PrinterId; 33 import android.print.PrinterInfo; 34 import android.print.cts.services.FirstPrintService; 35 import android.print.cts.services.PrintServiceCallbacks; 36 import android.print.cts.services.PrinterDiscoverySessionCallbacks; 37 import android.print.cts.services.SecondPrintService; 38 import android.print.cts.services.StubbablePrinterDiscoverySession; 39 import android.printservice.PrintJob; 40 import android.printservice.PrinterDiscoverySession; 41 42 import org.mockito.InOrder; 43 import org.mockito.exceptions.verification.VerificationInOrderFailure; 44 import org.mockito.invocation.InvocationOnMock; 45 import org.mockito.stubbing.Answer; 46 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.List; 50 51 /** 52 * This test verifies that the system respects the {@link PrinterDiscoverySession} 53 * contract is respected. 54 */ 55 public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest { 56 private static final String FIRST_PRINTER_NAME = "First printer"; 57 private static final String SECOND_PRINTER_NAME = "Second printer"; 58 59 private static final String FIRST_PRINTER_LOCAL_ID= "first_printer"; 60 private static final String SECOND_PRINTER_LOCAL_ID = "second_printer"; 61 testNormalLifecycle()62 public void testNormalLifecycle() throws Exception { 63 if (!supportsPrinting()) { 64 return; 65 } 66 // Create the session callbacks that we will be checking. 67 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 68 createFirstMockPrinterDiscoverySessionCallbacks(); 69 70 // Create the service callbacks for the first print service. 71 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 72 new Answer<PrinterDiscoverySessionCallbacks>() { 73 @Override 74 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 75 return firstSessionCallbacks; 76 } 77 }, 78 new Answer<Void>() { 79 @Override 80 public Void answer(InvocationOnMock invocation) { 81 PrintJob printJob = (PrintJob) invocation.getArguments()[0]; 82 // We pretend the job is handled immediately. 83 printJob.complete(); 84 return null; 85 } 86 }, null); 87 88 // Configure the print services. 89 FirstPrintService.setCallbacks(firstServiceCallbacks); 90 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 91 92 // Create a print adapter that respects the print contract. 93 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(); 94 95 // Start printing. 96 print(adapter); 97 98 // Wait for write of the first page. 99 waitForWriteAdapterCallback(1); 100 101 // Select the first printer. 102 selectPrinter(FIRST_PRINTER_NAME); 103 104 // Wait for layout as the printer has different capabilities. 105 waitForLayoutAdapterCallbackCount(2); 106 107 // Select the second printer (same capabilities as the other 108 // one so no layout should happen). 109 selectPrinter(SECOND_PRINTER_NAME); 110 111 // While the printer discovery session is still alive store the 112 // ids of printers as we want to make some assertions about them 113 // but only the print service can create printer ids which means 114 // that we need to get the created ones. 115 PrinterId firstPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks, 116 FIRST_PRINTER_LOCAL_ID); 117 PrinterId secondPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks, 118 SECOND_PRINTER_LOCAL_ID); 119 assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId); 120 assertNotNull("Coundn't find printer:" + SECOND_PRINTER_LOCAL_ID, secondPrinterId); 121 122 // Click the print button. 123 clickPrintButton(); 124 125 // Answer the dialog for the print service cloud warning 126 answerPrintServicesWarning(true); 127 128 // Wait for all print jobs to be handled after which the session destroyed. 129 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 130 131 // Verify the expected calls. 132 InOrder inOrder = inOrder(firstSessionCallbacks); 133 134 // We start discovery as the print dialog was up. 135 List<PrinterId> emptyPrinterIdList = Collections.emptyList(); 136 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery( 137 emptyPrinterIdList); 138 139 // We selected the first printer and now it should be tracked. 140 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 141 firstPrinterId); 142 143 // We selected the second printer so the first should not be tracked. 144 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 145 firstPrinterId); 146 147 // We selected the second printer and now it should be tracked. 148 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 149 secondPrinterId); 150 151 // The print dialog went away so we first stop the printer tracking... 152 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 153 secondPrinterId); 154 155 // ... next we stop printer discovery... 156 inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery(); 157 158 // ... last the session is destroyed. 159 inOrder.verify(firstSessionCallbacks).onDestroy(); 160 } 161 testCancelPrintServicesAlertDialog()162 public void testCancelPrintServicesAlertDialog() throws Exception { 163 if (!supportsPrinting()) { 164 return; 165 } 166 // Create the session callbacks that we will be checking. 167 final PrinterDiscoverySessionCallbacks firstSessionCallbacks = 168 createFirstMockPrinterDiscoverySessionCallbacks(); 169 170 // Create the service callbacks for the first print service. 171 PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks( 172 new Answer<PrinterDiscoverySessionCallbacks>() { 173 @Override 174 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 175 return firstSessionCallbacks; 176 } 177 }, 178 new Answer<Void>() { 179 @Override 180 public Void answer(InvocationOnMock invocation) { 181 PrintJob printJob = (PrintJob) invocation.getArguments()[0]; 182 // We pretend the job is handled immediately. 183 printJob.complete(); 184 return null; 185 } 186 }, null); 187 188 // Configure the print services. 189 FirstPrintService.setCallbacks(firstServiceCallbacks); 190 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 191 192 // Create a print adapter that respects the print contract. 193 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(); 194 195 // Start printing. 196 print(adapter); 197 198 // Wait for write of the first page. 199 waitForWriteAdapterCallback(1); 200 201 // Select the first printer. 202 selectPrinter(FIRST_PRINTER_NAME); 203 204 // While the printer discovery session is still alive store the 205 // ids of printers as we want to make some assertions about them 206 // but only the print service can create printer ids which means 207 // that we need to get the created ones. 208 PrinterId firstPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks, 209 FIRST_PRINTER_LOCAL_ID); 210 assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId); 211 212 // Click the print button. 213 clickPrintButton(); 214 215 // Cancel the dialog for the print service cloud warning 216 answerPrintServicesWarning(false); 217 218 // Click the print button again. 219 clickPrintButton(); 220 221 // Answer the dialog for the print service cloud warning 222 answerPrintServicesWarning(true); 223 224 // Wait for all print jobs to be handled after which the session destroyed. 225 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 226 227 // Verify the expected calls. 228 InOrder inOrder = inOrder(firstSessionCallbacks); 229 230 // We start discovery as the print dialog was up. 231 List<PrinterId> emptyPrinterIdList = Collections.emptyList(); 232 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery( 233 emptyPrinterIdList); 234 235 // We selected the first printer and now it should be tracked. 236 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 237 firstPrinterId); 238 239 // We selected the second printer so the first should not be tracked. 240 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 241 firstPrinterId); 242 243 // ... next we stop printer discovery... 244 inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery(); 245 246 // ... last the session is destroyed. 247 inOrder.verify(firstSessionCallbacks).onDestroy(); 248 } 249 testStartPrinterDiscoveryWithHistoricalPrinters()250 public void testStartPrinterDiscoveryWithHistoricalPrinters() throws Exception { 251 if (!supportsPrinting()) { 252 return; 253 } 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 new Answer<PrinterDiscoverySessionCallbacks>() { 261 @Override 262 public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) { 263 return firstSessionCallbacks; 264 } 265 }, 266 new Answer<Void>() { 267 @Override 268 public Void answer(InvocationOnMock invocation) { 269 PrintJob printJob = (PrintJob) invocation.getArguments()[0]; 270 // We pretend the job is handled immediately. 271 printJob.complete(); 272 return null; 273 } 274 }, null); 275 276 // Configure the print services. 277 FirstPrintService.setCallbacks(firstServiceCallbacks); 278 SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks()); 279 280 // Create a print adapter that respects the print contract. 281 PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(); 282 283 // Start printing. 284 print(adapter); 285 286 // Wait for write of the first page. 287 waitForWriteAdapterCallback(1); 288 289 // Select the first printer. 290 selectPrinter(FIRST_PRINTER_NAME); 291 292 // Wait for a layout to finish - first layout was for the 293 // PDF printer, second for the first printer in preview mode. 294 waitForLayoutAdapterCallbackCount(2); 295 296 // While the printer discovery session is still alive store the 297 // ids of printer as we want to make some assertions about it 298 // but only the print service can create printer ids which means 299 // that we need to get the created one. 300 PrinterId firstPrinterId = getAddedPrinterIdForLocalId( 301 firstSessionCallbacks, FIRST_PRINTER_LOCAL_ID); 302 303 // Click the print button. 304 clickPrintButton(); 305 306 // Answer the dialog for the print service cloud warning 307 answerPrintServicesWarning(true); 308 309 // Wait for the print to complete. 310 waitForAdapterFinishCallbackCalled(); 311 312 // Now print again as we want to confirm that the start 313 // printer discovery passes in the priority list. 314 print(adapter); 315 316 // Wait for a layout to finish - first layout was for the 317 // PDF printer, second for the first printer in preview mode, 318 // the third for the first printer in non-preview mode, and 319 // now a fourth for the PDF printer as we are printing again. 320 waitForLayoutAdapterCallbackCount(4); 321 322 // Cancel the printing. 323 getUiDevice().pressBack(); // wakes up the device. 324 getUiDevice().pressBack(); 325 326 // Wait for all print jobs to be handled after which the is session destroyed. 327 waitForPrinterDiscoverySessionDestroyCallbackCalled(1); 328 329 // Verify the expected calls. 330 InOrder inOrder = inOrder(firstSessionCallbacks); 331 332 // We start discovery with no printer history. 333 List<PrinterId> priorityList = new ArrayList<PrinterId>(); 334 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery( 335 priorityList); 336 337 // We selected the first printer and now it should be tracked. 338 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 339 firstPrinterId); 340 341 // We confirmed print so the first should not be tracked. 342 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 343 firstPrinterId); 344 345 // This is tricky. It is possible that the print activity was not 346 // destroyed (the platform delays destruction at convenient time as 347 // an optimization) and we get the same instance which means that 348 // the discovery session may not have been destroyed. We try the 349 // case with the activity being destroyed and if this fails the 350 // case with the activity brought to front. 351 priorityList.add(firstPrinterId); 352 try { 353 inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(priorityList); 354 } catch (VerificationInOrderFailure error) { 355 inOrder.verify(firstSessionCallbacks).onValidatePrinters(priorityList); 356 } 357 358 // The system selects the highest ranked historical printer. 359 inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking( 360 firstPrinterId); 361 362 // We canceled print so the first should not be tracked. 363 inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking( 364 firstPrinterId); 365 366 367 // Discovery is always stopped before the session is always destroyed. 368 inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery(); 369 370 // ...last the session is destroyed. 371 inOrder.verify(firstSessionCallbacks).onDestroy(); 372 } 373 getAddedPrinterIdForLocalId( final PrinterDiscoverySessionCallbacks sessionCallbacks, String printerLocalId)374 private PrinterId getAddedPrinterIdForLocalId( 375 final PrinterDiscoverySessionCallbacks sessionCallbacks, String printerLocalId) { 376 final List<PrinterInfo> reportedPrinters = new ArrayList<PrinterInfo>(); 377 getInstrumentation().runOnMainSync(new Runnable() { 378 @Override 379 public void run() { 380 // Grab the printer ids as only the service can create such. 381 StubbablePrinterDiscoverySession session = sessionCallbacks.getSession(); 382 reportedPrinters.addAll(session.getPrinters()); 383 } 384 }); 385 386 final int reportedPrinterCount = reportedPrinters.size(); 387 for (int i = 0; i < reportedPrinterCount; i++) { 388 PrinterInfo reportedPrinter = reportedPrinters.get(i); 389 String localId = reportedPrinter.getId().getLocalId(); 390 if (printerLocalId.equals(localId)) { 391 return reportedPrinter.getId(); 392 } 393 } 394 395 return null; 396 } 397 createSecondMockPrintServiceCallbacks()398 private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() { 399 return createMockPrintServiceCallbacks(null, null, null); 400 } 401 createFirstMockPrinterDiscoverySessionCallbacks()402 private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() { 403 return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() { 404 @Override 405 public Void answer(InvocationOnMock invocation) { 406 // Get the session. 407 StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks) 408 invocation.getMock()).getSession(); 409 410 if (session.getPrinters().isEmpty()) { 411 List<PrinterInfo> printers = new ArrayList<PrinterInfo>(); 412 413 // Add the first printer. 414 PrinterId firstPrinterId = session.getService().generatePrinterId( 415 FIRST_PRINTER_LOCAL_ID); 416 PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId, 417 FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE) 418 .build(); 419 printers.add(firstPrinter); 420 421 // Add the first printer. 422 PrinterId secondPrinterId = session.getService().generatePrinterId( 423 SECOND_PRINTER_LOCAL_ID); 424 PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId, 425 SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE) 426 .build(); 427 printers.add(secondPrinter); 428 429 session.addPrinters(printers); 430 } 431 return null; 432 } 433 }, null, null, new Answer<Void>() { 434 @Override 435 public Void answer(InvocationOnMock invocation) throws Throwable { 436 // Get the session. 437 StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks) 438 invocation.getMock()).getSession(); 439 440 PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0]; 441 List<PrinterInfo> reportedPrinters = session.getPrinters(); 442 443 // We should be tracking a printer that we added. 444 PrinterInfo trackedPrinter = null; 445 final int reportedPrinterCount = reportedPrinters.size(); 446 for (int i = 0; i < reportedPrinterCount; i++) { 447 PrinterInfo reportedPrinter = reportedPrinters.get(i); 448 if (reportedPrinter.getId().equals(trackedPrinterId)) { 449 trackedPrinter = reportedPrinter; 450 break; 451 } 452 } 453 assertNotNull("Can track only added printers", trackedPrinter); 454 455 // If the printer does not have capabilities reported add them. 456 if (trackedPrinter.getCapabilities() == null) { 457 458 // Add the capabilities to emulate lazy discovery. 459 // Same for each printer is fine for what we test. 460 PrinterCapabilitiesInfo capabilities = 461 new PrinterCapabilitiesInfo.Builder(trackedPrinterId) 462 .setMinMargins(new Margins(200, 200, 200, 200)) 463 .addMediaSize(MediaSize.ISO_A4, true) 464 .addMediaSize(MediaSize.ISO_A5, false) 465 .addResolution(new Resolution("300x300", "300x300", 300, 300), true) 466 .setColorModes(PrintAttributes.COLOR_MODE_COLOR, 467 PrintAttributes.COLOR_MODE_COLOR) 468 .build(); 469 PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter) 470 .setCapabilities(capabilities) 471 .build(); 472 473 // Update the printer. 474 List<PrinterInfo> printers = new ArrayList<PrinterInfo>(); 475 printers.add(updatedPrinter); 476 session.addPrinters(printers); 477 } 478 479 return null; 480 } 481 }, null, null, new Answer<Void>() { 482 @Override 483 public Void answer(InvocationOnMock invocation) throws Throwable { 484 // Take a note onDestroy was called. 485 onPrinterDiscoverySessionDestroyCalled(); 486 return null; 487 } 488 }); 489 } 490 491 public PrintDocumentAdapter createMockPrintDocumentAdapter() { 492 final PrintAttributes[] printAttributes = new PrintAttributes[1]; 493 494 return createMockPrintDocumentAdapter( 495 new Answer<Void>() { 496 @Override 497 public Void answer(InvocationOnMock invocation) throws Throwable { 498 printAttributes[0] = (PrintAttributes) invocation.getArguments()[1]; 499 LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3]; 500 PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME) 501 .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) 502 .setPageCount(3) 503 .build(); 504 callback.onLayoutFinished(info, false); 505 // Mark layout was called. 506 onLayoutCalled(); 507 return null; 508 } 509 }, new Answer<Void>() { 510 @Override 511 public Void answer(InvocationOnMock invocation) throws Throwable { 512 Object[] args = invocation.getArguments(); 513 PageRange[] pages = (PageRange[]) args[0]; 514 ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1]; 515 WriteResultCallback callback = (WriteResultCallback) args[3]; 516 writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd()); 517 fd.close(); 518 callback.onWriteFinished(pages); 519 // Mark write was called. 520 onWriteCalled(); 521 return null; 522 } 523 }, new Answer<Void>() { 524 @Override 525 public Void answer(InvocationOnMock invocation) throws Throwable { 526 // Mark finish was called. 527 onFinishCalled(); 528 return null; 529 } 530 }); 531 } 532 } 533