1 /* 2 * Copyright (C) 2008 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 com.android.cts; 18 19 import com.android.ddmlib.RawImage; 20 21 import java.awt.image.BufferedImage; 22 import java.awt.image.DataBuffer; 23 import java.awt.image.DataBufferUShort; 24 import java.awt.image.Raster; 25 import java.awt.image.SampleModel; 26 import java.awt.image.SinglePixelPackedSampleModel; 27 import java.io.BufferedInputStream; 28 import java.io.BufferedOutputStream; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileOutputStream; 32 import java.io.FilenameFilter; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.text.ParseException; 36 import java.text.SimpleDateFormat; 37 import java.util.Calendar; 38 import java.util.Date; 39 import java.util.Formatter; 40 import java.util.Locale; 41 import java.util.zip.ZipEntry; 42 import java.util.zip.ZipOutputStream; 43 44 /** 45 * Utilities for CTS host. 46 * 47 */ 48 public class HostUtils { 49 50 private static SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", 51 Locale.ENGLISH); 52 53 /** 54 * Check if the given file exists 55 * 56 * @param name the file name to be checked 57 * @return if the file exists, return true; 58 * else, return false 59 */ 60 public static boolean isFileExist(final String name) { 61 return new File(name).exists(); 62 } 63 64 /** 65 * Convert a 16bpp RawImage into a BufferedImage. 66 * 67 * @param rawImage the image to convert. 68 * @return the BufferedImage. 69 */ 70 public static BufferedImage convertRawImageToBufferedImage(RawImage rawImage) { 71 assert rawImage.bpp == 16; 72 73 BufferedImage im = new BufferedImage(rawImage.width, 74 rawImage.height, BufferedImage.TYPE_USHORT_565_RGB); 75 SampleModel sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT, 76 rawImage.width, 77 rawImage.height, 78 // RGB565 79 new int[] { 0xf800, 0x07e0, 0x001f }); 80 81 // It would be more efficient to just subclass DataBuffer and provide a 82 // TYPE_USHORT interface to the byte array. But Raster.createRaster at 83 // some point uses instanceof(DataBufferUShort) to verify that the DataBuffer 84 // is of the right type (instead of just checking DataBuffer.getDataType). 85 // And since DataBufferUShort is final, it can't be subclassed to get around 86 // the check either. So copy the data into a short[] instead to work around the problem. 87 short shortData[] = new short[rawImage.size / 2]; 88 for (int x = 0; x < shortData.length; x++) { 89 int rawImageOffset = x * 2; 90 int a = 0xff & rawImage.data[rawImageOffset]; 91 int b = 0xff & rawImage.data[rawImageOffset + 1]; 92 shortData[x] = (short)((b << 8) | a); 93 } 94 DataBuffer db = new DataBufferUShort(shortData, shortData.length); 95 Raster raster = Raster.createRaster(sampleModel, 96 db, null); 97 im.setData(raster); 98 return im; 99 } 100 101 /** 102 * Interface used with visitAllFilesUnder 103 */ 104 public interface FileVisitor { 105 /** 106 * Gets called on every file visited. 107 * @param f the File for the file being visited. 108 */ 109 void visitFile(File f); 110 } 111 112 /** 113 * Recursively visit all files under a given path. 114 * 115 * @param root the path to start at. 116 * @param filter the file filter to match. null means to visit all files. 117 * @param visitor the visitor to visit with. 118 */ 119 public static void visitAllFilesUnder(File root, FilenameFilter filter, FileVisitor visitor) { 120 File[] files = root.listFiles(filter); 121 // A null file may indicate not having enough permissions to view that directory 122 if (files != null) { 123 for (File f : files) { 124 visitor.visitFile(f); 125 126 if (f.isDirectory()) { 127 visitAllFilesUnder(f, filter, visitor); 128 } 129 } 130 } 131 } 132 133 /** 134 * Recursively visit all files under a given path. 135 * 136 * @param path the path to start at. 137 * @param filter the file filter to match. null means to visit all files. 138 * @param visitor the visitor to visit with. 139 */ 140 public static void visitAllFilesUnder(String path, FilenameFilter filter, FileVisitor visitor) { 141 visitAllFilesUnder(new File(path), filter, visitor); 142 } 143 144 // Private class to help zipUpDirectory 145 private static class ZipFileVisitor implements FileVisitor { 146 private final ZipOutputStream zipOutputStream; 147 private boolean ok = true; 148 private IOException caughtException; 149 private final ZipFilenameTransformer transformer; 150 151 public ZipFileVisitor(ZipOutputStream zipOutputStream, 152 ZipFilenameTransformer transformer) { 153 this.zipOutputStream = zipOutputStream; 154 this.transformer = transformer; 155 } 156 157 public void visitFile(File f) { 158 String path = f.getPath(); 159 if (transformer != null) { 160 path = transformer.transform(path); 161 } 162 ZipEntry ze = new ZipEntry(path); 163 try { 164 zipOutputStream.putNextEntry(ze); 165 InputStream is = new BufferedInputStream(new FileInputStream(f)); 166 byte[] buffer = new byte[4096]; 167 int bytesRead = is.read(buffer); 168 while (bytesRead > 0) { 169 zipOutputStream.write(buffer, 0, bytesRead); 170 bytesRead = is.read(buffer); 171 } 172 zipOutputStream.closeEntry(); 173 } catch (IOException e) { 174 ok = false; 175 caughtException = e; 176 } 177 } 178 179 /** 180 * Indicates that the visitor ran without errors 181 * @return true if everything ran OK. 182 */ 183 boolean isOk() { 184 return ok; 185 } 186 187 /** 188 * If an IOException was thrown while zipping, it gets kept here. 189 * 190 * @return the IOException that was caught, or null if none was. 191 */ 192 IOException getCaughtException() { 193 return caughtException; 194 } 195 196 } 197 198 /** 199 * Indicates some issue with zipping up the file. 200 */ 201 static class ZipFileException extends Exception { 202 ZipFileException(IOException ioException) { 203 super("Caught wrapped exception", ioException); 204 } 205 } 206 207 /** 208 * Interface provided to rename files before they get zipped. 209 */ 210 public interface ZipFilenameTransformer { 211 /** 212 * Transform a local filesystem filename into a zipfile filename. 213 * 214 * @param filename the input filename 215 * @return the filename to be saved to the zipfile as. 216 */ 217 String transform(String filename); 218 } 219 220 /** 221 * Recursively zip up a directory into a zip file. 222 * 223 * @param sourceDir the directory to zip up 224 * @param outputFilePath the zipfile to create. 225 * @param transformer filepath transformer. can be null. 226 * @throws IOException if there were issues writing the zipfile. 227 */ 228 public static void zipUpDirectory(String sourceDir, 229 String outputFilePath, 230 ZipFilenameTransformer transformer) 231 throws IOException, ZipFileException { 232 // I <3 abstractions 233 FileOutputStream fileOut = new FileOutputStream(outputFilePath); 234 BufferedOutputStream bufOut = new BufferedOutputStream(fileOut); 235 final ZipOutputStream zipOutputStream = new ZipOutputStream(bufOut); 236 237 ZipFileVisitor zfv = new ZipFileVisitor(zipOutputStream, transformer); 238 visitAllFilesUnder(sourceDir, null, zfv); 239 zipOutputStream.close(); 240 if (!zfv.isOk()) { 241 throw new ZipFileException(zfv.getCaughtException()); 242 } 243 } 244 245 /** 246 * Get the formatted time string. 247 * 248 * @param milliSec The time in milliseconds. 249 * @param separator The separator between the date information and time information. 250 * @param dateSeparator The date separator separating the date information nibbles. 251 * @param timeSeparator The time separator separating the time information nibbles. 252 * @return The formated time string. 253 */ 254 public static String getFormattedTimeString(long milliSec, String separator, 255 String dateSeparator, String timeSeparator) { 256 Calendar cal = Calendar.getInstance(); 257 cal.setTimeInMillis(milliSec); 258 int year = cal.get(Calendar.YEAR); 259 int month = cal.get(Calendar.MONTH) + 1; 260 int date = cal.get(Calendar.DATE); 261 int hour = cal.get(Calendar.HOUR_OF_DAY); 262 int min = cal.get(Calendar.MINUTE); 263 int sec = cal.get(Calendar.SECOND); 264 265 Formatter fmt = new Formatter(); 266 if ((separator == null) || (separator.length() == 0)) { 267 separator = "_"; 268 } 269 270 if ((dateSeparator == null) || (dateSeparator.length() == 0)) { 271 dateSeparator = "."; 272 } 273 274 if ((timeSeparator == null) || (timeSeparator.length() == 0)) { 275 timeSeparator = "."; 276 } 277 278 final String formatStr = "%4d" + dateSeparator + "%02d" + dateSeparator + "%02d" 279 + separator + "%02d" + timeSeparator + "%02d" + timeSeparator + "%02d"; 280 fmt.format(formatStr, year, month, date, hour, min, sec); 281 282 return fmt.toString(); 283 } 284 285 /** 286 * Convert the given byte array into a lowercase hex string. 287 * 288 * @param arr The array to convert. 289 * @return The hex encoded string. 290 */ 291 public static String toHexString(byte[] arr) { 292 StringBuffer buf = new StringBuffer(arr.length * 2); 293 for (byte b : arr) { 294 buf.append(String.format("%02x", b & 0xFF)); 295 } 296 return buf.toString(); 297 } 298 299 /** 300 * Strip control characters from the given string. 301 */ 302 public static String replaceControlChars(String s) { 303 // Replace any character < 0x20, except for tab, lf and cr 304 return s.replaceAll("[\\x00-\\x1f&&[^\t\n\r]]", "?"); 305 } 306 307 public static Date dateFromString(String s) throws ParseException { 308 return dateFormat.parse(s); 309 } 310 311 public static String dateToString(Date d) { 312 return dateFormat.format(d); 313 } 314 } 315