blob: bb76449a85290fa544dcc26ccb2159a33522138e [file] [log] [blame]
Gustav Sennton8b179262016-03-14 11:31:14 +00001/*
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.ActivityManagerNative;
20import android.app.AppGlobals;
21import android.content.Context;
Gustav Senntonff396f22016-04-08 18:33:30 +010022import android.content.pm.ApplicationInfo;
Gustav Sennton065b7e62016-04-01 15:11:43 +010023import android.content.pm.IPackageDeleteObserver;
Gustav Sennton8b179262016-03-14 11:31:14 +000024import android.content.pm.PackageInfo;
Gustav Sennton065b7e62016-04-01 15:11:43 +010025import android.content.pm.PackageManager;
26import android.content.pm.PackageManager.NameNotFoundException;
27import android.content.pm.UserInfo;
Gustav Sennton8b179262016-03-14 11:31:14 +000028import android.content.res.XmlResourceParser;
Gustav Sennton065b7e62016-04-01 15:11:43 +010029import android.os.Build;
Gustav Sennton8b179262016-03-14 11:31:14 +000030import android.os.RemoteException;
31import android.os.UserHandle;
Gustav Sennton065b7e62016-04-01 15:11:43 +010032import android.os.UserManager;
33import android.provider.Settings.Global;
Gustav Sennton8b179262016-03-14 11:31:14 +000034import android.provider.Settings;
35import android.util.AndroidRuntimeException;
36import android.util.Log;
Gustav Sennton065b7e62016-04-01 15:11:43 +010037import android.webkit.WebViewFactory;
Gustav Sennton8b179262016-03-14 11:31:14 +000038import android.webkit.WebViewProviderInfo;
39
40import com.android.internal.util.XmlUtils;
41
42import java.io.IOException;
43import java.util.ArrayList;
44import java.util.List;
45
46import org.xmlpull.v1.XmlPullParserException;
47
48/**
49 * Default implementation for the WebView preparation Utility interface.
50 * @hide
51 */
Gustav Sennton065b7e62016-04-01 15:11:43 +010052public class SystemImpl implements SystemInterface {
53 private static final String TAG = SystemImpl.class.getSimpleName();
Gustav Sennton8b179262016-03-14 11:31:14 +000054 private static final String TAG_START = "webviewproviders";
55 private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider";
56 private static final String TAG_PACKAGE_NAME = "packageName";
57 private static final String TAG_DESCRIPTION = "description";
58 // Whether or not the provider must be explicitly chosen by the user to be used.
59 private static final String TAG_AVAILABILITY = "availableByDefault";
60 private static final String TAG_SIGNATURE = "signature";
61 private static final String TAG_FALLBACK = "isFallback";
Hui Shu9455bd02016-04-08 13:25:26 -070062 private final WebViewProviderInfo[] mWebViewProviderPackages;
Gustav Sennton8b179262016-03-14 11:31:14 +000063
Hui Shu9455bd02016-04-08 13:25:26 -070064 // Initialization-on-demand holder idiom for getting the WebView provider packages once and
65 // for all in a thread-safe manner.
66 private static class LazyHolder {
67 private static final SystemImpl INSTANCE = new SystemImpl();
68 }
69
70 public static SystemImpl getInstance() {
71 return LazyHolder.INSTANCE;
72 }
73
74 private SystemImpl() {
Gustav Sennton8b179262016-03-14 11:31:14 +000075 int numFallbackPackages = 0;
Gustav Sennton631de99e2016-04-07 16:37:58 +010076 int numAvailableByDefaultPackages = 0;
Gustav Senntonc0bc9932016-04-13 16:43:44 +010077 int numAvByDefaultAndNotFallback = 0;
Gustav Sennton8b179262016-03-14 11:31:14 +000078 XmlResourceParser parser = null;
79 List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
80 try {
81 parser = AppGlobals.getInitialApplication().getResources().getXml(
82 com.android.internal.R.xml.config_webview_packages);
83 XmlUtils.beginDocument(parser, TAG_START);
84 while(true) {
85 XmlUtils.nextElement(parser);
86 String element = parser.getName();
87 if (element == null) {
88 break;
89 }
90 if (element.equals(TAG_WEBVIEW_PROVIDER)) {
91 String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
92 if (packageName == null) {
Gustav Sennton631de99e2016-04-07 16:37:58 +010093 throw new AndroidRuntimeException(
Gustav Sennton8b179262016-03-14 11:31:14 +000094 "WebView provider in framework resources missing package name");
95 }
96 String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
97 if (description == null) {
Gustav Sennton631de99e2016-04-07 16:37:58 +010098 throw new AndroidRuntimeException(
Gustav Sennton8b179262016-03-14 11:31:14 +000099 "WebView provider in framework resources missing description");
100 }
101 boolean availableByDefault = "true".equals(
102 parser.getAttributeValue(null, TAG_AVAILABILITY));
103 boolean isFallback = "true".equals(
104 parser.getAttributeValue(null, TAG_FALLBACK));
Gustav Senntondbf5eb02016-03-30 14:53:03 +0100105 WebViewProviderInfo currentProvider = new WebViewProviderInfo(
106 packageName, description, availableByDefault, isFallback,
107 readSignatures(parser));
108 if (currentProvider.isFallback) {
Gustav Sennton8b179262016-03-14 11:31:14 +0000109 numFallbackPackages++;
Gustav Sennton631de99e2016-04-07 16:37:58 +0100110 if (!currentProvider.availableByDefault) {
111 throw new AndroidRuntimeException(
112 "Each WebView fallback package must be available by default.");
113 }
Gustav Sennton8b179262016-03-14 11:31:14 +0000114 if (numFallbackPackages > 1) {
115 throw new AndroidRuntimeException(
Gustav Sennton631de99e2016-04-07 16:37:58 +0100116 "There can be at most one WebView fallback package.");
Gustav Sennton8b179262016-03-14 11:31:14 +0000117 }
118 }
Gustav Sennton631de99e2016-04-07 16:37:58 +0100119 if (currentProvider.availableByDefault) {
120 numAvailableByDefaultPackages++;
Gustav Senntonc0bc9932016-04-13 16:43:44 +0100121 if (!currentProvider.isFallback) {
122 numAvByDefaultAndNotFallback++;
123 }
Gustav Sennton631de99e2016-04-07 16:37:58 +0100124 }
Gustav Sennton8b179262016-03-14 11:31:14 +0000125 webViewProviders.add(currentProvider);
126 }
127 else {
Gustav Sennton631de99e2016-04-07 16:37:58 +0100128 Log.e(TAG, "Found an element that is not a WebView provider");
Gustav Sennton8b179262016-03-14 11:31:14 +0000129 }
130 }
Hui Shu9e8c0582016-03-24 17:02:58 -0700131 } catch (XmlPullParserException | IOException e) {
Gustav Sennton631de99e2016-04-07 16:37:58 +0100132 throw new AndroidRuntimeException("Error when parsing WebView config " + e);
Gustav Sennton8b179262016-03-14 11:31:14 +0000133 } finally {
134 if (parser != null) parser.close();
135 }
Gustav Sennton631de99e2016-04-07 16:37:58 +0100136 if (numAvailableByDefaultPackages == 0) {
137 throw new AndroidRuntimeException("There must be at least one WebView package "
138 + "that is available by default");
139 }
Gustav Senntonc0bc9932016-04-13 16:43:44 +0100140 if (numAvByDefaultAndNotFallback == 0) {
141 throw new AndroidRuntimeException("There must be at least one WebView package "
142 + "that is available by default and not a fallback");
143 }
Hui Shu9455bd02016-04-08 13:25:26 -0700144 mWebViewProviderPackages =
145 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
146 }
147 /**
148 * Returns all packages declared in the framework resources as potential WebView providers.
149 * @hide
150 * */
151 @Override
152 public WebViewProviderInfo[] getWebViewPackages() {
153 return mWebViewProviderPackages;
Gustav Sennton8b179262016-03-14 11:31:14 +0000154 }
155
Hui Shu9e8c0582016-03-24 17:02:58 -0700156 public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
157 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
158 return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode;
159 }
160
Gustav Sennton8b179262016-03-14 11:31:14 +0000161 /**
162 * Reads all signatures at the current depth (within the current provider) from the XML parser.
163 */
164 private static String[] readSignatures(XmlResourceParser parser) throws IOException,
165 XmlPullParserException {
166 List<String> signatures = new ArrayList<String>();
167 int outerDepth = parser.getDepth();
168 while(XmlUtils.nextElementWithin(parser, outerDepth)) {
169 if (parser.getName().equals(TAG_SIGNATURE)) {
170 // Parse the value within the signature tag
171 String signature = parser.nextText();
172 signatures.add(signature);
173 } else {
174 Log.e(TAG, "Found an element in a webview provider that is not a signature");
175 }
176 }
177 return signatures.toArray(new String[signatures.size()]);
178 }
179
180 @Override
181 public int onWebViewProviderChanged(PackageInfo packageInfo) {
182 return WebViewFactory.onWebViewProviderChanged(packageInfo);
183 }
184
185 @Override
186 public String getUserChosenWebViewProvider(Context context) {
187 return Settings.Global.getString(context.getContentResolver(),
188 Settings.Global.WEBVIEW_PROVIDER);
189 }
190
191 @Override
192 public void updateUserSetting(Context context, String newProviderName) {
193 Settings.Global.putString(context.getContentResolver(),
194 Settings.Global.WEBVIEW_PROVIDER,
195 newProviderName == null ? "" : newProviderName);
196 }
197
198 @Override
199 public void killPackageDependents(String packageName) {
200 try {
201 ActivityManagerNative.getDefault().killPackageDependents(packageName,
202 UserHandle.USER_ALL);
203 } catch (RemoteException e) {
204 }
205 }
Gustav Sennton065b7e62016-04-01 15:11:43 +0100206
207 @Override
208 public boolean isFallbackLogicEnabled() {
209 // Note that this is enabled by default (i.e. if the setting hasn't been set).
210 return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
211 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
212 }
213
214 @Override
215 public void enableFallbackLogic(boolean enable) {
216 Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
217 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
218 }
219
220 @Override
221 public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
Gustav Sennton5c153c52016-04-06 17:53:04 +0100222 enablePackageForAllUsers(context, packageName, false);
223 try {
224 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
Gustav Senntonff396f22016-04-08 18:33:30 +0100225 ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
226 if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) {
Gustav Sennton5c153c52016-04-06 17:53:04 +0100227 pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
228 public void packageDeleted(String packageName, int returnCode) {
229 enablePackageForAllUsers(context, packageName, false);
230 }
231 }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
Gustav Sennton065b7e62016-04-01 15:11:43 +0100232 }
Gustav Sennton5c153c52016-04-06 17:53:04 +0100233 } catch (NameNotFoundException e) {
234 }
Gustav Sennton065b7e62016-04-01 15:11:43 +0100235 }
236
237 @Override
238 public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
239 UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
240 for(UserInfo userInfo : userManager.getUsers()) {
241 enablePackageForUser(packageName, enable, userInfo.id);
242 }
243 }
244
245 @Override
246 public void enablePackageForUser(String packageName, boolean enable, int userId) {
247 try {
248 AppGlobals.getPackageManager().setApplicationEnabledSetting(
249 packageName,
250 enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
251 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
252 userId, null);
Gustav Senntonff396f22016-04-08 18:33:30 +0100253 } catch (RemoteException | IllegalArgumentException e) {
254 Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
255 + " for user " + userId + ": " + e);
Gustav Sennton065b7e62016-04-01 15:11:43 +0100256 }
257 }
258
259 @Override
260 public boolean systemIsDebuggable() {
261 return Build.IS_DEBUGGABLE;
262 }
263
264 @Override
265 public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
266 throws NameNotFoundException {
267 PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
268 return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
269 }
270
271 // flags declaring we want extra info from the package manager for webview providers
272 private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
Gustav Sennton0df2c552016-06-14 15:32:19 +0100273 | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
274 | PackageManager.MATCH_UNINSTALLED_PACKAGES;
Gustav Sennton8b179262016-03-14 11:31:14 +0000275}