Commit eb4e9ac5 authored by tedchoc's avatar tedchoc Committed by Commit bot

Add a callback mechanism for resource extraction completion on Android.

BUG=459642

Review URL: https://codereview.chromium.org/933343002

Cr-Commit-Position: refs/heads/master@{#316972}
parent f8476d31
......@@ -12,6 +12,8 @@ import android.content.pm.PackageManager;
import android.content.res.AssetManager;
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;
......@@ -57,6 +59,8 @@ public class ResourceExtractor {
private class ExtractTask extends AsyncTask<Void, Void, Void> {
private static final int BUFFER_SIZE = 16 * 1024;
private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
public ExtractTask() {
}
......@@ -211,6 +215,23 @@ public class ResourceExtractor {
return null;
}
private void onPostExecuteImpl() {
for (int i = 0; i < mCompletionCallbacks.size(); i++) {
mCompletionCallbacks.get(i).run();
}
mCompletionCallbacks.clear();
}
@Override
protected void onPostExecute(Void result) {
beginTraceSection("ResourceExtractor.ExtractTask.onPostExecute");
try {
onPostExecuteImpl();
} finally {
endTraceSection();
}
}
// Looks for a timestamp file on disk that indicates the version of the APK that
// the resource paks were extracted from. Returns null if a timestamp was found
// and it indicates that the resources match the current APK. Otherwise returns
......@@ -335,6 +356,13 @@ public class ResourceExtractor {
mContext = context.getApplicationContext();
}
/**
* Synchronously wait for the resource extraction to be completed.
* <p>
* This method is bad and you should feel bad for using it.
*
* @see #addCompletionCallback(Runnable)
*/
public void waitForCompletion() {
if (shouldSkipPakExtraction()) {
return;
......@@ -354,6 +382,35 @@ public class ResourceExtractor {
}
}
/**
* Adds a callback to be notified upon the completion of resource extraction.
* <p>
* If the resource task has already completed, the callback will be posted to the UI message
* queue. Otherwise, it will be executed after all the resources have been extracted.
* <p>
* This must be called on the UI thread. The callback will also always be executed on
* the UI thread.
*
* @param callback The callback to be enqueued.
*/
public void addCompletionCallback(Runnable callback) {
ThreadUtils.assertOnUiThread();
Handler handler = new Handler(Looper.getMainLooper());
if (shouldSkipPakExtraction()) {
handler.post(callback);
return;
}
assert mExtractTask != null;
assert !mExtractTask.isCancelled();
if (mExtractTask.getStatus() == AsyncTask.Status.FINISHED) {
handler.post(callback);
} else {
mExtractTask.mCompletionCallbacks.add(callback);
}
}
/**
* This will extract the application pak resources in an
* AsyncTask. Call waitForCompletion() at the point resources
......
......@@ -92,6 +92,9 @@ public class BrowserStartupController {
// Whether the async startup of the browser process has started.
private boolean mHasStartedInitializingBrowserProcess;
// Whether tasks that occur after resource extraction have been completed.
private boolean mPostResourceExtractionTasksCompleted;
// Whether the async startup of the browser process is complete.
private boolean mStartupDone;
......@@ -103,7 +106,7 @@ public class BrowserStartupController {
private int mLibraryProcessType;
BrowserStartupController(Context context, int libraryProcessType) {
mContext = context;
mContext = context.getApplicationContext();
mAsyncStartupCallbacks = new ArrayList<StartupCallback>();
mLibraryProcessType = libraryProcessType;
}
......@@ -123,8 +126,7 @@ public class BrowserStartupController {
if (sInstance == null) {
assert LibraryProcessType.PROCESS_BROWSER == libraryProcessType
|| LibraryProcessType.PROCESS_WEBVIEW == libraryProcessType;
sInstance = new BrowserStartupController(context.getApplicationContext(),
libraryProcessType);
sInstance = new BrowserStartupController(context, libraryProcessType);
}
assert sInstance.mLibraryProcessType == libraryProcessType : "Wrong process type";
return sInstance;
......@@ -164,13 +166,17 @@ public class BrowserStartupController {
// flag that indicates that we have kicked off starting the browser process.
mHasStartedInitializingBrowserProcess = true;
prepareToStartBrowserProcess(false);
setAsynchronousStartup(true);
if (contentStart() > 0) {
// Failed. The callbacks may not have run, so run them.
enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
}
prepareToStartBrowserProcess(false, new Runnable() {
@Override
public void run() {
ThreadUtils.assertOnUiThread();
if (contentStart() > 0) {
// Failed. The callbacks may not have run, so run them.
enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
}
}
});
}
}
......@@ -188,8 +194,8 @@ public class BrowserStartupController {
public void startBrowserProcessesSync(boolean singleProcess) throws ProcessInitException {
// If already started skip to checking the result
if (!mStartupDone) {
if (!mHasStartedInitializingBrowserProcess) {
prepareToStartBrowserProcess(singleProcess);
if (!mHasStartedInitializingBrowserProcess || !mPostResourceExtractionTasksCompleted) {
prepareToStartBrowserProcess(singleProcess, null);
}
setAsynchronousStartup(false);
......@@ -263,7 +269,9 @@ public class BrowserStartupController {
}
@VisibleForTesting
void prepareToStartBrowserProcess(boolean singleProcess) throws ProcessInitException {
void prepareToStartBrowserProcess(
final boolean singleProcess, final Runnable completionCallback)
throws ProcessInitException {
Log.i(TAG, "Initializing chromium process, singleProcess=" + singleProcess);
// Normally Main.java will have kicked this off asynchronously for Chrome. But other
......@@ -276,15 +284,31 @@ public class BrowserStartupController {
// to load it here if we arrived via another flow, e.g. bookmark access & sync setup.
LibraryLoader.get(mLibraryProcessType).ensureInitialized(mContext, true);
// TODO(yfriedman): Remove dependency on a command line flag for this.
DeviceUtils.addDeviceSpecificUserAgentSwitch(mContext);
Runnable postResourceExtraction = new Runnable() {
@Override
public void run() {
if (!mPostResourceExtractionTasksCompleted) {
// TODO(yfriedman): Remove dependency on a command line flag for this.
DeviceUtils.addDeviceSpecificUserAgentSwitch(mContext);
ContentMain.initApplicationContext(mContext);
nativeSetCommandLineFlags(
singleProcess, nativeIsPluginEnabled() ? getPlugins() : null);
mPostResourceExtractionTasksCompleted = true;
}
Context appContext = mContext.getApplicationContext();
// Now we really need to have the resources ready.
resourceExtractor.waitForCompletion();
if (completionCallback != null) completionCallback.run();
}
};
ContentMain.initApplicationContext(appContext);
nativeSetCommandLineFlags(singleProcess, nativeIsPluginEnabled() ? getPlugins() : null);
if (completionCallback == null) {
// If no continuation callback is specified, then force the resource extraction
// to complete.
resourceExtractor.waitForCompletion();
postResourceExtraction.run();
} else {
resourceExtractor.addCompletionCallback(postResourceExtraction);
}
}
/**
......
......@@ -28,10 +28,13 @@ public class BrowserStartupControllerTest extends InstrumentationTestCase {
private int mInitializedCounter = 0;
@Override
void prepareToStartBrowserProcess(boolean singleProcess) throws ProcessInitException {
void prepareToStartBrowserProcess(boolean singleProcess, Runnable completionCallback)
throws ProcessInitException {
if (!mLibraryLoadSucceeds) {
throw new ProcessInitException(
LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED);
} else if (completionCallback != null) {
completionCallback.run();
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment