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 package com.android.server.webkit;
     17 
     18 import android.content.Context;
     19 import android.content.pm.ApplicationInfo;
     20 import android.content.pm.PackageInfo;
     21 import android.content.pm.Signature;
     22 import android.os.UserHandle;
     23 import android.util.Slog;
     24 import android.webkit.UserPackage;
     25 import android.webkit.WebViewProviderInfo;
     26 import android.webkit.WebViewProviderResponse;
     27 
     28 import java.io.PrintWriter;
     29 import java.lang.Integer;
     30 import java.util.List;
     31 
     32 /**
     33  * Implementation of the WebViewUpdateService.
     34  * This class doesn't depend on the android system like the actual Service does and can be used
     35  * directly by tests (as long as they implement a SystemInterface).
     36  *
     37  * This class implements two main features - handling WebView fallback packages and keeping track
     38  * of, and preparing, the current WebView implementation. The fallback mechanism is meant to be
     39  * uncoupled from the rest of the WebView preparation and does not store any state. The code for
     40  * choosing and preparing a WebView implementation needs to keep track of a couple of different
     41  * things such as what package is used as WebView implementation.
     42  *
     43  * The public methods in this class are accessed from WebViewUpdateService either on the UI thread
     44  * or on one of multiple Binder threads. This means that the code in this class needs to be
     45  * thread-safe. The fallback mechanism shares (almost) no information between threads which makes
     46  * it easier to argue about thread-safety (in theory, if timed badly, the fallback mechanism can
     47  * incorrectly enable/disable a fallback package but that fault will be corrected when we later
     48  * receive an intent for that enabling/disabling). On the other hand, the WebView preparation code
     49  * shares state between threads meaning that code that chooses a new WebView implementation or
     50  * checks which implementation is being used needs to hold a lock.
     51  *
     52  * The WebViewUpdateService can be accessed in a couple of different ways.
     53  * 1. It is started from the SystemServer at boot - at that point we just initiate some state such
     54  * as the WebView preparation class.
     55  * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
     56  * and the WebViewUpdateService should not have been accessed before this call. In this call we
     57  * enable/disable fallback packages and then choose WebView implementation for the first time.
     58  * 3. The update service listens for Intents related to package installs and removals. These intents
     59  * are received and processed on the UI thread. Each intent can result in enabling/disabling
     60  * fallback packages and changing WebView implementation.
     61  * 4. The update service can be reached through Binder calls which are handled on specific binder
     62  * threads. These calls can be made from any process. Generally they are used for changing WebView
     63  * implementation (from Settings), getting information about the current WebView implementation (for
     64  * loading WebView into an app process), or notifying the service about Relro creation being
     65  * completed.
     66  *
     67  * @hide
     68  */
     69 public class WebViewUpdateServiceImpl {
     70     private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
     71 
     72     private SystemInterface mSystemInterface;
     73     private WebViewUpdater mWebViewUpdater;
     74     final private Context mContext;
     75 
     76     private final static int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
     77     private final static int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
     78 
     79     public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
     80         mContext = context;
     81         mSystemInterface = systemInterface;
     82         mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
     83     }
     84 
     85     void packageStateChanged(String packageName, int changedState, int userId) {
     86         // We don't early out here in different cases where we could potentially early-out (e.g. if
     87         // we receive PACKAGE_CHANGED for another user than the system user) since that would
     88         // complicate this logic further and open up for more edge cases.
     89         updateFallbackStateOnPackageChange(packageName, changedState);
     90         mWebViewUpdater.packageStateChanged(packageName, changedState);
     91     }
     92 
     93     void prepareWebViewInSystemServer() {
     94         updateFallbackStateOnBoot();
     95         mWebViewUpdater.prepareWebViewInSystemServer();
     96         mSystemInterface.notifyZygote(isMultiProcessEnabled());
     97     }
     98 
     99     private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
    100         for (WebViewProviderInfo provider : providers) {
    101             if (provider.availableByDefault && !provider.isFallback) {
    102                 // userPackages can contain null objects.
    103                 List<UserPackage> userPackages =
    104                         mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
    105                 if (WebViewUpdater.isInstalledAndEnabledForAllUsers(userPackages) &&
    106                         // Checking validity of the package for the system user (rather than all
    107                         // users) since package validity depends not on the user but on the package
    108                         // itself.
    109                         mWebViewUpdater.isValidProvider(provider,
    110                                 userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo())) {
    111                     return true;
    112                 }
    113             }
    114         }
    115         return false;
    116     }
    117 
    118     void handleNewUser(int userId) {
    119         // The system user is always started at boot, and by that point we have already run one
    120         // round of the package-changing logic (through prepareWebViewInSystemServer()), so early
    121         // out here.
    122         if (userId == UserHandle.USER_SYSTEM) return;
    123         handleUserChange();
    124     }
    125 
    126     void handleUserRemoved(int userId) {
    127         handleUserChange();
    128     }
    129 
    130     /**
    131      * Called when a user was added or removed to ensure fallback logic and WebView preparation are
    132      * triggered. This has to be done since the WebView package we use depends on the enabled-state
    133      * of packages for all users (so adding or removing a user might cause us to change package).
    134      */
    135     private void handleUserChange() {
    136         if (mSystemInterface.isFallbackLogicEnabled()) {
    137             updateFallbackState(mSystemInterface.getWebViewPackages());
    138         }
    139         // Potentially trigger package-changing logic.
    140         mWebViewUpdater.updateCurrentWebViewPackage(null);
    141     }
    142 
    143     void notifyRelroCreationCompleted() {
    144         mWebViewUpdater.notifyRelroCreationCompleted();
    145     }
    146 
    147     WebViewProviderResponse waitForAndGetProvider() {
    148         return mWebViewUpdater.waitForAndGetProvider();
    149     }
    150 
    151     String changeProviderAndSetting(String newProvider) {
    152         return mWebViewUpdater.changeProviderAndSetting(newProvider);
    153     }
    154 
    155     WebViewProviderInfo[] getValidWebViewPackages() {
    156         return mWebViewUpdater.getValidWebViewPackages();
    157     }
    158 
    159     WebViewProviderInfo[] getWebViewPackages() {
    160         return mSystemInterface.getWebViewPackages();
    161     }
    162 
    163     PackageInfo getCurrentWebViewPackage() {
    164         return mWebViewUpdater.getCurrentWebViewPackage();
    165     }
    166 
    167     void enableFallbackLogic(boolean enable) {
    168         mSystemInterface.enableFallbackLogic(enable);
    169     }
    170 
    171     private void updateFallbackStateOnBoot() {
    172         if (!mSystemInterface.isFallbackLogicEnabled()) return;
    173 
    174         WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
    175         updateFallbackState(webviewProviders);
    176     }
    177 
    178     /**
    179      * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
    180      * package that is valid (and available by default) then disable the fallback package,
    181      * otherwise, enable the fallback package.
    182      */
    183     private void updateFallbackStateOnPackageChange(String changedPackage, int changedState) {
    184         if (!mSystemInterface.isFallbackLogicEnabled()) return;
    185 
    186         WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
    187 
    188         // A package was changed / updated / downgraded, early out if it is not one of the
    189         // webview packages that are available by default.
    190         boolean changedPackageAvailableByDefault = false;
    191         for (WebViewProviderInfo provider : webviewProviders) {
    192             if (provider.packageName.equals(changedPackage)) {
    193                 if (provider.availableByDefault) {
    194                     changedPackageAvailableByDefault = true;
    195                 }
    196                 break;
    197             }
    198         }
    199         if (!changedPackageAvailableByDefault) return;
    200         updateFallbackState(webviewProviders);
    201     }
    202 
    203     private void updateFallbackState(WebViewProviderInfo[] webviewProviders) {
    204         // If there exists a valid and enabled non-fallback package - disable the fallback
    205         // package, otherwise, enable it.
    206         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
    207         if (fallbackProvider == null) return;
    208         boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
    209 
    210         List<UserPackage> userPackages =
    211                 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, fallbackProvider);
    212         if (existsValidNonFallbackProvider && !isDisabledForAllUsers(userPackages)) {
    213             mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext,
    214                     fallbackProvider.packageName);
    215         } else if (!existsValidNonFallbackProvider
    216                 && !WebViewUpdater.isInstalledAndEnabledForAllUsers(userPackages)) {
    217             // Enable the fallback package for all users.
    218             mSystemInterface.enablePackageForAllUsers(mContext,
    219                     fallbackProvider.packageName, true);
    220         }
    221     }
    222 
    223     /**
    224      * Returns the only fallback provider in the set of given packages, or null if there is none.
    225      */
    226     private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
    227         for (WebViewProviderInfo provider : webviewPackages) {
    228             if (provider.isFallback) {
    229                 return provider;
    230             }
    231         }
    232         return null;
    233     }
    234 
    235     boolean isFallbackPackage(String packageName) {
    236         if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
    237 
    238         WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
    239         WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
    240         return (fallbackProvider != null
    241                 && packageName.equals(fallbackProvider.packageName));
    242     }
    243 
    244     boolean isMultiProcessEnabled() {
    245         int settingValue = mSystemInterface.getMultiProcessSetting(mContext);
    246         if (mSystemInterface.isMultiProcessDefaultEnabled()) {
    247             // Multiprocess should be enabled unless the user has turned it off manually.
    248             return settingValue > MULTIPROCESS_SETTING_OFF_VALUE;
    249         } else {
    250             // Multiprocess should not be enabled, unless the user has turned it on manually.
    251             return settingValue >= MULTIPROCESS_SETTING_ON_VALUE;
    252         }
    253     }
    254 
    255     void enableMultiProcess(boolean enable) {
    256         PackageInfo current = getCurrentWebViewPackage();
    257         mSystemInterface.setMultiProcessSetting(mContext,
    258                 enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE);
    259         mSystemInterface.notifyZygote(enable);
    260         if (current != null) {
    261             mSystemInterface.killPackageDependents(current.packageName);
    262         }
    263     }
    264 
    265     private static boolean isDisabledForAllUsers(List<UserPackage> userPackages) {
    266         for (UserPackage userPackage : userPackages) {
    267             if (userPackage.getPackageInfo() != null && userPackage.isEnabledPackage()) {
    268                 return false;
    269             }
    270         }
    271         return true;
    272     }
    273 
    274     /**
    275      * Dump the state of this Service.
    276      */
    277     void dumpState(PrintWriter pw) {
    278         pw.println("Current WebView Update Service state");
    279         pw.println(String.format("  Fallback logic enabled: %b",
    280                 mSystemInterface.isFallbackLogicEnabled()));
    281         pw.println(String.format("  Multiprocess enabled: %b", isMultiProcessEnabled()));
    282         mWebViewUpdater.dumpState(pw);
    283     }
    284 }
    285