Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2017 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.PackageInfo;
     20 import android.content.pm.PackageManager.NameNotFoundException;
     21 import android.content.pm.Signature;
     22 import android.os.UserHandle;
     23 import android.util.Base64;
     24 import android.util.Slog;
     25 import android.webkit.UserPackage;
     26 import android.webkit.WebViewFactory;
     27 import android.webkit.WebViewProviderInfo;
     28 import android.webkit.WebViewProviderResponse;
     29 
     30 import java.io.PrintWriter;
     31 import java.util.ArrayList;
     32 import java.util.Arrays;
     33 import java.util.List;
     34 
     35 /**
     36  * Class that decides what WebView implementation to use and prepares that implementation for
     37  * use.
     38  */
     39 class WebViewUpdater {
     40     private static final String TAG = WebViewUpdater.class.getSimpleName();
     41 
     42     private static class WebViewPackageMissingException extends Exception {
     43         public WebViewPackageMissingException(String message) { super(message); }
     44         public WebViewPackageMissingException(Exception e) { super(e); }
     45     }
     46 
     47     private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
     48 
     49     private final static int VALIDITY_OK = 0;
     50     private final static int VALIDITY_INCORRECT_SDK_VERSION = 1;
     51     private final static int VALIDITY_INCORRECT_VERSION_CODE = 2;
     52     private final static int VALIDITY_INCORRECT_SIGNATURE = 3;
     53     private final static int VALIDITY_NO_LIBRARY_FLAG = 4;
     54 
     55     private Context mContext;
     56     private SystemInterface mSystemInterface;
     57     private long mMinimumVersionCode = -1;
     58 
     59     // Keeps track of the number of running relro creations
     60     private int mNumRelroCreationsStarted = 0;
     61     private int mNumRelroCreationsFinished = 0;
     62     // Implies that we need to rerun relro creation because we are using an out-of-date package
     63     private boolean mWebViewPackageDirty = false;
     64     private boolean mAnyWebViewInstalled = false;
     65 
     66     private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
     67 
     68     // The WebView package currently in use (or the one we are preparing).
     69     private PackageInfo mCurrentWebViewPackage = null;
     70 
     71     private final Object mLock = new Object();
     72 
     73     WebViewUpdater(Context context, SystemInterface systemInterface) {
     74         mContext = context;
     75         mSystemInterface = systemInterface;
     76     }
     77 
     78     void packageStateChanged(String packageName, int changedState) {
     79         for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
     80             String webviewPackage = provider.packageName;
     81 
     82             if (webviewPackage.equals(packageName)) {
     83                 boolean updateWebView = false;
     84                 boolean removedOrChangedOldPackage = false;
     85                 String oldProviderName = null;
     86                 PackageInfo newPackage = null;
     87                 synchronized(mLock) {
     88                     try {
     89                         newPackage = findPreferredWebViewPackage();
     90                         if (mCurrentWebViewPackage != null) {
     91                             oldProviderName = mCurrentWebViewPackage.packageName;
     92                             if (changedState == WebViewUpdateService.PACKAGE_CHANGED
     93                                     && newPackage.packageName.equals(oldProviderName)) {
     94                                 // If we don't change package name we should only rerun the
     95                                 // preparation phase if the current package has been replaced
     96                                 // (not if it has been enabled/disabled).
     97                                 return;
     98                             }
     99                             if (newPackage.packageName.equals(oldProviderName)
    100                                     && (newPackage.lastUpdateTime
    101                                         == mCurrentWebViewPackage.lastUpdateTime)) {
    102                                 // If the chosen package hasn't been updated, then early-out
    103                                 return;
    104                             }
    105                         }
    106                         // Only trigger update actions if the updated package is the one
    107                         // that will be used, or the one that was in use before the
    108                         // update, or if we haven't seen a valid WebView package before.
    109                         updateWebView =
    110                             provider.packageName.equals(newPackage.packageName)
    111                             || provider.packageName.equals(oldProviderName)
    112                             || mCurrentWebViewPackage == null;
    113                         // We removed the old package if we received an intent to remove
    114                         // or replace the old package.
    115                         removedOrChangedOldPackage =
    116                             provider.packageName.equals(oldProviderName);
    117                         if (updateWebView) {
    118                             onWebViewProviderChanged(newPackage);
    119                         }
    120                     } catch (WebViewPackageMissingException e) {
    121                         mCurrentWebViewPackage = null;
    122                         Slog.e(TAG, "Could not find valid WebView package to create " +
    123                                 "relro with " + e);
    124                     }
    125                 }
    126                 if(updateWebView && !removedOrChangedOldPackage
    127                         && oldProviderName != null) {
    128                     // If the provider change is the result of adding or replacing a
    129                     // package that was not the previous provider then we must kill
    130                     // packages dependent on the old package ourselves. The framework
    131                     // only kills dependents of packages that are being removed.
    132                     mSystemInterface.killPackageDependents(oldProviderName);
    133                 }
    134                 return;
    135             }
    136         }
    137     }
    138 
    139     void prepareWebViewInSystemServer() {
    140         try {
    141             synchronized(mLock) {
    142                 mCurrentWebViewPackage = findPreferredWebViewPackage();
    143                 // Don't persist the user-chosen setting across boots if the package being
    144                 // chosen is not used (could be disabled or uninstalled) so that the user won't
    145                 // be surprised by the device switching to using a certain webview package,
    146                 // that was uninstalled/disabled a long time ago, if it is installed/enabled
    147                 // again.
    148                 mSystemInterface.updateUserSetting(mContext,
    149                         mCurrentWebViewPackage.packageName);
    150                 onWebViewProviderChanged(mCurrentWebViewPackage);
    151             }
    152         } catch (Throwable t) {
    153             // Log and discard errors at this stage as we must not crash the system server.
    154             Slog.e(TAG, "error preparing webview provider from system server", t);
    155         }
    156     }
    157 
    158     /**
    159      * Change WebView provider and provider setting and kill packages using the old provider.
    160      * Return the new provider (in case we are in the middle of creating relro files, or
    161      * replacing that provider it will not be in use directly, but will be used when the relros
    162      * or the replacement are done).
    163      */
    164     String changeProviderAndSetting(String newProviderName) {
    165         PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
    166         if (newPackage == null) return "";
    167         return newPackage.packageName;
    168     }
    169 
    170     /**
    171      * Update the current WebView package.
    172      * @param newProviderName the package to switch to, null if no package has been explicitly
    173      * chosen.
    174      */
    175     PackageInfo updateCurrentWebViewPackage(String newProviderName) {
    176         PackageInfo oldPackage = null;
    177         PackageInfo newPackage = null;
    178         boolean providerChanged = false;
    179         synchronized(mLock) {
    180             oldPackage = mCurrentWebViewPackage;
    181 
    182             if (newProviderName != null) {
    183                 mSystemInterface.updateUserSetting(mContext, newProviderName);
    184             }
    185 
    186             try {
    187                 newPackage = findPreferredWebViewPackage();
    188                 providerChanged = (oldPackage == null)
    189                         || !newPackage.packageName.equals(oldPackage.packageName);
    190             } catch (WebViewPackageMissingException e) {
    191                 // If updated the Setting but don't have an installed WebView package, the
    192                 // Setting will be used when a package is available.
    193                 mCurrentWebViewPackage = null;
    194                 Slog.e(TAG, "Couldn't find WebView package to use " + e);
    195                 return null;
    196             }
    197             // Perform the provider change if we chose a new provider
    198             if (providerChanged) {
    199                 onWebViewProviderChanged(newPackage);
    200             }
    201         }
    202         // Kill apps using the old provider only if we changed provider
    203         if (providerChanged && oldPackage != null) {
    204             mSystemInterface.killPackageDependents(oldPackage.packageName);
    205         }
    206         // Return the new provider, this is not necessarily the one we were asked to switch to,
    207         // but the persistent setting will now be pointing to the provider we were asked to
    208         // switch to anyway.
    209         return newPackage;
    210     }
    211 
    212     /**
    213      * This is called when we change WebView provider, either when the current provider is
    214      * updated or a new provider is chosen / takes precedence.
    215      */
    216     private void onWebViewProviderChanged(PackageInfo newPackage) {
    217         synchronized(mLock) {
    218             mAnyWebViewInstalled = true;
    219             if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
    220                 mCurrentWebViewPackage = newPackage;
    221 
    222                 // The relro creations might 'finish' (not start at all) before
    223                 // WebViewFactory.onWebViewProviderChanged which means we might not know the
    224                 // number of started creations before they finish.
    225                 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
    226                 mNumRelroCreationsFinished = 0;
    227                 mNumRelroCreationsStarted =
    228                     mSystemInterface.onWebViewProviderChanged(newPackage);
    229                 // If the relro creations finish before we know the number of started creations
    230                 // we will have to do any cleanup/notifying here.
    231                 checkIfRelrosDoneLocked();
    232             } else {
    233                 mWebViewPackageDirty = true;
    234             }
    235         }
    236     }
    237 
    238     /**
    239      * Fetch only the currently valid WebView packages.
    240      **/
    241     WebViewProviderInfo[] getValidWebViewPackages() {
    242         ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
    243         WebViewProviderInfo[] providers =
    244             new WebViewProviderInfo[providersAndPackageInfos.length];
    245         for(int n = 0; n < providersAndPackageInfos.length; n++) {
    246             providers[n] = providersAndPackageInfos[n].provider;
    247         }
    248         return providers;
    249     }
    250 
    251     private static class ProviderAndPackageInfo {
    252         public final WebViewProviderInfo provider;
    253         public final PackageInfo packageInfo;
    254 
    255         public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
    256             this.provider = provider;
    257             this.packageInfo = packageInfo;
    258         }
    259     }
    260 
    261     private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
    262         WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
    263         List<ProviderAndPackageInfo> providers = new ArrayList<>();
    264         for(int n = 0; n < allProviders.length; n++) {
    265             try {
    266                 PackageInfo packageInfo =
    267                     mSystemInterface.getPackageInfoForProvider(allProviders[n]);
    268                 if (isValidProvider(allProviders[n], packageInfo)) {
    269                     providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
    270                 }
    271             } catch (NameNotFoundException e) {
    272                 // Don't add non-existent packages
    273             }
    274         }
    275         return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
    276     }
    277 
    278     /**
    279      * Returns either the package info of the WebView provider determined in the following way:
    280      * If the user has chosen a provider then use that if it is valid,
    281      * otherwise use the first package in the webview priority list that is valid.
    282      *
    283      */
    284     private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
    285         ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
    286 
    287         String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
    288 
    289         // If the user has chosen provider, use that (if it's installed and enabled for all
    290         // users).
    291         for (ProviderAndPackageInfo providerAndPackage : providers) {
    292             if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
    293                 // userPackages can contain null objects.
    294                 List<UserPackage> userPackages =
    295                         mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
    296                                 providerAndPackage.provider);
    297                 if (isInstalledAndEnabledForAllUsers(userPackages)) {
    298                     return providerAndPackage.packageInfo;
    299                 }
    300             }
    301         }
    302 
    303         // User did not choose, or the choice failed; use the most stable provider that is
    304         // installed and enabled for all users, and available by default (not through
    305         // user choice).
    306         for (ProviderAndPackageInfo providerAndPackage : providers) {
    307             if (providerAndPackage.provider.availableByDefault) {
    308                 // userPackages can contain null objects.
    309                 List<UserPackage> userPackages =
    310                         mSystemInterface.getPackageInfoForProviderAllUsers(mContext,
    311                                 providerAndPackage.provider);
    312                 if (isInstalledAndEnabledForAllUsers(userPackages)) {
    313                     return providerAndPackage.packageInfo;
    314                 }
    315             }
    316         }
    317 
    318         // This should never happen during normal operation (only with modified system images).
    319         mAnyWebViewInstalled = false;
    320         throw new WebViewPackageMissingException("Could not find a loadable WebView package");
    321     }
    322 
    323     /**
    324      * Return true iff {@param packageInfos} point to only installed and enabled packages.
    325      * The given packages {@param packageInfos} should all be pointing to the same package, but each
    326      * PackageInfo representing a different user's package.
    327      */
    328     static boolean isInstalledAndEnabledForAllUsers(
    329             List<UserPackage> userPackages) {
    330         for (UserPackage userPackage : userPackages) {
    331             if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
    332                 return false;
    333             }
    334         }
    335         return true;
    336     }
    337 
    338     void notifyRelroCreationCompleted() {
    339         synchronized (mLock) {
    340             mNumRelroCreationsFinished++;
    341             checkIfRelrosDoneLocked();
    342         }
    343     }
    344 
    345     WebViewProviderResponse waitForAndGetProvider() {
    346         PackageInfo webViewPackage = null;
    347         final long NS_PER_MS = 1000000;
    348         final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
    349         boolean webViewReady = false;
    350         int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
    351         synchronized (mLock) {
    352             webViewReady = webViewIsReadyLocked();
    353             while (!webViewReady) {
    354                 final long timeNowMs = System.nanoTime() / NS_PER_MS;
    355                 if (timeNowMs >= timeoutTimeMs) break;
    356                 try {
    357                     mLock.wait(timeoutTimeMs - timeNowMs);
    358                 } catch (InterruptedException e) {}
    359                 webViewReady = webViewIsReadyLocked();
    360             }
    361             // Make sure we return the provider that was used to create the relro file
    362             webViewPackage = mCurrentWebViewPackage;
    363             if (webViewReady) {
    364             } else if (!mAnyWebViewInstalled) {
    365                 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
    366             } else {
    367                 // Either the current relro creation  isn't done yet, or the new relro creatioin
    368                 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
    369                 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
    370                 Slog.e(TAG, "Timed out waiting for relro creation, relros started "
    371                         + mNumRelroCreationsStarted
    372                         + " relros finished " + mNumRelroCreationsFinished
    373                         + " package dirty? " + mWebViewPackageDirty);
    374             }
    375         }
    376         if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
    377         return new WebViewProviderResponse(webViewPackage, webViewStatus);
    378     }
    379 
    380     PackageInfo getCurrentWebViewPackage() {
    381         synchronized(mLock) {
    382             return mCurrentWebViewPackage;
    383         }
    384     }
    385 
    386     /**
    387      * Returns whether WebView is ready and is not going to go through its preparation phase
    388      * again directly.
    389      */
    390     private boolean webViewIsReadyLocked() {
    391         return !mWebViewPackageDirty
    392             && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
    393             // The current package might be replaced though we haven't received an intent
    394             // declaring this yet, the following flag makes anyone loading WebView to wait in
    395             // this case.
    396             && mAnyWebViewInstalled;
    397     }
    398 
    399     private void checkIfRelrosDoneLocked() {
    400         if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
    401             if (mWebViewPackageDirty) {
    402                 mWebViewPackageDirty = false;
    403                 // If we have changed provider since we started the relro creation we need to
    404                 // redo the whole process using the new package instead.
    405                 try {
    406                     PackageInfo newPackage = findPreferredWebViewPackage();
    407                     onWebViewProviderChanged(newPackage);
    408                 } catch (WebViewPackageMissingException e) {
    409                     mCurrentWebViewPackage = null;
    410                     // If we can't find any valid WebView package we are now in a state where
    411                     // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
    412                     // should simply wait until we receive an intent declaring a new package was
    413                     // installed.
    414                 }
    415             } else {
    416                 mLock.notifyAll();
    417             }
    418         }
    419     }
    420 
    421     /**
    422      * Returns whether this provider is valid for use as a WebView provider.
    423      */
    424     boolean isValidProvider(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
    425         return VALIDITY_OK == validityResult(configInfo, packageInfo);
    426     }
    427 
    428     private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
    429         // Ensure the provider targets this framework release (or a later one).
    430         if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
    431             return VALIDITY_INCORRECT_SDK_VERSION;
    432         }
    433         if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
    434                 && !mSystemInterface.systemIsDebuggable()) {
    435             // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
    436             // minimum version code. This check is only enforced for user builds.
    437             return VALIDITY_INCORRECT_VERSION_CODE;
    438         }
    439         if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
    440             return VALIDITY_INCORRECT_SIGNATURE;
    441         }
    442         if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
    443             return VALIDITY_NO_LIBRARY_FLAG;
    444         }
    445         return VALIDITY_OK;
    446     }
    447 
    448     /**
    449      * Both versionCodes should be from a WebView provider package implemented by Chromium.
    450      * VersionCodes from other kinds of packages won't make any sense in this method.
    451      *
    452      * An introduction to Chromium versionCode scheme:
    453      * "BBBBPPPAX"
    454      * BBBB: 4 digit branch number. It monotonically increases over time.
    455      * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
    456      * may change their meaning in the future.
    457      * A: architecture digit.
    458      * X: A digit to differentiate APKs for other reasons.
    459      *
    460      * This method takes the "BBBB" of versionCodes and compare them.
    461      *
    462      * @return true if versionCode1 is higher than or equal to versionCode2.
    463      */
    464     private static boolean versionCodeGE(long versionCode1, long versionCode2) {
    465         long v1 = versionCode1 / 100000;
    466         long v2 = versionCode2 / 100000;
    467 
    468         return v1 >= v2;
    469     }
    470 
    471     /**
    472      * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
    473      * of all available-by-default and non-fallback WebView provider packages. If there is no
    474      * such WebView provider package on the system, then return -1, which means all positive
    475      * versionCode WebView packages are accepted.
    476      *
    477      * Note that this is a private method in WebViewUpdater that handles a variable
    478      * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
    479      * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
    480      */
    481     private long getMinimumVersionCode() {
    482         if (mMinimumVersionCode > 0) {
    483             return mMinimumVersionCode;
    484         }
    485 
    486         long minimumVersionCode = -1;
    487         for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
    488             if (provider.availableByDefault && !provider.isFallback) {
    489                 try {
    490                     long versionCode =
    491                         mSystemInterface.getFactoryPackageVersion(provider.packageName);
    492                     if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
    493                         minimumVersionCode = versionCode;
    494                     }
    495                 } catch (NameNotFoundException e) {
    496                     // Safe to ignore.
    497                 }
    498             }
    499         }
    500 
    501         mMinimumVersionCode = minimumVersionCode;
    502         return mMinimumVersionCode;
    503     }
    504 
    505     private static boolean providerHasValidSignature(WebViewProviderInfo provider,
    506             PackageInfo packageInfo, SystemInterface systemInterface) {
    507         if (systemInterface.systemIsDebuggable()) {
    508             return true;
    509         }
    510         // If no signature is declared, instead check whether the package is included in the
    511         // system.
    512         if (provider.signatures == null || provider.signatures.length == 0) {
    513             return packageInfo.applicationInfo.isSystemApp();
    514         }
    515         if (packageInfo.signatures.length != 1) return false;
    516 
    517         // Return whether the package signature matches any of the valid signatures
    518         for (Signature signature : provider.signatures) {
    519             if (signature.equals(packageInfo.signatures[0])) return true;
    520         }
    521         return false;
    522     }
    523 
    524     void dumpState(PrintWriter pw) {
    525         synchronized (mLock) {
    526             if (mCurrentWebViewPackage == null) {
    527                 pw.println("  Current WebView package is null");
    528             } else {
    529                 pw.println(String.format("  Current WebView package (name, version): (%s, %s)",
    530                         mCurrentWebViewPackage.packageName,
    531                         mCurrentWebViewPackage.versionName));
    532             }
    533             pw.println(String.format("  Minimum WebView version code: %d",
    534                   mMinimumVersionCode));
    535             pw.println(String.format("  Number of relros started: %d",
    536                     mNumRelroCreationsStarted));
    537             pw.println(String.format("  Number of relros finished: %d",
    538                         mNumRelroCreationsFinished));
    539             pw.println(String.format("  WebView package dirty: %b", mWebViewPackageDirty));
    540             pw.println(String.format("  Any WebView package installed: %b",
    541                     mAnyWebViewInstalled));
    542 
    543             try {
    544                 PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
    545                 pw.println(String.format(
    546                         "  Preferred WebView package (name, version): (%s, %s)",
    547                         preferredWebViewPackage.packageName,
    548                         preferredWebViewPackage.versionName));
    549             } catch (WebViewPackageMissingException e) {
    550                 pw.println(String.format("  Preferred WebView package: none"));
    551             }
    552 
    553             dumpAllPackageInformationLocked(pw);
    554         }
    555     }
    556 
    557     private void dumpAllPackageInformationLocked(PrintWriter pw) {
    558         WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
    559         pw.println("  WebView packages:");
    560         for (WebViewProviderInfo provider : allProviders) {
    561             List<UserPackage> userPackages =
    562                     mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider);
    563             PackageInfo systemUserPackageInfo =
    564                     userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
    565             if (systemUserPackageInfo == null) {
    566                 pw.println(String.format("    %s is NOT installed.", provider.packageName));
    567                 continue;
    568             }
    569 
    570             int validity = validityResult(provider, systemUserPackageInfo);
    571             String packageDetails = String.format(
    572                     "versionName: %s, versionCode: %d, targetSdkVersion: %d",
    573                     systemUserPackageInfo.versionName,
    574                     systemUserPackageInfo.getLongVersionCode(),
    575                     systemUserPackageInfo.applicationInfo.targetSdkVersion);
    576             if (validity == VALIDITY_OK) {
    577                 boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
    578                         mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider));
    579                 pw.println(String.format(
    580                         "    Valid package %s (%s) is %s installed/enabled for all users",
    581                         systemUserPackageInfo.packageName,
    582                         packageDetails,
    583                         installedForAllUsers ? "" : "NOT"));
    584             } else {
    585                 pw.println(String.format("    Invalid package %s (%s), reason: %s",
    586                         systemUserPackageInfo.packageName,
    587                         packageDetails,
    588                         getInvalidityReason(validity)));
    589             }
    590         }
    591     }
    592 
    593     private static String getInvalidityReason(int invalidityReason) {
    594         switch (invalidityReason) {
    595             case VALIDITY_INCORRECT_SDK_VERSION:
    596                 return "SDK version too low";
    597             case VALIDITY_INCORRECT_VERSION_CODE:
    598                 return "Version code too low";
    599             case VALIDITY_INCORRECT_SIGNATURE:
    600                 return "Incorrect signature";
    601             case VALIDITY_NO_LIBRARY_FLAG:
    602                 return "No WebView-library manifest flag";
    603             default:
    604                 return "Unexcepted validity-reason";
    605         }
    606     }
    607 
    608 }
    609