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