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.ApplicationInfo; 23 import android.content.pm.IPackageDeleteObserver; 24 import android.content.pm.PackageInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.PackageManager.NameNotFoundException; 27 import android.content.pm.UserInfo; 28 import android.content.res.XmlResourceParser; 29 import android.database.ContentObserver; 30 import android.os.Build; 31 import android.os.RemoteException; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.provider.Settings.Global; 35 import android.provider.Settings; 36 import android.util.AndroidRuntimeException; 37 import android.util.Log; 38 import android.webkit.UserPackage; 39 import android.webkit.WebViewFactory; 40 import android.webkit.WebViewProviderInfo; 41 import android.webkit.WebViewZygote; 42 43 import com.android.internal.util.XmlUtils; 44 45 import java.io.IOException; 46 import java.util.ArrayList; 47 import java.util.List; 48 49 import org.xmlpull.v1.XmlPullParserException; 50 51 /** 52 * Default implementation for the WebView preparation Utility interface. 53 * @hide 54 */ 55 public class SystemImpl implements SystemInterface { 56 private static final String TAG = SystemImpl.class.getSimpleName(); 57 private static final String TAG_START = "webviewproviders"; 58 private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider"; 59 private static final String TAG_PACKAGE_NAME = "packageName"; 60 private static final String TAG_DESCRIPTION = "description"; 61 // Whether or not the provider must be explicitly chosen by the user to be used. 62 private static final String TAG_AVAILABILITY = "availableByDefault"; 63 private static final String TAG_SIGNATURE = "signature"; 64 private static final String TAG_FALLBACK = "isFallback"; 65 private final WebViewProviderInfo[] mWebViewProviderPackages; 66 67 // Initialization-on-demand holder idiom for getting the WebView provider packages once and 68 // for all in a thread-safe manner. 69 private static class LazyHolder { 70 private static final SystemImpl INSTANCE = new SystemImpl(); 71 } 72 73 public static SystemImpl getInstance() { 74 return LazyHolder.INSTANCE; 75 } 76 77 private SystemImpl() { 78 int numFallbackPackages = 0; 79 int numAvailableByDefaultPackages = 0; 80 int numAvByDefaultAndNotFallback = 0; 81 XmlResourceParser parser = null; 82 List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>(); 83 try { 84 parser = AppGlobals.getInitialApplication().getResources().getXml( 85 com.android.internal.R.xml.config_webview_packages); 86 XmlUtils.beginDocument(parser, TAG_START); 87 while(true) { 88 XmlUtils.nextElement(parser); 89 String element = parser.getName(); 90 if (element == null) { 91 break; 92 } 93 if (element.equals(TAG_WEBVIEW_PROVIDER)) { 94 String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); 95 if (packageName == null) { 96 throw new AndroidRuntimeException( 97 "WebView provider in framework resources missing package name"); 98 } 99 String description = parser.getAttributeValue(null, TAG_DESCRIPTION); 100 if (description == null) { 101 throw new AndroidRuntimeException( 102 "WebView provider in framework resources missing description"); 103 } 104 boolean availableByDefault = "true".equals( 105 parser.getAttributeValue(null, TAG_AVAILABILITY)); 106 boolean isFallback = "true".equals( 107 parser.getAttributeValue(null, TAG_FALLBACK)); 108 WebViewProviderInfo currentProvider = new WebViewProviderInfo( 109 packageName, description, availableByDefault, isFallback, 110 readSignatures(parser)); 111 if (currentProvider.isFallback) { 112 numFallbackPackages++; 113 if (!currentProvider.availableByDefault) { 114 throw new AndroidRuntimeException( 115 "Each WebView fallback package must be available by default."); 116 } 117 if (numFallbackPackages > 1) { 118 throw new AndroidRuntimeException( 119 "There can be at most one WebView fallback package."); 120 } 121 } 122 if (currentProvider.availableByDefault) { 123 numAvailableByDefaultPackages++; 124 if (!currentProvider.isFallback) { 125 numAvByDefaultAndNotFallback++; 126 } 127 } 128 webViewProviders.add(currentProvider); 129 } 130 else { 131 Log.e(TAG, "Found an element that is not a WebView provider"); 132 } 133 } 134 } catch (XmlPullParserException | IOException e) { 135 throw new AndroidRuntimeException("Error when parsing WebView config " + e); 136 } finally { 137 if (parser != null) parser.close(); 138 } 139 if (numAvailableByDefaultPackages == 0) { 140 throw new AndroidRuntimeException("There must be at least one WebView package " 141 + "that is available by default"); 142 } 143 if (numAvByDefaultAndNotFallback == 0) { 144 throw new AndroidRuntimeException("There must be at least one WebView package " 145 + "that is available by default and not a fallback"); 146 } 147 mWebViewProviderPackages = 148 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); 149 } 150 /** 151 * Returns all packages declared in the framework resources as potential WebView providers. 152 * @hide 153 * */ 154 @Override 155 public WebViewProviderInfo[] getWebViewPackages() { 156 return mWebViewProviderPackages; 157 } 158 159 public long getFactoryPackageVersion(String packageName) throws NameNotFoundException { 160 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); 161 return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY) 162 .getLongVersionCode(); 163 } 164 165 /** 166 * Reads all signatures at the current depth (within the current provider) from the XML parser. 167 */ 168 private static String[] readSignatures(XmlResourceParser parser) throws IOException, 169 XmlPullParserException { 170 List<String> signatures = new ArrayList<String>(); 171 int outerDepth = parser.getDepth(); 172 while(XmlUtils.nextElementWithin(parser, outerDepth)) { 173 if (parser.getName().equals(TAG_SIGNATURE)) { 174 // Parse the value within the signature tag 175 String signature = parser.nextText(); 176 signatures.add(signature); 177 } else { 178 Log.e(TAG, "Found an element in a webview provider that is not a signature"); 179 } 180 } 181 return signatures.toArray(new String[signatures.size()]); 182 } 183 184 @Override 185 public int onWebViewProviderChanged(PackageInfo packageInfo) { 186 return WebViewFactory.onWebViewProviderChanged(packageInfo); 187 } 188 189 @Override 190 public String getUserChosenWebViewProvider(Context context) { 191 return Settings.Global.getString(context.getContentResolver(), 192 Settings.Global.WEBVIEW_PROVIDER); 193 } 194 195 @Override 196 public void updateUserSetting(Context context, String newProviderName) { 197 Settings.Global.putString(context.getContentResolver(), 198 Settings.Global.WEBVIEW_PROVIDER, 199 newProviderName == null ? "" : newProviderName); 200 } 201 202 @Override 203 public void killPackageDependents(String packageName) { 204 try { 205 ActivityManager.getService().killPackageDependents(packageName, 206 UserHandle.USER_ALL); 207 } catch (RemoteException e) { 208 } 209 } 210 211 @Override 212 public boolean isFallbackLogicEnabled() { 213 // Note that this is enabled by default (i.e. if the setting hasn't been set). 214 return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(), 215 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1; 216 } 217 218 @Override 219 public void enableFallbackLogic(boolean enable) { 220 Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(), 221 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0); 222 } 223 224 @Override 225 public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) { 226 enablePackageForAllUsers(context, packageName, false); 227 try { 228 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); 229 ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0); 230 if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) { 231 pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() { 232 public void packageDeleted(String packageName, int returnCode) { 233 enablePackageForAllUsers(context, packageName, false); 234 } 235 }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS); 236 } 237 } catch (NameNotFoundException e) { 238 } 239 } 240 241 @Override 242 public void enablePackageForAllUsers(Context context, String packageName, boolean enable) { 243 UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE); 244 for(UserInfo userInfo : userManager.getUsers()) { 245 enablePackageForUser(packageName, enable, userInfo.id); 246 } 247 } 248 249 @Override 250 public void enablePackageForUser(String packageName, boolean enable, int userId) { 251 try { 252 AppGlobals.getPackageManager().setApplicationEnabledSetting( 253 packageName, 254 enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT : 255 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0, 256 userId, null); 257 } catch (RemoteException | IllegalArgumentException e) { 258 Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName 259 + " for user " + userId + ": " + e); 260 } 261 } 262 263 @Override 264 public boolean systemIsDebuggable() { 265 return Build.IS_DEBUGGABLE; 266 } 267 268 @Override 269 public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo) 270 throws NameNotFoundException { 271 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager(); 272 return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS); 273 } 274 275 @Override 276 public List<UserPackage> getPackageInfoForProviderAllUsers(Context context, 277 WebViewProviderInfo configInfo) { 278 return UserPackage.getPackageInfosAllUsers(context, configInfo.packageName, PACKAGE_FLAGS); 279 } 280 281 @Override 282 public int getMultiProcessSetting(Context context) { 283 return Settings.Global.getInt(context.getContentResolver(), 284 Settings.Global.WEBVIEW_MULTIPROCESS, 0); 285 } 286 287 @Override 288 public void setMultiProcessSetting(Context context, int value) { 289 Settings.Global.putInt(context.getContentResolver(), 290 Settings.Global.WEBVIEW_MULTIPROCESS, value); 291 } 292 293 @Override 294 public void notifyZygote(boolean enableMultiProcess) { 295 WebViewZygote.setMultiprocessEnabled(enableMultiProcess); 296 } 297 298 @Override 299 public boolean isMultiProcessDefaultEnabled() { 300 // Multiprocess is enabled for all 64-bit devices, since the ability to run the renderer 301 // process in 32-bit when it's a separate process typically results in a net memory saving. 302 // Multiprocess is also enabled for 32-bit devices unless they report they are "low ram". 303 return Build.SUPPORTED_64_BIT_ABIS.length > 0 || !ActivityManager.isLowRamDeviceStatic(); 304 } 305 306 // flags declaring we want extra info from the package manager for webview providers 307 private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA 308 | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES 309 | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER; 310 } 311