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