/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.webkit; import android.app.ActivityManager; import android.app.AppGlobals; import android.content.Context; 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.UserInfo; import android.content.res.XmlResourceParser; import android.database.ContentObserver; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings.Global; import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.Log; import android.webkit.UserPackage; import android.webkit.WebViewFactory; import android.webkit.WebViewProviderInfo; import android.webkit.WebViewZygote; import com.android.internal.util.XmlUtils; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlPullParserException; /** * Default implementation for the WebView preparation Utility interface. * @hide */ public class SystemImpl implements SystemInterface { private static final String TAG = SystemImpl.class.getSimpleName(); private static final String TAG_START = "webviewproviders"; private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider"; private static final String TAG_PACKAGE_NAME = "packageName"; private static final String TAG_DESCRIPTION = "description"; // Whether or not the provider must be explicitly chosen by the user to be used. private static final String TAG_AVAILABILITY = "availableByDefault"; private static final String TAG_SIGNATURE = "signature"; private static final String TAG_FALLBACK = "isFallback"; private final WebViewProviderInfo[] mWebViewProviderPackages; // Initialization-on-demand holder idiom for getting the WebView provider packages once and // for all in a thread-safe manner. private static class LazyHolder { private static final SystemImpl INSTANCE = new SystemImpl(); } public static SystemImpl getInstance() { return LazyHolder.INSTANCE; } private SystemImpl() { int numFallbackPackages = 0; int numAvailableByDefaultPackages = 0; int numAvByDefaultAndNotFallback = 0; XmlResourceParser parser = null; List webViewProviders = new ArrayList(); try { parser = AppGlobals.getInitialApplication().getResources().getXml( com.android.internal.R.xml.config_webview_packages); XmlUtils.beginDocument(parser, TAG_START); while(true) { XmlUtils.nextElement(parser); String element = parser.getName(); if (element == null) { break; } if (element.equals(TAG_WEBVIEW_PROVIDER)) { String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); if (packageName == null) { throw new AndroidRuntimeException( "WebView provider in framework resources missing package name"); } String description = parser.getAttributeValue(null, TAG_DESCRIPTION); if (description == null) { throw new AndroidRuntimeException( "WebView provider in framework resources missing description"); } boolean availableByDefault = "true".equals( 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.isFallback) { numFallbackPackages++; if (!currentProvider.availableByDefault) { throw new AndroidRuntimeException( "Each WebView fallback package must be available by default."); } if (numFallbackPackages > 1) { throw new AndroidRuntimeException( "There can be at most one WebView fallback package."); } } if (currentProvider.availableByDefault) { numAvailableByDefaultPackages++; if (!currentProvider.isFallback) { numAvByDefaultAndNotFallback++; } } webViewProviders.add(currentProvider); } else { Log.e(TAG, "Found an element that is not a WebView provider"); } } } catch (XmlPullParserException | IOException e) { throw new AndroidRuntimeException("Error when parsing WebView config " + e); } finally { if (parser != null) parser.close(); } if (numAvailableByDefaultPackages == 0) { throw new AndroidRuntimeException("There must be at least one WebView package " + "that is available by default"); } if (numAvByDefaultAndNotFallback == 0) { throw new AndroidRuntimeException("There must be at least one WebView package " + "that is available by default and not a fallback"); } mWebViewProviderPackages = webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); } /** * Returns all packages declared in the framework resources as potential WebView providers. * @hide * */ @Override public WebViewProviderInfo[] getWebViewPackages() { return mWebViewProviderPackages; } public int getFactoryPackageVersion(String packageName) throws NameNotFoundException { PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode; } /** * Reads all signatures at the current depth (within the current provider) from the XML parser. */ private static String[] readSignatures(XmlResourceParser parser) throws IOException, XmlPullParserException { List signatures = new ArrayList(); int outerDepth = parser.getDepth(); while(XmlUtils.nextElementWithin(parser, outerDepth)) { if (parser.getName().equals(TAG_SIGNATURE)) { // Parse the value within the signature tag String signature = parser.nextText(); signatures.add(signature); } else { Log.e(TAG, "Found an element in a webview provider that is not a signature"); } } return signatures.toArray(new String[signatures.size()]); } @Override public int onWebViewProviderChanged(PackageInfo packageInfo) { return WebViewFactory.onWebViewProviderChanged(packageInfo); } @Override public String getUserChosenWebViewProvider(Context context) { return Settings.Global.getString(context.getContentResolver(), Settings.Global.WEBVIEW_PROVIDER); } @Override public void updateUserSetting(Context context, String newProviderName) { Settings.Global.putString(context.getContentResolver(), Settings.Global.WEBVIEW_PROVIDER, newProviderName == null ? "" : newProviderName); } @Override public void killPackageDependents(String packageName) { try { ActivityManager.getService().killPackageDependents(packageName, UserHandle.USER_ALL); } catch (RemoteException e) { } } @Override public boolean isFallbackLogicEnabled() { // Note that this is enabled by default (i.e. if the setting hasn't been set). return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(), Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1; } @Override public void enableFallbackLogic(boolean enable) { Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(), Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0); } @Override public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) { enablePackageForAllUsers(context, packageName, false); try { PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0); if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) { pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() { public void packageDeleted(String packageName, int returnCode) { enablePackageForAllUsers(context, packageName, false); } }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS); } } catch (NameNotFoundException e) { } } @Override public void enablePackageForAllUsers(Context context, String packageName, boolean enable) { UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE); for(UserInfo userInfo : userManager.getUsers()) { enablePackageForUser(packageName, enable, userInfo.id); } } @Override public void enablePackageForUser(String packageName, boolean enable, int userId) { try { AppGlobals.getPackageManager().setApplicationEnabledSetting( packageName, enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT : PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0, userId, null); } catch (RemoteException | IllegalArgumentException e) { Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName + " for user " + userId + ": " + e); } } @Override public boolean systemIsDebuggable() { return Build.IS_DEBUGGABLE; } @Override public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo) throws NameNotFoundException { PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS); } @Override public List getPackageInfoForProviderAllUsers(Context context, WebViewProviderInfo configInfo) { return UserPackage.getPackageInfosAllUsers(context, configInfo.packageName, PACKAGE_FLAGS); } @Override public int getMultiProcessSetting(Context context) { return Settings.Global.getInt(context.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, 0); } @Override public void setMultiProcessSetting(Context context, int value) { Settings.Global.putInt(context.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, value); } @Override public void notifyZygote(boolean enableMultiProcess) { WebViewZygote.setMultiprocessEnabled(enableMultiProcess); } @Override public boolean isMultiProcessDefaultEnabled() { // Multiprocess is enabled for all 64-bit devices, since the ability to run the renderer // process in 32-bit when it's a separate process typically results in a net memory saving. // Multiprocess is also enabled for 32-bit devices unless they report they are "low ram". return Build.SUPPORTED_64_BIT_ABIS.length > 0 || !ActivityManager.isLowRamDeviceStatic(); } // 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 | PackageManager.MATCH_ANY_USER; }