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