Change WebView fallback mechanism to consider all users' package states.

MultiProcess WebView will use a Service to start a separate process (the
renderer process). This Service can only be started if the WebView
package is enabled for the current user. To ensure that the current
WebView package is usable by all users on a single device we now only
use a WebView package as WebView implementation if that package is
enabled and installed for all user on the device. This also means that
the WebView-fallback mechanism will trigger when disabling the primary
WebView package for any user (not just the system user).

Also add multi-user unit tests to cover this new change.

Bug: 32894152
Test: run unit tests in WebViewUpdateServiceTest
Test: ensure the standalone WebView package becomes enabled (for all
device users) when disabling Chrome for a secondary user.
Test: load WebView (both using Monochrome, and using the standalone
WebView).

Change-Id: Iad3fc48aa50273062c2f29ae48a343c2dea38116
This commit is contained in:
Gustav Sennton
2016-12-14 09:10:50 +00:00
parent 1628b97e54
commit 364e160290
7 changed files with 537 additions and 189 deletions

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2017 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 android.webkit;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.UserManager;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class for storing a (user,PackageInfo) mapping.
* @hide
*/
public class UserPackage {
private final UserInfo mUserInfo;
private final PackageInfo mPackageInfo;
public UserPackage(UserInfo user, PackageInfo packageInfo) {
this.mUserInfo = user;
this.mPackageInfo = packageInfo;
}
/**
* Returns a list of (User,PackageInfo) pairs corresponding to the PackageInfos for all
* device users for the package named {@param packageName}.
*/
public static List<UserPackage> getPackageInfosAllUsers(Context context,
String packageName, int packageFlags) {
List<UserInfo> users = getAllUsers(context);
List<UserPackage> userPackages = new ArrayList<UserPackage>(users.size());
for (UserInfo user : users) {
PackageInfo packageInfo = null;
try {
packageInfo = context.getPackageManager().getPackageInfoAsUser(
packageName, packageFlags, user.id);
} catch (NameNotFoundException e) {
}
userPackages.add(new UserPackage(user, packageInfo));
}
return userPackages;
}
/**
* Returns whether the given package is enabled.
* This state can be changed by the user from Settings->Apps
*/
public boolean isEnabledPackage() {
if (mPackageInfo == null) return false;
return mPackageInfo.applicationInfo.enabled;
}
/**
* Return true if the package is installed and not hidden
*/
public boolean isInstalledPackage() {
if (mPackageInfo == null) return false;
return (((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
&& ((mPackageInfo.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
}
public UserInfo getUserInfo() {
return mUserInfo;
}
public PackageInfo getPackageInfo() {
return mPackageInfo;
}
private static List<UserInfo> getAllUsers(Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
return userManager.getUsers(false);
}
}

View File

@@ -35,6 +35,7 @@ import android.provider.Settings.Global;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewZygote;
@@ -270,6 +271,12 @@ public class SystemImpl implements SystemInterface {
return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
}
@Override
public List<UserPackage> getPackageInfoForProviderAllUsers(Context context,
WebViewProviderInfo configInfo) {
return UserPackage.getPackageInfosAllUsers(context, configInfo.packageName, PACKAGE_FLAGS);
}
@Override
public int getMultiProcessSetting(Context context) {
return Settings.Global.getInt(context.getContentResolver(),

View File

@@ -20,8 +20,11 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.webkit.UserPackage;
import android.webkit.WebViewProviderInfo;
import java.util.List;
/**
* System interface for the WebViewUpdateService.
* This interface provides a way to test the WebView preparation mechanism - during normal use this
@@ -49,6 +52,14 @@ public interface SystemInterface {
public boolean systemIsDebuggable();
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException;
/**
* Get the PackageInfos of all users for the package represented by {@param configInfo}.
* @return an array of UserPackages for a certain package, each UserPackage being belonging to a
* certain user. The returned array can contain null PackageInfos if the given package
* is uninstalled for some user.
*/
public List<UserPackage> getPackageInfoForProviderAllUsers(Context context,
WebViewProviderInfo configInfo);
public int getMultiProcessSetting(Context context);
public void setMultiProcessSetting(Context context, int value);

View File

@@ -94,6 +94,9 @@ public class WebViewUpdateService extends SystemService {
case Intent.ACTION_USER_ADDED:
mImpl.handleNewUser(userId);
break;
case Intent.ACTION_USER_REMOVED:
mImpl.handleUserRemoved(userId);
break;
}
}
};
@@ -112,6 +115,7 @@ public class WebViewUpdateService extends SystemService {
IntentFilter userAddedFilter = new IntentFilter();
userAddedFilter.addAction(Intent.ACTION_USER_ADDED);
userAddedFilter.addAction(Intent.ACTION_USER_REMOVED);
getContext().registerReceiverAsUser(mWebViewUpdatedReceiver, UserHandle.ALL,
userAddedFilter, null /* broadcast permission */, null /* handler */);

View File

@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.UserHandle;
import android.util.Base64;
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -100,32 +101,41 @@ public class WebViewUpdateServiceImpl {
private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
for (WebViewProviderInfo provider : providers) {
if (provider.availableByDefault && !provider.isFallback) {
try {
PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
if (isInstalledPackage(packageInfo) && isEnabledPackage(packageInfo)
&& mWebViewUpdater.isValidProvider(provider, packageInfo)) {
return true;
}
} catch (NameNotFoundException e) {
// A non-existent provider is neither valid nor enabled
// userPackages can contain null objects.
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
if (isInstalledAndEnabledForAllUsers(userPackages) &&
// Checking validity of the package for the system user (rather than all
// users) since package validity depends not on the user but on the package
// itself.
mWebViewUpdater.isValidProvider(provider,
userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo())) {
return true;
}
}
}
return false;
}
/**
* Called when a new user has been added to update the state of its fallback package.
*/
void handleNewUser(int userId) {
if (!mSystemInterface.isFallbackLogicEnabled()) return;
handleUserChange();
}
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider == null) return;
void handleUserRemoved(int userId) {
handleUserChange();
}
mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
!existsValidNonFallbackProvider(webviewProviders), userId);
/**
* Called when a user was added or removed to ensure fallback logic and WebView preparation are
* triggered. This has to be done since the WebView package we use depends on the enabled-state
* of packages for all users (so adding or removing a user might cause us to change package).
*/
private void handleUserChange() {
if (mSystemInterface.isFallbackLogicEnabled()) {
updateFallbackState(mSystemInterface.getWebViewPackages());
}
// Potentially trigger package-changing logic.
mWebViewUpdater.updateCurrentWebViewPackage(null);
}
void notifyRelroCreationCompleted() {
@@ -141,7 +151,7 @@ public class WebViewUpdateServiceImpl {
}
WebViewProviderInfo[] getValidWebViewPackages() {
return mWebViewUpdater.getValidAndInstalledWebViewPackages();
return mWebViewUpdater.getValidWebViewPackages();
}
WebViewProviderInfo[] getWebViewPackages() {
@@ -160,7 +170,7 @@ public class WebViewUpdateServiceImpl {
if (!mSystemInterface.isFallbackLogicEnabled()) return;
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
updateFallbackState(webviewProviders, true);
updateFallbackState(webviewProviders);
}
/**
@@ -185,35 +195,23 @@ public class WebViewUpdateServiceImpl {
}
}
if (!changedPackageAvailableByDefault) return;
updateFallbackState(webviewProviders, false);
updateFallbackState(webviewProviders);
}
private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
private void updateFallbackState(WebViewProviderInfo[] webviewProviders) {
// If there exists a valid and enabled non-fallback package - disable the fallback
// package, otherwise, enable it.
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider == null) return;
boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
boolean isFallbackEnabled = false;
try {
isFallbackEnabled = isEnabledPackage(
mSystemInterface.getPackageInfoForProvider(fallbackProvider));
} catch (NameNotFoundException e) {
// No fallback package installed -> early out.
return;
}
if (existsValidNonFallbackProvider
// During an OTA the primary user's WebView state might differ from other users', so
// ignore the state of that user during boot.
&& (isFallbackEnabled || isBoot)) {
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(mContext, fallbackProvider);
if (existsValidNonFallbackProvider && !isDisabledForAllUsers(userPackages)) {
mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext,
fallbackProvider.packageName);
} else if (!existsValidNonFallbackProvider
// During an OTA the primary user's WebView state might differ from other users', so
// ignore the state of that user during boot.
&& (!isFallbackEnabled || isBoot)) {
&& !isInstalledAndEnabledForAllUsers(userPackages)) {
// Enable the fallback package for all users.
mSystemInterface.enablePackageForAllUsers(mContext,
fallbackProvider.packageName, true);
@@ -376,38 +374,8 @@ public class WebViewUpdateServiceImpl {
* or the replacement are done).
*/
public String changeProviderAndSetting(String newProviderName) {
PackageInfo oldPackage = null;
PackageInfo newPackage = null;
boolean providerChanged = false;
synchronized(mLock) {
oldPackage = mCurrentWebViewPackage;
mSystemInterface.updateUserSetting(mContext, newProviderName);
try {
newPackage = findPreferredWebViewPackage();
providerChanged = (oldPackage == null)
|| !newPackage.packageName.equals(oldPackage.packageName);
} catch (WebViewPackageMissingException e) {
mCurrentWebViewPackage = null;
Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
"package " + e);
// If we don't perform the user change but don't have an installed WebView
// package, we will have changed the setting and it will be used when a package
// is available.
return "";
}
// Perform the provider change if we chose a new provider
if (providerChanged) {
onWebViewProviderChanged(newPackage);
}
}
// Kill apps using the old provider only if we changed provider
if (providerChanged && oldPackage != null) {
mSystemInterface.killPackageDependents(oldPackage.packageName);
}
// Return the new provider, this is not necessarily the one we were asked to switch to
// But the persistent setting will now be pointing to the provider we were asked to
// switch to anyway
PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
if (newPackage == null) return "";
return newPackage.packageName;
}
@@ -437,15 +405,14 @@ public class WebViewUpdateServiceImpl {
}
}
private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos(boolean onlyInstalled) {
private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
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 ((!onlyInstalled || isInstalledPackage(packageInfo))
&& isValidProvider(allProviders[n], packageInfo)) {
if (isValidProvider(allProviders[n], packageInfo)) {
providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
}
} catch (NameNotFoundException e) {
@@ -458,9 +425,8 @@ public class WebViewUpdateServiceImpl {
/**
* Fetch only the currently valid WebView packages.
**/
public WebViewProviderInfo[] getValidAndInstalledWebViewPackages() {
ProviderAndPackageInfo[] providersAndPackageInfos =
getValidWebViewPackagesAndInfos(true /* only fetch installed packages */);
public WebViewProviderInfo[] getValidWebViewPackages() {
ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
WebViewProviderInfo[] providers =
new WebViewProviderInfo[providersAndPackageInfos.length];
for(int n = 0; n < providersAndPackageInfos.length; n++) {
@@ -487,39 +453,49 @@ public class WebViewUpdateServiceImpl {
*
*/
private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
ProviderAndPackageInfo[] providers =
getValidWebViewPackagesAndInfos(false /* onlyInstalled */);
ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
// If the user has chosen provider, use that
// If the user has chosen provider, use that (if it's installed and enabled for all
// users).
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.packageName.equals(userChosenProvider)
&& isInstalledPackage(providerAndPackage.packageInfo)
&& isEnabledPackage(providerAndPackage.packageInfo)) {
return providerAndPackage.packageInfo;
if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
// userPackages can contain null objects.
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
providerAndPackage.provider);
if (isInstalledAndEnabledForAllUsers(userPackages)) {
return providerAndPackage.packageInfo;
}
}
}
// User did not choose, or the choice failed; use the most stable provider that is
// installed and enabled for the device owner, and available by default (not through
// installed and enabled for all users, 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;
if (providerAndPackage.provider.availableByDefault) {
// userPackages can contain null objects.
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
providerAndPackage.provider);
if (isInstalledAndEnabledForAllUsers(userPackages)) {
return providerAndPackage.packageInfo;
}
}
}
// Could not find any installed and enabled package either, use the most stable and
// default-available provider.
// TODO(gsennton) remove this when we have a functional WebView stub.
for (ProviderAndPackageInfo providerAndPackage : providers) {
if (providerAndPackage.provider.availableByDefault) {
return providerAndPackage.packageInfo;
}
}
// This should never happen during normal operation (only with modified system images).
mAnyWebViewInstalled = false;
throw new WebViewPackageMissingException("Could not find a loadable WebView package");
}
@@ -702,6 +678,48 @@ public class WebViewUpdateServiceImpl {
mAnyWebViewInstalled));
}
}
/**
* Update the current WebView package.
* @param newProviderName the package to switch to, null if no package has been explicitly
* chosen.
*/
public PackageInfo updateCurrentWebViewPackage(String newProviderName) {
PackageInfo oldPackage = null;
PackageInfo newPackage = null;
boolean providerChanged = false;
synchronized(mLock) {
oldPackage = mCurrentWebViewPackage;
if (newProviderName != null) {
mSystemInterface.updateUserSetting(mContext, newProviderName);
}
try {
newPackage = findPreferredWebViewPackage();
providerChanged = (oldPackage == null)
|| !newPackage.packageName.equals(oldPackage.packageName);
} catch (WebViewPackageMissingException e) {
// If updated the Setting but don't have an installed WebView package, the
// Setting will be used when a package is available.
mCurrentWebViewPackage = null;
Slog.e(TAG, "Couldn't find WebView package to use " + e);
return null;
}
// Perform the provider change if we chose a new provider
if (providerChanged) {
onWebViewProviderChanged(newPackage);
}
}
// Kill apps using the old provider only if we changed provider
if (providerChanged && oldPackage != null) {
mSystemInterface.killPackageDependents(oldPackage.packageName);
}
// Return the new provider, this is not necessarily the one we were asked to switch to,
// but the persistent setting will now be pointing to the provider we were asked to
// switch to anyway.
return newPackage;
}
}
private static boolean providerHasValidSignature(WebViewProviderInfo provider,
@@ -730,20 +748,27 @@ public class WebViewUpdateServiceImpl {
}
/**
* Returns whether the given package is enabled.
* This state can be changed by the user from Settings->Apps
* Return true iff {@param packageInfos} point to only installed and enabled packages.
* The given packages {@param packageInfos} should all be pointing to the same package, but each
* PackageInfo representing a different user's package.
*/
private static boolean isEnabledPackage(PackageInfo packageInfo) {
return packageInfo.applicationInfo.enabled;
private static boolean isInstalledAndEnabledForAllUsers(
List<UserPackage> userPackages) {
for (UserPackage userPackage : userPackages) {
if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
return false;
}
}
return true;
}
/**
* 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));
private static boolean isDisabledForAllUsers(List<UserPackage> userPackages) {
for (UserPackage userPackage : userPackages) {
if (userPackage.getPackageInfo() != null && userPackage.isEnabledPackage()) {
return false;
}
}
return true;
}
/**

View File

@@ -19,25 +19,39 @@ package com.android.server.webkit;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.webkit.UserPackage;
import android.webkit.WebViewProviderInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestSystemImpl implements SystemInterface {
private String mUserProvider = null;
private final WebViewProviderInfo[] mPackageConfigs;
HashMap<String, PackageInfo> mPackages = new HashMap();
List<Integer> mUsers = new ArrayList<>();
// Package -> [user, package]
Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
private boolean mFallbackLogicEnabled;
private final int mNumRelros;
private final boolean mIsDebuggable;
private int mMultiProcessSetting;
public static final int PRIMARY_USER_ID = 0;
public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
int numRelros, boolean isDebuggable) {
mPackageConfigs = packageConfigs;
mFallbackLogicEnabled = fallbackLogicEnabled;
mNumRelros = numRelros;
mIsDebuggable = isDebuggable;
mUsers.add(PRIMARY_USER_ID);
}
public void addUser(int userId) {
mUsers.add(userId);
}
@Override
@@ -78,17 +92,20 @@ public class TestSystemImpl implements SystemInterface {
@Override
public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
enablePackageForUser(packageName, enable, 0);
for(int userId : mUsers) {
enablePackageForUser(packageName, enable, userId);
}
}
@Override
public void enablePackageForUser(String packageName, boolean enable, int userId) {
PackageInfo packageInfo = mPackages.get(packageName);
if (packageInfo == null) {
Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
if (userPackages == null) {
throw new IllegalArgumentException("There is no package called " + packageName);
}
PackageInfo packageInfo = userPackages.get(userId);
packageInfo.applicationInfo.enabled = enable;
setPackageInfo(packageInfo);
setPackageInfoForUser(userId, packageInfo);
}
@Override
@@ -97,23 +114,61 @@ public class TestSystemImpl implements SystemInterface {
@Override
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo info) throws
NameNotFoundException {
PackageInfo ret = mPackages.get(info.packageName);
Map<Integer, PackageInfo> userPackages = mPackages.get(info.packageName);
if (userPackages == null) throw new NameNotFoundException(info.packageName);
PackageInfo ret = userPackages.get(PRIMARY_USER_ID);
if (ret == null) throw new NameNotFoundException(info.packageName);
return ret;
}
public void setPackageInfo(PackageInfo pi) {
mPackages.put(pi.packageName, pi);
@Override
public List<UserPackage> getPackageInfoForProviderAllUsers(
Context context, WebViewProviderInfo info) {
Map<Integer, PackageInfo> userPackages = mPackages.get(info.packageName);
List<UserPackage> ret = new ArrayList();
// Loop over defined users, and find the corresponding package for each user.
for (int userId : mUsers) {
ret.add(new UserPackage(createUserInfo(userId),
userPackages == null ? null : userPackages.get(userId)));
}
return ret;
}
private static UserInfo createUserInfo(int userId) {
return new UserInfo(userId, "User nr. " + userId, 0 /* flags */);
}
/**
* Set package for primary user.
*/
public void setPackageInfo(PackageInfo pi) {
setPackageInfoForUser(PRIMARY_USER_ID, pi);
}
public void setPackageInfoForUser(int userId, PackageInfo pi) {
if (!mUsers.contains(userId)) {
throw new IllegalArgumentException("User nr. " + userId + " doesn't exist");
}
if (!mPackages.containsKey(pi.packageName)) {
mPackages.put(pi.packageName, new HashMap<Integer, PackageInfo>());
}
mPackages.get(pi.packageName).put(userId, pi);
}
/**
* Removes the package {@param packageName} for the primary user.
*/
public void removePackageInfo(String packageName) {
mPackages.remove(packageName);
mPackages.get(packageName).remove(PRIMARY_USER_ID);
}
@Override
public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
PackageInfo pi = null;
pi = mPackages.get(packageName);
Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
if (userPackages == null) throw new NameNotFoundException();
pi = userPackages.get(PRIMARY_USER_ID);
if (pi != null && pi.applicationInfo.isSystemApp()) {
return pi.applicationInfo.versionCode;
}

View File

@@ -92,9 +92,15 @@ public class WebViewUpdateServiceTest {
}
private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
// Set package infos for the primary user (user 0).
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, providers);
}
private void setEnabledAndValidPackageInfosForUser(int userId,
WebViewProviderInfo[] providers) {
for(WebViewProviderInfo wpi : providers) {
mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
true /* valid */, true /* installed */));
mTestSystemImpl.setPackageInfoForUser(userId, createPackageInfo(wpi.packageName,
true /* enabled */, true /* valid */, true /* installed */));
}
}
@@ -335,7 +341,7 @@ public class WebViewUpdateServiceTest {
setEnabledAndValidPackageInfos(packages);
mWebViewUpdateServiceImpl.packageStateChanged(singlePackage,
WebViewUpdateService.PACKAGE_ADDED, 0);
WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
checkPreparationPhasesForPackage(singlePackage, 1 /* number of finished preparations */);
assertEquals(singlePackage,
@@ -344,7 +350,7 @@ public class WebViewUpdateServiceTest {
// Remove the package again
mTestSystemImpl.removePackageInfo(singlePackage);
mWebViewUpdateServiceImpl.packageStateChanged(singlePackage,
WebViewUpdateService.PACKAGE_ADDED, 0);
WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
// Package removed - ensure our interface states that there is no package
response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
@@ -374,7 +380,7 @@ public class WebViewUpdateServiceTest {
createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */,
true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName,
WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
checkPreparationPhasesForPackage(wpi.packageName, 1);
}
@@ -429,16 +435,8 @@ public class WebViewUpdateServiceTest {
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 */, true /* installed */));
}
}
// Have all packages be enabled, so that we can change provider however we want to
setEnabledAndValidPackageInfos(packages);
CountDownLatch countdown = new CountDownLatch(1);
@@ -457,8 +455,12 @@ public class WebViewUpdateServiceTest {
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));
// Verify that we killed the first package if we performed a settings change -
// otherwise we had to disable the first package, in which case its dependents
// should have been killed by the framework.
if (settingsChange) {
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
}
countdown.countDown();
}
}).start();
@@ -470,11 +472,21 @@ public class WebViewUpdateServiceTest {
if (settingsChange) {
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
} else {
// Switch provider by enabling the second one
// Enable the second provider
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(
secondPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
secondPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
// Ensure we haven't changed package yet.
assertEquals(firstPackage,
mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
// Switch provider by disabling the first one
mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, false /* enabled */,
true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(
firstPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
}
mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
// first package done, should start on second
@@ -528,7 +540,7 @@ public class WebViewUpdateServiceTest {
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(
fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, 0);
fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
if (fallbackLogicEnabled) {
// Check that we have now disabled the fallback package twice
@@ -573,7 +585,7 @@ public class WebViewUpdateServiceTest {
createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */,
true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
// Verify fallback disabled, primary package used as provider, and fallback package killed
Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
@@ -583,7 +595,31 @@ public class WebViewUpdateServiceTest {
}
@Test
public void testFallbackChangesEnabledState() {
public void testFallbackChangesEnabledStateSingleUser() {
for (PackageRemovalType removalType : REMOVAL_TYPES) {
checkFallbackChangesEnabledState(false /* multiUser */, removalType);
}
}
@Test
public void testFallbackChangesEnabledStateMultiUser() {
for (PackageRemovalType removalType : REMOVAL_TYPES) {
checkFallbackChangesEnabledState(true /* multiUser */, removalType);
}
}
/**
* Represents how to remove a package during a tests (disabling it / uninstalling it / hiding
* it).
*/
private enum PackageRemovalType {
UNINSTALL, DISABLE, HIDE
}
private PackageRemovalType[] REMOVAL_TYPES = PackageRemovalType.class.getEnumConstants();
public void checkFallbackChangesEnabledState(boolean multiUser,
PackageRemovalType removalType) {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
@@ -592,46 +628,68 @@ public class WebViewUpdateServiceTest {
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, true /* fallbackLogicEnabled */);
setEnabledAndValidPackageInfos(packages);
int secondaryUserId = 10;
int userIdToChangePackageFor = multiUser ? secondaryUserId : TestSystemImpl.PRIMARY_USER_ID;
if (multiUser) {
mTestSystemImpl.addUser(secondaryUserId);
setEnabledAndValidPackageInfosForUser(secondaryUserId, packages);
}
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
runWebViewBootPreparationOnMainSync();
// Verify fallback disabled at boot when primary package enabled
Mockito.verify(mTestSystemImpl).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
Matchers.anyInt());
checkEnablePackageForUserCalled(fallbackPackage, false, multiUser
? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId}
: new int[] {TestSystemImpl.PRIMARY_USER_ID}, 1 /* numUsages */);
checkPreparationPhasesForPackage(primaryPackage, 1);
boolean enabled = !(removalType == PackageRemovalType.DISABLE);
boolean installed = !(removalType == PackageRemovalType.UNINSTALL);
boolean hidden = (removalType == PackageRemovalType.HIDE);
// Disable primary package and ensure fallback becomes enabled and used
mTestSystemImpl.setPackageInfo(
createPackageInfo(primaryPackage, false /* enabled */, true /* valid */,
true /* installed */));
mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor,
createPackageInfo(primaryPackage, enabled /* enabled */, true /* valid */,
installed /* installed */, null /* signature */, 0 /* updateTime */,
hidden /* hidden */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_CHANGED, 0);
removalType == PackageRemovalType.DISABLE
? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_REMOVED,
userIdToChangePackageFor); // USER ID
Mockito.verify(mTestSystemImpl).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
Matchers.anyInt());
checkEnablePackageForUserCalled(fallbackPackage, true, multiUser
? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId}
: new int[] {TestSystemImpl.PRIMARY_USER_ID}, 1 /* numUsages */);
checkPreparationPhasesForPackage(fallbackPackage, 1);
// Again enable primary package and verify primary is used and fallback becomes disabled
mTestSystemImpl.setPackageInfo(
mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor,
createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
WebViewUpdateService.PACKAGE_CHANGED, 0);
removalType == PackageRemovalType.DISABLE
? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_ADDED,
userIdToChangePackageFor);
// 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());
checkEnablePackageForUserCalled(fallbackPackage, false, multiUser
? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId}
: new int[] {TestSystemImpl.PRIMARY_USER_ID}, 2 /* numUsages */);
checkPreparationPhasesForPackage(primaryPackage, 2);
}
private void checkEnablePackageForUserCalled(String packageName, boolean expectEnabled,
int[] userIds, int numUsages) {
for (int userId : userIds) {
Mockito.verify(mTestSystemImpl, Mockito.times(numUsages)).enablePackageForUser(
Mockito.eq(packageName), Mockito.eq(expectEnabled), Mockito.eq(userId));
}
}
@Test
public void testAddUserWhenFallbackLogicEnabled() {
checkAddingNewUser(true);
@@ -651,8 +709,10 @@ public class WebViewUpdateServiceTest {
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, fallbackLogicEnabled);
setEnabledAndValidPackageInfos(packages);
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
int newUser = 100;
mTestSystemImpl.addUser(newUser);
setEnabledAndValidPackageInfosForUser(newUser, packages);
mWebViewUpdateServiceImpl.handleNewUser(newUser);
if (fallbackLogicEnabled) {
// Verify fallback package becomes disabled for new user
@@ -667,6 +727,42 @@ public class WebViewUpdateServiceTest {
}
}
/**
* Ensures that adding a new user for which the current WebView package is uninstalled causes a
* change of WebView provider.
*/
@Test
public void testAddingNewUserWithUninstalledPackage() {
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 */);
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
int newUser = 100;
mTestSystemImpl.addUser(newUser);
// Let the primary package be uninstalled for the new user
mTestSystemImpl.setPackageInfoForUser(newUser,
createPackageInfo(primaryPackage, true /* enabled */, true /* valid */,
false /* installed */));
mTestSystemImpl.setPackageInfoForUser(newUser,
createPackageInfo(fallbackPackage, false /* enabled */, true /* valid */,
true /* installed */));
mWebViewUpdateServiceImpl.handleNewUser(newUser);
// Verify fallback package doesn't become disabled for the primary user.
Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
Mockito.anyObject(), Mockito.eq(false) /* enable */,
Mockito.eq(TestSystemImpl.PRIMARY_USER_ID) /* user */);
// Verify that we enable the fallback package for the secondary user.
Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
Mockito.eq(newUser) /* user */);
checkPreparationPhasesForPackage(fallbackPackage, 1 /* numRelros */);
}
/**
* 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.
@@ -713,7 +809,7 @@ public class WebViewUpdateServiceTest {
1 /* updateTime */ ));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
// Ensure we use firstPackage
checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */);
@@ -742,14 +838,14 @@ public class WebViewUpdateServiceTest {
mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
false /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
WebViewUpdateService.PACKAGE_ADDED, 0);
WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
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 /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
WebViewUpdateService.PACKAGE_ADDED, 0);
WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID);
checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */);
}
@@ -820,7 +916,7 @@ public class WebViewUpdateServiceTest {
mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
} else {
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
}
WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
@@ -831,7 +927,7 @@ public class WebViewUpdateServiceTest {
true /* valid */, true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(secondPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
checkPreparationPhasesForPackage(secondPackage, 1);
@@ -863,11 +959,11 @@ public class WebViewUpdateServiceTest {
createPackageInfo(firstPackage, true /* enabled */, false /* valid */,
true /* installed */));
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0);
WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
} else {
mTestSystemImpl.removePackageInfo(firstPackage);
mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
WebViewUpdateService.PACKAGE_REMOVED, 0);
WebViewUpdateService.PACKAGE_REMOVED, TestSystemImpl.PRIMARY_USER_ID);
}
checkPreparationPhasesForPackage(secondPackage, 1);
@@ -1098,8 +1194,10 @@ public class WebViewUpdateServiceTest {
}
}
// Ensure that the update service uses an uninstalled package if that is the only package
// available.
/**
* Ensure that the update service does use an uninstalled package when that is the only
* package available.
*/
@Test
public void testWithSingleUninstalledPackage() {
String testPackageName = "test.package.name";
@@ -1113,21 +1211,32 @@ public class WebViewUpdateServiceTest {
runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */);
// TODO(gsennton) change this logic to use the code below when we have created a functional
// stub.
//Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
// Matchers.anyObject());
//WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
//assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
//assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
}
@Test
public void testNonhiddenPackageUserOverHidden() {
checkVisiblePackageUserOverNonVisible(false /* true == uninstalled, false == hidden */);
checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.HIDE);
checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.HIDE);
}
@Test
public void testInstalledPackageUsedOverUninstalled() {
checkVisiblePackageUserOverNonVisible(true /* true == uninstalled, false == hidden */);
checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.UNINSTALL);
checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.UNINSTALL);
}
private void checkVisiblePackageUserOverNonVisible(boolean uninstalledNotHidden) {
boolean testUninstalled = uninstalledNotHidden;
boolean testHidden = !uninstalledNotHidden;
private void checkVisiblePackageUserOverNonVisible(boolean multiUser,
PackageRemovalType removalType) {
assert removalType != PackageRemovalType.DISABLE;
boolean testUninstalled = removalType == PackageRemovalType.UNINSTALL;
boolean testHidden = removalType == PackageRemovalType.HIDE;
String installedPackage = "installedPackage";
String uninstalledPackage = "uninstalledPackage";
WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
@@ -1137,11 +1246,25 @@ public class WebViewUpdateServiceTest {
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 */,
int secondaryUserId = 5;
if (multiUser) {
mTestSystemImpl.addUser(secondaryUserId);
// Install all packages for the primary user.
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(
installedPackage, true /* enabled */, true /* valid */, true /* installed */));
// Hide or uninstall the primary package for the second user
mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
true /* valid */, (testUninstalled ? false : true) /* installed */,
null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
} else {
mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */,
true /* valid */, true /* installed */));
// Hide or uninstall the primary package
mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */,
true /* valid */, (testUninstalled ? false : true) /* installed */,
null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
}
runWebViewBootPreparationOnMainSync();
@@ -1160,9 +1283,7 @@ public class WebViewUpdateServiceTest {
}
/**
* 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).
* Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen.
*/
private void checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden) {
boolean testUninstalled = uninstalledNotHidden;
@@ -1176,27 +1297,31 @@ public class WebViewUpdateServiceTest {
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 */));
int secondaryUserId = 412;
mTestSystemImpl.addUser(secondaryUserId);
// Let all packages be installed and enabled for the primary user.
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
// Only uninstall the 'uninstalled package' for the secondary user.
mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage,
true /* enabled */, true /* valid */, true /* installed */));
mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage,
true /* enabled */, true /* valid */, !testUninstalled /* installed */,
null /* signatures */, 0 /* updateTime */, testHidden /* hidden */));
runWebViewBootPreparationOnMainSync();
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));
// Ensure both packages are considered valid.
assertEquals(2, mWebViewUpdateServiceImpl.getValidWebViewPackages().length);
// We should only have called onWebViewProviderChanged once (before calling
// changeProviderAndSetting
Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged(
@@ -1227,12 +1352,16 @@ public class WebViewUpdateServiceTest {
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 */));
int secondaryUserId = 4;
mTestSystemImpl.addUser(secondaryUserId);
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages);
mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage,
true /* enabled */, true /* valid */, true /* installed */));
mTestSystemImpl.setPackageInfoForUser(secondaryUserId, 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);
@@ -1242,12 +1371,21 @@ public class WebViewUpdateServiceTest {
checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
}
@Test
public void testFallbackEnabledIfPrimaryUninstalledSingleUser() {
checkFallbackEnabledIfPrimaryUninstalled(false /* multiUser */);
}
@Test
public void testFallbackEnabledIfPrimaryUninstalledMultiUser() {
checkFallbackEnabledIfPrimaryUninstalled(true /* multiUser */);
}
/**
* Ensures that fallback becomes enabled if the primary package is uninstalled for the current
* Ensures that fallback becomes enabled at boot if the primary package is uninstalled for some
* user.
*/
@Test
public void testFallbackEnabledIfPrimaryUninstalled() {
private void checkFallbackEnabledIfPrimaryUninstalled(boolean multiUser) {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
@@ -1256,10 +1394,24 @@ public class WebViewUpdateServiceTest {
new WebViewProviderInfo(
fallbackPackage, "", true /* default available */, true /* fallback */, null)};
setupWithPackages(packages, true /* fallback logic enabled */);
mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
int secondaryUserId = 5;
if (multiUser) {
mTestSystemImpl.addUser(secondaryUserId);
// Install all packages for the primary user.
setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages);
// Only install fallback package for secondary user.
mTestSystemImpl.setPackageInfoForUser(secondaryUserId,
createPackageInfo(primaryPackage, true /* enabled */,
true /* valid */, false /* installed */));
mTestSystemImpl.setPackageInfoForUser(secondaryUserId,
createPackageInfo(fallbackPackage, false /* enabled */,
true /* valid */, true /* installed */));
} else {
mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
true /* valid */, false /* installed */));
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, false /* enabled */,
true /* valid */, true /* installed */));
}
runWebViewBootPreparationOnMainSync();
// Verify that we enable the fallback package