summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Gustav Sennton <gsennton@google.com> 2016-06-14 15:32:19 +0100
committer Gustav Sennton <gsennton@google.com> 2016-06-16 15:45:45 +0100
commit0df2c5566a9d7d71ca84cb072114f051bbe7a344 (patch)
tree5bea704533281c61bc3b9bc99771a2520ca83a24
parent7ce75172ddb1c6535bdf3f7f7bc84f65d681e958 (diff)
Support using uninstalled WebView packages as WebView implementation.
A package can be uninstalled for the system user but still be installed on the device. Currently, we do not use such a package as WebView implementation which means we fall back to using the fallback WebView package. However, if the fallback package is not valid (because it needs to be updated first) loading WebView will fail. With this change we fetch allow the fetching of information from packages that are uninstalled for the system user so that we can still load WebView when the only valid WebView provider is uninstalled for the system user. Also listen to package additions/changes/removals for all users - otherwise we won't notice when a package becomes replaced if it was already uninstalled for the system user. Bug: 29321185 Change-Id: Ia23c4493844877aea1b4eab7e666fd37540c4f97
-rw-r--r--services/core/java/com/android/server/webkit/SystemImpl.java3
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java16
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java336
4 files changed, 342 insertions, 60 deletions
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 361f0d47fc61..bb76449a8529 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 @@ public class SystemImpl implements SystemInterface {
// 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 9b971e0557f8..846169cbf9c3 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 @@ public class WebViewUpdateService extends SystemService {
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 @@ public class WebViewUpdateService extends SystemService {
// 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 @@ public class WebViewUpdateService extends SystemService {
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 ecab009ed12c..2cf17229a5bf 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.ApplicationInfo;
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 @@ public class WebViewUpdateServiceImpl {
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 @@ public class WebViewUpdateServiceImpl {
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 @@ public class WebViewUpdateServiceImpl {
}
WebViewProviderInfo[] getValidWebViewPackages() {
- return mWebViewUpdater.getValidWebViewPackages();
+ return mWebViewUpdater.getValidAndInstalledWebViewPackages();
}
WebViewProviderInfo[] getWebViewPackages() {
@@ -254,6 +258,12 @@ public class WebViewUpdateServiceImpl {
// (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 @@ public class WebViewUpdateServiceImpl {
}
}
- 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 @@ public class WebViewUpdateServiceImpl {
/**
* 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 @@ public class WebViewUpdateServiceImpl {
*
*/
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 @@ public class WebViewUpdateServiceImpl {
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 c03324aa9e32..b7370331ad0e 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
}
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
}
}).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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
} 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
// 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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 @@ public class WebViewUpdateServiceTest extends AndroidTestCase {
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 */);
+ }
+
}