Home | History | Annotate | Download | only in webkit
      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.ApplicationInfo;
     23 import android.content.pm.IPackageDeleteObserver;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.PackageManager.NameNotFoundException;
     27 import android.content.pm.UserInfo;
     28 import android.content.res.XmlResourceParser;
     29 import android.database.ContentObserver;
     30 import android.os.Build;
     31 import android.os.RemoteException;
     32 import android.os.UserHandle;
     33 import android.os.UserManager;
     34 import android.provider.Settings.Global;
     35 import android.provider.Settings;
     36 import android.util.AndroidRuntimeException;
     37 import android.util.Log;
     38 import android.webkit.UserPackage;
     39 import android.webkit.WebViewFactory;
     40 import android.webkit.WebViewProviderInfo;
     41 import android.webkit.WebViewZygote;
     42 
     43 import com.android.internal.util.XmlUtils;
     44 
     45 import java.io.IOException;
     46 import java.util.ArrayList;
     47 import java.util.List;
     48 
     49 import org.xmlpull.v1.XmlPullParserException;
     50 
     51 /**
     52  * Default implementation for the WebView preparation Utility interface.
     53  * @hide
     54  */
     55 public class SystemImpl implements SystemInterface {
     56     private static final String TAG = SystemImpl.class.getSimpleName();
     57     private static final String TAG_START = "webviewproviders";
     58     private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider";
     59     private static final String TAG_PACKAGE_NAME = "packageName";
     60     private static final String TAG_DESCRIPTION = "description";
     61     // Whether or not the provider must be explicitly chosen by the user to be used.
     62     private static final String TAG_AVAILABILITY = "availableByDefault";
     63     private static final String TAG_SIGNATURE = "signature";
     64     private static final String TAG_FALLBACK = "isFallback";
     65     private final WebViewProviderInfo[] mWebViewProviderPackages;
     66 
     67     // Initialization-on-demand holder idiom for getting the WebView provider packages once and
     68     // for all in a thread-safe manner.
     69     private static class LazyHolder {
     70         private static final SystemImpl INSTANCE = new SystemImpl();
     71     }
     72 
     73     public static SystemImpl getInstance() {
     74         return LazyHolder.INSTANCE;
     75     }
     76 
     77     private SystemImpl() {
     78         int numFallbackPackages = 0;
     79         int numAvailableByDefaultPackages = 0;
     80         int numAvByDefaultAndNotFallback = 0;
     81         XmlResourceParser parser = null;
     82         List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
     83         try {
     84             parser = AppGlobals.getInitialApplication().getResources().getXml(
     85                     com.android.internal.R.xml.config_webview_packages);
     86             XmlUtils.beginDocument(parser, TAG_START);
     87             while(true) {
     88                 XmlUtils.nextElement(parser);
     89                 String element = parser.getName();
     90                 if (element == null) {
     91                     break;
     92                 }
     93                 if (element.equals(TAG_WEBVIEW_PROVIDER)) {
     94                     String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
     95                     if (packageName == null) {
     96                         throw new AndroidRuntimeException(
     97                                 "WebView provider in framework resources missing package name");
     98                     }
     99                     String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
    100                     if (description == null) {
    101                         throw new AndroidRuntimeException(
    102                                 "WebView provider in framework resources missing description");
    103                     }
    104                     boolean availableByDefault = "true".equals(
    105                             parser.getAttributeValue(null, TAG_AVAILABILITY));
    106                     boolean isFallback = "true".equals(
    107                             parser.getAttributeValue(null, TAG_FALLBACK));
    108                     WebViewProviderInfo currentProvider = new WebViewProviderInfo(
    109                             packageName, description, availableByDefault, isFallback,
    110                             readSignatures(parser));
    111                     if (currentProvider.isFallback) {
    112                         numFallbackPackages++;
    113                         if (!currentProvider.availableByDefault) {
    114                             throw new AndroidRuntimeException(
    115                                     "Each WebView fallback package must be available by default.");
    116                         }
    117                         if (numFallbackPackages > 1) {
    118                             throw new AndroidRuntimeException(
    119                                     "There can be at most one WebView fallback package.");
    120                         }
    121                     }
    122                     if (currentProvider.availableByDefault) {
    123                         numAvailableByDefaultPackages++;
    124                         if (!currentProvider.isFallback) {
    125                             numAvByDefaultAndNotFallback++;
    126                         }
    127                     }
    128                     webViewProviders.add(currentProvider);
    129                 }
    130                 else {
    131                     Log.e(TAG, "Found an element that is not a WebView provider");
    132                 }
    133             }
    134         } catch (XmlPullParserException | IOException e) {
    135             throw new AndroidRuntimeException("Error when parsing WebView config " + e);
    136         } finally {
    137             if (parser != null) parser.close();
    138         }
    139         if (numAvailableByDefaultPackages == 0) {
    140             throw new AndroidRuntimeException("There must be at least one WebView package "
    141                     + "that is available by default");
    142         }
    143         if (numAvByDefaultAndNotFallback == 0) {
    144             throw new AndroidRuntimeException("There must be at least one WebView package "
    145                     + "that is available by default and not a fallback");
    146         }
    147         mWebViewProviderPackages =
    148                 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
    149     }
    150     /**
    151      * Returns all packages declared in the framework resources as potential WebView providers.
    152      * @hide
    153      * */
    154     @Override
    155     public WebViewProviderInfo[] getWebViewPackages() {
    156         return mWebViewProviderPackages;
    157     }
    158 
    159     public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
    160         PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
    161         return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY)
    162                 .getLongVersionCode();
    163     }
    164 
    165     /**
    166      * Reads all signatures at the current depth (within the current provider) from the XML parser.
    167      */
    168     private static String[] readSignatures(XmlResourceParser parser) throws IOException,
    169             XmlPullParserException {
    170         List<String> signatures = new ArrayList<String>();
    171         int outerDepth = parser.getDepth();
    172         while(XmlUtils.nextElementWithin(parser, outerDepth)) {
    173             if (parser.getName().equals(TAG_SIGNATURE)) {
    174                 // Parse the value within the signature tag
    175                 String signature = parser.nextText();
    176                 signatures.add(signature);
    177             } else {
    178                 Log.e(TAG, "Found an element in a webview provider that is not a signature");
    179             }
    180         }
    181         return signatures.toArray(new String[signatures.size()]);
    182     }
    183 
    184     @Override
    185     public int onWebViewProviderChanged(PackageInfo packageInfo) {
    186         return WebViewFactory.onWebViewProviderChanged(packageInfo);
    187     }
    188 
    189     @Override
    190     public String getUserChosenWebViewProvider(Context context) {
    191         return Settings.Global.getString(context.getContentResolver(),
    192                 Settings.Global.WEBVIEW_PROVIDER);
    193     }
    194 
    195     @Override
    196     public void updateUserSetting(Context context, String newProviderName) {
    197         Settings.Global.putString(context.getContentResolver(),
    198                 Settings.Global.WEBVIEW_PROVIDER,
    199                 newProviderName == null ? "" : newProviderName);
    200     }
    201 
    202     @Override
    203     public void killPackageDependents(String packageName) {
    204         try {
    205             ActivityManager.getService().killPackageDependents(packageName,
    206                     UserHandle.USER_ALL);
    207         } catch (RemoteException e) {
    208         }
    209     }
    210 
    211     @Override
    212     public boolean isFallbackLogicEnabled() {
    213         // Note that this is enabled by default (i.e. if the setting hasn't been set).
    214         return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
    215                 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
    216     }
    217 
    218     @Override
    219     public void enableFallbackLogic(boolean enable) {
    220         Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
    221                 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
    222     }
    223 
    224     @Override
    225     public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
    226         enablePackageForAllUsers(context, packageName, false);
    227         try {
    228             PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
    229             ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
    230             if (applicationInfo != null && applicationInfo.isUpdatedSystemApp()) {
    231                 pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
    232                         public void packageDeleted(String packageName, int returnCode) {
    233                             enablePackageForAllUsers(context, packageName, false);
    234                         }
    235                     }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
    236             }
    237         } catch (NameNotFoundException e) {
    238         }
    239     }
    240 
    241     @Override
    242     public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
    243         UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
    244         for(UserInfo userInfo : userManager.getUsers()) {
    245             enablePackageForUser(packageName, enable, userInfo.id);
    246         }
    247     }
    248 
    249     @Override
    250     public void enablePackageForUser(String packageName, boolean enable, int userId) {
    251         try {
    252             AppGlobals.getPackageManager().setApplicationEnabledSetting(
    253                     packageName,
    254                     enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
    255                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
    256                     userId, null);
    257         } catch (RemoteException | IllegalArgumentException e) {
    258             Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
    259                     + " for user " + userId + ": " + e);
    260         }
    261     }
    262 
    263     @Override
    264     public boolean systemIsDebuggable() {
    265         return Build.IS_DEBUGGABLE;
    266     }
    267 
    268     @Override
    269     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
    270             throws NameNotFoundException {
    271         PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
    272         return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
    273     }
    274 
    275     @Override
    276     public List<UserPackage> getPackageInfoForProviderAllUsers(Context context,
    277             WebViewProviderInfo configInfo) {
    278         return UserPackage.getPackageInfosAllUsers(context, configInfo.packageName, PACKAGE_FLAGS);
    279     }
    280 
    281     @Override
    282     public int getMultiProcessSetting(Context context) {
    283         return Settings.Global.getInt(context.getContentResolver(),
    284                                       Settings.Global.WEBVIEW_MULTIPROCESS, 0);
    285     }
    286 
    287     @Override
    288     public void setMultiProcessSetting(Context context, int value) {
    289         Settings.Global.putInt(context.getContentResolver(),
    290                                Settings.Global.WEBVIEW_MULTIPROCESS, value);
    291     }
    292 
    293     @Override
    294     public void notifyZygote(boolean enableMultiProcess) {
    295         WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
    296     }
    297 
    298     @Override
    299     public boolean isMultiProcessDefaultEnabled() {
    300         // Multiprocess is enabled for all 64-bit devices, since the ability to run the renderer
    301         // process in 32-bit when it's a separate process typically results in a net memory saving.
    302         // Multiprocess is also enabled for 32-bit devices unless they report they are "low ram".
    303         return Build.SUPPORTED_64_BIT_ABIS.length > 0 || !ActivityManager.isLowRamDeviceStatic();
    304     }
    305 
    306     // flags declaring we want extra info from the package manager for webview providers
    307     private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
    308             | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
    309             | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
    310 }
    311