Merge \"Support using uninstalled WebView packages as WebView implementation.\" into nyc-dev
am: e25c8532b6
Change-Id: Ie9f405765c146d21f1399037dd236d440902f0f9
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 361f0d4..bb76449 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -270,5 +270,6 @@
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+ | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 9b971e0..846169c 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -63,6 +63,7 @@
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
switch (intent.getAction()) {
case Intent.ACTION_PACKAGE_REMOVED:
// When a package is replaced we will receive two intents, one
@@ -73,24 +74,22 @@
// run the update-logic twice.
if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return;
mImpl.packageStateChanged(packageNameFromIntent(intent),
- PACKAGE_REMOVED);
+ PACKAGE_REMOVED, userId);
break;
case Intent.ACTION_PACKAGE_CHANGED:
// Ensure that we only heed PACKAGE_CHANGED intents if they change an
// entire package, not just a component
if (entirePackageChanged(intent)) {
mImpl.packageStateChanged(packageNameFromIntent(intent),
- PACKAGE_CHANGED);
+ PACKAGE_CHANGED, userId);
}
break;
case Intent.ACTION_PACKAGE_ADDED:
mImpl.packageStateChanged(packageNameFromIntent(intent),
(intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)
- ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED));
+ ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED), userId);
break;
case Intent.ACTION_USER_ADDED:
- int userId =
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mImpl.handleNewUser(userId);
break;
}
@@ -105,11 +104,14 @@
for (WebViewProviderInfo provider : mImpl.getWebViewPackages()) {
filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
}
- getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
+
+ getContext().registerReceiverAsUser(mWebViewUpdatedReceiver, UserHandle.ALL, filter,
+ null /* broadcast permission */, null /* handler */);
IntentFilter userAddedFilter = new IntentFilter();
userAddedFilter.addAction(Intent.ACTION_USER_ADDED);
- getContext().registerReceiver(mWebViewUpdatedReceiver, userAddedFilter);
+ getContext().registerReceiverAsUser(mWebViewUpdatedReceiver, UserHandle.ALL,
+ userAddedFilter, null /* broadcast permission */, null /* handler */);
publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index ecab009..2cf1722 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
+import android.os.UserHandle;
import android.util.Base64;
import android.util.Slog;
import android.webkit.WebViewFactory;
@@ -49,7 +50,10 @@
mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
}
- void packageStateChanged(String packageName, int changedState) {
+ void packageStateChanged(String packageName, int changedState, int userId) {
+ // We don't early out here in different cases where we could potentially early-out (e.g. if
+ // we receive PACKAGE_CHANGED for another user than the system user) since that would
+ // complicate this logic further and open up for more edge cases.
updateFallbackStateOnPackageChange(packageName, changedState);
mWebViewUpdater.packageStateChanged(packageName, changedState);
}
@@ -64,7 +68,7 @@
if (provider.availableByDefault && !provider.isFallback) {
try {
PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
- if (isEnabledPackage(packageInfo)
+ if (isInstalledPackage(packageInfo) && isEnabledPackage(packageInfo)
&& mWebViewUpdater.isValidProvider(provider, packageInfo)) {
return true;
}
@@ -103,7 +107,7 @@
}
WebViewProviderInfo[] getValidWebViewPackages() {
- return mWebViewUpdater.getValidWebViewPackages();
+ return mWebViewUpdater.getValidAndInstalledWebViewPackages();
}
WebViewProviderInfo[] getWebViewPackages() {
@@ -254,6 +258,12 @@
// (not if it has been enabled/disabled).
return;
}
+ if (newPackage.packageName.equals(oldProviderName)
+ && (newPackage.lastUpdateTime
+ == mCurrentWebViewPackage.lastUpdateTime)) {
+ // If the chosen package hasn't been updated, then early-out
+ return;
+ }
}
// Only trigger update actions if the updated package is the one
// that will be used, or the one that was in use before the
@@ -373,14 +383,15 @@
}
}
- private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+ private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos(boolean onlyInstalled) {
WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
List<ProviderAndPackageInfo> providers = new ArrayList<>();
for(int n = 0; n < allProviders.length; n++) {
try {
PackageInfo packageInfo =
mSystemInterface.getPackageInfoForProvider(allProviders[n]);
- if (isValidProvider(allProviders[n], packageInfo)) {
+ if ((!onlyInstalled || isInstalledPackage(packageInfo))
+ && isValidProvider(allProviders[n], packageInfo)) {
providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
}
} catch (NameNotFoundException e) {
@@ -393,8 +404,9 @@
/**
* Fetch only the currently valid WebView packages.
**/
- public WebViewProviderInfo[] getValidWebViewPackages() {
- ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+ public WebViewProviderInfo[] getValidAndInstalledWebViewPackages() {
+ ProviderAndPackageInfo[] providersAndPackageInfos =
+ getValidWebViewPackagesAndInfos(true /* only fetch installed packages */);
WebViewProviderInfo[] providers =
new WebViewProviderInfo[providersAndPackageInfos.length];
for(int n = 0; n < providersAndPackageInfos.length; n++) {
@@ -421,29 +433,33 @@
*
*/
private PackageInfo findPreferredWebViewPackage() {
- ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+ ProviderAndPackageInfo[] providers =
+ getValidWebViewPackagesAndInfos(false /* onlyInstalled */);
String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
// If the user has chosen provider, use that
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.packageName.equals(userChosenProvider)
+ && isInstalledPackage(providerAndPackage.packageInfo)
&& isEnabledPackage(providerAndPackage.packageInfo)) {
return providerAndPackage.packageInfo;
}
}
// User did not choose, or the choice failed; use the most stable provider that is
- // enabled and available by default (not through user choice).
+ // installed and enabled for the device owner, and available by default (not through
+ // user choice).
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.availableByDefault
+ && isInstalledPackage(providerAndPackage.packageInfo)
&& isEnabledPackage(providerAndPackage.packageInfo)) {
return providerAndPackage.packageInfo;
}
}
- // Could not find any enabled package either, use the most stable and default-available
- // provider.
+ // Could not find any installed and enabled package either, use the most stable and
+ // default-available provider.
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.availableByDefault) {
return providerAndPackage.packageInfo;
@@ -642,4 +658,13 @@
return packageInfo.applicationInfo.enabled;
}
+ /**
+ * Return true if the package is installed and not hidden
+ */
+ private static boolean isInstalledPackage(PackageInfo packageInfo) {
+ return (((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
+ && ((packageInfo.applicationInfo.privateFlags
+ & ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index c03324a..b737033 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -86,7 +86,7 @@
private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
for(WebViewProviderInfo wpi : providers) {
mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
- true /* valid */));
+ true /* valid */, true /* installed */));
}
}
@@ -137,12 +137,17 @@
}
private static PackageInfo createPackageInfo(
- String packageName, boolean enabled, boolean valid) {
+ String packageName, boolean enabled, boolean valid, boolean installed) {
PackageInfo p = new PackageInfo();
p.packageName = packageName;
p.applicationInfo = new ApplicationInfo();
p.applicationInfo.enabled = enabled;
p.applicationInfo.metaData = new Bundle();
+ if (installed) {
+ p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ } else {
+ p.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
if (valid) {
// no flag means invalid
p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
@@ -150,10 +155,23 @@
return p;
}
- private static PackageInfo createPackageInfo(
- String packageName, boolean enabled, boolean valid, Signature[] signatures) {
- PackageInfo p = createPackageInfo(packageName, enabled, valid);
+ private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
+ boolean installed, Signature[] signatures, long updateTime) {
+ PackageInfo p = createPackageInfo(packageName, enabled, valid, installed);
p.signatures = signatures;
+ p.lastUpdateTime = updateTime;
+ return p;
+ }
+
+ private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
+ boolean installed, Signature[] signatures, long updateTime, boolean hidden) {
+ PackageInfo p =
+ createPackageInfo(packageName, enabled, valid, installed, signatures, updateTime);
+ if (hidden) {
+ p.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+ } else {
+ p.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+ }
return p;
}
@@ -223,9 +241,11 @@
setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
false /* isDebuggable */);
mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
- true /* valid */, new Signature[]{invalidPackageSignature}));
+ true /* valid */, true /* installed */, new Signature[]{invalidPackageSignature}
+ , 0 /* updateTime */));
mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
- true /* valid */, new Signature[]{validSignature}));
+ true /* valid */, true /* installed */, new Signature[]{validSignature}
+ , 0 /* updateTime */));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
@@ -273,7 +293,8 @@
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
setupWithPackages(packages);
mTestSystemImpl.setPackageInfo(
- createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */));
+ createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
@@ -285,9 +306,10 @@
// Verify that we can recover from failing to list webview packages.
mTestSystemImpl.setPackageInfo(
- createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */));
+ createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
- WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
checkPreparationPhasesForPackage(wpi.packageName, 1);
}
@@ -345,7 +367,7 @@
// 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 */));
+ false /* enabled */, true /* valid */, true /* installed */));
}
}
@@ -371,7 +393,7 @@
}
}).start();
try {
- Thread.sleep(1000); // Let the new thread run / be blocked
+ Thread.sleep(500); // Let the new thread run / be blocked
} catch (InterruptedException e) {
}
@@ -380,9 +402,9 @@
} else {
// Switch provider by enabling the second one
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
- true /* valid */));
+ true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(
- secondPackage, WebViewUpdateService.PACKAGE_CHANGED);
+ secondPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
}
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
// first package done, should start on second
@@ -432,9 +454,9 @@
// Enable fallback package
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
- true /* valid */));
+ true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(
- fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED);
+ fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
if (fallbackLogicEnabled) {
// Check that we have now disabled the fallback package twice
@@ -463,7 +485,8 @@
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, true /* isFallbackLogicEnabled */);
mTestSystemImpl.setPackageInfo(
- createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */));
+ createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
@@ -474,9 +497,10 @@
// Install primary package
mTestSystemImpl.setPackageInfo(
- createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
+ createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
- WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
// Verify fallback disabled, primary package used as provider, and fallback package killed
Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
@@ -507,9 +531,10 @@
// Disable primary package and ensure fallback becomes enabled and used
mTestSystemImpl.setPackageInfo(
- createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
+ createPackageInfo(primaryPackage, false /* enabled */, true /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
- WebViewUpdateService.PACKAGE_CHANGED);
+ WebViewUpdateService.PACKAGE_CHANGED, 0);
Mockito.verify(mTestSystemImpl).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
@@ -520,9 +545,10 @@
// Again enable primary package and verify primary is used and fallback becomes disabled
mTestSystemImpl.setPackageInfo(
- createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
+ createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
- WebViewUpdateService.PACKAGE_CHANGED);
+ WebViewUpdateService.PACKAGE_CHANGED, 0);
// Verify fallback is disabled a second time when primary package becomes enabled
Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
@@ -593,9 +619,10 @@
// Make packages invalid to cause exception to be thrown
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
- false /* valid */));
+ false /* valid */, true /* installed */, null /* signatures */,
+ 0 /* updateTime */));
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
- false /* valid */));
+ false /* valid */, true /* installed */));
// This shouldn't throw an exception!
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
@@ -605,10 +632,11 @@
// Now make a package valid again and verify that we can switch back to that
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
- true /* valid */));
+ true /* valid */, true /* installed */, null /* signatures */,
+ 1 /* updateTime */ ));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
- WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
// Ensure we use firstPackage
checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
@@ -634,16 +662,16 @@
// Remove second package (invalidate it) and verify that first package is used
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
- false /* valid */));
+ false /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
- WebViewUpdateService.PACKAGE_ADDED);
+ WebViewUpdateService.PACKAGE_ADDED, 0);
checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */);
// Now make the second package valid again and verify that it is used again
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
- true /* valid */));
+ true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
- WebViewUpdateService.PACKAGE_ADDED);
+ WebViewUpdateService.PACKAGE_ADDED, 0);
checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
}
@@ -663,7 +691,7 @@
setupWithPackages(packages);
// Only 'install' nonChosenPackage
mTestSystemImpl.setPackageInfo(
- createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */));
+ createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */, true /* installed */));
// Set user-chosen package
mTestSystemImpl.updateUserSetting(null, chosenPackage);
@@ -702,16 +730,16 @@
// Make both packages invalid so that we fail listing WebView packages
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
- false /* valid */));
+ false /* valid */, true /* installed */));
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
- false /* valid */));
+ false /* valid */, true /* installed */));
// Change package to hit the webview packages listing problem.
if (settingsChange) {
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
} else {
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
- WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
}
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
@@ -719,10 +747,10 @@
// Make second package valid and verify that we can load it again
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
- true /* valid */));
+ true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
- WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
checkPreparationPhasesForPackage(secondPackage, 1);
@@ -749,13 +777,14 @@
// Replace or remove the current webview package
if (replaced) {
mTestSystemImpl.setPackageInfo(
- createPackageInfo(firstPackage, true /* enabled */, false /* valid */));
+ createPackageInfo(firstPackage, true /* enabled */, false /* valid */,
+ true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
- WebViewUpdateService.PACKAGE_ADDED_REPLACED);
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
} else {
mTestSystemImpl.removePackageInfo(firstPackage);
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
- WebViewUpdateService.PACKAGE_REMOVED);
+ WebViewUpdateService.PACKAGE_REMOVED, 0);
}
checkPreparationPhasesForPackage(secondPackage, 1);
@@ -806,7 +835,7 @@
checkPreparationPhasesForPackage(thirdPackage, 1);
mTestSystemImpl.setPackageInfo(
- createPackageInfo(secondPackage, true /* enabled */, false /* valid */));
+ createPackageInfo(secondPackage, true /* enabled */, false /* valid */, true /* installed */));
// 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.
@@ -817,4 +846,229 @@
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage));
}
+
+ // Ensure that the update service uses an uninstalled package if that is the only package
+ // available.
+ public void testWithSingleUninstalledPackage() {
+ String testPackageName = "test.package.name";
+ WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(testPackageName, "",
+ true /*default available*/, false /* fallback */, null)};
+ setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */,
+ true /* valid */, false /* installed */));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */);
+ }
+
+ public void testNonhiddenPackageUserOverHidden() {
+ checkVisiblePackageUserOverNonVisible(false /* true == uninstalled, false == hidden */);
+ }
+
+ public void testInstalledPackageUsedOverUninstalled() {
+ checkVisiblePackageUserOverNonVisible(true /* true == uninstalled, false == hidden */);
+ }
+
+ private void checkVisiblePackageUserOverNonVisible(boolean uninstalledNotHidden) {
+ boolean testUninstalled = uninstalledNotHidden;
+ boolean testHidden = !uninstalledNotHidden;
+ String installedPackage = "installedPackage";
+ String uninstalledPackage = "uninstalledPackage";
+ WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
+ false /* fallback */, null),
+ new WebViewProviderInfo(installedPackage, "", true /* available by default */,
+ false /* fallback */, null)};
+
+ setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
+ true /* valid */, true /* installed */));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
+ true /* valid */, (testUninstalled ? false : true) /* installed */,
+ null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
+ }
+
+ public void testCantSwitchToHiddenPackage () {
+ checkCantSwitchToNonVisiblePackage(false /* true == uninstalled, false == hidden */);
+ }
+
+
+ public void testCantSwitchToUninstalledPackage () {
+ checkCantSwitchToNonVisiblePackage(true /* true == uninstalled, false == hidden */);
+ }
+
+ /**
+ * Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen,
+ * and that an uninstalled (or hidden) package is not considered valid (in the
+ * getValidWebViewPackages() API).
+ */
+ private void checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden) {
+ boolean testUninstalled = uninstalledNotHidden;
+ boolean testHidden = !uninstalledNotHidden;
+ String installedPackage = "installedPackage";
+ String uninstalledPackage = "uninstalledPackage";
+ WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
+ false /* fallback */, null),
+ new WebViewProviderInfo(installedPackage, "", true /* available by default */,
+ false /* fallback */, null)};
+
+ setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
+ true /* valid */, true /* installed */));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
+ true /* valid */, (testUninstalled ? false : true) /* installed */,
+ null /* signatures */, 0 /* updateTime */,
+ (testHidden ? true : false) /* hidden */));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
+
+ // Ensure that only the installed package is considered valid
+ WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
+ assertEquals(1, validPackages.length);
+ assertEquals(installedPackage, validPackages[0].packageName);
+
+ // ensure that we don't switch to the uninstalled package (it will be used if it becomes
+ // installed later)
+ assertEquals(installedPackage,
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(uninstalledPackage));
+
+ // We should only have called onWebViewProviderChanged once (before calling
+ // changeProviderAndSetting
+ Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(installedPackage)));
+ }
+
+ public void testHiddenPackageNotPrioritizedEvenIfChosen() {
+ checkNonvisiblePackageNotPrioritizedEvenIfChosen(
+ false /* true == uninstalled, false == hidden */);
+ }
+
+ public void testUninstalledPackageNotPrioritizedEvenIfChosen() {
+ checkNonvisiblePackageNotPrioritizedEvenIfChosen(
+ true /* true == uninstalled, false == hidden */);
+ }
+
+ public void checkNonvisiblePackageNotPrioritizedEvenIfChosen(boolean uninstalledNotHidden) {
+ boolean testUninstalled = uninstalledNotHidden;
+ boolean testHidden = !uninstalledNotHidden;
+ String installedPackage = "installedPackage";
+ String uninstalledPackage = "uninstalledPackage";
+ WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */,
+ false /* fallback */, null),
+ new WebViewProviderInfo(installedPackage, "", true /* available by default */,
+ false /* fallback */, null)};
+
+ setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
+ true /* valid */, true /* installed */));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
+ true /* valid */, (testUninstalled ? false : true) /* installed */,
+ null /* signatures */, 0 /* updateTime */,
+ (testHidden ? true : false) /* hidden */));
+
+ // Start with the setting pointing to the uninstalled package
+ mTestSystemImpl.updateUserSetting(null, uninstalledPackage);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
+ }
+
+ /**
+ * Ensures that fallback becomes enabled if the primary package is uninstalled for the current
+ * user.
+ */
+ public void testFallbackEnabledIfPrimaryUninstalled() {
+ 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 /* fallback logic enabled */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+ true /* valid */, false /* installed */));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
+ true /* valid */, true /* installed */));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ // Verify that we enable the fallback package
+ Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
+ Mockito.anyObject(), Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */);
+
+ checkPreparationPhasesForPackage(fallbackPackage, 1 /* first preparation phase */);
+ }
+
+ public void testPreparationRunsIffNewPackage() {
+ 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 /* fallback logic enabled */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+ true /* valid */, true /* installed */, null /* signatures */,
+ 10 /* lastUpdateTime*/ ));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
+ true /* valid */, true /* installed */));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
+ Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Matchers.anyInt() /* user */);
+
+
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0 /* userId */);
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 1 /* userId */);
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2 /* userId */);
+ // package still has the same update-time so we shouldn't run preparation here
+ Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+ Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Matchers.anyInt() /* user */);
+
+ // Ensure we can still load the package
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(primaryPackage, response.packageInfo.packageName);
+
+
+ mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+ true /* valid */, true /* installed */, null /* signatures */,
+ 20 /* lastUpdateTime*/ ));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
+ // The package has now changed - ensure that we have run the preparation phase a second time
+ checkPreparationPhasesForPackage(primaryPackage, 2 /* second preparation phase */);
+
+
+ mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
+ true /* valid */, true /* installed */, null /* signatures */,
+ 50 /* lastUpdateTime*/ ));
+ // Receive intent for different user
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2);
+
+ checkPreparationPhasesForPackage(primaryPackage, 3 /* third preparation phase */);
+ }
+
}