Loading core/java/android/webkit/UserPackage.java 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * 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); } } services/core/java/com/android/server/webkit/SystemImpl.java +7 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(), Loading services/core/java/com/android/server/webkit/SystemInterface.java +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading services/core/java/com/android/server/webkit/WebViewUpdateService.java +4 −0 Original line number Diff line number Diff line Loading @@ -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; } } }; Loading @@ -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 */); Loading services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java +123 −98 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)) { // 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; } } catch (NameNotFoundException e) { // A non-existent provider is neither valid nor enabled } } } 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() { Loading @@ -141,7 +151,7 @@ public class WebViewUpdateServiceImpl { } WebViewProviderInfo[] getValidWebViewPackages() { return mWebViewUpdater.getValidAndInstalledWebViewPackages(); return mWebViewUpdater.getValidWebViewPackages(); } WebViewProviderInfo[] getWebViewPackages() { Loading @@ -160,7 +170,7 @@ public class WebViewUpdateServiceImpl { if (!mSystemInterface.isFallbackLogicEnabled()) return; WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); updateFallbackState(webviewProviders, true); updateFallbackState(webviewProviders); } /** Loading @@ -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); Loading Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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++) { Loading @@ -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)) { 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)) { 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"); } Loading Loading @@ -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, Loading Loading @@ -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; } /** Loading Loading
core/java/android/webkit/UserPackage.java 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * 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); } }
services/core/java/com/android/server/webkit/SystemImpl.java +7 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(), Loading
services/core/java/com/android/server/webkit/SystemInterface.java +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading
services/core/java/com/android/server/webkit/WebViewUpdateService.java +4 −0 Original line number Diff line number Diff line Loading @@ -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; } } }; Loading @@ -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 */); Loading
services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java +123 −98 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)) { // 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; } } catch (NameNotFoundException e) { // A non-existent provider is neither valid nor enabled } } } 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() { Loading @@ -141,7 +151,7 @@ public class WebViewUpdateServiceImpl { } WebViewProviderInfo[] getValidWebViewPackages() { return mWebViewUpdater.getValidAndInstalledWebViewPackages(); return mWebViewUpdater.getValidWebViewPackages(); } WebViewProviderInfo[] getWebViewPackages() { Loading @@ -160,7 +170,7 @@ public class WebViewUpdateServiceImpl { if (!mSystemInterface.isFallbackLogicEnabled()) return; WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); updateFallbackState(webviewProviders, true); updateFallbackState(webviewProviders); } /** Loading @@ -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); Loading Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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++) { Loading @@ -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)) { 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)) { 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"); } Loading Loading @@ -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, Loading Loading @@ -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; } /** Loading