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