OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.content.browser; | 5 package org.chromium.content.browser; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
8 import android.content.SharedPreferences; | 8 import android.content.SharedPreferences; |
9 import android.content.pm.PackageInfo; | 9 import android.content.pm.PackageInfo; |
10 import android.content.pm.PackageManager; | 10 import android.content.pm.PackageManager; |
(...skipping 18 matching lines...) Expand all Loading... | |
29 | 29 |
30 /** | 30 /** |
31 * Handles extracting the necessary resources bundled in an APK and moving them to a location on | 31 * Handles extracting the necessary resources bundled in an APK and moving them to a location on |
32 * the file system accessible from the native code. | 32 * the file system accessible from the native code. |
33 */ | 33 */ |
34 public class ResourceExtractor { | 34 public class ResourceExtractor { |
35 | 35 |
36 private static final String LOGTAG = "ResourceExtractor"; | 36 private static final String LOGTAG = "ResourceExtractor"; |
37 private static final String LAST_LANGUAGE = "Last language"; | 37 private static final String LAST_LANGUAGE = "Last language"; |
38 private static final String PAK_FILENAMES = "Pak filenames"; | 38 private static final String PAK_FILENAMES = "Pak filenames"; |
39 private static final String ICU_DATA_FILENAME = "icudtl.dat"; | |
39 | 40 |
40 private static String[] sMandatoryPaks = null; | 41 private static String[] sMandatoryPaks = null; |
41 | 42 |
42 // By default, we attempt to extract a pak file for the users | 43 // By default, we attempt to extract a pak file for the users |
43 // current device locale. Use setExtractImplicitLocale() to | 44 // current device locale. Use setExtractImplicitLocale() to |
44 // change this behavior. | 45 // change this behavior. |
45 private static boolean sExtractImplicitLocalePak = true; | 46 private static boolean sExtractImplicitLocalePak = true; |
46 | 47 |
47 private class ExtractTask extends AsyncTask<Void, Void, Void> { | 48 private class ExtractTask extends AsyncTask<Void, Void, Void> { |
48 private static final int BUFFER_SIZE = 16 * 1024; | 49 private static final int BUFFER_SIZE = 16 * 1024; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 } | 90 } |
90 | 91 |
91 if (sExtractImplicitLocalePak) { | 92 if (sExtractImplicitLocalePak) { |
92 if (p.length() > 0) p.append('|'); | 93 if (p.length() > 0) p.append('|'); |
93 // As well as the minimum required set of .paks above, we'll als o add all .paks that | 94 // As well as the minimum required set of .paks above, we'll als o add all .paks that |
94 // we have for the user's currently selected language. | 95 // we have for the user's currently selected language. |
95 | 96 |
96 p.append(currentLanguage); | 97 p.append(currentLanguage); |
97 p.append("(-\\w+)?\\.pak"); | 98 p.append("(-\\w+)?\\.pak"); |
98 } | 99 } |
100 | |
101 if (p.length() > 0) p.append('|'); | |
102 p.append(ICU_DATA_FILENAME); | |
103 | |
99 Pattern paksToInstall = Pattern.compile(p.toString()); | 104 Pattern paksToInstall = Pattern.compile(p.toString()); |
100 | 105 |
101 AssetManager manager = mContext.getResources().getAssets(); | 106 AssetManager manager = mContext.getResources().getAssets(); |
102 try { | 107 try { |
103 // Loop through every asset file that we have in the APK, and lo ok for the | 108 // Loop through every asset file that we have in the APK, and lo ok for the |
104 // ones that we need to extract by trying to match the Patterns that we | 109 // ones that we need to extract by trying to match the Patterns that we |
105 // created above. | 110 // created above. |
106 byte[] buffer = null; | 111 byte[] buffer = null; |
107 String[] files = manager.list(""); | 112 String[] files = manager.list(""); |
108 for (String file : files) { | 113 for (String file : files) { |
109 if (!paksToInstall.matcher(file).matches()) { | 114 if (!paksToInstall.matcher(file).matches()) { |
110 continue; | 115 continue; |
111 } | 116 } |
112 File output = new File(mOutputDir, file); | 117 boolean isICUData = file.equals(ICU_DATA_FILENAME); |
118 File output = new File(isICUData ? mAppDataDir : mOutputDir, file); | |
113 if (output.exists()) { | 119 if (output.exists()) { |
114 continue; | 120 continue; |
115 } | 121 } |
116 | 122 |
117 InputStream is = null; | 123 InputStream is = null; |
118 OutputStream os = null; | 124 OutputStream os = null; |
119 try { | 125 try { |
120 is = manager.open(file); | 126 is = manager.open(file); |
121 os = new FileOutputStream(output); | 127 os = new FileOutputStream(output); |
122 Log.i(LOGTAG, "Extracting resource " + file); | 128 Log.i(LOGTAG, "Extracting resource " + file); |
123 if (buffer == null) { | 129 if (buffer == null) { |
124 buffer = new byte[BUFFER_SIZE]; | 130 buffer = new byte[BUFFER_SIZE]; |
125 } | 131 } |
126 | 132 |
127 int count = 0; | 133 int count = 0; |
128 while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { | 134 while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { |
129 os.write(buffer, 0, count); | 135 os.write(buffer, 0, count); |
130 } | 136 } |
131 os.flush(); | 137 os.flush(); |
132 | 138 |
133 // Ensure something reasonable was written. | 139 // Ensure something reasonable was written. |
134 if (output.length() == 0) { | 140 if (output.length() == 0) { |
135 throw new IOException(file + " extracted with 0 leng th!"); | 141 throw new IOException(file + " extracted with 0 leng th!"); |
136 } | 142 } |
137 | 143 |
138 filenames.add(file); | 144 if (!isICUData) { |
145 filenames.add(file); | |
146 } else { | |
147 // icudata needs to be accessed by a renderer proces s. | |
148 output.setReadable(true, false); | |
149 } | |
139 } finally { | 150 } finally { |
140 try { | 151 try { |
141 if (is != null) { | 152 if (is != null) { |
142 is.close(); | 153 is.close(); |
143 } | 154 } |
144 } finally { | 155 } finally { |
145 if (os != null) { | 156 if (os != null) { |
146 os.close(); | 157 os.close(); |
147 } | 158 } |
148 } | 159 } |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
216 return expectedTimestamp; | 227 return expectedTimestamp; |
217 } | 228 } |
218 | 229 |
219 // timestamp file is already up-to date. | 230 // timestamp file is already up-to date. |
220 return null; | 231 return null; |
221 } | 232 } |
222 } | 233 } |
223 | 234 |
224 private final Context mContext; | 235 private final Context mContext; |
225 private ExtractTask mExtractTask; | 236 private ExtractTask mExtractTask; |
237 private final File mAppDataDir; | |
226 private final File mOutputDir; | 238 private final File mOutputDir; |
227 | 239 |
228 private static ResourceExtractor sInstance; | 240 private static ResourceExtractor sInstance; |
229 | 241 |
230 public static ResourceExtractor get(Context context) { | 242 public static ResourceExtractor get(Context context) { |
231 if (sInstance == null) { | 243 if (sInstance == null) { |
232 sInstance = new ResourceExtractor(context); | 244 sInstance = new ResourceExtractor(context); |
233 } | 245 } |
234 return sInstance; | 246 return sInstance; |
235 } | 247 } |
(...skipping 20 matching lines...) Expand all Loading... | |
256 * pak files specified in sMandatoryPaks. | 268 * pak files specified in sMandatoryPaks. |
257 */ | 269 */ |
258 public static void setExtractImplicitLocaleForTesting(boolean extract) { | 270 public static void setExtractImplicitLocaleForTesting(boolean extract) { |
259 assert (sInstance == null || sInstance.mExtractTask == null) | 271 assert (sInstance == null || sInstance.mExtractTask == null) |
260 : "Must be called before startExtractingResources is called"; | 272 : "Must be called before startExtractingResources is called"; |
261 sExtractImplicitLocalePak = extract; | 273 sExtractImplicitLocalePak = extract; |
262 } | 274 } |
263 | 275 |
264 private ResourceExtractor(Context context) { | 276 private ResourceExtractor(Context context) { |
265 mContext = context; | 277 mContext = context; |
278 mAppDataDir = getAppDataDirFromContext(mContext); | |
266 mOutputDir = getOutputDirFromContext(mContext); | 279 mOutputDir = getOutputDirFromContext(mContext); |
267 } | 280 } |
268 | 281 |
269 public void waitForCompletion() { | 282 public void waitForCompletion() { |
270 if (shouldSkipPakExtraction()) { | 283 if (shouldSkipExtraction()) { |
271 return; | 284 return; |
272 } | 285 } |
273 | 286 |
274 assert mExtractTask != null; | 287 assert mExtractTask != null; |
275 | 288 |
276 try { | 289 try { |
277 mExtractTask.get(); | 290 mExtractTask.get(); |
278 } catch (CancellationException e) { | 291 } catch (CancellationException e) { |
279 // Don't leave the files in an inconsistent state. | 292 // Don't leave the files in an inconsistent state. |
280 deleteFiles(mContext); | 293 deleteFiles(mContext); |
281 } catch (ExecutionException e2) { | 294 } catch (ExecutionException e2) { |
282 deleteFiles(mContext); | 295 deleteFiles(mContext); |
283 } catch (InterruptedException e3) { | 296 } catch (InterruptedException e3) { |
284 deleteFiles(mContext); | 297 deleteFiles(mContext); |
285 } | 298 } |
286 } | 299 } |
287 | 300 |
288 /** | 301 /** |
289 * This will extract the application pak resources in an | 302 * This will extract the application pak resources in an |
290 * AsyncTask. Call waitForCompletion() at the point resources | 303 * AsyncTask. Call waitForCompletion() at the point resources |
291 * are needed to block until the task completes. | 304 * are needed to block until the task completes. |
292 */ | 305 */ |
293 public void startExtractingResources() { | 306 public void startExtractingResources() { |
294 if (mExtractTask != null) { | 307 if (mExtractTask != null) { |
295 return; | 308 return; |
296 } | 309 } |
297 | 310 |
298 if (shouldSkipPakExtraction()) { | 311 if (shouldSkipExtraction()) { |
299 return; | 312 return; |
300 } | 313 } |
301 | 314 |
302 mExtractTask = new ExtractTask(); | 315 mExtractTask = new ExtractTask(); |
303 mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | 316 mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
304 } | 317 } |
305 | 318 |
319 public static File getAppDataDirFromContext(Context context) { | |
320 return new File(PathUtils.getDataDirectory(context.getApplicationContext ())); | |
321 } | |
306 public static File getOutputDirFromContext(Context context) { | 322 public static File getOutputDirFromContext(Context context) { |
307 return new File(PathUtils.getDataDirectory(context.getApplicationContext ()), "paks"); | 323 return new File(getAppDataDirFromContext(context), "paks"); |
308 } | 324 } |
309 | 325 |
310 public static void deleteFiles(Context context) { | 326 public static void deleteFiles(Context context) { |
327 File icudata = new File(getAppDataDirFromContext(context), ICU_DATA_FILE NAME); | |
328 if (icudata.exists() && !icudata.delete()) { | |
329 Log.w(LOGTAG, "Unable to remove the icudata " + icudata.getName()); | |
330 } | |
311 File dir = getOutputDirFromContext(context); | 331 File dir = getOutputDirFromContext(context); |
312 if (dir.exists()) { | 332 if (dir.exists()) { |
313 File[] files = dir.listFiles(); | 333 File[] files = dir.listFiles(); |
314 for (File file : files) { | 334 for (File file : files) { |
315 if (!file.delete()) { | 335 if (!file.delete()) { |
316 Log.w(LOGTAG, "Unable to remove existing resource " + file.g etName()); | 336 Log.w(LOGTAG, "Unable to remove existing resource " + file.g etName()); |
317 } | 337 } |
318 } | 338 } |
319 } | 339 } |
320 } | 340 } |
321 | 341 |
322 /** | 342 /** |
323 * Pak extraction not necessarily required by the embedder; we allow them to skip | 343 * Pak extraction not necessarily required by the embedder; we allow them to skip |
324 * this process if they call setMandatoryPaksToExtract with a single empty S tring. | 344 * this process if they call setMandatoryPaksToExtract with a single empty S tring and the |
345 * icudata is absent. icudtl.dat will be present only when icu_use_data_file _flag == 1, which is | |
346 * false for the WebView build. | |
benm (inactive)
2014/02/10 21:30:45
i think that if an app using WebView had bundled a
jungshik at Google
2014/02/10 22:10:31
Although that's not likely, I agree that we'd bett
benm (inactive)
2014/02/10 22:28:32
I think that the other use will only happen in the
jungshik at Google
2014/02/10 23:41:46
That's what I expect us to do, but my reading of t
| |
325 */ | 347 */ |
348 private boolean shouldSkipExtraction() { | |
349 AssetManager manager = mContext.getResources().getAssets(); | |
350 String[] files = null; | |
351 try { | |
352 files = manager.list(ICU_DATA_FILENAME); | |
353 } catch (IOException e) { | |
354 Log.w(LOGTAG, ICU_DATA_FILENAME + " is not found."); | |
355 } | |
356 return (files == null) && shouldSkipPakExtraction(); | |
357 } | |
358 | |
326 private static boolean shouldSkipPakExtraction() { | 359 private static boolean shouldSkipPakExtraction() { |
327 // Must call setMandatoryPaksToExtract before beginning resource extract ion. | 360 // Must call setMandatoryPaksToExtract before beginning resource extract ion. |
328 assert sMandatoryPaks != null; | 361 assert sMandatoryPaks != null; |
329 return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]); | 362 return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]); |
330 } | 363 } |
331 } | 364 } |
OLD | NEW |