OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.mojo.shell; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.content.pm.PackageInfo; |
| 9 import android.content.pm.PackageManager; |
| 10 import android.util.Log; |
| 11 |
| 12 import java.io.BufferedInputStream; |
| 13 import java.io.BufferedOutputStream; |
| 14 import java.io.File; |
| 15 import java.io.FileInputStream; |
| 16 import java.io.FileNotFoundException; |
| 17 import java.io.FileOutputStream; |
| 18 import java.io.FilenameFilter; |
| 19 import java.io.IOException; |
| 20 import java.io.InputStream; |
| 21 import java.io.OutputStream; |
| 22 import java.util.zip.ZipEntry; |
| 23 import java.util.zip.ZipInputStream; |
| 24 |
| 25 /** |
| 26 * Helper methods for file extraction from APK assets and zip archives. |
| 27 */ |
| 28 class FileHelper { |
| 29 public static final String TAG = "MojoFileHelper"; |
| 30 |
| 31 // Size of the buffer used in streaming file operations. |
| 32 private static final int BUFFER_SIZE = 1024 * 1024; |
| 33 // Prefix used when naming temporary files. |
| 34 private static final String TEMP_FILE_PREFIX = "temp-"; |
| 35 // Prefix used when naming timestamp files. |
| 36 private static final String TIMESTAMP_PREFIX = "asset_timestamp-"; |
| 37 |
| 38 /** |
| 39 * Looks for a timestamp file on disk that indicates the version of the APK
that the resource |
| 40 * assets were extracted from. Returns null if a timestamp was found and it
indicates that the |
| 41 * resources match the current APK. Otherwise returns a String that represen
ts the filename of a |
| 42 * timestamp to create. |
| 43 */ |
| 44 private static String checkAssetTimestamp(Context context, File outputDir) { |
| 45 PackageManager pm = context.getPackageManager(); |
| 46 PackageInfo pi = null; |
| 47 |
| 48 try { |
| 49 pi = pm.getPackageInfo(context.getPackageName(), 0); |
| 50 } catch (PackageManager.NameNotFoundException e) { |
| 51 return TIMESTAMP_PREFIX; |
| 52 } |
| 53 |
| 54 if (pi == null) { |
| 55 return TIMESTAMP_PREFIX; |
| 56 } |
| 57 |
| 58 String expectedTimestamp = TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.
lastUpdateTime; |
| 59 |
| 60 String[] timestamps = outputDir.list(new FilenameFilter() { |
| 61 @Override |
| 62 public boolean accept(File dir, String name) { |
| 63 return name.startsWith(TIMESTAMP_PREFIX); |
| 64 } |
| 65 }); |
| 66 |
| 67 if (timestamps.length != 1) { |
| 68 // If there's no timestamp, nuke to be safe as we can't tell the age
of the files. |
| 69 // If there's multiple timestamps, something's gone wrong so nuke. |
| 70 return expectedTimestamp; |
| 71 } |
| 72 |
| 73 if (!expectedTimestamp.equals(timestamps[0])) { |
| 74 return expectedTimestamp; |
| 75 } |
| 76 |
| 77 // Timestamp file is already up-to date. |
| 78 return null; |
| 79 } |
| 80 |
| 81 public static File extractFromAssets(Context context, String assetName, File
outputDirectory, |
| 82 boolean useTempFile) throws IOException, FileNotFoundException { |
| 83 String timestampToCreate = null; |
| 84 if (!useTempFile) { |
| 85 timestampToCreate = checkAssetTimestamp(context, outputDirectory); |
| 86 if (timestampToCreate != null) { |
| 87 for (File child : outputDirectory.listFiles()) { |
| 88 deleteRecursively(child); |
| 89 } |
| 90 } |
| 91 } |
| 92 |
| 93 File outputFile; |
| 94 if (useTempFile) { |
| 95 // Make the original filename part of the temp file name. |
| 96 // TODO(ppi): do we need to sanitize the suffix? |
| 97 String suffix = "-" + assetName; |
| 98 outputFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, outputDir
ectory); |
| 99 } else { |
| 100 outputFile = new File(outputDirectory, assetName); |
| 101 if (outputFile.exists()) { |
| 102 return outputFile; |
| 103 } |
| 104 } |
| 105 |
| 106 BufferedInputStream inputStream = new BufferedInputStream( |
| 107 context.getAssets().open(assetName)); |
| 108 try { |
| 109 writeStreamToFile(inputStream, outputFile); |
| 110 } finally { |
| 111 inputStream.close(); |
| 112 } |
| 113 |
| 114 if (timestampToCreate != null) { |
| 115 try { |
| 116 new File(outputDirectory, timestampToCreate).createNewFile(); |
| 117 } catch (IOException e) { |
| 118 // In the worst case we don't write a timestamp, so we'll re-ext
ract the asset next |
| 119 // time. |
| 120 Log.w(TAG, "Failed to write asset timestamp!"); |
| 121 } |
| 122 } |
| 123 |
| 124 return outputFile; |
| 125 } |
| 126 |
| 127 /** |
| 128 * Extracts the file of the given extension from the archive. Throws FileNot
FoundException if no |
| 129 * matching file is found. |
| 130 */ |
| 131 static File extractFromArchive(File archive, String suffixToMatch, |
| 132 File outputDirectory) throws IOException, FileNotFoundException { |
| 133 ZipInputStream zip = new ZipInputStream(new BufferedInputStream(new File
InputStream( |
| 134 archive))); |
| 135 ZipEntry entry; |
| 136 while ((entry = zip.getNextEntry()) != null) { |
| 137 if (entry.getName().endsWith(suffixToMatch)) { |
| 138 // Make the original filename part of the temp file name. |
| 139 // TODO(ppi): do we need to sanitize the suffix? |
| 140 String suffix = "-" + new File(entry.getName()).getName(); |
| 141 File extractedFile = File.createTempFile(TEMP_FILE_PREFIX, suffi
x, |
| 142 outputDirectory); |
| 143 writeStreamToFile(zip, extractedFile); |
| 144 zip.close(); |
| 145 return extractedFile; |
| 146 } |
| 147 } |
| 148 zip.close(); |
| 149 throw new FileNotFoundException(); |
| 150 } |
| 151 |
| 152 /** |
| 153 * Deletes a file or directory. Directory will be deleted even if not empty. |
| 154 */ |
| 155 static void deleteRecursively(File file) { |
| 156 if (file.isDirectory()) { |
| 157 for (File child : file.listFiles()) { |
| 158 deleteRecursively(child); |
| 159 } |
| 160 } |
| 161 if (!file.delete()) { |
| 162 Log.w(TAG, "Unable to delete file: " + file.getAbsolutePath()); |
| 163 } |
| 164 } |
| 165 |
| 166 private static void writeStreamToFile(InputStream inputStream, File outputFi
le) |
| 167 throws IOException { |
| 168 byte[] buffer = new byte[BUFFER_SIZE]; |
| 169 OutputStream outputStream = new BufferedOutputStream(new FileOutputStrea
m(outputFile)); |
| 170 try { |
| 171 int read; |
| 172 while ((read = inputStream.read(buffer, 0, BUFFER_SIZE)) > 0) { |
| 173 outputStream.write(buffer, 0, read); |
| 174 } |
| 175 } finally { |
| 176 outputStream.close(); |
| 177 } |
| 178 } |
| 179 } |
OLD | NEW |