1 /*
2  * Copyright (C) 2015 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.assertException;
20 import static android.print.test.Utils.eventually;
21 import static android.print.test.Utils.getPrintJob;
22 import static android.print.test.Utils.runOnMainThread;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertTrue;
28 
29 import android.app.Activity;
30 import android.app.PendingIntent;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.pm.ApplicationInfo;
35 import android.content.pm.PackageInfo;
36 import android.content.pm.PackageManager;
37 import android.graphics.Bitmap;
38 import android.graphics.BitmapFactory;
39 import android.graphics.Canvas;
40 import android.graphics.drawable.Drawable;
41 import android.graphics.drawable.Icon;
42 import android.print.PrintAttributes;
43 import android.print.PrintAttributes.Margins;
44 import android.print.PrintAttributes.MediaSize;
45 import android.print.PrintAttributes.Resolution;
46 import android.print.PrintDocumentAdapter;
47 import android.print.PrintManager;
48 import android.print.PrinterCapabilitiesInfo;
49 import android.print.PrinterId;
50 import android.print.PrinterInfo;
51 import android.print.test.BasePrintTest;
52 import android.print.test.services.FirstPrintService;
53 import android.print.test.services.InfoActivity;
54 import android.print.test.services.PrintServiceCallbacks;
55 import android.print.test.services.PrinterDiscoverySessionCallbacks;
56 import android.print.test.services.SecondPrintService;
57 import android.print.test.services.StubbablePrintService;
58 import android.print.test.services.StubbablePrinterDiscoverySession;
59 import android.printservice.CustomPrinterIconCallback;
60 import android.printservice.PrintJob;
61 import android.printservice.PrintService;
62 import android.support.test.runner.AndroidJUnit4;
63 import android.support.test.uiautomator.UiDevice;
64 import android.support.test.uiautomator.UiObject;
65 import android.support.test.uiautomator.UiSelector;
66 
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 
70 import java.util.ArrayList;
71 import java.util.List;
72 
73 /**
74  * Test the interface from a print service to the print manager
75  */
76 @RunWith(AndroidJUnit4.class)
77 public class PrintServicesTest extends BasePrintTest {
78     private static final String PRINTER_NAME = "Test printer";
79 
80     /** The print job processed in the test */
81     private static PrintJob sPrintJob;
82 
83     /** Printer under test */
84     private static PrinterInfo sPrinter;
85 
86     /** The custom printer icon to use */
87     private Icon mIcon;
88 
89     /**
90      * Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a single printer with
91      * minimal capabilities.
92      *
93      * @return The mock session callbacks
94      */
createMockPrinterDiscoverySessionCallbacks( String printerName, ArrayList<String> trackedPrinters)95     private PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
96             String printerName, ArrayList<String> trackedPrinters) {
97         return createMockPrinterDiscoverySessionCallbacks(invocation -> {
98             // Get the session.
99             StubbablePrinterDiscoverySession session =
100                     ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
101 
102             if (session.getPrinters().isEmpty()) {
103                 List<PrinterInfo> printers = new ArrayList<>();
104 
105                 // Add the printer.
106                 PrinterId printerId = session.getService()
107                         .generatePrinterId(printerName);
108 
109                 PrinterCapabilitiesInfo capabilities = new PrinterCapabilitiesInfo.Builder(
110                         printerId)
111                         .setMinMargins(new Margins(200, 200, 200, 200))
112                         .addMediaSize(MediaSize.ISO_A4, true)
113                         .addResolution(new Resolution("300x300", "300x300", 300, 300),
114                                 true)
115                         .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
116                                 PrintAttributes.COLOR_MODE_COLOR)
117                         .build();
118 
119                 Intent infoIntent = new Intent(getActivity(), InfoActivity.class);
120                 infoIntent.putExtra("PRINTER_NAME", PRINTER_NAME);
121 
122                 PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
123                         infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
124 
125                 sPrinter = new PrinterInfo.Builder(printerId, printerName,
126                         PrinterInfo.STATUS_IDLE)
127                         .setCapabilities(capabilities)
128                         .setDescription("Minimal capabilities")
129                         .setInfoIntent(infoPendingIntent)
130                         .build();
131                 printers.add(sPrinter);
132 
133                 session.addPrinters(printers);
134             }
135 
136             onPrinterDiscoverySessionCreateCalled();
137 
138             return null;
139         }, null, null, invocation -> {
140             if (trackedPrinters != null) {
141                 synchronized (trackedPrinters) {
142                     trackedPrinters
143                             .add(((PrinterId) invocation.getArguments()[0]).getLocalId());
144                     trackedPrinters.notifyAll();
145                 }
146             }
147             return null;
148         }, invocation -> {
149             CustomPrinterIconCallback callback = (CustomPrinterIconCallback) invocation
150                     .getArguments()[2];
151 
152             if (mIcon != null) {
153                 callback.onCustomPrinterIconLoaded(mIcon);
154             }
155             return null;
156         }, invocation -> {
157             if (trackedPrinters != null) {
158                 synchronized (trackedPrinters) {
159                     trackedPrinters.remove(((PrinterId) invocation.getArguments()[0]).getLocalId());
160                     trackedPrinters.notifyAll();
161                 }
162             }
163 
164             return null;
165         }, invocation -> {
166             // Take a note onDestroy was called.
167             onPrinterDiscoverySessionDestroyCalled();
168             return null;
169         });
170     }
171 
172     /**
173      * Get the current progress of #sPrintJob
174      *
175      * @return The current progress
176      *
177      * @throws InterruptedException If the thread was interrupted while setting the progress
178      * @throws Throwable            If anything is unexpected.
179      */
getProgress()180     private float getProgress() throws Throwable {
181         float[] printProgress = new float[1];
182         runOnMainThread(() -> printProgress[0] = sPrintJob.getInfo().getProgress());
183 
184         return printProgress[0];
185     }
186 
187     /**
188      * Get the current status of #sPrintJob
189      *
190      * @return The current status
191      *
192      * @throws InterruptedException If the thread was interrupted while getting the status
193      * @throws Throwable            If anything is unexpected.
194      */
getStatus()195     private CharSequence getStatus() throws Throwable {
196         CharSequence[] printStatus = new CharSequence[1];
197         runOnMainThread(() -> printStatus[0] = sPrintJob.getInfo().getStatus(getActivity()
198                 .getPackageManager()));
199 
200         return printStatus[0];
201     }
202 
203     /**
204      * Check if a print progress is correct.
205      *
206      * @param desiredProgress The expected @{link PrintProgresses}
207      *
208      * @throws Throwable If anything goes wrong or this takes more than 5 seconds
209      */
checkNotification(float desiredProgress, CharSequence desiredStatus)210     private void checkNotification(float desiredProgress, CharSequence desiredStatus)
211             throws Throwable {
212         eventually(() -> assertEquals(desiredProgress, getProgress(), 0.1));
213         eventually(() -> assertEquals(desiredStatus.toString(), getStatus().toString()));
214     }
215 
216     /**
217      * Set a new progress and status for #sPrintJob
218      *
219      * @param progress The new progress to set
220      * @param status   The new status to set
221      *
222      * @throws InterruptedException If the thread was interrupted while setting
223      * @throws Throwable            If anything is unexpected.
224      */
setProgressAndStatus(final float progress, final CharSequence status)225     private void setProgressAndStatus(final float progress, final CharSequence status)
226             throws Throwable {
227         runOnMainThread(() -> {
228             sPrintJob.setProgress(progress);
229             sPrintJob.setStatus(status);
230         });
231     }
232 
233     /**
234      * Progress print job and check the print job state.
235      *
236      * @param progress How much to progress
237      * @param status   The status to set
238      *
239      * @throws Throwable If anything goes wrong.
240      */
progress(float progress, CharSequence status)241     private void progress(float progress, CharSequence status) throws Throwable {
242         setProgressAndStatus(progress, status);
243 
244         // Check that progress of job is correct
245         checkNotification(progress, status);
246     }
247 
248     /**
249      * Create mock service callback for a session.
250      *
251      * @param sessionCallbacks The callbacks of the sessopm
252      */
createMockPrinterServiceCallbacks( final PrinterDiscoverySessionCallbacks sessionCallbacks)253     private PrintServiceCallbacks createMockPrinterServiceCallbacks(
254             final PrinterDiscoverySessionCallbacks sessionCallbacks) {
255         return createMockPrintServiceCallbacks(
256                 invocation -> sessionCallbacks,
257                 invocation -> {
258                     sPrintJob = (PrintJob) invocation.getArguments()[0];
259                     sPrintJob.start();
260                     onPrintJobQueuedCalled();
261 
262                     return null;
263                 }, invocation -> {
264                     sPrintJob = (PrintJob) invocation.getArguments()[0];
265                     sPrintJob.cancel();
266 
267                     return null;
268                 });
269     }
270 
271     /**
272      * Test that the progress and status is propagated correctly.
273      *
274      * @throws Throwable If anything is unexpected.
275      */
276     @Test
277     public void progress() throws Throwable {
278         // Create the session callbacks that we will be checking.
279         PrinterDiscoverySessionCallbacks sessionCallbacks
280                 = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
281 
282         // Create the service callbacks for the first print service.
283         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
284                 sessionCallbacks);
285 
286         // Configure the print services.
287         FirstPrintService.setCallbacks(serviceCallbacks);
288 
289         // We don't use the second service, but we have to still configure it
290         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
291 
292         // Create a print adapter that respects the print contract.
293         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
294 
295         // Start printing.
296         print(adapter);
297 
298         // Wait for write of the first page.
299         waitForWriteAdapterCallback(1);
300 
301         // Select the printer.
302         selectPrinter(PRINTER_NAME);
303 
304         // Click the print button.
305         clickPrintButton();
306 
307         // Answer the dialog for the print service cloud warning
308         answerPrintServicesWarning(true);
309 
310         // Wait until the print job is queued and #sPrintJob is set
311         waitForServiceOnPrintJobQueuedCallbackCalled(1);
312 
313         // Progress print job and check for appropriate notifications
314         progress(0, "printed 0");
315         progress(0.5f, "printed 50");
316         progress(1, "printed 100");
317 
318         // Call complete from the main thread
319         runOnMainThread(sPrintJob::complete);
320 
321         // Wait for all print jobs to be handled after which the session destroyed.
322         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
323     }
324 
325     /**
326      * Render a {@link Drawable} into a {@link Bitmap}.
327      *
328      * @param d the drawable to be rendered
329      *
330      * @return the rendered bitmap
331      */
332     private static Bitmap renderDrawable(Drawable d) {
333         Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
334                 Bitmap.Config.ARGB_8888);
335 
336         Canvas canvas = new Canvas(bitmap);
337         d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
338         d.draw(canvas);
339 
340         return bitmap;
341     }
342 
343     /**
344      * Update the printer
345      *
346      * @param sessionCallbacks The callbacks for the service the printer belongs to
347      * @param printer the new printer to use
348      *
349      * @throws InterruptedException If we were interrupted while the printer was updated.
350      * @throws Throwable            If anything is unexpected.
351      */
352     private void updatePrinter(PrinterDiscoverySessionCallbacks sessionCallbacks,
353             final PrinterInfo printer) throws Throwable {
354         runOnMainThread(() -> {
355             ArrayList<PrinterInfo> printers = new ArrayList<>(1);
356             printers.add(printer);
357             sessionCallbacks.getSession().addPrinters(printers);
358         });
359 
360         // Update local copy of printer
361         sPrinter = printer;
362     }
363 
364     /**
365      * Assert is the printer icon does not match the bitmap. As the icon update might take some time
366      * we try up to 5 seconds.
367      *
368      * @param bitmap The bitmap to match
369      *
370      * @throws Throwable If anything is unexpected.
371      */
372     private void assertThatIconIs(Bitmap bitmap) throws Throwable {
373         eventually(
374                 () -> assertTrue(bitmap.sameAs(renderDrawable(sPrinter.loadIcon(getActivity())))));
375     }
376 
377     /**
378      * Test that the icon get be updated.
379      *
380      * @throws Throwable If anything is unexpected.
381      */
382     @Test
383     public void updateIcon() throws Throwable {
384         // Create the session callbacks that we will be checking.
385         final PrinterDiscoverySessionCallbacks sessionCallbacks
386                 = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
387 
388         // Create the service callbacks for the first print service.
389         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
390                 sessionCallbacks);
391 
392         // Configure the print services.
393         FirstPrintService.setCallbacks(serviceCallbacks);
394 
395         // We don't use the second service, but we have to still configure it
396         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
397 
398         // Create a print adapter that respects the print contract.
399         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
400 
401         // Start printing.
402         print(adapter);
403 
404         // Open printer selection dropdown list to display icon on screen
405         UiObject destinationSpinner = UiDevice.getInstance(getInstrumentation())
406                 .findObject(new UiSelector().resourceId(
407                         "com.android.printspooler:id/destination_spinner"));
408         destinationSpinner.click();
409 
410         // Get the print service's icon
411         PackageManager packageManager = getActivity().getPackageManager();
412         PackageInfo packageInfo = packageManager.getPackageInfo(
413                 new ComponentName(getActivity(), FirstPrintService.class).getPackageName(), 0);
414         ApplicationInfo appInfo = packageInfo.applicationInfo;
415         Drawable printServiceIcon = appInfo.loadIcon(packageManager);
416 
417         assertThatIconIs(renderDrawable(printServiceIcon));
418 
419         // Update icon to resource
420         updatePrinter(sessionCallbacks,
421                 (new PrinterInfo.Builder(sPrinter)).setIconResourceId(R.drawable.red_printer)
422                 .build());
423 
424         assertThatIconIs(renderDrawable(getActivity().getDrawable(R.drawable.red_printer)));
425 
426         // Update icon to bitmap
427         Bitmap bm = BitmapFactory.decodeResource(getActivity().getResources(),
428                 R.raw.yellow);
429         // Icon will be picked up from the discovery session once setHasCustomPrinterIcon is set
430         mIcon = Icon.createWithBitmap(bm);
431         updatePrinter(sessionCallbacks,
432                 (new PrinterInfo.Builder(sPrinter)).setHasCustomPrinterIcon(true).build());
433 
434         assertThatIconIs(renderDrawable(mIcon.loadDrawable(getActivity())));
435 
436         getUiDevice().pressBack();
437         getUiDevice().pressBack();
438         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
439     }
440 
441     /**
442      * Test that we cannot call attachBaseContext
443      *
444      * @throws Throwable If anything is unexpected.
445      */
446     @Test
447     public void cannotUseAttachBaseContext() throws Throwable {
448         // Create the session callbacks that we will be checking.
449         final PrinterDiscoverySessionCallbacks sessionCallbacks
450                 = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
451 
452         // Create the service callbacks for the first print service.
453         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
454                 sessionCallbacks);
455 
456         // Configure the print services.
457         FirstPrintService.setCallbacks(serviceCallbacks);
458 
459         // Create a print adapter that respects the print contract.
460         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
461 
462         // We don't use the second service, but we have to still configure it
463         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
464 
465         // Start printing to set serviceCallbacks.getService()
466         print(adapter);
467         eventually(() -> assertNotNull(serviceCallbacks.getService()));
468 
469         // attachBaseContext should always throw an exception no matter what input value
470         assertException(() -> serviceCallbacks.getService().callAttachBaseContext(null),
471                 IllegalStateException.class);
472         assertException(() -> serviceCallbacks.getService().callAttachBaseContext(getActivity()),
473                 IllegalStateException.class);
474 
475         getUiDevice().pressBack();
476         getUiDevice().pressBack();
477         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
478     }
479 
480     /**
481      * Test that the active print jobs can be read
482      *
483      * @throws Throwable If anything is unexpected.
484      */
485     @Test
486     public void getActivePrintJobs() throws Throwable {
487         clearPrintSpoolerData();
488 
489         try {
490             PrintManager pm = (PrintManager) getActivity().getSystemService(Context.PRINT_SERVICE);
491 
492             // Configure first print service
493             PrinterDiscoverySessionCallbacks sessionCallbacks1
494                     = createMockPrinterDiscoverySessionCallbacks("Printer1", null);
495             PrintServiceCallbacks serviceCallbacks1 = createMockPrinterServiceCallbacks(
496                     sessionCallbacks1);
497             FirstPrintService.setCallbacks(serviceCallbacks1);
498 
499             // Configure second print service
500             PrinterDiscoverySessionCallbacks sessionCallbacks2
501                     = createMockPrinterDiscoverySessionCallbacks("Printer2", null);
502             PrintServiceCallbacks serviceCallbacks2 = createMockPrinterServiceCallbacks(
503                     sessionCallbacks2);
504             SecondPrintService.setCallbacks(serviceCallbacks2);
505 
506             // Create a print adapter that respects the print contract.
507             PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
508 
509             runOnMainThread(() -> pm.print("job1", adapter, null));
510 
511             // Init services
512             waitForPrinterDiscoverySessionCreateCallbackCalled();
513             StubbablePrintService firstService = serviceCallbacks1.getService();
514 
515             waitForWriteAdapterCallback(1);
516             selectPrinter("Printer1");
517 
518             // Job is not yet confirmed, hence it is not yet "active"
519             runOnMainThread(() -> assertEquals(0, firstService.callGetActivePrintJobs().size()));
520 
521             clickPrintButton();
522             answerPrintServicesWarning(true);
523             onPrintJobQueuedCalled();
524 
525             eventually(() -> runOnMainThread(
526                     () -> assertEquals(1, firstService.callGetActivePrintJobs().size())));
527 
528             // Add another print job to first service
529             resetCounters();
530             runOnMainThread(() -> pm.print("job2", adapter, null));
531             waitForWriteAdapterCallback(1);
532             clickPrintButton();
533             onPrintJobQueuedCalled();
534 
535             eventually(() -> runOnMainThread(
536                     () -> assertEquals(2, firstService.callGetActivePrintJobs().size())));
537 
538             // Create print job in second service
539             resetCounters();
540             runOnMainThread(() -> pm.print("job3", adapter, null));
541 
542             waitForPrinterDiscoverySessionCreateCallbackCalled();
543 
544             StubbablePrintService secondService = serviceCallbacks2.getService();
545             runOnMainThread(() -> assertEquals(0, secondService.callGetActivePrintJobs().size()));
546 
547             waitForWriteAdapterCallback(1);
548             selectPrinter("Printer2");
549             clickPrintButton();
550             answerPrintServicesWarning(true);
551             onPrintJobQueuedCalled();
552 
553             eventually(() -> runOnMainThread(
554                     () -> assertEquals(1, secondService.callGetActivePrintJobs().size())));
555             runOnMainThread(() -> assertEquals(2, firstService.callGetActivePrintJobs().size()));
556 
557             // Block last print job. Blocked jobs are still considered active
558             runOnMainThread(() -> sPrintJob.block(null));
559             eventually(() -> runOnMainThread(() -> assertTrue(sPrintJob.isBlocked())));
560             runOnMainThread(() -> assertEquals(1, secondService.callGetActivePrintJobs().size()));
561 
562             // Fail last print job. Failed job are not active
563             runOnMainThread(() -> sPrintJob.fail(null));
564             eventually(() -> runOnMainThread(() -> assertTrue(sPrintJob.isFailed())));
565             runOnMainThread(() -> assertEquals(0, secondService.callGetActivePrintJobs().size()));
566 
567             // Cancel job. Canceled jobs are not active
568             runOnMainThread(() -> assertEquals(2, firstService.callGetActivePrintJobs().size()));
569             android.print.PrintJob job2 = getPrintJob(pm, "job2");
570             runOnMainThread(job2::cancel);
571             eventually(() -> runOnMainThread(() -> assertTrue(job2.isCancelled())));
572             runOnMainThread(() -> assertEquals(1, firstService.callGetActivePrintJobs().size()));
573 
574             waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
575         } finally {
576             clearPrintSpoolerData();
577         }
578     }
579 
580     /**
581      * Test that the icon get be updated.
582      *
583      * @throws Throwable If anything is unexpected.
584      */
585     @Test
586     public void selectViaInfoIntent() throws Throwable {
587         ArrayList<String> trackedPrinters = new ArrayList<>();
588 
589         // Create the session callbacks that we will be checking.
590         final PrinterDiscoverySessionCallbacks sessionCallbacks
591                 = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, trackedPrinters);
592 
593         // Create the service callbacks for the first print service.
594         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
595                 sessionCallbacks);
596 
597         // Configure the print services.
598         FirstPrintService.setCallbacks(serviceCallbacks);
599 
600         // We don't use the second service, but we have to still configure it
601         SecondPrintService.setCallbacks(createMockPrintServiceCallbacks(null, null, null));
602 
603         // Create a print adapter that respects the print contract.
604         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
605 
606         // Start printing.
607         print(adapter);
608 
609         // Enter select printer activity
610         selectPrinter("All printers…");
611 
612         assertFalse(trackedPrinters.contains(PRINTER_NAME));
613 
614         InfoActivity.addObserver(activity -> {
615             Intent intent = activity.getIntent();
616 
617             assertEquals(PRINTER_NAME, intent.getStringExtra("PRINTER_NAME"));
618             assertTrue(intent.getBooleanExtra(PrintService.EXTRA_CAN_SELECT_PRINTER,
619                             false));
620 
621             activity.setResult(Activity.RESULT_OK,
622                     (new Intent()).putExtra(PrintService.EXTRA_SELECT_PRINTER, true));
623             activity.finish();
624         });
625 
626         // Open info activity which executed the code above
627         UiObject moreInfoButton = getUiDevice().findObject(new UiSelector().resourceId(
628                 "com.android.printspooler:id/more_info"));
629         moreInfoButton.click();
630 
631         // Wait until printer is selected and thereby tracked
632         eventually(() -> assertTrue(trackedPrinters.contains(PRINTER_NAME)));
633 
634         InfoActivity.clearObservers();
635 
636         getUiDevice().pressBack();
637         getUiDevice().pressBack();
638         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
639     }
640 }
641