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 dd654ff6efb67fe4b05333701e21f418fcc9f6c7..fdada01ce1b627e0f8fe74ffe69e445b93be79b8 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,26 @@ 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; |
- |
+ sLocalePaksResId = localePaksResId; |
+ sMandatoryPaks = paths; |
} |
- /** |
- * 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; |
+ // TODO(agrieve): Delete this method ones all usages of it are updated. |
+ public static void setMandatoryPaksToExtract(String... paths) { |
+ if (paths.length == 1 && "".equals(paths[0])) { |
+ paths = new String[0]; |
+ } |
+ setMandatoryPaksToExtract(0, paths); |
} |
/** |
@@ -347,7 +321,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 +394,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 +450,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; |
} |
} |