SystemImpl.java revision ff396f2710e60a4c596e30c7dc74ad7ef24a7ecc
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.MissingWebViewPackageException;
38import android.webkit.WebViewFactory;
39import android.webkit.WebViewProviderInfo;
40
41import com.android.internal.util.XmlUtils;
42
43import java.io.IOException;
44import java.util.ArrayList;
45import java.util.List;
46
47import org.xmlpull.v1.XmlPullParserException;
48
49/**
50 * Default implementation for the WebView preparation Utility interface.
51 * @hide
52 */
53public class SystemImpl implements SystemInterface {
54    private static final String TAG = SystemImpl.class.getSimpleName();
55    private static final String TAG_START = "webviewproviders";
56    private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider";
57    private static final String TAG_PACKAGE_NAME = "packageName";
58    private static final String TAG_DESCRIPTION = "description";
59    // Whether or not the provider must be explicitly chosen by the user to be used.
60    private static final String TAG_AVAILABILITY = "availableByDefault";
61    private static final String TAG_SIGNATURE = "signature";
62    private static final String TAG_FALLBACK = "isFallback";
63
64    /**
65     * Returns all packages declared in the framework resources as potential WebView providers.
66     * @hide
67     * */
68    @Override
69    public WebViewProviderInfo[] getWebViewPackages() {
70        int numFallbackPackages = 0;
71        XmlResourceParser parser = null;
72        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
73        try {
74            parser = AppGlobals.getInitialApplication().getResources().getXml(
75                    com.android.internal.R.xml.config_webview_packages);
76            XmlUtils.beginDocument(parser, TAG_START);
77            while(true) {
78                XmlUtils.nextElement(parser);
79                String element = parser.getName();
80                if (element == null) {
81                    break;
82                }
83                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
84                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
85                    if (packageName == null) {
86                        throw new MissingWebViewPackageException(
87                                "WebView provider in framework resources missing package name");
88                    }
89                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
90                    if (description == null) {
91                        throw new MissingWebViewPackageException(
92                                "WebView provider in framework resources missing description");
93                    }
94                    boolean availableByDefault = "true".equals(
95                            parser.getAttributeValue(null, TAG_AVAILABILITY));
96                    boolean isFallback = "true".equals(
97                            parser.getAttributeValue(null, TAG_FALLBACK));
98                    WebViewProviderInfo currentProvider = new WebViewProviderInfo(
99                            packageName, description, availableByDefault, isFallback,
100                            readSignatures(parser));
101                    if (currentProvider.isFallback) {
102                        numFallbackPackages++;
103                        if (numFallbackPackages > 1) {
104                            throw new AndroidRuntimeException(
105                                    "There can be at most one webview fallback package.");
106                        }
107                    }
108                    webViewProviders.add(currentProvider);
109                }
110                else {
111                    Log.e(TAG, "Found an element that is not a webview provider");
112                }
113            }
114        } catch (XmlPullParserException | IOException e) {
115            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
116        } finally {
117            if (parser != null) parser.close();
118        }
119        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
120    }
121
122    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
123        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
124        return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode;
125    }
126
127    /**
128     * Reads all signatures at the current depth (within the current provider) from the XML parser.
129     */
130    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
131            XmlPullParserException {
132        List<String> signatures = new ArrayList<String>();
133        int outerDepth = parser.getDepth();
134        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
135            if (parser.getName().equals(TAG_SIGNATURE)) {
136                // Parse the value within the signature tag
137                String signature = parser.nextText();
138                signatures.add(signature);
139            } else {
140                Log.e(TAG, "Found an element in a webview provider that is not a signature");
141            }
142        }
143        return signatures.toArray(new String[signatures.size()]);
144    }
145
146    @Override
147    public int onWebViewProviderChanged(PackageInfo packageInfo) {
148        return WebViewFactory.onWebViewProviderChanged(packageInfo);
149    }
150
151    @Override
152    public String getUserChosenWebViewProvider(Context context) {
153        return Settings.Global.getString(context.getContentResolver(),
154                Settings.Global.WEBVIEW_PROVIDER);
155    }
156
157    @Override
158    public void updateUserSetting(Context context, String newProviderName) {
159        Settings.Global.putString(context.getContentResolver(),
160                Settings.Global.WEBVIEW_PROVIDER,
161                newProviderName == null ? "" : newProviderName);
162    }
163
164    @Override
165    public void killPackageDependents(String packageName) {
166        try {
167            ActivityManagerNative.getDefault().killPackageDependents(packageName,
168                    UserHandle.USER_ALL);
169        } catch (RemoteException e) {
170        }
171    }
172
173    @Override
174    public boolean isFallbackLogicEnabled() {
175        // Note that this is enabled by default (i.e. if the setting hasn't been set).
176        return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
177                Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
178    }
179
180    @Override
181    public void enableFallbackLogic(boolean enable) {
182        Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
183                Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
184    }
185
186    @Override
187    public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
188        enablePackageForAllUsers(context, packageName, false);
189        try {
190            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
191            ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
192            if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) {
193                pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
194                        public void packageDeleted(String packageName, int returnCode) {
195                            enablePackageForAllUsers(context, packageName, false);
196                        }
197                    }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
198            }
199        } catch (NameNotFoundException e) {
200        }
201    }
202
203    @Override
204    public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
205        UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
206        for(UserInfo userInfo : userManager.getUsers()) {
207            enablePackageForUser(packageName, enable, userInfo.id);
208        }
209    }
210
211    @Override
212    public void enablePackageForUser(String packageName, boolean enable, int userId) {
213        try {
214            AppGlobals.getPackageManager().setApplicationEnabledSetting(
215                    packageName,
216                    enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
217                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
218                    userId, null);
219        } catch (RemoteException | IllegalArgumentException e) {
220            Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
221                    + " for user " + userId + ": " + e);
222        }
223    }
224
225    @Override
226    public boolean systemIsDebuggable() {
227        return Build.IS_DEBUGGABLE;
228    }
229
230    @Override
231    public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
232            throws NameNotFoundException {
233        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
234        return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
235    }
236
237    // flags declaring we want extra info from the package manager for webview providers
238    private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
239            | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
240}
241