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