Chromium Code Reviews| Index: base/android/java/src/org/chromium/base/ResourceExtractor.java |
| diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java |
| index 1adeb08b1bd584eb19757772a99a364211906ec0..d9f83f96b8406959c8cd01590a1cc8794eb9806c 100644 |
| --- a/base/android/java/src/org/chromium/base/ResourceExtractor.java |
| +++ b/base/android/java/src/org/chromium/base/ResourceExtractor.java |
| @@ -6,16 +6,16 @@ package org.chromium.base; |
| import android.annotation.TargetApi; |
| import android.content.Context; |
| -import android.content.SharedPreferences; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.res.AssetManager; |
| +import android.content.res.Resources; |
| +import android.content.res.TypedArray; |
| import android.os.AsyncTask; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Trace; |
| -import android.preference.PreferenceManager; |
| import android.util.Log; |
| import java.io.File; |
| @@ -26,9 +26,9 @@ import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.List; |
| +import java.util.Locale; |
| import java.util.concurrent.CancellationException; |
| import java.util.concurrent.ExecutionException; |
| -import java.util.regex.Pattern; |
| /** |
| * Handles extracting the necessary resources bundled in an APK and moving them to a location on |
| @@ -37,23 +37,24 @@ import java.util.regex.Pattern; |
| public class ResourceExtractor { |
| private static final String LOGTAG = "ResourceExtractor"; |
| - private static final String LAST_LANGUAGE = "Last language"; |
| - private static final String PAK_FILENAMES_LEGACY_NOREUSE = "Pak filenames"; |
| private static final String ICU_DATA_FILENAME = "icudtl.dat"; |
| private static final String V8_NATIVES_DATA_FILENAME = "natives_blob.bin"; |
| private static final String V8_SNAPSHOT_DATA_FILENAME = "snapshot_blob.bin"; |
| private static String[] sMandatoryPaks = null; |
| + private static int sLocalePaksResId = -1; |
| - // By default, we attempt to extract a pak file for the users |
| - // current device locale. Use setExtractImplicitLocale() to |
| - // change this behavior. |
| - private static boolean sExtractImplicitLocalePak = true; |
| - |
| - private static boolean isAppDataFile(String file) { |
| - return ICU_DATA_FILENAME.equals(file) |
| - || V8_NATIVES_DATA_FILENAME.equals(file) |
| - || V8_SNAPSHOT_DATA_FILENAME.equals(file); |
| + /** |
| + * Applies the reverse mapping done by locale_pak_resources.py. |
| + */ |
| + private static String toChromeLocaleName(String srcFileName) { |
| + String[] parts = srcFileName.split("_"); |
| + if (parts.length > 1) { |
| + int dotIdx = parts[1].indexOf('.'); |
| + return parts[0] + "-" + parts[1].substring(0, dotIdx).toUpperCase(Locale.ENGLISH) |
| + + parts[1].substring(dotIdx); |
| + } |
| + return srcFileName; |
| } |
| private class ExtractTask extends AsyncTask<Void, Void, Void> { |
| @@ -61,12 +62,41 @@ public class ResourceExtractor { |
| private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>(); |
| - public ExtractTask() { |
| + private void extractResourceHelper(InputStream is, File outFile, byte[] buffer) |
| + throws IOException { |
| + OutputStream os = null; |
| + try { |
| + os = new FileOutputStream(outFile); |
| + Log.i(LOGTAG, "Extracting resource " + outFile); |
| + if (buffer == null) { |
|
Yaron
2015/06/16 03:50:45
This is unnecessary - you've changed the declarati
agrieve
2015/06/16 15:47:42
Done.
|
| + buffer = new byte[BUFFER_SIZE]; |
| + } |
| + |
| + int count = 0; |
| + while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { |
| + os.write(buffer, 0, count); |
| + } |
| + os.flush(); |
| + |
| + // Ensure something reasonable was written. |
| + if (outFile.length() == 0) { |
| + throw new IOException(outFile + " extracted with 0 length!"); |
| + } |
| + } finally { |
| + try { |
| + if (is != null) { |
| + is.close(); |
| + } |
| + } finally { |
| + if (os != null) { |
| + os.close(); |
| + } |
| + } |
| + } |
| } |
| private void doInBackgroundImpl() { |
| final File outputDir = getOutputDir(); |
| - final File appDataDir = getAppDataDir(); |
| if (!outputDir.exists() && !outputDir.mkdirs()) { |
| Log.e(LOGTAG, "Unable to create pak resources directory!"); |
| return; |
| @@ -83,97 +113,50 @@ public class ResourceExtractor { |
| deleteFiles(); |
| } |
| - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); |
| - String currentLocale = LocaleUtils.getDefaultLocale(); |
| - String currentLanguage = currentLocale.split("-", 2)[0]; |
| - // If everything we need is already there (and the locale hasn't |
| - // changed), quick exit. |
| - if (prefs.getString(LAST_LANGUAGE, "").equals(currentLanguage)) { |
| - boolean filesPresent = true; |
| - for (String file : sMandatoryPaks) { |
| - File directory = isAppDataFile(file) ? appDataDir : outputDir; |
| - if (!new File(directory, file).exists()) { |
| - filesPresent = false; |
| - break; |
| - } |
| - } |
| - if (filesPresent) return; |
| - } else { |
| - prefs.edit().putString(LAST_LANGUAGE, currentLanguage).apply(); |
| - } |
| - |
| - StringBuilder p = new StringBuilder(); |
| - for (String mandatoryPak : sMandatoryPaks) { |
| - if (p.length() > 0) p.append('|'); |
| - p.append("\\Q" + mandatoryPak + "\\E"); |
| - } |
| - |
| - if (sExtractImplicitLocalePak) { |
| - if (p.length() > 0) p.append('|'); |
| - // As well as the minimum required set of .paks above, we'll |
| - // also add all .paks that we have for the user's currently |
| - // selected language. |
| - p.append(currentLanguage); |
| - p.append("(-\\w+)?\\.pak"); |
| - } |
| - |
| - Pattern paksToInstall = Pattern.compile(p.toString()); |
| - |
| - AssetManager manager = mContext.getResources().getAssets(); |
| beginTraceSection("WalkAssets"); |
| + AssetManager assetManager = mContext.getAssets(); |
| + byte[] buffer = new byte[BUFFER_SIZE]; |
| try { |
| - // Loop through every asset file that we have in the APK, and look for the |
| - // ones that we need to extract by trying to match the Patterns that we |
| - // created above. |
| - byte[] buffer = null; |
| - String[] files = manager.list(""); |
| - for (String file : files) { |
| - if (!paksToInstall.matcher(file).matches()) { |
| - continue; |
| - } |
| - File output = new File(isAppDataFile(file) ? appDataDir : outputDir, file); |
| + // Extract all files that don't already exist. |
| + for (String fileName : sMandatoryPaks) { |
| + File output = new File(outputDir, fileName); |
| if (output.exists()) { |
| continue; |
| } |
| - |
| - InputStream is = null; |
| - OutputStream os = null; |
| beginTraceSection("ExtractResource"); |
| + InputStream inputStream = assetManager.open(fileName); |
| try { |
| - is = manager.open(file); |
| - os = new FileOutputStream(output); |
| - Log.i(LOGTAG, "Extracting resource " + file); |
| - if (buffer == null) { |
| - buffer = new byte[BUFFER_SIZE]; |
| - } |
| - |
| - int count = 0; |
| - while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { |
| - os.write(buffer, 0, count); |
| - } |
| - os.flush(); |
| - |
| - // Ensure something reasonable was written. |
| - if (output.length() == 0) { |
| - throw new IOException(file + " extracted with 0 length!"); |
| - } |
| - |
| - if (isAppDataFile(file)) { |
| - // icu and V8 data need to be accessed by a renderer |
| - // process. |
| - output.setReadable(true, false); |
| - } |
| + extractResourceHelper(inputStream, output, buffer); |
| } finally { |
| - try { |
| - if (is != null) { |
| - is.close(); |
| + endTraceSection(); // ExtractResource |
| + } |
| + } |
| + |
| + if (sLocalePaksResId != 0) { |
| + // locale_paks yields the current language's pak file paths. |
| + Resources resources = mContext.getResources(); |
| + TypedArray resIds = resources.obtainTypedArray(sLocalePaksResId); |
| + try { |
| + int len = resIds.length(); |
| + for (int i = 0; i < len; ++i) { |
| + int resId = resIds.getResourceId(i, 0); |
| + String resPath = resources.getString(resId); |
| + String srcBaseName = new File(resPath).getName(); |
| + String dstBaseName = toChromeLocaleName(srcBaseName); |
| + File output = new File(outputDir, dstBaseName); |
| + if (output.exists()) { |
|
Yaron
2015/06/16 03:50:45
Hmm. Just confirming this doesn't break the case o
agrieve
2015/06/16 15:47:42
Right - the deletion code (line 113) is unchanged
|
| + continue; |
| } |
| - } finally { |
| - if (os != null) { |
| - os.close(); |
| + beginTraceSection("ExtractResource"); |
| + InputStream inputStream = resources.openRawResource(resId); |
| + try { |
| + extractResourceHelper(inputStream, output, buffer); |
| + } finally { |
| + endTraceSection(); // ExtractResource |
| } |
| - endTraceSection(); // ExtractResource |
| } |
| + } finally { |
| + resIds.recycle(); |
| } |
| } |
| } catch (IOException e) { |
| @@ -303,31 +286,17 @@ public class ResourceExtractor { |
| } |
| /** |
| - * Specifies the .pak files that should be extracted from the APK's asset resources directory |
| + * Specifies the files that should be extracted from the APK. |
| * and moved to {@link #getOutputDirFromContext(Context)}. |
| - * @param mandatoryPaks The list of pak files to be loaded. If no pak files are |
| - * required, pass a single empty string. |
| - */ |
| - public static void setMandatoryPaksToExtract(String... mandatoryPaks) { |
| - assert (sInstance == null || sInstance.mExtractTask == null) |
| - : "Must be called before startExtractingResources is called"; |
| - sMandatoryPaks = mandatoryPaks; |
| - |
| - } |
| - |
| - /** |
| - * By default the ResourceExtractor will attempt to extract a pak resource for the users |
| - * currently specified locale. This behavior can be changed with this function and is |
| - * only needed by tests. |
| - * @param extract False if we should not attempt to extract a pak file for |
| - * the users currently selected locale and try to extract only the |
| - * pak files specified in sMandatoryPaks. |
| + * @param loaclePaksResId Resource ID for the locale_paks string array. Pass |
|
Yaron
2015/06/16 03:50:45
localePaksResId
agrieve
2015/06/16 15:47:42
Done.
|
| + * in 0 to disable locale pak extraction. |
| + * @param paths The list of paths to be extracted. |
| */ |
| - @VisibleForTesting |
| - public static void setExtractImplicitLocaleForTesting(boolean extract) { |
| + public static void setMandatoryPaksToExtract(int loaclePaksResId, String... paths) { |
| assert (sInstance == null || sInstance.mExtractTask == null) |
| : "Must be called before startExtractingResources is called"; |
| - sExtractImplicitLocalePak = extract; |
| + sLocalePaksResId = loaclePaksResId; |
| + sMandatoryPaks = paths; |
| } |
| /** |
| @@ -346,7 +315,7 @@ public class ResourceExtractor { |
| } catch (IOException e) { |
| Log.w(LOGTAG, "Exception while accessing assets: " + e.getMessage(), e); |
| } |
| - setMandatoryPaksToExtract(pakAndSnapshotFileAssets.toArray( |
| + setMandatoryPaksToExtract(0, pakAndSnapshotFileAssets.toArray( |
| new String[pakAndSnapshotFileAssets.size()])); |
| } |
| @@ -419,6 +388,10 @@ public class ResourceExtractor { |
| return; |
| } |
| + // If a previous release extracted resources, and the current release does not, |
| + // deleteFiles() will not run and some files will be left. This currently |
| + // can happen for ContentShell, but not for Chrome proper, since we always extract |
| + // locale pak files. |
| if (shouldSkipPakExtraction()) { |
| return; |
| } |
| @@ -471,12 +444,11 @@ public class ResourceExtractor { |
| } |
| /** |
| - * Pak extraction not necessarily required by the embedder; we allow them to skip |
| - * this process if they call setMandatoryPaksToExtract with a single empty String. |
| + * Pak extraction not necessarily required by the embedder. |
| */ |
| private static boolean shouldSkipPakExtraction() { |
| - // Must call setMandatoryPaksToExtract before beginning resource extraction. |
| - assert sMandatoryPaks != null; |
| - return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]); |
| + assert (sLocalePaksResId != -1 && sMandatoryPaks != null) |
| + : "setMandatoryPaksToExtract() must be called before startExtractingResources()"; |
| + return sMandatoryPaks.length == 0 && sLocalePaksResId == 0; |
| } |
| } |