Merge "Fix the behaviour for explicitly switching WebView provider" into nyc-dev

This commit is contained in:
Gustav Sennton
2016-04-14 17:30:57 +00:00
committed by Android (Google) Code Review
3 changed files with 189 additions and 19 deletions

View File

@@ -308,38 +308,42 @@ public class WebViewUpdateServiceImpl {
/**
* Change WebView provider and provider setting and kill packages using the old provider.
* Return the new provider (in case we are in the middle of creating relro files this new
* provider will not be in use directly, but will when the relros are done).
* Return the new provider (in case we are in the middle of creating relro files, or
* replacing that provider it will not be in use directly, but will be used when the relros
* or the replacement are done).
*/
public String changeProviderAndSetting(String newProviderName) {
PackageInfo oldPackage = null;
PackageInfo newPackage = null;
boolean providerChanged = false;
synchronized(mLock) {
oldPackage = mCurrentWebViewPackage;
mSystemInterface.updateUserSetting(mContext, newProviderName);
try {
newPackage = findPreferredWebViewPackage();
if (oldPackage != null
&& newPackage.packageName.equals(oldPackage.packageName)) {
// If we don't perform the user change, revert the settings change.
mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
return newPackage.packageName;
}
providerChanged = (oldPackage == null)
|| !newPackage.packageName.equals(oldPackage.packageName);
} catch (WebViewFactory.MissingWebViewPackageException e) {
Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
"package " + e);
// If we don't perform the user change but don't have an installed WebView
// package, we will have changed the setting and it will be used when a package
// is available.
return newProviderName;
return "";
}
// Perform the provider change if we chose a new provider
if (providerChanged) {
onWebViewProviderChanged(newPackage);
}
onWebViewProviderChanged(newPackage);
}
// Kill apps using the old provider
if (oldPackage != null) {
// Kill apps using the old provider only if we changed provider
if (providerChanged && oldPackage != null) {
mSystemInterface.killPackageDependents(oldPackage.packageName);
}
// Return the new provider, this is not necessarily the one we were asked to switch to
// But the persistent setting will now be pointing to the provider we were asked to
// switch to anyway
return newPackage.packageName;
}

View File

@@ -24,7 +24,7 @@ import android.webkit.WebViewProviderInfo;
import java.util.HashMap;
public class TestSystemImpl implements SystemInterface {
private String mUserProvider = "";
private String mUserProvider = null;
private final WebViewProviderInfo[] mPackageConfigs;
HashMap<String, PackageInfo> mPackages = new HashMap();
private boolean mFallbackLogicEnabled;
@@ -105,6 +105,10 @@ public class TestSystemImpl implements SystemInterface {
mPackages.put(pi.packageName, pi);
}
public void removePackageInfo(String packageName) {
mPackages.remove(packageName);
}
@Override
public int getFactoryPackageVersion(String packageName) {
return 0;

View File

@@ -269,14 +269,27 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
}
public void testFailListingInvalidWebviewPackage() {
WebViewProviderInfo wpi = new WebViewProviderInfo("", "", true, true, null);
WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null);
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
setupWithPackages(packages);
mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true, false));
mTestSystemImpl.setPackageInfo(
createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
Matchers.anyObject());
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
// Verify that we can recover from failing to list webview packages.
mTestSystemImpl.setPackageInfo(
createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
WebViewUpdateService.PACKAGE_ADDED_REPLACED);
checkPreparationPhasesForPackage(wpi.packageName, 1);
}
// Test that switching provider using changeProviderAndSetting works.
@@ -463,7 +476,7 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
mTestSystemImpl.setPackageInfo(
createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_ADDED);
WebViewUpdateService.PACKAGE_ADDED_REPLACED);
// Verify fallback disabled, primary package used as provider, and fallback package killed
Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
@@ -490,16 +503,22 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
Matchers.anyInt());
checkPreparationPhasesForPackage(primaryPackage, 1);
// Disable primary package and ensure fallback becomes enabled and used
mTestSystemImpl.setPackageInfo(
createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_CHANGED);
// Verify fallback becomes enabled when primary package becomes disabled
Mockito.verify(mTestSystemImpl).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
Matchers.anyInt());
checkPreparationPhasesForPackage(fallbackPackage, 1);
// Again enable primary package and verify primary is used and fallback becomes disabled
mTestSystemImpl.setPackageInfo(
createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
@@ -509,6 +528,8 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
Matchers.anyInt());
checkPreparationPhasesForPackage(primaryPackage, 2);
}
public void testAddUserWhenFallbackLogicEnabled() {
@@ -565,6 +586,9 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
// Change provider during relro creation to enter a state where we are
// waiting for relro creation to complete just to re-run relro creation.
// (so that in next notifyRelroCreationCompleted() call we have to list webview packages)
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
// Make packages invalid to cause exception to be thrown
@@ -584,7 +608,7 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_ADDED);
WebViewUpdateService.PACKAGE_ADDED_REPLACED);
// Ensure we use firstPackage
checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
@@ -654,5 +678,143 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
checkPreparationPhasesForPackage(nonChosenPackage, 1);
}
// TODO (gsennton) add more tests for ensuring killPackageDependents is called / not called
public void testRecoverFailedListingWebViewPackagesSettingsChange() {
checkRecoverAfterFailListingWebviewPackages(true);
}
public void testRecoverFailedListingWebViewPackagesAddedPackage() {
checkRecoverAfterFailListingWebviewPackages(false);
}
/**
* Test that we can recover correctly from failing to list WebView packages.
* settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged
*/
public void checkRecoverAfterFailListingWebviewPackages(boolean settingsChange) {
String firstPackage = "first";
String secondPackage = "second";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage, "", true /* default available */,
false /* fallback */, null),
new WebViewProviderInfo(secondPackage, "", true /* default available */,
false /* fallback */, null)};
checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
// Make both packages invalid so that we fail listing WebView packages
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
false /* valid */));
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
false /* valid */));
// Change package to hit the webview packages listing problem.
if (settingsChange) {
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
} else {
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED);
}
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
// Make second package valid and verify that we can load it again
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED);
checkPreparationPhasesForPackage(secondPackage, 1);
}
public void testDontKillIfPackageReplaced() {
checkDontKillIfPackageRemoved(true);
}
public void testDontKillIfPackageRemoved() {
checkDontKillIfPackageRemoved(false);
}
public void checkDontKillIfPackageRemoved(boolean replaced) {
String firstPackage = "first";
String secondPackage = "second";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage, "", true /* default available */,
false /* fallback */, null),
new WebViewProviderInfo(secondPackage, "", true /* default available */,
false /* fallback */, null)};
checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
// Replace or remove the current webview package
if (replaced) {
mTestSystemImpl.setPackageInfo(
createPackageInfo(firstPackage, true /* enabled */, false /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED);
} else {
mTestSystemImpl.removePackageInfo(firstPackage);
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_REMOVED);
}
checkPreparationPhasesForPackage(secondPackage, 1);
Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents(
Mockito.anyObject());
}
public void testKillIfSettingChanged() {
String firstPackage = "first";
String secondPackage = "second";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage, "", true /* default available */,
false /* fallback */, null),
new WebViewProviderInfo(secondPackage, "", true /* default available */,
false /* fallback */, null)};
checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages);
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
checkPreparationPhasesForPackage(secondPackage, 1);
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
}
/**
* Test that we kill apps using an old provider when we change the provider setting, even if the
* new provider is not the one we intended to change to.
*/
public void testKillIfChangeProviderIncorrectly() {
String firstPackage = "first";
String secondPackage = "second";
String thirdPackage = "third";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage, "", true /* default available */,
false /* fallback */, null),
new WebViewProviderInfo(secondPackage, "", true /* default available */,
false /* fallback */, null),
new WebViewProviderInfo(thirdPackage, "", true /* default available */,
false /* fallback */, null)};
setupWithPackages(packages);
setEnabledAndValidPackageInfos(packages);
// Start with the setting pointing to the third package
mTestSystemImpl.updateUserSetting(null, thirdPackage);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
checkPreparationPhasesForPackage(thirdPackage, 1);
mTestSystemImpl.setPackageInfo(
createPackageInfo(secondPackage, true /* enabled */, false /* valid */));
// Try to switch to the invalid second package, this should result in switching to the first
// package, since that is more preferred than the third one.
assertEquals(firstPackage,
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage));
checkPreparationPhasesForPackage(firstPackage, 1);
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage));
}
}