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 = null; 166 try { 167 is = new BufferedInputStream(new FileInputStream(f)); 168 byte[] buffer = new byte[4096]; 169 int bytesRead = is.read(buffer); 170 while (bytesRead > 0) { 171 zipOutputStream.write(buffer, 0, bytesRead); 172 bytesRead = is.read(buffer); 173 } 174 zipOutputStream.closeEntry(); 175 } finally { 176 if (is != null) { 177 is.close(); 178 } 179 } 180 } catch (IOException e) { 181 ok = false; 182 caughtException = e; 183 } 184 } 185 186 /** 187 * Indicates that the visitor ran without errors 188 * @return true if everything ran OK. 189 */ 190 boolean isOk() { 191 return ok; 192 } 193 194 /** 195 * If an IOException was thrown while zipping, it gets kept here. 196 * 197 * @return the IOException that was caught, or null if none was. 198 */ 199 IOException getCaughtException() { 200 return caughtException; 201 } 202 203 } 204 205 /** 206 * Indicates some issue with zipping up the file. 207 */ 208 static class ZipFileException extends Exception { 209 ZipFileException(IOException ioException) { 210 super("Caught wrapped exception", ioException); 211 } 212 } 213 214 /** 215 * Interface provided to rename files before they get zipped. 216 */ 217 public interface ZipFilenameTransformer { 218 /** 219 * Transform a local filesystem filename into a zipfile filename. 220 * 221 * @param filename the input filename 222 * @return the filename to be saved to the zipfile as. 223 */ 224 String transform(String filename); 225 } 226 227 /** 228 * Recursively zip up a directory into a zip file. 229 * 230 * @param sourceDir the directory to zip up 231 * @param outputFilePath the zipfile to create. 232 * @param transformer filepath transformer. can be null. 233 * @throws IOException if there were issues writing the zipfile. 234 */ 235 public static void zipUpDirectory(String sourceDir, 236 String outputFilePath, 237 ZipFilenameTransformer transformer) 238 throws IOException, ZipFileException { 239 // I <3 abstractions 240 FileOutputStream fileOut = new FileOutputStream(outputFilePath); 241 BufferedOutputStream bufOut = new BufferedOutputStream(fileOut); 242 final ZipOutputStream zipOutputStream = new ZipOutputStream(bufOut); 243 244 ZipFileVisitor zfv = new ZipFileVisitor(zipOutputStream, transformer); 245 visitAllFilesUnder(sourceDir, null, zfv); 246 zipOutputStream.close(); 247 if (!zfv.isOk()) { 248 throw new ZipFileException(zfv.getCaughtException()); 249 } 250 } 251 252 /** 253 * Get the formatted time string. 254 * 255 * @param milliSec The time in milliseconds. 256 * @param separator The separator between the date information and time information. 257 * @param dateSeparator The date separator separating the date information nibbles. 258 * @param timeSeparator The time separator separating the time information nibbles. 259 * @return The formated time string. 260 */ 261 public static String getFormattedTimeString(long milliSec, String separator, 262 String dateSeparator, String timeSeparator) { 263 Calendar cal = Calendar.getInstance(); 264 cal.setTimeInMillis(milliSec); 265 int year = cal.get(Calendar.YEAR); 266 int month = cal.get(Calendar.MONTH) + 1; 267 int date = cal.get(Calendar.DATE); 268 int hour = cal.get(Calendar.HOUR_OF_DAY); 269 int min = cal.get(Calendar.MINUTE); 270 int sec = cal.get(Calendar.SECOND); 271 272 Formatter fmt = new Formatter(); 273 if ((separator == null) || (separator.length() == 0)) { 274 separator = "_"; 275 } 276 277 if ((dateSeparator == null) || (dateSeparator.length() == 0)) { 278 dateSeparator = "."; 279 } 280 281 if ((timeSeparator == null) || (timeSeparator.length() == 0)) { 282 timeSeparator = "."; 283 } 284 285 final String formatStr = "%4d" + dateSeparator + "%02d" + dateSeparator + "%02d" 286 + separator + "%02d" + timeSeparator + "%02d" + timeSeparator + "%02d"; 287 fmt.format(formatStr, year, month, date, hour, min, sec); 288 289 return fmt.toString(); 290 } 291 292 /** 293 * Convert the given byte array into a lowercase hex string. 294 * 295 * @param arr The array to convert. 296 * @return The hex encoded string. 297 */ 298 public static String toHexString(byte[] arr) { 299 StringBuffer buf = new StringBuffer(arr.length * 2); 300 for (byte b : arr) { 301 buf.append(String.format("%02x", b & 0xFF)); 302 } 303 return buf.toString(); 304 } 305 306 /** 307 * Strip control characters from the given string. 308 */ 309 public static String replaceControlChars(String s) { 310 // Replace any character < 0x20, except for tab, lf and cr 311 return s.replaceAll("[\\x00-\\x1f&&[^\t\n\r]]", "?"); 312 } 313 314 public static Date dateFromString(String s) throws ParseException { 315 return dateFormat.parse(s); 316 } 317 318 public static String dateToString(Date d) { 319 return dateFormat.format(d); 320 } 321 } 322