1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.webkit; 18 19 import android.app.ActivityManager; 20 import android.app.AppGlobals; 21 import android.content.Context; 22 import android.content.pm.PackageInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.PackageManager.NameNotFoundException; 25 import android.content.pm.UserInfo; 26 import android.content.res.XmlResourceParser; 27 import android.os.Build; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.provider.Settings; 32 import android.util.AndroidRuntimeException; 33 import android.util.Log; 34 import android.webkit.UserPackage; 35 import android.webkit.WebViewFactory; 36 import android.webkit.WebViewProviderInfo; 37 import android.webkit.WebViewZygote; 38 39 import com.android.internal.util.XmlUtils; 40 41 import org.xmlpull.v1.XmlPullParserException; 42 43 import java.io.IOException; 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Default implementation for the WebView preparation Utility interface. 49 * @hide 50 */ 51 public class SystemImpl implements SystemInterface { 52 private static final String TAG = SystemImpl.class.getSimpleName(); 53 private static final String TAG_START = "webviewproviders"; 54 private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider"; 55 private static final String TAG_PACKAGE_NAME = "packageName"; 56 private static final String TAG_DESCRIPTION = "description"; 57 // Whether or not the provider must be explicitly chosen by the user to be used. 58 private static final String TAG_AVAILABILITY = "availableByDefault"; 59 private static final String TAG_SIGNATURE = "signature"; 60 private static final String TAG_FALLBACK = "isFallback"; 61 private final WebViewProviderInfo[] mWebViewProviderPackages; 62 63 // Initialization-on-demand holder idiom for getting the WebView provider packages once and 64 // for all in a thread-safe manner. 65 private static class LazyHolder { 66 private static final SystemImpl INSTANCE = new SystemImpl(); 67 } 68 getInstance()69 public static SystemImpl getInstance() { 70 return LazyHolder.INSTANCE; 71 } 72 SystemImpl()73 private SystemImpl() { 74 int numFallbackPackages = 0; 75 int numAvailableByDefaultPackages = 0; 76 XmlResourceParser parser = null; 77 List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>(); 78 try { 79 parser = AppGlobals.getInitialApplication().getResources().getXml( 80 com.android.internal.R.xml.config_webview_packages); 81 XmlUtils.beginDocument(parser, TAG_START); 82 while(true) { 83 XmlUtils.nextElement(parser); 84 String element = parser.getName(); 85 if (element == null) { 86 break; 87 } 88 if (element.equals(TAG_WEBVIEW_PROVIDER)) { 89 String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); 90 if (packageName == null) { 91 throw new AndroidRuntimeException( 92 "WebView provider in framework resources missing package name"); 93 } 94 String description = parser.getAttributeValue(null, TAG_DESCRIPTION); 95 if (description == null) { 96 throw new AndroidRuntimeException( 97 "WebView provider in framework resources missing description"); 98 } 99 boolean availableByDefault = "true".equals( 100 parser.getAttributeValue(null, TAG_AVAILABILITY)); 101 boolean isFallback = "true".equals( 102 parser.getAttributeValue(null, TAG_FALLBACK)); 103 WebViewProviderInfo currentProvider = new WebViewProviderInfo( 104 packageName, description, availableByDefault, isFallback, 105 readSignatures(parser)); 106 if (currentProvider.isFallback) { 107 numFallbackPackages++; 108 if (!currentProvider.availableByDefault) { 109 throw new AndroidRuntimeException( 110 "Each WebView fallback package must be available by default."); 111 } 112 if (numFallbackPackages > 1) { 113 throw new AndroidRuntimeException( 114 "There can be at most one WebView fallback package."); 115 } 116 } 117 if (currentProvider.availableByDefault) { 118 numAvailableByDefaultPackages++; 119 } 120 webViewProviders.add(currentProvider); 121 } 122 else { 123 Log.e(TAG, "Found an element that is not a WebView provider"); 124 } 125 } 126 } catch (XmlPullParserException | IOException e) { 127 throw new AndroidRuntimeException("Error when parsing WebView config " + e); 128 } finally { 129 if (parser != null) parser.close(); 130 } 131 if (numAvailableByDefaultPackages == 0) { 132 throw new AndroidRuntimeException("There must be at least one WebView package " 133 + "that is available by default"); 134 } 135 mWebViewProviderPackages = 136 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); 137 } 138 /** 139 * Returns all packages declared in the framework resources as potential WebView providers. 140 * @hide 141 * */ 142 @Override getWebViewPackages()143 public WebViewProviderInfo[] getWebViewPackages() { 144 return mWebViewProviderPackages; 145 } 146 getFactoryPackageVersion(String packageName)147 public long getFactoryPackageVersion(String packageName) throws NameNotFoundException { 148 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); 149 return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY) 150 .getLongVersionCode(); 151 } 152 153 /** 154 * Reads all signatures at the current depth (within the current provider) from the XML parser. 155 */ readSignatures(XmlResourceParser parser)156 private static String[] readSignatures(XmlResourceParser parser) throws IOException, 157 XmlPullParserException { 158 List<String> signatures = new ArrayList<String>(); 159 int outerDepth = parser.getDepth(); 160 while(XmlUtils.nextElementWithin(parser, outerDepth)) { 161 if (parser.getName().equals(TAG_SIGNATURE)) { 162 // Parse the value within the signature tag 163 String signature = parser.nextText(); 164 signatures.add(signature); 165 } else { 166 Log.e(TAG, "Found an element in a webview provider that is not a signature"); 167 } 168 } 169 return signatures.toArray(new String[signatures.size()]); 170 } 171 172 @Override onWebViewProviderChanged(PackageInfo packageInfo)173 public int onWebViewProviderChanged(PackageInfo packageInfo) { 174 return WebViewFactory.onWebViewProviderChanged(packageInfo); 175 } 176 177 @Override getUserChosenWebViewProvider(Context context)178 public String getUserChosenWebViewProvider(Context context) { 179 return Settings.Global.getString(context.getContentResolver(), 180 Settings.Global.WEBVIEW_PROVIDER); 181 } 182 183 @Override updateUserSetting(Context context, String newProviderName)184 public void updateUserSetting(Context context, String newProviderName) { 185 Settings.Global.putString(context.getContentResolver(), 186 Settings.Global.WEBVIEW_PROVIDER, 187 newProviderName == null ? "" : newProviderName); 188 } 189 190 @Override killPackageDependents(String packageName)191 public void killPackageDependents(String packageName) { 192 try { 193 ActivityManager.getService().killPackageDependents(packageName, 194 UserHandle.USER_ALL); 195 } catch (RemoteException e) { 196 } 197 } 198 199 @Override enablePackageForAllUsers(Context context, String packageName, boolean enable)200 public void enablePackageForAllUsers(Context context, String packageName, boolean enable) { 201 UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE); 202 for(UserInfo userInfo : userManager.getUsers()) { 203 enablePackageForUser(packageName, enable, userInfo.id); 204 } 205 } 206 enablePackageForUser(String packageName, boolean enable, int userId)207 private void enablePackageForUser(String packageName, boolean enable, int userId) { 208 try { 209 AppGlobals.getPackageManager().setApplicationEnabledSetting( 210 packageName, 211 enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT : 212 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0, 213 userId, null); 214 } catch (RemoteException | IllegalArgumentException e) { 215 Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName 216 + " for user " + userId + ": " + e); 217 } 218 } 219 220 @Override systemIsDebuggable()221 public boolean systemIsDebuggable() { 222 return Build.IS_DEBUGGABLE; 223 } 224 225 @Override getPackageInfoForProvider(WebViewProviderInfo configInfo)226 public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo) 227 throws NameNotFoundException { 228 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); 229 return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS); 230 } 231 232 @Override getPackageInfoForProviderAllUsers(Context context, WebViewProviderInfo configInfo)233 public List<UserPackage> getPackageInfoForProviderAllUsers(Context context, 234 WebViewProviderInfo configInfo) { 235 return UserPackage.getPackageInfosAllUsers(context, configInfo.packageName, PACKAGE_FLAGS); 236 } 237 238 @Override getMultiProcessSetting(Context context)239 public int getMultiProcessSetting(Context context) { 240 return Settings.Global.getInt(context.getContentResolver(), 241 Settings.Global.WEBVIEW_MULTIPROCESS, 0); 242 } 243 244 @Override setMultiProcessSetting(Context context, int value)245 public void setMultiProcessSetting(Context context, int value) { 246 Settings.Global.putInt(context.getContentResolver(), 247 Settings.Global.WEBVIEW_MULTIPROCESS, value); 248 } 249 250 @Override notifyZygote(boolean enableMultiProcess)251 public void notifyZygote(boolean enableMultiProcess) { 252 WebViewZygote.setMultiprocessEnabled(enableMultiProcess); 253 } 254 255 @Override ensureZygoteStarted()256 public void ensureZygoteStarted() { 257 WebViewZygote.getProcess(); 258 } 259 260 @Override isMultiProcessDefaultEnabled()261 public boolean isMultiProcessDefaultEnabled() { 262 // Multiprocess is enabled by default for all devices. 263 return true; 264 } 265 266 // flags declaring we want extra info from the package manager for webview providers 267 private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA 268 | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES 269 | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER; 270 } 271