diff options
author | agrieve <agrieve@chromium.org> | 2015-06-19 14:20:03 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-19 21:21:24 +0000 |
commit | f30e04470453ccd7e6a9abf586185b991d174902 (patch) | |
tree | 6294dd378c05275f7f142c8665d3f83b455c4e84 /base | |
parent | e33731cd7108536b56e18e722af5c9d425c47d55 (diff) | |
download | chromium_src-f30e04470453ccd7e6a9abf586185b991d174902.zip chromium_src-f30e04470453ccd7e6a9abf586185b991d174902.tar.gz chromium_src-f30e04470453ccd7e6a9abf586185b991d174902.tar.bz2 |
Android: Store language .pak files in res/raw rather than assets
This is a prerequisite for having them live within APK splits.
Although pak files need to be renamed to be stored under res/raw,
ResourceExtractor restores their original name during extraction (so no
change to native code).
All sub-locales are stored and extracted together, just as before.
BUG=371610
Review URL: https://codereview.chromium.org/1158053005
Cr-Commit-Position: refs/heads/master@{#335350}
Diffstat (limited to 'base')
-rw-r--r-- | base/android/java/src/org/chromium/base/ResourceExtractor.java | 215 |
1 files changed, 92 insertions, 123 deletions
diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java index dd654ff..584dfae 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,38 @@ 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); + + 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 +110,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()) { + 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,32 +283,18 @@ 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. + * @param localePaksResId Resource ID for the locale_paks string array. Pass + * in 0 to disable locale pak extraction. + * @param paths The list of paths to be extracted. */ - public static void setMandatoryPaksToExtract(String... mandatoryPaks) { + public static void setMandatoryPaksToExtract(int localePaksResId, String... paths) { // TODO(agrieve): Remove the need to call this once all files are loaded from the apk. 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. - */ - @VisibleForTesting - public static void setExtractImplicitLocaleForTesting(boolean extract) { - assert (sInstance == null || sInstance.mExtractTask == null) - : "Must be called before startExtractingResources is called"; - sExtractImplicitLocalePak = extract; + sLocalePaksResId = localePaksResId; + sMandatoryPaks = paths; } /** @@ -347,7 +313,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()])); } @@ -420,6 +386,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; } @@ -472,12 +442,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; } } |