diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java new file mode 100644 index 0000000000000..404bcf453aaf9 --- /dev/null +++ b/core/java/android/webkit/UserPackage.java @@ -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 getPackageInfosAllUsers(Context context, + String packageName, int packageFlags) { + List users = getAllUsers(context); + List userPackages = new ArrayList(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 getAllUsers(Context context) { + UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + return userManager.getUsers(false); + } + +} diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java index 02b46ecfc258e..4fd51b2274dfd 100644 --- a/services/core/java/com/android/server/webkit/SystemImpl.java +++ b/services/core/java/com/android/server/webkit/SystemImpl.java @@ -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 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(), diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java index fd137eb245d47..b06f829712476 100644 --- a/services/core/java/com/android/server/webkit/SystemInterface.java +++ b/services/core/java/com/android/server/webkit/SystemInterface.java @@ -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 getPackageInfoForProviderAllUsers(Context context, + WebViewProviderInfo configInfo); public int getMultiProcessSetting(Context context); public void setMultiProcessSetting(Context context, int value); diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 311570e893cff..4a105e18e9bae 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -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 */); diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java index edfb11c6634ba..f016830a26735 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java @@ -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 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 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 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 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 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 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 userPackages) { + for (UserPackage userPackage : userPackages) { + if (userPackage.getPackageInfo() != null && userPackage.isEnabledPackage()) { + return false; + } + } + return true; } /** diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java index 83a61ca99d513..67e78d4a2108e 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java +++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java @@ -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 mPackages = new HashMap(); + List mUsers = new ArrayList<>(); + // Package -> [user, package] + Map> 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 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 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 getPackageInfoForProviderAllUsers( + Context context, WebViewProviderInfo info) { + Map userPackages = mPackages.get(info.packageName); + List 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()); + } + 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 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; } 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 05194488b08a1..33cedfa41e517 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java @@ -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