Merge "Split WebViewUpdateService logic into fallback logic and update-logic." into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e1ccdd13dc
@@ -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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
private static class WebViewUpdater {
|
||||
private Context mContext;
|
||||
private SystemInterface mSystemInterface;
|
||||
private int mMinimumVersionCode = -1;
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(getContext());
|
||||
|
||||
// If the user has chosen provider, use that
|
||||
for (ProviderAndPackageInfo providerAndPackage : providers) {
|
||||
if (providerAndPackage.provider.packageName.equals(userChosenProvider)
|
||||
&& isEnabledPackage(providerAndPackage.packageInfo)) {
|
||||
return providerAndPackage.packageInfo;
|
||||
}
|
||||
public WebViewUpdater(Context context, SystemInterface systemInterface) {
|
||||
mContext = context;
|
||||
mSystemInterface = systemInterface;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
|
||||
|
||||
// Could not find any enabled package either, use the most stable provider.
|
||||
for (ProviderAndPackageInfo providerAndPackage : providers) {
|
||||
return providerAndPackage.packageInfo;
|
||||
}
|
||||
// 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;
|
||||
|
||||
mAnyWebViewInstalled = false;
|
||||
throw new WebViewFactory.MissingWebViewPackageException(
|
||||
"Could not find a loadable WebView package");
|
||||
}
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
private Object mLock = new Object();
|
||||
|
||||
for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
|
||||
if (provider.availableByDefault && !provider.isFallback) {
|
||||
try {
|
||||
int versionCode = mSystemInterface.getFactoryPackageVersion(provider.packageName);
|
||||
if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) {
|
||||
mMinimumVersionCode = versionCode;
|
||||
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);
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Safe to ignore.
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mMinimumVersionCode;
|
||||
}
|
||||
public void prepareWebViewInSystemServer() {
|
||||
try {
|
||||
synchronized(mLock) {
|
||||
mCurrentWebViewPackage = findPreferredWebViewPackage();
|
||||
onWebViewProviderChanged(mCurrentWebViewPackage);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
private class ProviderAndPackageInfo {
|
||||
public final WebViewProviderInfo provider;
|
||||
public final 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.
|
||||
*
|
||||
*/
|
||||
private PackageInfo findPreferredWebViewPackage() {
|
||||
ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
public void notifyRelroCreationCompleted() {
|
||||
synchronized (mLock) {
|
||||
mNumRelroCreationsFinished++;
|
||||
checkIfRelrosDoneLocked();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
mLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
Reference in New Issue
Block a user