diff options
| -rw-r--r-- | services/core/java/com/android/server/webkit/WebViewUpdateService.java | 731 |
1 files changed, 395 insertions, 336 deletions
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 17efcc2d2830..ebec44554210 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -51,28 +51,21 @@ import java.util.List; public class WebViewUpdateService extends SystemService { private static final String TAG = "WebViewUpdateService"; - private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000. - - // Keeps track of the number of running relro creations - private int mNumRelroCreationsStarted = 0; - private int mNumRelroCreationsFinished = 0; - // Implies that we need to rerun relro creation because we are using an out-of-date package - private boolean mWebViewPackageDirty = false; - private boolean mAnyWebViewInstalled = false; - - private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; - - private int mMinimumVersionCode = -1; - - // The WebView package currently in use (or the one we are preparing). - private PackageInfo mCurrentWebViewPackage = null; private BroadcastReceiver mWebViewUpdatedReceiver; private SystemInterface mSystemInterface; + static final int PACKAGE_CHANGED = 0; + static final int PACKAGE_ADDED = 1; + static final int PACKAGE_ADDED_REPLACED = 2; + static final int PACKAGE_REMOVED = 3; + + private WebViewUpdater mWebViewUpdater; + public WebViewUpdateService(Context context) { super(context); mSystemInterface = new SystemImpl(); + mWebViewUpdater = new WebViewUpdater(getContext(), mSystemInterface); } @Override @@ -80,76 +73,34 @@ public class WebViewUpdateService extends SystemService { mWebViewUpdatedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - // When a package is replaced we will receive two intents, one representing - // the removal of the old package and one representing the addition of the - // new package. - // In the case where we receive an intent to remove the old version of the - // package that is being replaced we early-out here so that we don't run the - // update-logic twice. - if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) - && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) { - return; - } - - // Ensure that we only heed PACKAGE_CHANGED intents if they change an entire - // package, not just a component - if (intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED)) { - if (!entirePackageChanged(intent)) { - return; - } - } - - if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) { - int userId = - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - handleNewUser(userId); - return; - } - - updateFallbackState(context, intent); - - for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { - String webviewPackage = "package:" + provider.packageName; - - if (webviewPackage.equals(intent.getDataString())) { - boolean updateWebView = false; - boolean removedOrChangedOldPackage = false; - String oldProviderName = null; - PackageInfo newPackage = null; - synchronized(WebViewUpdateService.this) { - try { - newPackage = findPreferredWebViewPackage(); - if (mCurrentWebViewPackage != null) - oldProviderName = mCurrentWebViewPackage.packageName; - // Only trigger update actions if the updated package is the one - // that will be used, or the one that was in use before the - // update, or if we haven't seen a valid WebView package before. - updateWebView = - provider.packageName.equals(newPackage.packageName) - || provider.packageName.equals(oldProviderName) - || mCurrentWebViewPackage == null; - // We removed the old package if we received an intent to remove - // or replace the old package. - removedOrChangedOldPackage = - provider.packageName.equals(oldProviderName); - if (updateWebView) { - onWebViewProviderChanged(newPackage); - } - } catch (WebViewFactory.MissingWebViewPackageException e) { - Slog.e(TAG, "Could not find valid WebView package to create " + - "relro with " + e); - } - } - if(updateWebView && !removedOrChangedOldPackage - && oldProviderName != null) { - // If the provider change is the result of adding or replacing a - // package that was not the previous provider then we must kill - // packages dependent on the old package ourselves. The framework - // only kills dependents of packages that are being removed. - mSystemInterface.killPackageDependents(oldProviderName); + switch (intent.getAction()) { + case Intent.ACTION_PACKAGE_REMOVED: + // When a package is replaced we will receive two intents, one + // representing the removal of the old package and one representing the + // addition of the new package. + // In the case where we receive an intent to remove the old version of + // the package that is being replaced we early-out here so that we don't + // run the update-logic twice. + if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return; + packageStateChanged(packageNameFromIntent(intent), PACKAGE_REMOVED); + break; + case Intent.ACTION_PACKAGE_CHANGED: + // Ensure that we only heed PACKAGE_CHANGED intents if they change an + // entire package, not just a component + if (entirePackageChanged(intent)) { + packageStateChanged(packageNameFromIntent(intent), PACKAGE_CHANGED); } - return; - } + break; + case Intent.ACTION_PACKAGE_ADDED: + packageStateChanged(packageNameFromIntent(intent), + (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING) + ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED)); + break; + case Intent.ACTION_USER_ADDED: + int userId = + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + handleNewUser(userId); + break; } } }; @@ -171,12 +122,27 @@ public class WebViewUpdateService extends SystemService { publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/); } + private void packageStateChanged(String packageName, int changedState) { + updateFallbackState(packageName, changedState); + mWebViewUpdater.packageStateChanged(packageName, changedState); + } + + public void prepareWebViewInSystemServer() { + updateFallbackStateOnBoot(); + mWebViewUpdater.prepareWebViewInSystemServer(); + } + + private static String packageNameFromIntent(Intent intent) { + return intent.getDataString().substring("package:".length()); + } + private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) { for (WebViewProviderInfo provider : providers) { if (provider.availableByDefault && !provider.isFallback) { try { PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider); - if (isEnabledPackage(packageInfo) && isValidProvider(provider, packageInfo)) { + if (isEnabledPackage(packageInfo) + && mWebViewUpdater.isValidProvider(provider, packageInfo)) { return true; } } catch (NameNotFoundException e) { @@ -201,33 +167,37 @@ public class WebViewUpdateService extends SystemService { !existsValidNonFallbackProvider(webviewProviders), userId); } + public void updateFallbackStateOnBoot() { + WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); + updateFallbackState(webviewProviders, true); + } + /** * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback * package that is valid (and available by default) then disable the fallback package, * otherwise, enable the fallback package. */ - void updateFallbackState(final Context context, final Intent intent) { + public void updateFallbackState(String changedPackage, int changedState) { if (!mSystemInterface.isFallbackLogicEnabled()) return; WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); - if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED) - || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) { - // A package was changed / updated / downgraded, early out if it is not one of the - // webview packages that are available by default. - String changedPackage = null; - for (WebViewProviderInfo provider : webviewProviders) { - String webviewPackage = "package:" + provider.packageName; - if (webviewPackage.equals(intent.getDataString())) { - if (provider.availableByDefault) { - changedPackage = provider.packageName; - } - break; + // A package was changed / updated / downgraded, early out if it is not one of the + // webview packages that are available by default. + boolean changedPackageAvailableByDefault = false; + for (WebViewProviderInfo provider : webviewProviders) { + if (provider.packageName.equals(changedPackage)) { + if (provider.availableByDefault) { + changedPackageAvailableByDefault = true; } + break; } - if (changedPackage == null) return; } + if (!changedPackageAvailableByDefault) return; + updateFallbackState(webviewProviders, false); + } + private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) { // If there exists a valid and enabled non-fallback package - disable the fallback // package, otherwise, enable it. WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); @@ -236,28 +206,29 @@ public class WebViewUpdateService extends SystemService { boolean isFallbackEnabled = false; try { - isFallbackEnabled = - isEnabledPackage(mSystemInterface.getPackageInfoForProvider(fallbackProvider)); + isFallbackEnabled = isEnabledPackage( + mSystemInterface.getPackageInfoForProvider(fallbackProvider)); } catch (NameNotFoundException e) { } 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 || intent == null)) { - mSystemInterface.uninstallAndDisablePackageForAllUsers(context, + && (isFallbackEnabled || isBoot)) { + mSystemInterface.uninstallAndDisablePackageForAllUsers(getContext(), 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 || intent==null)) { + && (!isFallbackEnabled || isBoot)) { // Enable the fallback package for all users. - mSystemInterface.enablePackageForAllUsers(context, fallbackProvider.packageName, true); + mSystemInterface.enablePackageForAllUsers(getContext(), + fallbackProvider.packageName, true); } } /** - * Returns the only fallback provider, or null if there is none. + * Returns the only fallback provider in the set of given packages, or null if there is none. */ private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { for (WebViewProviderInfo provider : webviewPackages) { @@ -278,214 +249,361 @@ public class WebViewUpdateService extends SystemService { } /** - * Perform any WebView loading preparations that must happen at boot from the system server, - * after the package manager has started or after an update to the webview is installed. - * This must be called in the system server. - * Currently, this means spawning the child processes which will create the relro files. + * Class that decides what WebView implementation to use and prepares that implementation for + * use. */ - public void prepareWebViewInSystemServer() { - updateFallbackState(getContext(), null); - try { - synchronized(this) { - mCurrentWebViewPackage = findPreferredWebViewPackage(); - onWebViewProviderChanged(mCurrentWebViewPackage); + private static class WebViewUpdater { + private Context mContext; + private SystemInterface mSystemInterface; + private int mMinimumVersionCode = -1; + + public WebViewUpdater(Context context, SystemInterface systemInterface) { + mContext = context; + mSystemInterface = systemInterface; + } + + private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000. + + // Keeps track of the number of running relro creations + private int mNumRelroCreationsStarted = 0; + private int mNumRelroCreationsFinished = 0; + // Implies that we need to rerun relro creation because we are using an out-of-date package + private boolean mWebViewPackageDirty = false; + private boolean mAnyWebViewInstalled = false; + + private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; + + // The WebView package currently in use (or the one we are preparing). + private PackageInfo mCurrentWebViewPackage = null; + + private Object mLock = new Object(); + + public void packageStateChanged(String packageName, int changedState) { + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + String webviewPackage = provider.packageName; + + if (webviewPackage.equals(packageName)) { + boolean updateWebView = false; + boolean removedOrChangedOldPackage = false; + String oldProviderName = null; + PackageInfo newPackage = null; + synchronized(mLock) { + try { + newPackage = findPreferredWebViewPackage(); + if (mCurrentWebViewPackage != null) + oldProviderName = mCurrentWebViewPackage.packageName; + // Only trigger update actions if the updated package is the one + // that will be used, or the one that was in use before the + // update, or if we haven't seen a valid WebView package before. + updateWebView = + provider.packageName.equals(newPackage.packageName) + || provider.packageName.equals(oldProviderName) + || mCurrentWebViewPackage == null; + // We removed the old package if we received an intent to remove + // or replace the old package. + removedOrChangedOldPackage = + provider.packageName.equals(oldProviderName); + if (updateWebView) { + onWebViewProviderChanged(newPackage); + } + } catch (WebViewFactory.MissingWebViewPackageException e) { + Slog.e(TAG, "Could not find valid WebView package to create " + + "relro with " + e); + } + } + if(updateWebView && !removedOrChangedOldPackage + && oldProviderName != null) { + // If the provider change is the result of adding or replacing a + // package that was not the previous provider then we must kill + // packages dependent on the old package ourselves. The framework + // only kills dependents of packages that are being removed. + mSystemInterface.killPackageDependents(oldProviderName); + } + return; + } } - } catch (Throwable t) { - // Log and discard errors at this stage as we must not crash the system server. - Slog.e(TAG, "error preparing webview provider from system server", t); } - } - - - /** - * Change WebView provider and provider setting and kill packages using the old provider. - * Return the new provider (in case we are in the middle of creating relro files this new - * provider will not be in use directly, but will when the relros are done). - */ - private String changeProviderAndSetting(String newProviderName) { - PackageInfo oldPackage = null; - PackageInfo newPackage = null; - synchronized(this) { - oldPackage = mCurrentWebViewPackage; - mSystemInterface.updateUserSetting(getContext(), newProviderName); + public void prepareWebViewInSystemServer() { try { - newPackage = findPreferredWebViewPackage(); - if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) { - // If we don't perform the user change, revert the settings change. - mSystemInterface.updateUserSetting(getContext(), newPackage.packageName); - return newPackage.packageName; + synchronized(mLock) { + mCurrentWebViewPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(mCurrentWebViewPackage); } - } catch (WebViewFactory.MissingWebViewPackageException e) { - 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 newProviderName; + } catch (Throwable t) { + // Log and discard errors at this stage as we must not crash the system server. + Slog.e(TAG, "error preparing webview provider from system server", t); } - onWebViewProviderChanged(newPackage); } - // Kill apps using the old provider - if (oldPackage != null) { - mSystemInterface.killPackageDependents(oldPackage.packageName); + + /** + * Change WebView provider and provider setting and kill packages using the old provider. + * Return the new provider (in case we are in the middle of creating relro files this new + * provider will not be in use directly, but will when the relros are done). + */ + public String changeProviderAndSetting(String newProviderName) { + PackageInfo oldPackage = null; + PackageInfo newPackage = null; + synchronized(mLock) { + oldPackage = mCurrentWebViewPackage; + mSystemInterface.updateUserSetting(mContext, newProviderName); + + try { + newPackage = findPreferredWebViewPackage(); + if (oldPackage != null + && newPackage.packageName.equals(oldPackage.packageName)) { + // If we don't perform the user change, revert the settings change. + mSystemInterface.updateUserSetting(mContext, newPackage.packageName); + return newPackage.packageName; + } + } catch (WebViewFactory.MissingWebViewPackageException e) { + 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 newProviderName; + } + onWebViewProviderChanged(newPackage); + } + // Kill apps using the old provider + if (oldPackage != null) { + mSystemInterface.killPackageDependents(oldPackage.packageName); + } + return newPackage.packageName; } - return newPackage.packageName; - } - /** - * This is called when we change WebView provider, either when the current provider is updated - * or a new provider is chosen / takes precedence. - */ - private void onWebViewProviderChanged(PackageInfo newPackage) { - synchronized(this) { - mAnyWebViewInstalled = true; - if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { - mCurrentWebViewPackage = newPackage; - mSystemInterface.updateUserSetting(getContext(), newPackage.packageName); - - // The relro creations might 'finish' (not start at all) before - // WebViewFactory.onWebViewProviderChanged which means we might not know the number - // of started creations before they finish. - mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; - mNumRelroCreationsFinished = 0; - mNumRelroCreationsStarted = mSystemInterface.onWebViewProviderChanged(newPackage); - // If the relro creations finish before we know the number of started creations we - // will have to do any cleanup/notifying here. - checkIfRelrosDoneLocked(); - } else { - mWebViewPackageDirty = true; + /** + * This is called when we change WebView provider, either when the current provider is + * updated or a new provider is chosen / takes precedence. + */ + private void onWebViewProviderChanged(PackageInfo newPackage) { + synchronized(mLock) { + mAnyWebViewInstalled = true; + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + mCurrentWebViewPackage = newPackage; + mSystemInterface.updateUserSetting(mContext, newPackage.packageName); + + // The relro creations might 'finish' (not start at all) before + // WebViewFactory.onWebViewProviderChanged which means we might not know the + // number of started creations before they finish. + mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; + mNumRelroCreationsFinished = 0; + mNumRelroCreationsStarted = + mSystemInterface.onWebViewProviderChanged(newPackage); + // If the relro creations finish before we know the number of started creations + // we will have to do any cleanup/notifying here. + checkIfRelrosDoneLocked(); + } else { + mWebViewPackageDirty = true; + } } } - } - 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 (isValidProvider(allProviders[n], packageInfo)) { - providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); + 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 (isValidProvider(allProviders[n], packageInfo)) { + providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); + } + } catch (NameNotFoundException e) { + // Don't add non-existent packages } - } catch (NameNotFoundException e) { - // Don't add non-existent packages } + return providers.toArray(new ProviderAndPackageInfo[providers.size()]); + } + + /** + * Fetch only the currently valid WebView packages. + **/ + public WebViewProviderInfo[] getValidWebViewPackages() { + ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); + WebViewProviderInfo[] providers = + new WebViewProviderInfo[providersAndPackageInfos.length]; + for(int n = 0; n < providersAndPackageInfos.length; n++) { + providers[n] = providersAndPackageInfos[n].provider; + } + return providers; } - return providers.toArray(new ProviderAndPackageInfo[providers.size()]); - } - /** - * Fetch only the currently valid WebView packages. - **/ - private WebViewProviderInfo[] getValidWebViewPackages() { - ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); - WebViewProviderInfo[] providers = new WebViewProviderInfo[providersAndPackageInfos.length]; - for(int n = 0; n < providersAndPackageInfos.length; n++) { - providers[n] = providersAndPackageInfos[n].provider; - } - return providers; - } - private class ProviderAndPackageInfo { - public final WebViewProviderInfo provider; - public final PackageInfo packageInfo; + private class ProviderAndPackageInfo { + public final WebViewProviderInfo provider; + public final PackageInfo packageInfo; - public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { - this.provider = provider; - this.packageInfo = packageInfo; + public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { + this.provider = provider; + this.packageInfo = packageInfo; + } } - } - /** - * Returns either the package info of the WebView provider determined in the following way: - * If the user has chosen a provider then use that if it is valid, - * otherwise use the first package in the webview priority list that is valid. - * - * @hide - */ - private PackageInfo findPreferredWebViewPackage() { - ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); + /** + * Returns either the package info of the WebView provider determined in the following way: + * If the user has chosen a provider then use that if it is valid, + * otherwise use the first package in the webview priority list that is valid. + * + */ + private PackageInfo findPreferredWebViewPackage() { + ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); - String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(getContext()); + String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext); - // If the user has chosen provider, use that - for (ProviderAndPackageInfo providerAndPackage : providers) { - if (providerAndPackage.provider.packageName.equals(userChosenProvider) - && isEnabledPackage(providerAndPackage.packageInfo)) { - return providerAndPackage.packageInfo; + // If the user has chosen provider, use that + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.packageName.equals(userChosenProvider) + && isEnabledPackage(providerAndPackage.packageInfo)) { + return providerAndPackage.packageInfo; + } } - } - // User did not choose, or the choice failed; use the most stable provider that is - // enabled and available by default (not through user choice). - for (ProviderAndPackageInfo providerAndPackage : providers) { - if (providerAndPackage.provider.availableByDefault - && isEnabledPackage(providerAndPackage.packageInfo)) { + // User did not choose, or the choice failed; use the most stable provider that is + // enabled and available by default (not through user choice). + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.availableByDefault + && isEnabledPackage(providerAndPackage.packageInfo)) { + return providerAndPackage.packageInfo; + } + } + + // Could not find any enabled package either, use the most stable provider. + for (ProviderAndPackageInfo providerAndPackage : providers) { return providerAndPackage.packageInfo; } + + mAnyWebViewInstalled = false; + throw new WebViewFactory.MissingWebViewPackageException( + "Could not find a loadable WebView package"); } - // Could not find any enabled package either, use the most stable provider. - for (ProviderAndPackageInfo providerAndPackage : providers) { - return providerAndPackage.packageInfo; + public void notifyRelroCreationCompleted() { + synchronized (mLock) { + mNumRelroCreationsFinished++; + checkIfRelrosDoneLocked(); + } } - mAnyWebViewInstalled = false; - throw new WebViewFactory.MissingWebViewPackageException( - "Could not find a loadable WebView package"); - } + public WebViewProviderResponse waitForAndGetProvider() { + PackageInfo webViewPackage = null; + final long NS_PER_MS = 1000000; + final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; + boolean webViewReady = false; + int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; + synchronized (mLock) { + webViewReady = webViewIsReadyLocked(); + while (!webViewReady) { + final long timeNowMs = System.nanoTime() / NS_PER_MS; + if (timeNowMs >= timeoutTimeMs) break; + try { + mLock.wait(timeoutTimeMs - timeNowMs); + } catch (InterruptedException e) {} + webViewReady = webViewIsReadyLocked(); + } + // Make sure we return the provider that was used to create the relro file + webViewPackage = mCurrentWebViewPackage; + if (webViewReady) { + } else if (!mAnyWebViewInstalled) { + webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; + } else { + // Either the current relro creation isn't done yet, or the new relro creatioin + // hasn't kicked off yet (the last relro creation used an out-of-date WebView). + webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; + } + } + if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); + return new WebViewProviderResponse(webViewPackage, webViewStatus); + } + public String getCurrentWebViewPackageName() { + synchronized(mLock) { + if (mCurrentWebViewPackage == null) + return null; + return mCurrentWebViewPackage.packageName; + } + } - /** - * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode of - * all available-by-default and non-fallback WebView provider packages. If there is no such - * WebView provider package on the system, then return -1, which means all positive versionCode - * WebView packages are accepted. - */ - private int getMinimumVersionCode() { - if (mMinimumVersionCode > 0) { - return mMinimumVersionCode; + /** + * Returns whether WebView is ready and is not going to go through its preparation phase + * again directly. + */ + private boolean webViewIsReadyLocked() { + return !mWebViewPackageDirty + && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) + // The current package might be replaced though we haven't received an intent + // declaring this yet, the following flag makes anyone loading WebView to wait in + // this case. + && mAnyWebViewInstalled; } - for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { - if (provider.availableByDefault && !provider.isFallback) { - try { - int versionCode = mSystemInterface.getFactoryPackageVersion(provider.packageName); - if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) { - mMinimumVersionCode = versionCode; - } - } catch (PackageManager.NameNotFoundException e) { - // Safe to ignore. + private void checkIfRelrosDoneLocked() { + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + if (mWebViewPackageDirty) { + mWebViewPackageDirty = false; + // If we have changed provider since we started the relro creation we need to + // redo the whole process using the new package instead. + PackageInfo newPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(newPackage); + } else { + mLock.notifyAll(); } } } - return mMinimumVersionCode; - } - - /** - * Returns whether this provider is valid for use as a WebView provider. - */ - private boolean isValidProvider(WebViewProviderInfo configInfo, - PackageInfo packageInfo) { - if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 - && packageInfo.versionCode < getMinimumVersionCode() - && !mSystemInterface.systemIsDebuggable()) { - // Non-system package webview providers may be downgraded arbitrarily low, prevent that - // by enforcing minimum version code. This check is only enforced for user builds. + /** + * Returns whether this provider is valid for use as a WebView provider. + */ + public boolean isValidProvider(WebViewProviderInfo configInfo, + PackageInfo packageInfo) { + if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 + && packageInfo.versionCode < getMinimumVersionCode() + && !mSystemInterface.systemIsDebuggable()) { + // Non-system package webview providers may be downgraded arbitrarily low, prevent + // that by enforcing minimum version code. This check is only enforced for user + // builds. + return false; + } + if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) && + WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) { + return true; + } return false; } - if (providerHasValidSignature(configInfo, packageInfo) - && WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) { - return true; + + /** + * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode + * of all available-by-default and non-fallback WebView provider packages. If there is no + * such WebView provider package on the system, then return -1, which means all positive + * versionCode WebView packages are accepted. + */ + private int getMinimumVersionCode() { + if (mMinimumVersionCode > 0) { + return mMinimumVersionCode; + } + + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + if (provider.availableByDefault && !provider.isFallback) { + try { + int versionCode = + mSystemInterface.getFactoryPackageVersion(provider.packageName); + if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) { + mMinimumVersionCode = versionCode; + } + } catch (PackageManager.NameNotFoundException e) { + // Safe to ignore. + } + } + } + + return mMinimumVersionCode; } - return false; } - private boolean providerHasValidSignature(WebViewProviderInfo provider, - PackageInfo packageInfo) { - if (mSystemInterface.systemIsDebuggable()) { + private static boolean providerHasValidSignature(WebViewProviderInfo provider, + PackageInfo packageInfo, SystemInterface systemInterface) { + if (systemInterface.systemIsDebuggable()) { return true; } Signature[] packageSignatures; @@ -512,7 +630,7 @@ public class WebViewUpdateService extends SystemService { * Returns whether the given package is enabled. * This state can be changed by the user from Settings->Apps */ - public boolean isEnabledPackage(PackageInfo packageInfo) { + private static boolean isEnabledPackage(PackageInfo packageInfo) { return packageInfo.applicationInfo.enabled; } @@ -528,32 +646,6 @@ public class WebViewUpdateService extends SystemService { intent.getDataString().substring("package:".length())); } - /** - * Returns whether WebView is ready and is not going to go through its preparation phase again - * directly. - */ - private boolean webViewIsReadyLocked() { - return !mWebViewPackageDirty - && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) - // The current package might be replaced though we haven't received an intent declaring - // this yet, the following flag makes anyone loading WebView to wait in this case. - && mAnyWebViewInstalled; - } - - private void checkIfRelrosDoneLocked() { - if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { - if (mWebViewPackageDirty) { - mWebViewPackageDirty = false; - // If we have changed provider since we started the relro creation we need to - // redo the whole process using the new package instead. - PackageInfo newPackage = findPreferredWebViewPackage(); - onWebViewProviderChanged(newPackage); - } else { - this.notifyAll(); - } - } - } - private class BinderService extends IWebViewUpdateService.Stub { @Override @@ -581,10 +673,7 @@ public class WebViewUpdateService extends SystemService { long callingId = Binder.clearCallingIdentity(); try { - synchronized (WebViewUpdateService.this) { - mNumRelroCreationsFinished++; - checkIfRelrosDoneLocked(); - } + WebViewUpdateService.this.mWebViewUpdater.notifyRelroCreationCompleted(); } finally { Binder.restoreCallingIdentity(callingId); } @@ -604,34 +693,7 @@ public class WebViewUpdateService extends SystemService { throw new IllegalStateException("Cannot create a WebView from the SystemServer"); } - PackageInfo webViewPackage = null; - final long NS_PER_MS = 1000000; - final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; - boolean webViewReady = false; - int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; - synchronized (WebViewUpdateService.this) { - webViewReady = WebViewUpdateService.this.webViewIsReadyLocked(); - while (!webViewReady) { - final long timeNowMs = System.nanoTime() / NS_PER_MS; - if (timeNowMs >= timeoutTimeMs) break; - try { - WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs); - } catch (InterruptedException e) {} - webViewReady = WebViewUpdateService.this.webViewIsReadyLocked(); - } - // Make sure we return the provider that was used to create the relro file - webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage; - if (webViewReady) { - } else if (!mAnyWebViewInstalled) { - webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; - } else { - // Either the current relro creation isn't done yet, or the new relro creatioin - // hasn't kicked off yet (the last relro creation used an out-of-date WebView). - webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; - } - } - if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); - return new WebViewProviderResponse(webViewPackage, webViewStatus); + return WebViewUpdateService.this.mWebViewUpdater.waitForAndGetProvider(); } /** @@ -652,7 +714,8 @@ public class WebViewUpdateService extends SystemService { long callingId = Binder.clearCallingIdentity(); try { - return WebViewUpdateService.this.changeProviderAndSetting(newProvider); + return WebViewUpdateService.this.mWebViewUpdater.changeProviderAndSetting( + newProvider); } finally { Binder.restoreCallingIdentity(callingId); } @@ -660,7 +723,7 @@ public class WebViewUpdateService extends SystemService { @Override // Binder call public WebViewProviderInfo[] getValidWebViewPackages() { - return WebViewUpdateService.this.getValidWebViewPackages(); + return WebViewUpdateService.this.mWebViewUpdater.getValidWebViewPackages(); } @Override // Binder call @@ -670,11 +733,7 @@ public class WebViewUpdateService extends SystemService { @Override // Binder call public String getCurrentWebViewPackageName() { - synchronized(WebViewUpdateService.this) { - if (WebViewUpdateService.this.mCurrentWebViewPackage == null) - return null; - return WebViewUpdateService.this.mCurrentWebViewPackage.packageName; - } + return WebViewUpdateService.this.mWebViewUpdater.getCurrentWebViewPackageName(); } @Override // Binder call |