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.test;
18 
19 import static android.print.test.BasePrintTest.getInstrumentation;
20 
21 import android.content.Context;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.ParcelFileDescriptor;
25 import android.print.PrintJob;
26 import android.print.PrintManager;
27 import android.service.print.nano.PrintServiceDumpProto;
28 import android.util.Log;
29 
30 import androidx.annotation.NonNull;
31 
32 import java.io.ByteArrayOutputStream;
33 import java.io.FileInputStream;
34 import java.util.concurrent.TimeUnit;
35 
36 /**
37  * Utilities for print tests
38  */
39 public class Utils {
40     private static final String LOG_TAG = "Utils";
41 
42     /**
43      * A {@link Runnable} that can throw an {@link Throwable}.
44      */
45     public interface Invokable {
run()46         void run() throws Throwable;
47     }
48 
49     /**
50      * Run a {@link Invokable} and expect and {@link Throwable} of a certain type.
51      *
52      * @param r             The {@link Invokable} to run
53      * @param expectedClass The expected {@link Throwable} type
54      */
assertException(@onNull Invokable r, @NonNull Class<? extends Throwable> expectedClass)55     public static void assertException(@NonNull Invokable r,
56             @NonNull Class<? extends Throwable> expectedClass) throws Throwable {
57         try {
58             r.run();
59         } catch (Throwable e) {
60             if (e.getClass().isAssignableFrom(expectedClass)) {
61                 return;
62             } else {
63                 Log.e(LOG_TAG, "Expected: " + expectedClass.getName() + ", got: "
64                         + e.getClass().getName());
65                 throw e;
66             }
67         }
68 
69         throw new AssertionError("No throwable thrown");
70     }
71 
72     /**
73      * Run a {@link Invokable} on the main thread and forward the {@link Throwable} if one was
74      * thrown.
75      *
76      * @param r The {@link Invokable} to run
77      *
78      * @throws Throwable If the {@link Runnable} caused an issue
79      */
runOnMainThread(@onNull final Invokable r)80     public static void runOnMainThread(@NonNull final Invokable r) throws Throwable {
81         final Object synchronizer = new Object();
82         final Throwable[] thrown = new Throwable[1];
83 
84         synchronized (synchronizer) {
85             (new Handler(Looper.getMainLooper())).post(() -> {
86                 synchronized (synchronizer) {
87                     try {
88                         r.run();
89                     } catch (Throwable t) {
90                         thrown[0] = t;
91                     }
92 
93                     synchronizer.notify();
94                 }
95             });
96 
97             synchronizer.wait();
98         }
99 
100         if (thrown[0] != null) {
101             throw thrown[0];
102         }
103     }
104 
105     /**
106      * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
107      *
108      * @param r The {@link Invokable} to run.
109      * @param timeout the maximum time to wait
110      */
eventually(@onNull Invokable r, long timeout)111     public static void eventually(@NonNull Invokable r, long timeout) throws Throwable {
112         long start = System.nanoTime();
113 
114         while (true) {
115             try {
116                 r.run();
117                 break;
118             } catch (Throwable e) {
119                 if (System.nanoTime() - start < TimeUnit.NANOSECONDS.convert(timeout,
120                         TimeUnit.MILLISECONDS)) {
121                     Log.e(LOG_TAG, "Ignoring exception", e);
122 
123                     try {
124                         Thread.sleep(500);
125                     } catch (InterruptedException e1) {
126                         Log.e(LOG_TAG, "Interrupted", e);
127                     }
128                 } else {
129                     throw e;
130                 }
131             }
132         }
133     }
134 
135     /**
136      * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
137      *
138      * @param r The {@link Invokable} to run.
139      */
eventually(@onNull Invokable r)140     public static void eventually(@NonNull Invokable r) throws Throwable {
141         eventually(r, BasePrintTest.OPERATION_TIMEOUT_MILLIS);
142     }
143 
144     /**
145      * @param name Name of print job
146      *
147      * @return The print job for the name
148      *
149      * @throws Exception If print job could not be found
150      */
getPrintJob(@onNull PrintManager pm, @NonNull String name)151     public static @NonNull PrintJob getPrintJob(@NonNull PrintManager pm, @NonNull String name)
152             throws Exception {
153         for (android.print.PrintJob job : pm.getPrintJobs()) {
154             if (job.getInfo().getLabel().equals(name)) {
155                 return job;
156             }
157         }
158 
159         throw new Exception("Print job " + name + " not found in " + pm.getPrintJobs());
160     }
161 
162     /**
163      * @return The print manager
164      */
getPrintManager(@onNull Context context)165     public static @NonNull PrintManager getPrintManager(@NonNull Context context) {
166         return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
167     }
168 
169     /**
170      * Get the {@link PrintServiceDumpProto}
171      */
getProtoDump()172     public static PrintServiceDumpProto getProtoDump() throws Exception {
173         ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
174                 .executeShellCommand("dumpsys print --proto");
175 
176         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
177             try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
178                 byte[] buffer = new byte[16384];
179 
180                 while (true) {
181                     int numRead = is.read(buffer);
182 
183                     if (numRead == -1) {
184                         break;
185                     } else {
186                         os.write(buffer, 0, numRead);
187                     }
188                 }
189             }
190 
191             return PrintServiceDumpProto.parseFrom(os.toByteArray());
192         }
193     }
194 }
195