Merge "Simplify WebViewProviderInfo - move its logic into WebViewUpdateService." into nyc-dev

This commit is contained in:
Gustav Sennton
2016-03-31 15:28:52 +00:00
committed by Android (Google) Code Review
3 changed files with 137 additions and 178 deletions

View File

@@ -16,32 +16,16 @@
package android.webkit;
import android.app.AppGlobals;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AndroidRuntimeException;
import android.util.Base64;
import java.util.Arrays;
/** @hide */
public class WebViewProviderInfo implements Parcelable {
public final class WebViewProviderInfo implements Parcelable {
/**
* @hide
*/
public static class WebViewPackageNotFoundException extends AndroidRuntimeException {
public WebViewPackageNotFoundException(String message) { super(message); }
public WebViewPackageNotFoundException(Exception e) { super(e); }
}
public WebViewProviderInfo(String packageName, String description, boolean availableByDefault,
boolean isFallback, String[] signatures) {
public WebViewProviderInfo(String packageName, String description,
boolean availableByDefault, boolean isFallback, String[] signatures) {
this.packageName = packageName;
this.description = description;
this.availableByDefault = availableByDefault;
@@ -49,92 +33,6 @@ public class WebViewProviderInfo implements Parcelable {
this.signatures = signatures;
}
private boolean hasValidSignature() {
if (Build.IS_DEBUGGABLE)
return true;
Signature[] packageSignatures;
try {
// If no signature is declared, instead check whether the package is included in the
// system.
if (signatures == null || signatures.length == 0)
return getPackageInfo().applicationInfo.isSystemApp();
packageSignatures = getPackageInfo().signatures;
} catch (WebViewPackageNotFoundException e) {
return false;
}
if (packageSignatures.length != 1)
return false;
final byte[] packageSignature = packageSignatures[0].toByteArray();
// Return whether the package signature matches any of the valid signatures
for (String signature : signatures) {
final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
if (Arrays.equals(packageSignature, validSignature))
return true;
}
return false;
}
/**
* Returns whether this provider is valid for use as a WebView provider.
*/
public boolean isValidProvider() {
ApplicationInfo applicationInfo;
try {
applicationInfo = getPackageInfo().applicationInfo;
} catch (WebViewPackageNotFoundException e) {
return false;
}
if (hasValidSignature() && WebViewFactory.getWebViewLibrary(applicationInfo) != null) {
return true;
}
return false;
}
/**
* Returns whether this package is enabled.
* This state can be changed by the user from Settings->Apps
*/
public boolean isEnabled() {
try {
// Explicitly fetch up-to-date package info here since the enabled-state of the package
// might have changed since we last fetched its package info.
updatePackageInfo();
return getPackageInfo().applicationInfo.enabled;
} catch (WebViewPackageNotFoundException e) {
return false;
}
}
/**
* Returns whether the provider is always available as long as it is valid.
* If this returns false, the provider will only be used if the user chose this provider.
*/
public boolean isAvailableByDefault() {
return availableByDefault;
}
public boolean isFallbackPackage() {
return isFallback;
}
private void updatePackageInfo() {
try {
PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
} catch (PackageManager.NameNotFoundException e) {
throw new WebViewPackageNotFoundException(e);
}
}
public PackageInfo getPackageInfo() {
if (packageInfo == null) {
updatePackageInfo();
}
return packageInfo;
}
// aidl stuff
public static final Parcelable.Creator<WebViewProviderInfo> CREATOR =
new Parcelable.Creator<WebViewProviderInfo>() {
@@ -153,7 +51,6 @@ public class WebViewProviderInfo implements Parcelable {
availableByDefault = (in.readInt() > 0);
isFallback = (in.readInt() > 0);
signatures = in.createStringArray();
packageInfo = null;
}
@Override
@@ -171,16 +68,9 @@ public class WebViewProviderInfo implements Parcelable {
}
// fields read from framework resource
public String packageName;
public String description;
private boolean availableByDefault;
private boolean isFallback;
private String[] signatures;
private PackageInfo packageInfo;
// flags declaring we want extra info from the package manager
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
public final String packageName;
public final String description;
public final boolean availableByDefault;
public final boolean isFallback;
public final String[] signatures;
}

View File

@@ -26,9 +26,11 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Build;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteException;
@@ -38,6 +40,7 @@ import android.os.UserManager;
import android.provider.Settings.Global;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.Base64;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
import android.webkit.WebViewFactory;
@@ -72,8 +75,6 @@ public class WebViewUpdateService extends SystemService {
// The WebView package currently in use (or the one we are preparing).
private PackageInfo mCurrentWebViewPackage = null;
// The WebView providers that are currently available.
private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
private BroadcastReceiver mWebViewUpdatedReceiver;
private WebViewUtilityInterface mWebViewUtility;
@@ -126,7 +127,6 @@ public class WebViewUpdateService extends SystemService {
PackageInfo newPackage = null;
synchronized(WebViewUpdateService.this) {
try {
updateValidWebViewPackages();
newPackage = findPreferredWebViewPackage();
if (mCurrentWebViewPackage != null)
oldProviderName = mCurrentWebViewPackage.packageName;
@@ -180,11 +180,17 @@ public class WebViewUpdateService extends SystemService {
publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
}
private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
for (WebViewProviderInfo provider : providers) {
if (provider.isAvailableByDefault() && provider.isEnabled()
&& provider.isValidProvider() && !provider.isFallbackPackage()) {
return true;
if (provider.availableByDefault && !provider.isFallback) {
try {
PackageInfo packageInfo = getPackageInfoForProvider(provider);
if (isEnabledPackage(packageInfo) && isValidProvider(provider, packageInfo)) {
return true;
}
} catch (NameNotFoundException e) {
// A non-existent provider is neither valid nor enabled
}
}
}
return false;
@@ -211,11 +217,9 @@ public class WebViewUpdateService extends SystemService {
WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider == null) return;
boolean existsValidNonFallbackProvider =
existsValidNonFallbackProvider(webviewProviders);
enablePackageForUser(fallbackProvider.packageName, !existsValidNonFallbackProvider,
userId);
enablePackageForUser(fallbackProvider.packageName,
!existsValidNonFallbackProvider(webviewProviders), userId);
}
/**
@@ -236,7 +240,7 @@ public class WebViewUpdateService extends SystemService {
for (WebViewProviderInfo provider : webviewProviders) {
String webviewPackage = "package:" + provider.packageName;
if (webviewPackage.equals(intent.getDataString())) {
if (provider.isAvailableByDefault()) {
if (provider.availableByDefault) {
changedPackage = provider.packageName;
}
break;
@@ -251,10 +255,16 @@ public class WebViewUpdateService extends SystemService {
if (fallbackProvider == null) return;
boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
boolean isFallbackEnabled = false;
try {
isFallbackEnabled = isEnabledPackage(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.
&& (fallbackProvider.isEnabled() || intent == null)) {
&& (isFallbackEnabled || intent == null)) {
// Uninstall and disable fallback package for all users.
context.getPackageManager().deletePackage(fallbackProvider.packageName,
new IPackageDeleteObserver.Stub() {
@@ -273,7 +283,7 @@ public class WebViewUpdateService extends SystemService {
} 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.
&& (!fallbackProvider.isEnabled() || intent==null)) {
&& (!isFallbackEnabled || intent==null)) {
// Enable the fallback package for all users.
UserManager userManager =
(UserManager)context.getSystemService(Context.USER_SERVICE);
@@ -299,24 +309,13 @@ public class WebViewUpdateService extends SystemService {
*/
private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
for (WebViewProviderInfo provider : webviewPackages) {
if (provider.isFallbackPackage()) {
if (provider.isFallback) {
return provider;
}
}
return null;
}
private static boolean containsAvailableNonFallbackProvider(
WebViewProviderInfo[] webviewPackages) {
for (WebViewProviderInfo provider : webviewPackages) {
if (provider.isAvailableByDefault() && provider.isEnabled()
&& provider.isValidProvider() && !provider.isFallbackPackage()) {
return true;
}
}
return false;
}
private boolean isFallbackPackage(String packageName) {
if (packageName == null || !isFallbackLogicEnabled()) return false;
@@ -336,7 +335,6 @@ public class WebViewUpdateService extends SystemService {
updateFallbackState(getContext(), null);
try {
synchronized(this) {
updateValidWebViewPackages();
mCurrentWebViewPackage = findPreferredWebViewPackage();
onWebViewProviderChanged(mCurrentWebViewPackage);
}
@@ -408,24 +406,41 @@ public class WebViewUpdateService extends SystemService {
}
}
/**
* Updates the currently valid WebView provider packages.
* Should be used when a provider has been installed or removed.
* @hide
* */
private void updateValidWebViewPackages() {
List<WebViewProviderInfo> webViewProviders =
new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages()));
Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
// remove non-valid packages
while(it.hasNext()) {
WebViewProviderInfo current = it.next();
if (!current.isValidProvider())
it.remove();
private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
WebViewProviderInfo[] allProviders = mWebViewUtility.getWebViewPackages();
List<ProviderAndPackageInfo> providers = new ArrayList<>();
for(int n = 0; n < allProviders.length; n++) {
try {
PackageInfo packageInfo = getPackageInfoForProvider(allProviders[n]);
if (isValidProvider(allProviders[n], packageInfo)) {
providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
}
} catch (NameNotFoundException e) {
// Don't add non-existent packages
}
}
synchronized(this) {
mCurrentValidWebViewPackages =
webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
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;
}
}
@@ -437,28 +452,30 @@ public class WebViewUpdateService extends SystemService {
* @hide
*/
private PackageInfo findPreferredWebViewPackage() {
WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext());
// If the user has chosen provider, use that
for (WebViewProviderInfo provider : providers) {
if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
return provider.getPackageInfo();
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 (WebViewProviderInfo provider : providers) {
if (provider.isAvailableByDefault() && provider.isEnabled()) {
return provider.getPackageInfo();
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 (WebViewProviderInfo provider : providers) {
return provider.getPackageInfo();
for (ProviderAndPackageInfo providerAndPackage : providers) {
return providerAndPackage.packageInfo;
}
mAnyWebViewInstalled = false;
@@ -466,6 +483,60 @@ public class WebViewUpdateService extends SystemService {
"Could not find a loadable WebView package");
}
/**
* Returns whether this provider is valid for use as a WebView provider.
*/
private static boolean isValidProvider(WebViewProviderInfo configInfo,
PackageInfo packageInfo) {
if (providerHasValidSignature(configInfo, packageInfo) &&
WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
return true;
}
return false;
}
private static boolean providerHasValidSignature(WebViewProviderInfo provider,
PackageInfo packageInfo) {
if (Build.IS_DEBUGGABLE)
return true;
Signature[] packageSignatures;
// If no signature is declared, instead check whether the package is included in the
// system.
if (provider.signatures == null || provider.signatures.length == 0) {
return packageInfo.applicationInfo.isSystemApp();
}
packageSignatures = packageInfo.signatures;
if (packageSignatures.length != 1)
return false;
final byte[] packageSignature = packageSignatures[0].toByteArray();
// Return whether the package signature matches any of the valid signatures
for (String signature : provider.signatures) {
final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
if (Arrays.equals(packageSignature, validSignature))
return true;
}
return false;
}
/**
* Returns whether the given package is enabled.
* This state can be changed by the user from Settings->Apps
*/
private static boolean isEnabledPackage(PackageInfo packageInfo) {
return packageInfo.applicationInfo.enabled;
}
private static PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException {
PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
}
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
/**
* Returns whether WebView is ready and is not going to go through its preparation phase again
* directly.
@@ -593,9 +664,7 @@ public class WebViewUpdateService extends SystemService {
@Override // Binder call
public WebViewProviderInfo[] getValidWebViewPackages() {
synchronized(WebViewUpdateService.this) {
return mCurrentValidWebViewPackages;
}
return WebViewUpdateService.this.getValidWebViewPackages();
}
@Override // Binder call

View File

@@ -87,10 +87,10 @@ public class WebViewUtilityImpl implements WebViewUtilityInterface {
parser.getAttributeValue(null, TAG_AVAILABILITY));
boolean isFallback = "true".equals(
parser.getAttributeValue(null, TAG_FALLBACK));
WebViewProviderInfo currentProvider =
new WebViewProviderInfo(packageName, description, availableByDefault,
isFallback, readSignatures(parser));
if (currentProvider.isFallbackPackage()) {
WebViewProviderInfo currentProvider = new WebViewProviderInfo(
packageName, description, availableByDefault, isFallback,
readSignatures(parser));
if (currentProvider.isFallback) {
numFallbackPackages++;
if (numFallbackPackages > 1) {
throw new AndroidRuntimeException(