Add initial unit tests for WebViewUpdateService.

The logic in the WebViewUpdateService is now more complex and there
are lots of edge cases that should be tested to make sure we don't
regress anything.

Also refrain from running fallback logic on boot if fallback logic not
enabled (bug found through a failing test, yeeah buddy!).

Catch uncaught MissingWebViewException (very timing-dependent!) and add
test for this.

Add tests for:
Some package setups (single package, non-default vs. default packages,
    valid vs. invalid packages).
Ensure correct error codes used
Switching provider through settings-method
Switching provider through adding more prioritized provider
Switching provider in the middle of already running webview preparation
Ensure fallback logic is run when enabled and not run when disabled (at
    boot, packageStateChanged, and when adding a new user).

Bug: 27635535

Change-Id: I275ecb0f6129f71258da0fa053e7f7b36a675886
This commit is contained in:
Gustav Sennton
2016-04-07 15:56:10 +01:00
parent b026b6a40f
commit 53b78241ff
3 changed files with 736 additions and 2 deletions

View File

@@ -119,6 +119,8 @@ public class WebViewUpdateServiceImpl {
}
private void updateFallbackStateOnBoot() {
if (!mSystemInterface.isFallbackLogicEnabled()) return;
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
updateFallbackState(webviewProviders, true);
}
@@ -497,8 +499,15 @@ public class WebViewUpdateServiceImpl {
mWebViewPackageDirty = false;
// If we have changed provider since we started the relro creation we need to
// redo the whole process using the new package instead.
PackageInfo newPackage = findPreferredWebViewPackage();
onWebViewProviderChanged(newPackage);
try {
PackageInfo newPackage = findPreferredWebViewPackage();
onWebViewProviderChanged(newPackage);
} catch (WebViewFactory.MissingWebViewPackageException e) {
// If we can't find any valid WebView package we are now in a state where
// mAnyWebViewInstalled is false, so loading WebView will be blocked and we
// should simply wait until we receive an intent declaring a new package was
// installed.
}
} else {
mLock.notifyAll();
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.webkit;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.webkit.WebViewProviderInfo;
import java.util.HashMap;
public class TestSystemImpl implements SystemInterface {
private String mUserProvider = "";
private final WebViewProviderInfo[] mPackageConfigs;
HashMap<String, PackageInfo> mPackages = new HashMap();
private boolean mFallbackLogicEnabled;
private final int mNumRelros;
private final boolean mIsDebuggable;
public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
int numRelros, boolean isDebuggable) {
mPackageConfigs = packageConfigs;
mFallbackLogicEnabled = fallbackLogicEnabled;
mNumRelros = numRelros;
mIsDebuggable = isDebuggable;
}
@Override
public WebViewProviderInfo[] getWebViewPackages() {
return mPackageConfigs;
}
@Override
public int onWebViewProviderChanged(PackageInfo packageInfo) {
return mNumRelros;
}
@Override
public String getUserChosenWebViewProvider(Context context) { return mUserProvider; }
@Override
public void updateUserSetting(Context context, String newProviderName) {
mUserProvider = newProviderName;
}
@Override
public void killPackageDependents(String packageName) {}
@Override
public boolean isFallbackLogicEnabled() {
return mFallbackLogicEnabled;
}
@Override
public void enableFallbackLogic(boolean enable) {
mFallbackLogicEnabled = enable;
}
@Override
public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
enablePackageForAllUsers(context, packageName, false);
}
@Override
public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
enablePackageForUser(packageName, enable, 0);
}
@Override
public void enablePackageForUser(String packageName, boolean enable, int userId) {
PackageInfo packageInfo = mPackages.get(packageName);
if (packageInfo == null) {
throw new IllegalArgumentException("There is no package called " + packageName);
}
packageInfo.applicationInfo.enabled = enable;
setPackageInfo(packageInfo);
}
@Override
public boolean systemIsDebuggable() { return mIsDebuggable; }
@Override
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo info) throws
NameNotFoundException {
PackageInfo ret = mPackages.get(info.packageName);
if (ret == null) throw new NameNotFoundException(info.packageName);
return ret;
}
public void setPackageInfo(PackageInfo pi) {
mPackages.put(pi.packageName, pi);
}
@Override
public int getFactoryPackageVersion(String packageName) {
return 0;
}
}

View File

@@ -0,0 +1,613 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.webkit;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.os.Bundle;
import android.util.Base64;
import android.test.AndroidTestCase;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
import java.util.concurrent.CountDownLatch;
import org.hamcrest.Description;
import org.mockito.Mockito;
import org.mockito.Matchers;
import org.mockito.ArgumentMatcher;
/**
* Tests for WebViewUpdateService
*/
public class WebViewUpdateServiceTest extends AndroidTestCase {
private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
private TestSystemImpl mTestSystemImpl;
private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
@Override
protected void setUp() throws Exception {
super.setUp();
}
/**
* Creates a new instance.
*/
public WebViewUpdateServiceTest() {
}
private void setupWithPackages(WebViewProviderInfo[] packages) {
setupWithPackages(packages, true);
}
private void setupWithPackages(WebViewProviderInfo[] packages,
boolean fallbackLogicEnabled) {
setupWithPackages(packages, fallbackLogicEnabled, 1);
}
private void setupWithPackages(WebViewProviderInfo[] packages,
boolean fallbackLogicEnabled, int numRelros) {
setupWithPackages(packages, fallbackLogicEnabled, numRelros,
true /* isDebuggable == true -> don't check package signatures */);
}
private void setupWithPackages(WebViewProviderInfo[] packages,
boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) {
TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
isDebuggable);
mTestSystemImpl = Mockito.spy(testing);
mWebViewUpdateServiceImpl =
new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
}
private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
for(WebViewProviderInfo wpi : providers) {
mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
true /* valid */));
}
}
private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
WebViewProviderInfo[] webviewPackages) {
checkCertainPackageUsedAfterWebViewPreparation(expectedProviderName, webviewPackages, 1);
}
private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
WebViewProviderInfo[] webviewPackages, int numRelros) {
setupWithPackages(webviewPackages, true, numRelros);
// Add (enabled and valid) package infos for each provider
setEnabledAndValidPackageInfos(webviewPackages);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
for (int n = 0; n < numRelros; n++) {
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
}
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
assertEquals(expectedProviderName, response.packageInfo.packageName);
}
// For matching the package name of a PackageInfo
private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> {
private final String mPackageName;
IsPackageInfoWithName(String name) {
mPackageName = name;
}
@Override
public boolean matches(Object p) {
return ((PackageInfo) p).packageName.equals(mPackageName);
}
// Provide a more useful description in case of mismatch
@Override
public void describeTo (Description description) {
description.appendText(String.format("PackageInfo with name '%s'", mPackageName));
}
}
private static PackageInfo createPackageInfo(
String packageName, boolean enabled, boolean valid) {
PackageInfo p = new PackageInfo();
p.packageName = packageName;
p.applicationInfo = new ApplicationInfo();
p.applicationInfo.enabled = enabled;
p.applicationInfo.metaData = new Bundle();
if (valid) {
// no flag means invalid
p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
}
return p;
}
private static PackageInfo createPackageInfo(
String packageName, boolean enabled, boolean valid, Signature[] signatures) {
PackageInfo p = createPackageInfo(packageName, enabled, valid);
p.signatures = signatures;
return p;
}
// ****************
// Tests
// ****************
public void testWithSinglePackage() {
String testPackageName = "test.package.name";
checkCertainPackageUsedAfterWebViewPreparation(testPackageName,
new WebViewProviderInfo[] {
new WebViewProviderInfo(testPackageName, "",
true /*default available*/, false /* fallback */, null)});
}
public void testDefaultPackageUsedOverNonDefault() {
String defaultPackage = "defaultPackage";
String nonDefaultPackage = "nonDefaultPackage";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
new WebViewProviderInfo(defaultPackage, "", true, false, null)};
checkCertainPackageUsedAfterWebViewPreparation(defaultPackage, packages);
}
public void testSeveralRelros() {
String singlePackage = "singlePackage";
checkCertainPackageUsedAfterWebViewPreparation(
singlePackage,
new WebViewProviderInfo[] {
new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
2);
}
// Ensure that package with valid signatures is chosen rather than package with invalid
// signatures.
public void testWithSignatures() {
String validPackage = "valid package";
String invalidPackage = "invalid package";
Signature validSignature = new Signature("11");
Signature invalidExpectedSignature = new Signature("22");
Signature invalidPackageSignature = new Signature("33");
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
Base64.encodeToString(
invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
new WebViewProviderInfo(validPackage, "", true, false, new String[]{
Base64.encodeToString(
validSignature.toByteArray(), Base64.DEFAULT)})
};
setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
false /* isDebuggable */);
mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
true /* valid */, new Signature[]{invalidPackageSignature}));
mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
true /* valid */, new Signature[]{validSignature}));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(validPackage)));
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
assertEquals(validPackage, response.packageInfo.packageName);
WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
assertEquals(1, validPackages.length);
assertEquals(validPackage, validPackages[0].packageName);
}
public void testFailWaitingForRelro() {
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo("packagename", "", true, true, null)};
setupWithPackages(packages);
setEnabledAndValidPackageInfos(packages);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
// Never call notifyRelroCreation()
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
}
public void testFailListingEmptyWebviewPackages() {
WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
setupWithPackages(packages);
setEnabledAndValidPackageInfos(packages);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
Matchers.anyObject());
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
}
public void testFailListingInvalidWebviewPackage() {
WebViewProviderInfo wpi = new WebViewProviderInfo("", "", true, true, null);
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
setupWithPackages(packages);
mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true, false));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
}
// Test that switching provider using changeProviderAndSetting works.
public void testSwitchingProvider() {
String firstPackage = "first";
String secondPackage = "second";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage, "", true, false, null),
new WebViewProviderInfo(secondPackage, "", true, false, null)};
checkSwitchingProvider(packages, firstPackage, secondPackage);
}
public void testSwitchingProviderToNonDefault() {
String defaultPackage = "defaultPackage";
String nonDefaultPackage = "nonDefaultPackage";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(defaultPackage, "", true, false, null),
new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
}
private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
String finalPackage) {
checkCertainPackageUsedAfterWebViewPreparation(initialPackage, packages);
mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(finalPackage)));
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
WebViewProviderResponse secondResponse = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, secondResponse.status);
assertEquals(finalPackage, secondResponse.packageInfo.packageName);
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
}
// Change provider during relro creation by using changeProviderAndSetting
public void testSwitchingProviderDuringRelroCreation() {
checkChangingProviderDuringRelroCreation(true);
}
// Change provider during relro creation by enabling a provider
public void testChangingProviderThroughEnablingDuringRelroCreation() {
checkChangingProviderDuringRelroCreation(false);
}
private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
String firstPackage = "first";
String secondPackage = "second";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage, "", true, false, null),
new WebViewProviderInfo(secondPackage, "", true, false, null)};
setupWithPackages(packages);
if (settingsChange) {
// Have all packages be enabled, so that we can change provider however we want to
setEnabledAndValidPackageInfos(packages);
} else {
// Have all packages be disabled so that we can change one to enabled later
for(WebViewProviderInfo wpi : packages) {
mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
false /* enabled */, true /* valid */));
}
}
CountDownLatch countdown = new CountDownLatch(1);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
assertEquals(firstPackage, mWebViewUpdateServiceImpl.getCurrentWebViewPackageName());
new Thread(new Runnable() {
@Override
public void run() {
WebViewProviderResponse threadResponse =
mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
assertEquals(secondPackage, threadResponse.packageInfo.packageName);
// Verify that we killed the first package
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
countdown.countDown();
}
}).start();
try {
Thread.sleep(1000); // Let the new thread run / be blocked
} catch (InterruptedException e) {
}
if (settingsChange) {
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
} else {
// Switch provider by enabling the second one
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(
secondPackage, WebViewUpdateService.PACKAGE_CHANGED);
}
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
// first package done, should start on second
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
// second package done, the other thread should now be unblocked
try {
countdown.await();
} catch (InterruptedException e) {
}
}
public void testRunFallbackLogicIfEnabled() {
checkFallbackLogicBeingRun(true);
}
public void testDontRunFallbackLogicIfDisabled() {
checkFallbackLogicBeingRun(false);
}
private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(
primaryPackage, "", true /* default available */, false /* fallback */, null),
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, fallbackLogicEnabled);
setEnabledAndValidPackageInfos(packages);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
// Verify that we disable the fallback package if fallback logic enabled, and don't disable
// the fallback package if that logic is disabled
if (fallbackLogicEnabled) {
Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Mockito.eq(fallbackPackage));
} else {
Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Matchers.anyObject());
}
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
// Enable fallback package
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(
fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED);
if (fallbackLogicEnabled) {
// Check that we have now disabled the fallback package twice
Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Mockito.eq(fallbackPackage));
} else {
// Check that we still haven't disabled any package
Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Matchers.anyObject());
}
}
/**
* Scenario for installing primary package when fallback enabled.
* 1. Start with only fallback installed
* 2. Install non-fallback
* 3. Fallback should be disabled
*/
public void testInstallingNonFallbackPackage() {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(
primaryPackage, "", true /* default available */, false /* fallback */, null),
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, true /* isFallbackLogicEnabled */);
mTestSystemImpl.setPackageInfo(
createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Matchers.anyObject());
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(fallbackPackage)));
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
assertEquals(fallbackPackage, response.packageInfo.packageName);
// Install primary package
mTestSystemImpl.setPackageInfo(
createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_ADDED);
// Verify fallback disabled and primary package used as provider
Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Mockito.eq(fallbackPackage));
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
// Finish the webview preparation and ensure primary package used and fallback killed
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
assertEquals(primaryPackage, response.packageInfo.packageName);
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
}
public void testFallbackChangesEnabledState() {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(
primaryPackage, "", true /* default available */, false /* fallback */, null),
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, true /* fallbackLogicEnabled */);
setEnabledAndValidPackageInfos(packages);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
// Verify fallback disabled at boot when primary package enabled
Mockito.verify(mTestSystemImpl).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
Matchers.anyInt());
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());
mTestSystemImpl.setPackageInfo(
createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_CHANGED);
// Verify fallback is disabled a second time when primary package becomes enabled
Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
Matchers.anyInt());
}
public void testAddUserWhenFallbackLogicEnabled() {
checkAddingNewUser(true);
}
public void testAddUserWhenFallbackLogicDisabled() {
checkAddingNewUser(false);
}
public void checkAddingNewUser(boolean fallbackLogicEnabled) {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(
primaryPackage, "", true /* default available */, false /* fallback */, null),
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, fallbackLogicEnabled);
setEnabledAndValidPackageInfos(packages);
int newUser = 100;
mWebViewUpdateServiceImpl.handleNewUser(newUser);
if (fallbackLogicEnabled) {
// Verify fallback package becomes disabled for new user
Mockito.verify(mTestSystemImpl).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
Mockito.eq(newUser));
} else {
// Verify that we don't disable fallback for new user
Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
Mockito.anyObject(), Matchers.anyBoolean() /* enable */,
Matchers.anyInt() /* user */);
}
}
/**
* Timing dependent test where we verify that the list of valid webview packages becoming empty
* at a certain point doesn't crash us or break our state.
*/
public void testNotifyRelroDoesntCrashIfNoPackages() {
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)};
setupWithPackages(packages);
// Add (enabled and valid) package infos for each provider
setEnabledAndValidPackageInfos(packages);
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
// Make packages invalid to cause exception to be thrown
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
false /* valid */));
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
false /* valid */));
// This shouldn't throw an exception!
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
// Now make a package valid again and verify that we can switch back to that
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
true /* valid */));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_ADDED);
// Second time we call onWebViewProviderChanged for firstPackage
Mockito.verify(mTestSystemImpl, Mockito.times(2)).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
assertEquals(firstPackage, response.packageInfo.packageName);
}
// TODO (gsennton) add more tests for ensuring killPackageDependents is called / not called
}