SystemImpl.java revision 9455bd0105f974de3d1164890353c2a0d0605840
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.ActivityManagerNative;
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.os.Build;
30import android.os.RemoteException;
31import android.os.UserHandle;
32import android.os.UserManager;
33import android.provider.Settings.Global;
34import android.provider.Settings;
35import android.util.AndroidRuntimeException;
36import android.util.Log;
37import android.webkit.WebViewFactory;
38import 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 */
52public class SystemImpl implements SystemInterface {
53    private static final String TAG = SystemImpl.class.getSimpleName();
54    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";
62    private final WebViewProviderInfo[] mWebViewProviderPackages;
63
64    // 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() {
75        int numFallbackPackages = 0;
76        int numAvailableByDefaultPackages = 0;
77        int numAvByDefaultAndNotFallback = 0;
78        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) {
93                        throw new AndroidRuntimeException(
94                                "WebView provider in framework resources missing package name");
95                    }
96                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
97                    if (description == null) {
98                        throw new AndroidRuntimeException(
99                                "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));
105                    WebViewProviderInfo currentProvider = new WebViewProviderInfo(
106                            packageName, description, availableByDefault, isFallback,
107                            readSignatures(parser));
108                    if (currentProvider.isFallback) {
109                        numFallbackPackages++;
110                        if (!currentProvider.availableByDefault) {
111                            throw new AndroidRuntimeException(
112                                    "Each WebView fallback package must be available by default.");
113                        }
114                        if (numFallbackPackages > 1) {
115                            throw new AndroidRuntimeException(
116                                    "There can be at most one WebView fallback package.");
117                        }
118                    }
119                    if (currentProvider.availableByDefault) {
120                        numAvailableByDefaultPackages++;
121                        if (!currentProvider.isFallback) {
122                            numAvByDefaultAndNotFallback++;
123                        }
124                    }
125                    webViewProviders.add(currentProvider);
126                }
127                else {
128                    Log.e(TAG, "Found an element that is not a WebView provider");
129                }
130            }
131        } catch (XmlPullParserException | IOException e) {
132            throw new AndroidRuntimeException("Error when parsing WebView config " + e);
133        } finally {
134            if (parser != null) parser.close();
135        }
136        if (numAvailableByDefaultPackages == 0) {
137            throw new AndroidRuntimeException("There must be at least one WebView package "
138                    + "that is available by default");
139        }
140        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        }
144        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;
154    }
155
156    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
161    /**
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    }
206
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) {
222        enablePackageForAllUsers(context, packageName, false);
223        try {
224            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
225            ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
226            if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) {
227                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);
232            }
233        } catch (NameNotFoundException e) {
234        }
235    }
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);
253        } catch (RemoteException | IllegalArgumentException e) {
254            Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
255                    + " for user " + userId + ": " + e);
256        }
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
273            | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
274}
275