This source file includes following definitions.
- doInBackground
- checkPakTimestamp
- get
- setMandatoryPaksToExtract
- setExtractImplicitLocaleForTesting
- waitForCompletion
- startExtractingResources
- getAppDataDir
- getOutputDir
- deleteFiles
- shouldSkipPakExtraction
package org.chromium.content.browser;
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.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import org.chromium.base.PathUtils;
import org.chromium.ui.base.LocalizationUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
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 = "Pak filenames";
private static final String ICU_DATA_FILENAME = "icudtl.dat";
private static String[] sMandatoryPaks = null;
private static boolean sExtractImplicitLocalePak = true;
private class ExtractTask extends AsyncTask<Void, Void, Void> {
private static final int BUFFER_SIZE = 16 * 1024;
public ExtractTask() {
}
@Override
protected Void doInBackground(Void... unused) {
if (!mOutputDir.exists() && !mOutputDir.mkdirs()) {
Log.e(LOGTAG, "Unable to create pak resources directory!");
return null;
}
String timestampFile = checkPakTimestamp();
if (timestampFile != null) {
deleteFiles();
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
HashSet<String> filenames = (HashSet<String>) prefs.getStringSet(
PAK_FILENAMES, new HashSet<String>());
String currentLocale = LocalizationUtils.getDefaultLocale();
String currentLanguage = currentLocale.split("-", 2)[0];
if (prefs.getString(LAST_LANGUAGE, "").equals(currentLanguage)
&& filenames.size() >= sMandatoryPaks.length) {
boolean filesPresent = true;
for (String file : filenames) {
if (!new File(mOutputDir, file).exists()) {
filesPresent = false;
break;
}
}
if (filesPresent) return null;
} 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('|');
p.append(currentLanguage);
p.append("(-\\w+)?\\.pak");
}
Pattern paksToInstall = Pattern.compile(p.toString());
AssetManager manager = mContext.getResources().getAssets();
try {
byte[] buffer = null;
String[] files = manager.list("");
for (String file : files) {
if (!paksToInstall.matcher(file).matches()) {
continue;
}
boolean isICUData = file.equals(ICU_DATA_FILENAME);
File output = new File(isICUData ? mAppDataDir : mOutputDir, file);
if (output.exists()) {
continue;
}
InputStream is = null;
OutputStream os = null;
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();
if (output.length() == 0) {
throw new IOException(file + " extracted with 0 length!");
}
if (!isICUData) {
filenames.add(file);
} else {
output.setReadable(true, false);
}
} finally {
try {
if (is != null) {
is.close();
}
} finally {
if (os != null) {
os.close();
}
}
}
}
} catch (IOException e) {
Log.w(LOGTAG, "Exception unpacking required pak resources: " + e.getMessage());
deleteFiles();
return null;
}
if (timestampFile != null) {
try {
new File(mOutputDir, timestampFile).createNewFile();
} catch (IOException e) {
Log.w(LOGTAG, "Failed to write resource pak timestamp!");
}
}
prefs.edit().remove(PAK_FILENAMES).apply();
prefs.edit().putStringSet(PAK_FILENAMES, filenames).apply();
return null;
}
private String checkPakTimestamp() {
final String TIMESTAMP_PREFIX = "pak_timestamp-";
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = null;
try {
pi = pm.getPackageInfo(mContext.getPackageName(), 0);
} catch (PackageManager.NameNotFoundException e) {
return TIMESTAMP_PREFIX;
}
if (pi == null) {
return TIMESTAMP_PREFIX;
}
String expectedTimestamp = TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.lastUpdateTime;
String[] timestamps = mOutputDir.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(TIMESTAMP_PREFIX);
}
});
if (timestamps.length != 1) {
return expectedTimestamp;
}
if (!expectedTimestamp.equals(timestamps[0])) {
return expectedTimestamp;
}
return null;
}
}
private final Context mContext;
private ExtractTask mExtractTask;
private final File mAppDataDir;
private final File mOutputDir;
private static ResourceExtractor sInstance;
public static ResourceExtractor get(Context context) {
if (sInstance == null) {
sInstance = new ResourceExtractor(context);
}
return sInstance;
}
public static void setMandatoryPaksToExtract(String... mandatoryPaks) {
assert (sInstance == null || sInstance.mExtractTask == null)
: "Must be called before startExtractingResources is called";
sMandatoryPaks = mandatoryPaks;
}
public static void setExtractImplicitLocaleForTesting(boolean extract) {
assert (sInstance == null || sInstance.mExtractTask == null)
: "Must be called before startExtractingResources is called";
sExtractImplicitLocalePak = extract;
}
private ResourceExtractor(Context context) {
mContext = context.getApplicationContext();
mAppDataDir = getAppDataDir();
mOutputDir = getOutputDir();
}
public void waitForCompletion() {
if (shouldSkipPakExtraction()) {
return;
}
assert mExtractTask != null;
try {
mExtractTask.get();
} catch (CancellationException e) {
deleteFiles();
} catch (ExecutionException e2) {
deleteFiles();
} catch (InterruptedException e3) {
deleteFiles();
}
}
public void startExtractingResources() {
if (mExtractTask != null) {
return;
}
if (shouldSkipPakExtraction()) {
return;
}
mExtractTask = new ExtractTask();
mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private File getAppDataDir() {
return new File(PathUtils.getDataDirectory(mContext));
}
private File getOutputDir() {
return new File(getAppDataDir(), "paks");
}
private void deleteFiles() {
File icudata = new File(getAppDataDir(), ICU_DATA_FILENAME);
if (icudata.exists() && !icudata.delete()) {
Log.e(LOGTAG, "Unable to remove the icudata " + icudata.getName());
}
File dir = getOutputDir();
if (dir.exists()) {
File[] files = dir.listFiles();
for (File file : files) {
if (!file.delete()) {
Log.e(LOGTAG, "Unable to remove existing resource " + file.getName());
}
}
}
}
private static boolean shouldSkipPakExtraction() {
assert sMandatoryPaks != null;
return sMandatoryPaks.length == 1 && "".equals(sMandatoryPaks[0]);
}
}