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 |
99 Pattern paksToInstall = Pattern.compile(p.toString()); | 101 Pattern paksToInstall = Pattern.compile(p.toString()); |
100 | 102 |
101 AssetManager manager = mContext.getResources().getAssets(); | 103 AssetManager manager = mContext.getResources().getAssets(); |
102 try { | 104 try { |
103 // Loop through every asset file that we have in the APK, and lo
ok for the | 105 // 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 | 106 // ones that we need to extract by trying to match the Patterns
that we |
105 // created above. | 107 // created above. |
106 byte[] buffer = null; | 108 byte[] buffer = null; |
107 String[] files = manager.list(""); | 109 String[] files = manager.list(""); |
108 for (String file : files) { | 110 for (String file : files) { |
109 if (!paksToInstall.matcher(file).matches()) { | 111 if (!paksToInstall.matcher(file).matches()) { |
110 continue; | 112 continue; |
111 } | 113 } |
112 File output = new File(mOutputDir, file); | 114 boolean isICUData = file.equals(ICU_DATA_FILENAME); |
| 115 File output = new File(isICUData ? mAppDataDir : mOutputDir,
file); |
113 if (output.exists()) { | 116 if (output.exists()) { |
114 continue; | 117 continue; |
115 } | 118 } |
116 | 119 |
117 InputStream is = null; | 120 InputStream is = null; |
118 OutputStream os = null; | 121 OutputStream os = null; |
119 try { | 122 try { |
120 is = manager.open(file); | 123 is = manager.open(file); |
121 os = new FileOutputStream(output); | 124 os = new FileOutputStream(output); |
122 Log.i(LOGTAG, "Extracting resource " + file); | 125 Log.i(LOGTAG, "Extracting resource " + file); |
123 if (buffer == null) { | 126 if (buffer == null) { |
124 buffer = new byte[BUFFER_SIZE]; | 127 buffer = new byte[BUFFER_SIZE]; |
125 } | 128 } |
126 | 129 |
127 int count = 0; | 130 int count = 0; |
128 while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1)
{ | 131 while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1)
{ |
129 os.write(buffer, 0, count); | 132 os.write(buffer, 0, count); |
130 } | 133 } |
131 os.flush(); | 134 os.flush(); |
132 | 135 |
133 // Ensure something reasonable was written. | 136 // Ensure something reasonable was written. |
134 if (output.length() == 0) { | 137 if (output.length() == 0) { |
135 throw new IOException(file + " extracted with 0 leng
th!"); | 138 throw new IOException(file + " extracted with 0 leng
th!"); |
136 } | 139 } |
137 | 140 |
138 filenames.add(file); | 141 if (!isICUData) { |
| 142 filenames.add(file); |
| 143 } else { |
| 144 // icudata needs to be accessed by a renderer proces
s. |
| 145 output.setReadable(true, false); |
| 146 } |
139 } finally { | 147 } finally { |
140 try { | 148 try { |
141 if (is != null) { | 149 if (is != null) { |
142 is.close(); | 150 is.close(); |
143 } | 151 } |
144 } finally { | 152 } finally { |
145 if (os != null) { | 153 if (os != null) { |
146 os.close(); | 154 os.close(); |
147 } | 155 } |
148 } | 156 } |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 return expectedTimestamp; | 224 return expectedTimestamp; |
217 } | 225 } |
218 | 226 |
219 // timestamp file is already up-to date. | 227 // timestamp file is already up-to date. |
220 return null; | 228 return null; |
221 } | 229 } |
222 } | 230 } |
223 | 231 |
224 private final Context mContext; | 232 private final Context mContext; |
225 private ExtractTask mExtractTask; | 233 private ExtractTask mExtractTask; |
| 234 private final File mAppDataDir; |
226 private final File mOutputDir; | 235 private final File mOutputDir; |
227 | 236 |
228 private static ResourceExtractor sInstance; | 237 private static ResourceExtractor sInstance; |
229 | 238 |
230 public static ResourceExtractor get(Context context) { | 239 public static ResourceExtractor get(Context context) { |
231 if (sInstance == null) { | 240 if (sInstance == null) { |
232 sInstance = new ResourceExtractor(context); | 241 sInstance = new ResourceExtractor(context); |
233 } | 242 } |
234 return sInstance; | 243 return sInstance; |
235 } | 244 } |
(...skipping 20 matching lines...) Expand all Loading... |
256 * pak files specified in sMandatoryPaks. | 265 * pak files specified in sMandatoryPaks. |
257 */ | 266 */ |
258 public static void setExtractImplicitLocaleForTesting(boolean extract) { | 267 public static void setExtractImplicitLocaleForTesting(boolean extract) { |
259 assert (sInstance == null || sInstance.mExtractTask == null) | 268 assert (sInstance == null || sInstance.mExtractTask == null) |
260 : "Must be called before startExtractingResources is called"; | 269 : "Must be called before startExtractingResources is called"; |
261 sExtractImplicitLocalePak = extract; | 270 sExtractImplicitLocalePak = extract; |
262 } | 271 } |
263 | 272 |
264 private ResourceExtractor(Context context) { | 273 private ResourceExtractor(Context context) { |
265 mContext = context; | 274 mContext = context; |
| 275 mAppDataDir = getAppDataDirFromContext(mContext); |
266 mOutputDir = getOutputDirFromContext(mContext); | 276 mOutputDir = getOutputDirFromContext(mContext); |
267 } | 277 } |
268 | 278 |
269 public void waitForCompletion() { | 279 public void waitForCompletion() { |
270 if (shouldSkipPakExtraction()) { | 280 if (shouldSkipPakExtraction()) { |
271 return; | 281 return; |
272 } | 282 } |
273 | 283 |
274 assert mExtractTask != null; | 284 assert mExtractTask != null; |
275 | 285 |
(...skipping 20 matching lines...) Expand all Loading... |
296 } | 306 } |
297 | 307 |
298 if (shouldSkipPakExtraction()) { | 308 if (shouldSkipPakExtraction()) { |
299 return; | 309 return; |
300 } | 310 } |
301 | 311 |
302 mExtractTask = new ExtractTask(); | 312 mExtractTask = new ExtractTask(); |
303 mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | 313 mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
304 } | 314 } |
305 | 315 |
306 public static File getOutputDirFromContext(Context context) { | 316 public static File getAppDataDirFromContext(Context context) { |
307 return new File(PathUtils.getDataDirectory(context.getApplicationContext
()), "paks"); | 317 return new File(PathUtils.getDataDirectory(context.getApplicationContext
())); |
308 } | 318 } |
309 | 319 |
| 320 public static File getOutputDirFromContext(Context context) { |
| 321 return new File(getAppDataDirFromContext(context), "paks"); |
| 322 } |
| 323 |
| 324 /** |
| 325 * Pak files (UI strings and other resources) should be updated along with |
| 326 * Chrome. A version mismatch can lead to a rather broken user experience. |
| 327 * The ICU data (icudtl.dat) is less version-sensitive, but still can |
| 328 * lead to malfunction/UX misbehavior. So, we regard failing to update them |
| 329 * as an error. |
| 330 */ |
310 public static void deleteFiles(Context context) { | 331 public static void deleteFiles(Context context) { |
| 332 File icudata = new File(getAppDataDirFromContext(context), ICU_DATA_FILE
NAME); |
| 333 if (icudata.exists() && !icudata.delete()) { |
| 334 Log.e(LOGTAG, "Unable to remove the icudata " + icudata.getName()); |
| 335 } |
311 File dir = getOutputDirFromContext(context); | 336 File dir = getOutputDirFromContext(context); |
312 if (dir.exists()) { | 337 if (dir.exists()) { |
313 File[] files = dir.listFiles(); | 338 File[] files = dir.listFiles(); |
314 for (File file : files) { | 339 for (File file : files) { |
315 if (!file.delete()) { | 340 if (!file.delete()) { |
316 Log.w(LOGTAG, "Unable to remove existing resource " + file.g
etName()); | 341 Log.e(LOGTAG, "Unable to remove existing resource " + file.g
etName()); |
317 } | 342 } |
318 } | 343 } |
319 } | 344 } |
320 } | 345 } |
321 | 346 |
322 /** | 347 /** |
323 * Pak extraction not necessarily required by the embedder; we allow them to
skip | 348 * 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. | 349 * this process if they call setMandatoryPaksToExtract with a single empty S
tring. |
325 */ | 350 */ |
326 private static boolean shouldSkipPakExtraction() { | 351 private static boolean shouldSkipPakExtraction() { |
327 // Must call setMandatoryPaksToExtract before beginning resource extract
ion. | 352 // Must call setMandatoryPaksToExtract before beginning resource extract
ion. |
328 assert sMandatoryPaks != null; | 353 assert sMandatoryPaks != null; |
329 return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]); | 354 return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]); |
330 } | 355 } |
331 } | 356 } |
OLD | NEW |