Commit 12c659f0 authored by Michael van Ouwerkerk's avatar Michael van Ouwerkerk Committed by Commit Bot

Load dynamic module entry point class in AsyncTask.

Bug: 853728
Change-Id: I0d46d3004852fbbd0cb4a0793bbb899e43f38b2c
Reviewed-on: https://chromium-review.googlesource.com/1130526
Commit-Queue: Michael van Ouwerkerk <mvanouwerkerk@chromium.org>
Reviewed-by: default avatarBernhard Bauer <bauerb@chromium.org>
Reviewed-by: default avatarBenoit L <lizeb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575665}
parent b215a089
......@@ -11,6 +11,7 @@ import static org.chromium.chrome.browser.webapps.WebappActivity.ACTIVITY_TYPE_W
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
......@@ -37,6 +38,7 @@ import android.view.Window;
import android.widget.RemoteViews;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
......@@ -99,6 +101,7 @@ import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.WindowAndroid;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.concurrent.TimeUnit;
......@@ -158,7 +161,10 @@ public class CustomTabActivity extends ChromeActivity {
private WebappCustomTabTimeSpentLogger mWebappTimeSpentLogger;
@Nullable private ActivityDelegate mActivityDelegate;
@Nullable private ActivityDelegate mModuleActivityDelegate;
@Nullable private Runnable mLoadModuleCancelRunnable;
private boolean mModuleOnStartPending;
private boolean mModuleOnResumePending;
private boolean mHasSetOverlayView;
private static class PageLoadMetricsObserver implements PageLoadMetrics.Observer {
......@@ -347,16 +353,15 @@ public class CustomTabActivity extends ChromeActivity {
}
/**
* Dynamically loads a module using the package and class names specified in the intent, if it
* is not loaded yet.
* Dynamically loads a module using the component name specified in the intent if the feature is
* enabled, the package is Google-signed, and it is not loaded yet.
*/
private void maybeLoadModule() {
String packageName = mIntentDataProvider.getModulePackageName();
String className = mIntentDataProvider.getModuleClassName();
// Return early if these were not provided. It's important to do this before checking the
// feature experiment group, to avoid entering users into the experiment that do not even
// receive the intent extras for using the feature.
if (packageName == null || className == null) return;
ComponentName componentName = mIntentDataProvider.getModuleComponentName();
// Return early if no component name was provided. It's important to do this before checking
// the feature experiment group, to avoid entering users into the experiment that do not
// even receive the extras for using the feature.
if (componentName == null) return;
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_MODULE)) {
Log.w(TAG, "The %s feature is disabled.", ChromeFeatureList.CCT_MODULE);
......@@ -364,20 +369,66 @@ public class CustomTabActivity extends ChromeActivity {
return;
}
if (!ExternalAuthUtils.getInstance().isGoogleSigned(packageName)) {
Log.w(TAG, "The %s package is not Google-signed.", packageName);
if (!ExternalAuthUtils.getInstance().isGoogleSigned(componentName.getPackageName())) {
Log.w(TAG, "The %s package is not Google-signed.", componentName.getPackageName());
ModuleMetrics.recordLoadResult(ModuleMetrics.LOAD_RESULT_NOT_GOOGLE_SIGNED);
return;
}
// TODO(https://crbug.com/853728): Load the module in the background.
ModuleEntryPoint entryPoint = mConnection.loadModule(packageName, className);
mLoadModuleCancelRunnable =
mConnection.getModuleLoader(componentName).loadModule(new LoadModuleCallback(this));
}
private boolean isModuleLoading() {
return mLoadModuleCancelRunnable != null;
}
private static class LoadModuleCallback implements Callback<ModuleEntryPoint> {
private final WeakReference<CustomTabActivity> mActivity;
LoadModuleCallback(CustomTabActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void onResult(@Nullable ModuleEntryPoint entryPoint) {
CustomTabActivity activity = mActivity.get();
if (activity == null || activity.isActivityDestroyed()) return;
activity.onModuleLoaded(entryPoint);
}
}
/**
* Receives the entry point if it was loaded successfully, or null if there was a problem. This
* is always called on the UI thread.
*/
private void onModuleLoaded(@Nullable ModuleEntryPoint entryPoint) {
mLoadModuleCancelRunnable = null;
if (entryPoint == null) return;
long createActivityDelegateStartTime = ModuleMetrics.now();
mActivityDelegate = entryPoint.createActivityDelegate(new ActivityHostImpl(this));
mModuleActivityDelegate = entryPoint.createActivityDelegate(new ActivityHostImpl(this));
ModuleMetrics.recordCreateActivityDelegateTime(createActivityDelegateStartTime);
mActivityDelegate.onCreate(getSavedInstanceState());
mModuleActivityDelegate.onCreate(getSavedInstanceState());
if (mModuleOnStartPending) startModule();
if (mModuleOnResumePending) resumeModule();
}
private void startModule() {
assert mModuleActivityDelegate != null;
mModuleOnStartPending = false;
mModuleActivityDelegate.onStart();
mModuleActivityDelegate.onRestoreInstanceState(getSavedInstanceState());
mModuleActivityDelegate.onPostCreate(getSavedInstanceState());
}
private void resumeModule() {
assert mModuleActivityDelegate != null;
mModuleOnResumePending = false;
mModuleActivityDelegate.onResume();
}
public void setBottomBarContentView(View view) {
......@@ -770,10 +821,11 @@ public class CustomTabActivity extends ChromeActivity {
super.onStartWithNative();
BrowserSessionContentUtils.setActiveContentHandler(mBrowserSessionContentHandler);
if (mHasCreatedTabEarly && !mMainTab.isLoading()) postDeferredStartupIfNeeded();
if (mActivityDelegate != null) {
mActivityDelegate.onStart();
mActivityDelegate.onRestoreInstanceState(getSavedInstanceState());
mActivityDelegate.onPostCreate(getSavedInstanceState());
if (mModuleActivityDelegate != null) {
startModule();
} else if (isModuleLoading()) {
mModuleOnStartPending = true;
}
}
......@@ -811,7 +863,12 @@ public class CustomTabActivity extends ChromeActivity {
mWebappTimeSpentLogger = WebappCustomTabTimeSpentLogger.createInstanceAndStartTimer(
getIntent().getIntExtra(CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE,
ACTIVITY_TYPE_OTHER));
if (mActivityDelegate != null) mActivityDelegate.onResume();
if (mModuleActivityDelegate != null) {
resumeModule();
} else if (isModuleLoading()) {
mModuleOnResumePending = true;
}
}
@Override
......@@ -820,14 +877,16 @@ public class CustomTabActivity extends ChromeActivity {
if (mWebappTimeSpentLogger != null) {
mWebappTimeSpentLogger.onPause();
}
if (mActivityDelegate != null) mActivityDelegate.onPause();
if (mModuleActivityDelegate != null) mModuleActivityDelegate.onPause();
mModuleOnResumePending = false;
}
@Override
public void onStopWithNative() {
super.onStopWithNative();
BrowserSessionContentUtils.setActiveContentHandler(null);
if (mActivityDelegate != null) mActivityDelegate.onStop();
if (mModuleActivityDelegate != null) mModuleActivityDelegate.onStop();
mModuleOnStartPending = false;
if (mIsClosing) {
getTabModelSelector().closeAllTabs(true);
mTabPersistencePolicy.deleteMetadataStateFileAsync();
......@@ -839,21 +898,30 @@ public class CustomTabActivity extends ChromeActivity {
@Override
protected void onDestroyInternal() {
super.onDestroyInternal();
if (mActivityDelegate != null) mActivityDelegate.onDestroy();
mConnection.maybeUnloadModule(mIntentDataProvider.getModulePackageName(),
mIntentDataProvider.getModuleClassName());
if (mLoadModuleCancelRunnable != null) {
mLoadModuleCancelRunnable.run();
mLoadModuleCancelRunnable = null;
}
if (mModuleActivityDelegate != null) {
mModuleActivityDelegate.onDestroy();
mModuleActivityDelegate = null;
}
ComponentName moduleComponentName = mIntentDataProvider.getModuleComponentName();
if (moduleComponentName != null) {
mConnection.getModuleLoader(moduleComponentName).maybeUnloadModule();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivityDelegate != null) mActivityDelegate.onSaveInstanceState(outState);
if (mModuleActivityDelegate != null) mModuleActivityDelegate.onSaveInstanceState(outState);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (mActivityDelegate != null) mActivityDelegate.onWindowFocusChanged(hasFocus);
if (mModuleActivityDelegate != null) mModuleActivityDelegate.onWindowFocusChanged(hasFocus);
}
/**
......@@ -1023,7 +1091,7 @@ public class CustomTabActivity extends ChromeActivity {
if (exitFullscreenIfShowing()) return true;
if (mActivityDelegate != null && mActivityDelegate.onBackPressed()) return true;
if (mModuleActivityDelegate != null && mModuleActivityDelegate.onBackPressed()) return true;
if (!getToolbarManager().back()) {
if (getCurrentTabModel().getCount() > 1) {
......
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.customtabs;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
......@@ -131,8 +132,7 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
private final int mInitialBackgroundColor;
private final boolean mDisableStar;
private final boolean mDisableDownload;
@Nullable private final String mModulePackageName;
@Nullable private final String mModuleClassName;
@Nullable private final ComponentName mModuleComponentName;
private int mToolbarColor;
private int mBottomBarColor;
......@@ -243,8 +243,15 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
mDisableStar = IntentUtils.safeGetBooleanExtra(intent, EXTRA_DISABLE_STAR_BUTTON, false);
mDisableDownload =
IntentUtils.safeGetBooleanExtra(intent, EXTRA_DISABLE_DOWNLOAD_BUTTON, false);
mModulePackageName = IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_PACKAGE_NAME);
mModuleClassName = IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_CLASS_NAME);
String modulePackageName =
IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_PACKAGE_NAME);
String moduleClassName = IntentUtils.safeGetStringExtra(intent, EXTRA_MODULE_CLASS_NAME);
if (modulePackageName != null && moduleClassName != null) {
mModuleComponentName = new ComponentName(modulePackageName, moduleClassName);
} else {
mModuleComponentName = null;
}
}
/**
......@@ -609,18 +616,10 @@ public class CustomTabIntentDataProvider extends BrowserSessionDataProvider {
}
/**
* @return The APK package to load the module from, or null if not specified.
*/
@Nullable
String getModulePackageName() {
return mModulePackageName;
}
/**
* @return The class name of the module entry point, or null if not specified.
* @return The component name of the module entry point, or null if not specified.
*/
@Nullable
String getModuleClassName() {
return mModuleClassName;
ComponentName getModuleComponentName() {
return mModuleComponentName;
}
}
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.customtabs;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
......@@ -23,7 +24,6 @@ import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsService;
import android.support.customtabs.CustomTabsSessionToken;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.RemoteViews;
import org.json.JSONException;
......@@ -53,9 +53,7 @@ import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils;
import org.chromium.chrome.browser.browserservices.Origin;
import org.chromium.chrome.browser.browserservices.PostMessageHandler;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleEntryPoint;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleLoader;
import org.chromium.chrome.browser.customtabs.dynamicmodule.ModuleMetrics;
import org.chromium.chrome.browser.device.DeviceClassManager;
import org.chromium.chrome.browser.init.ChainedTasks;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
......@@ -234,12 +232,7 @@ public class CustomTabsConnection {
private volatile ChainedTasks mWarmupTasks;
/** The module package name and class name. */
private Pair<String, String> mModuleNames;
private int mModuleUseCount;
/** The module entry point. */
private ModuleEntryPoint mModuleEntryPoint;
private ModuleLoader mModuleLoader;
/**
* <strong>DO NOT CALL</strong>
......@@ -1420,45 +1413,13 @@ public class CustomTabsConnection {
private static native void nativeCreateAndStartDetachedResourceRequest(
Profile profile, String url, String origin, @WebReferrerPolicy int referrerPolicy);
/**
* Dynamically loads the class {@code className} from the application identified by
* {@code packageName} and wraps it in a {@link ModuleEntryPoint}.
* @param packageName The package name of the application to load form.
* @param className The fully-qualified name of the class to load.
* @return The loaded class, cast to an AIDL interface and wrapped in a more user friendly form.
*/
@Nullable
public ModuleEntryPoint loadModule(String packageName, String className) {
if (mModuleEntryPoint != null && mModuleNames != null) {
if ((!mModuleNames.first.equals(packageName)
|| !mModuleNames.second.equals(className))) {
throw new IllegalStateException("Only one module can be loaded at a time.");
}
mModuleUseCount++;
ModuleMetrics.recordLoadResult(ModuleMetrics.LOAD_RESULT_SUCCESS_CACHED);
return mModuleEntryPoint;
}
mModuleNames = new Pair<>(packageName, className);
mModuleEntryPoint = ModuleLoader.loadModule(packageName, className);
if (mModuleEntryPoint != null) {
mModuleUseCount++;
ModuleMetrics.recordLoadResult(ModuleMetrics.LOAD_RESULT_SUCCESS_NEW);
}
return mModuleEntryPoint;
}
public void maybeUnloadModule(String packageName, String className) {
if (mModuleEntryPoint == null || mModuleNames == null) return;
if ((!mModuleNames.first.equals(packageName) || !mModuleNames.second.equals(className))) {
throw new IllegalStateException(
"There is no module for package " + packageName + " and class " + className);
}
mModuleUseCount--;
if (mModuleUseCount == 0) {
mModuleEntryPoint.onDestroy();
mModuleEntryPoint = null;
mModuleNames = null;
public ModuleLoader getModuleLoader(ComponentName componentName) {
if (mModuleLoader == null) mModuleLoader = new ModuleLoader(componentName);
if (!componentName.equals(mModuleLoader.getComponentName())) {
throw new IllegalStateException("The given component name " + componentName
+ " does not match the initialized component name "
+ mModuleLoader.getComponentName());
}
return mModuleLoader;
}
}
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