5package io.flutter.embedding.engine.loader;
7import static io.flutter.Build.API_LEVELS;
9import android.content.pm.PackageInfo;
10import android.content.pm.PackageManager;
11import android.content.res.AssetManager;
14import androidx.annotation.NonNull;
15import androidx.annotation.WorkerThread;
16import io.flutter.BuildConfig;
19import java.util.Collection;
20import java.util.HashSet;
21import java.util.concurrent.CancellationException;
22import java.util.concurrent.ExecutionException;
26 private static final String TAG =
"ResourceExtractor";
27 private static final String TIMESTAMP_PREFIX =
"res_timestamp-";
28 private static final String[] SUPPORTED_ABIS =
Build.SUPPORTED_ABIS;
30 @SuppressWarnings(
"deprecation")
34 return packageInfo.getLongVersionCode();
36 return packageInfo.versionCode;
40 private static class ExtractTask
extends AsyncTask<Void, Void, Void> {
41 @NonNull
private final String mDataDirPath;
42 @NonNull
private final HashSet<String> mResources;
43 @NonNull
private final AssetManager mAssetManager;
44 @NonNull
private final String mPackageName;
45 @NonNull
private final PackageManager mPackageManager;
48 @NonNull String dataDirPath,
49 @NonNull HashSet<String> resources,
50 @NonNull String packageName,
51 @NonNull PackageManager packageManager,
52 @NonNull AssetManager assetManager) {
53 mDataDirPath = dataDirPath;
54 mResources = resources;
55 mAssetManager = assetManager;
56 mPackageName = packageName;
57 mPackageManager = packageManager;
61 protected Void doInBackground(Void...
unused) {
62 final File dataDir =
new File(mDataDirPath);
64 final String timestamp = checkTimestamp(dataDir, mPackageManager, mPackageName);
65 if (timestamp ==
null) {
69 deleteFiles(mDataDirPath, mResources);
71 if (!extractAPK(dataDir)) {
75 if (timestamp !=
null) {
77 new File(dataDir, timestamp).createNewFile();
78 }
catch (IOException
e) {
79 Log.w(TAG,
"Failed to write resource timestamp");
89 private boolean extractAPK(@NonNull File dataDir) {
90 for (String asset : mResources) {
92 final String
resource =
"assets/" + asset;
93 final File
output =
new File(dataDir, asset);
97 if (
output.getParentFile() !=
null) {
98 output.getParentFile().mkdirs();
101 try (InputStream is = mAssetManager.open(asset);
102 OutputStream os =
new FileOutputStream(
output)) {
105 if (BuildConfig.DEBUG) {
106 Log.i(TAG,
"Extracted baseline resource " +
resource);
108 }
catch (FileNotFoundException fnfe) {
111 }
catch (IOException ioe) {
112 Log.w(TAG,
"Exception unpacking resources: " + ioe.getMessage());
113 deleteFiles(mDataDirPath, mResources);
122 @NonNull
private final String mDataDirPath;
123 @NonNull
private final String mPackageName;
124 @NonNull
private final PackageManager mPackageManager;
125 @NonNull
private final AssetManager mAssetManager;
126 @NonNull
private final HashSet<String> mResources;
127 private ExtractTask mExtractTask;
130 @NonNull String dataDirPath,
131 @NonNull String packageName,
132 @NonNull PackageManager packageManager,
133 @NonNull AssetManager assetManager) {
134 mDataDirPath = dataDirPath;
135 mPackageName = packageName;
136 mPackageManager = packageManager;
137 mAssetManager = assetManager;
138 mResources =
new HashSet<>();
147 mResources.addAll(resources);
154 TAG,
"Attempted to start resource extraction while another extraction was in progress.");
157 new ExtractTask(mDataDirPath, mResources, mPackageName, mPackageManager, mAssetManager);
158 mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
163 if (mExtractTask ==
null) {
169 }
catch (CancellationException | ExecutionException | InterruptedException
e) {
170 deleteFiles(mDataDirPath, mResources);
174 private static String[] getExistingTimestamps(File dataDir) {
176 new FilenameFilter() {
178 public boolean accept(File
dir, String
name) {
179 return name.startsWith(TIMESTAMP_PREFIX);
184 private static void deleteFiles(@NonNull String dataDirPath, @NonNull HashSet<String> resources) {
185 final File dataDir =
new File(dataDirPath);
192 final String[] existingTimestamps = getExistingTimestamps(dataDir);
193 if (existingTimestamps ==
null) {
196 for (String timestamp : existingTimestamps) {
197 new File(dataDir, timestamp).delete();
203 private static String checkTimestamp(
204 @NonNull File dataDir, @NonNull PackageManager packageManager, @NonNull String packageName) {
205 PackageInfo packageInfo =
null;
208 packageInfo = packageManager.getPackageInfo(packageName, 0);
209 }
catch (PackageManager.NameNotFoundException
e) {
210 return TIMESTAMP_PREFIX;
213 if (packageInfo ==
null) {
214 return TIMESTAMP_PREFIX;
217 String expectedTimestamp =
218 TIMESTAMP_PREFIX +
getVersionCode(packageInfo) +
"-" + packageInfo.lastUpdateTime;
220 final String[] existingTimestamps = getExistingTimestamps(dataDir);
222 if (existingTimestamps ==
null) {
223 if (BuildConfig.DEBUG) {
224 Log.i(TAG,
"No extracted resources found");
226 return expectedTimestamp;
229 if (existingTimestamps.length == 1) {
230 if (BuildConfig.DEBUG) {
231 Log.i(TAG,
"Found extracted resources " + existingTimestamps[0]);
235 if (existingTimestamps.length != 1 || !expectedTimestamp.equals(existingTimestamps[0])) {
236 if (BuildConfig.DEBUG) {
237 Log.i(TAG,
"Resource version mismatch " + expectedTimestamp);
239 return expectedTimestamp;
245 private static void copy(@NonNull InputStream in, @NonNull OutputStream
out)
throws IOException {
246 byte[] buf =
new byte[16 * 1024];
247 for (
int i; (
i = in.read(buf)) >= 0; ) {
248 out.write(buf, 0,
i);
static SkString resource(SkPDFResourceType type, int index)
static final boolean DEBUG
static void e(@NonNull String tag, @NonNull String message)
void Log(const char *format,...) SK_PRINTF_LIKE(1
def Build(configs, env, options)
DEF_SWITCHES_START aot vmservice shared library name
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir