Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2012 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 android.webkit;
     18 
     19 import android.annotation.SystemApi;
     20 import android.app.ActivityManager;
     21 import android.app.ActivityManagerInternal;
     22 import android.app.AppGlobals;
     23 import android.app.Application;
     24 import android.content.Context;
     25 import android.content.pm.ApplicationInfo;
     26 import android.content.pm.PackageInfo;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.Signature;
     29 import android.os.Build;
     30 import android.os.Process;
     31 import android.os.RemoteException;
     32 import android.os.ServiceManager;
     33 import android.os.StrictMode;
     34 import android.os.SystemProperties;
     35 import android.os.Trace;
     36 import android.text.TextUtils;
     37 import android.util.AndroidRuntimeException;
     38 import android.util.ArraySet;
     39 import android.util.Log;
     40 
     41 import com.android.server.LocalServices;
     42 
     43 import dalvik.system.VMRuntime;
     44 
     45 import java.lang.reflect.Method;
     46 import java.io.File;
     47 import java.io.IOException;
     48 import java.util.Arrays;
     49 import java.util.zip.ZipEntry;
     50 import java.util.zip.ZipFile;
     51 
     52 /**
     53  * Top level factory, used creating all the main WebView implementation classes.
     54  *
     55  * @hide
     56  */
     57 @SystemApi
     58 public final class WebViewFactory {
     59 
     60     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     61     /** @hide */
     62     private static final String CHROMIUM_WEBVIEW_FACTORY =
     63             "com.android.webview.chromium.WebViewChromiumFactoryProviderForO";
     64 
     65     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
     66 
     67     private static final String NULL_WEBVIEW_FACTORY =
     68             "com.android.webview.nullwebview.NullWebViewFactoryProvider";
     69 
     70     private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
     71             "/data/misc/shared_relro/libwebviewchromium32.relro";
     72     private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
     73             "/data/misc/shared_relro/libwebviewchromium64.relro";
     74 
     75     public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
     76             "persist.sys.webview.vmsize";
     77     private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024;
     78 
     79     private static final String LOGTAG = "WebViewFactory";
     80 
     81     private static final boolean DEBUG = false;
     82 
     83     // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
     84     // same provider.
     85     private static WebViewFactoryProvider sProviderInstance;
     86     private static final Object sProviderLock = new Object();
     87     private static boolean sAddressSpaceReserved = false;
     88     private static PackageInfo sPackageInfo;
     89 
     90     // Error codes for loadWebViewNativeLibraryFromPackage
     91     public static final int LIBLOAD_SUCCESS = 0;
     92     public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
     93     public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2;
     94 
     95     // error codes for waiting for WebView preparation
     96     public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3;
     97     public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
     98 
     99     // native relro loading error codes
    100     public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5;
    101     public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6;
    102     public static final int LIBLOAD_FAILED_JNI_CALL = 7;
    103 
    104     // more error codes for waiting for WebView preparation
    105     public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8;
    106 
    107     // error for namespace lookup
    108     public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10;
    109 
    110     private static String getWebViewPreparationErrorReason(int error) {
    111         switch (error) {
    112             case LIBLOAD_FAILED_WAITING_FOR_RELRO:
    113                 return "Time out waiting for Relro files being created";
    114             case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
    115                 return "No WebView installed";
    116             case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN:
    117                 return "Crashed for unknown reason";
    118         }
    119         return "Unknown";
    120     }
    121 
    122     /**
    123      * @hide
    124      */
    125     public static class MissingWebViewPackageException extends AndroidRuntimeException {
    126         public MissingWebViewPackageException(String message) { super(message); }
    127         public MissingWebViewPackageException(Exception e) { super(e); }
    128     }
    129 
    130     /**
    131      * @hide
    132      */
    133     public static String getWebViewLibrary(ApplicationInfo ai) {
    134         if (ai.metaData != null)
    135             return ai.metaData.getString("com.android.webview.WebViewLibrary");
    136         return null;
    137     }
    138 
    139     public static PackageInfo getLoadedPackageInfo() {
    140         synchronized (sProviderLock) {
    141             return sPackageInfo;
    142         }
    143     }
    144 
    145     /**
    146      * @hide
    147      */
    148     public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader)
    149             throws ClassNotFoundException {
    150         return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
    151                 true, clazzLoader);
    152     }
    153 
    154     /**
    155      * Load the native library for the given package name iff that package
    156      * name is the same as the one providing the webview.
    157      */
    158     public static int loadWebViewNativeLibraryFromPackage(String packageName,
    159                                                           ClassLoader clazzLoader) {
    160         WebViewProviderResponse response = null;
    161         try {
    162             response = getUpdateService().waitForAndGetProvider();
    163         } catch (RemoteException e) {
    164             Log.e(LOGTAG, "error waiting for relro creation", e);
    165             return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
    166         }
    167 
    168 
    169         if (response.status != LIBLOAD_SUCCESS
    170                 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
    171             return response.status;
    172         }
    173         if (!response.packageInfo.packageName.equals(packageName)) {
    174             return LIBLOAD_WRONG_PACKAGE_NAME;
    175         }
    176 
    177         PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager();
    178         PackageInfo packageInfo;
    179         try {
    180             packageInfo = packageManager.getPackageInfo(packageName,
    181                     PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
    182         } catch (PackageManager.NameNotFoundException e) {
    183             Log.e(LOGTAG, "Couldn't find package " + packageName);
    184             return LIBLOAD_WRONG_PACKAGE_NAME;
    185         }
    186 
    187         int loadNativeRet = loadNativeLibrary(clazzLoader, packageInfo);
    188         // If we failed waiting for relro we want to return that fact even if we successfully load
    189         // the relro file.
    190         if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
    191         return loadNativeRet;
    192     }
    193 
    194     static WebViewFactoryProvider getProvider() {
    195         synchronized (sProviderLock) {
    196             // For now the main purpose of this function (and the factory abstraction) is to keep
    197             // us honest and minimize usage of WebView internals when binding the proxy.
    198             if (sProviderInstance != null) return sProviderInstance;
    199 
    200             final int uid = android.os.Process.myUid();
    201             if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
    202                     || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
    203                     || uid == android.os.Process.BLUETOOTH_UID) {
    204                 throw new UnsupportedOperationException(
    205                         "For security reasons, WebView is not allowed in privileged processes");
    206             }
    207 
    208             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
    209             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
    210             try {
    211                 Class<WebViewFactoryProvider> providerClass = getProviderClass();
    212                 Method staticFactory = null;
    213                 try {
    214                     staticFactory = providerClass.getMethod(
    215                         CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
    216                 } catch (Exception e) {
    217                     if (DEBUG) {
    218                         Log.w(LOGTAG, "error instantiating provider with static factory method", e);
    219                     }
    220                 }
    221 
    222                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
    223                 try {
    224                     sProviderInstance = (WebViewFactoryProvider)
    225                             staticFactory.invoke(null, new WebViewDelegate());
    226                     if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
    227                     return sProviderInstance;
    228                 } catch (Exception e) {
    229                     Log.e(LOGTAG, "error instantiating provider", e);
    230                     throw new AndroidRuntimeException(e);
    231                 } finally {
    232                     Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    233                 }
    234             } finally {
    235                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    236                 StrictMode.setThreadPolicy(oldPolicy);
    237             }
    238         }
    239     }
    240 
    241     /**
    242      * Returns true if the signatures match, false otherwise
    243      */
    244     private static boolean signaturesEquals(Signature[] s1, Signature[] s2) {
    245         if (s1 == null) {
    246             return s2 == null;
    247         }
    248         if (s2 == null) return false;
    249 
    250         ArraySet<Signature> set1 = new ArraySet<>();
    251         for(Signature signature : s1) {
    252             set1.add(signature);
    253         }
    254         ArraySet<Signature> set2 = new ArraySet<>();
    255         for(Signature signature : s2) {
    256             set2.add(signature);
    257         }
    258         return set1.equals(set2);
    259     }
    260 
    261     // Throws MissingWebViewPackageException on failure
    262     private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) {
    263         if (!chosen.packageName.equals(toUse.packageName)) {
    264             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
    265                     + "packageName mismatch, expected: "
    266                     + chosen.packageName + " actual: " + toUse.packageName);
    267         }
    268         if (chosen.versionCode > toUse.versionCode) {
    269             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
    270                     + "version code is lower than expected: " + chosen.versionCode
    271                     + " actual: " + toUse.versionCode);
    272         }
    273         if (getWebViewLibrary(toUse.applicationInfo) == null) {
    274             throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
    275                     + toUse.packageName);
    276         }
    277         if (!signaturesEquals(chosen.signatures, toUse.signatures)) {
    278             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
    279                     + "signature mismatch");
    280         }
    281     }
    282 
    283     /**
    284      * If the ApplicationInfo provided is for a stub WebView, fix up the object to include the
    285      * required values from the donor package. If the ApplicationInfo is for a full WebView,
    286      * leave it alone. Throws MissingWebViewPackageException if the donor is missing.
    287      */
    288     private static void fixupStubApplicationInfo(ApplicationInfo ai, PackageManager pm) {
    289         String donorPackageName = null;
    290         if (ai.metaData != null) {
    291             donorPackageName = ai.metaData.getString("com.android.webview.WebViewDonorPackage");
    292         }
    293         if (donorPackageName != null) {
    294             PackageInfo donorPackage;
    295             try {
    296                 donorPackage = pm.getPackageInfo(
    297                         donorPackageName,
    298                         PackageManager.GET_SHARED_LIBRARY_FILES
    299                         | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
    300                         | PackageManager.MATCH_UNINSTALLED_PACKAGES
    301                         | PackageManager.MATCH_FACTORY_ONLY);
    302             } catch (PackageManager.NameNotFoundException e) {
    303                 throw new MissingWebViewPackageException("Failed to find donor package: " +
    304                                                          donorPackageName);
    305             }
    306             ApplicationInfo donorInfo = donorPackage.applicationInfo;
    307 
    308             // Replace the stub's code locations with the donor's.
    309             ai.sourceDir = donorInfo.sourceDir;
    310             ai.splitSourceDirs = donorInfo.splitSourceDirs;
    311             ai.nativeLibraryDir = donorInfo.nativeLibraryDir;
    312             ai.secondaryNativeLibraryDir = donorInfo.secondaryNativeLibraryDir;
    313 
    314             // Copy the donor's primary and secondary ABIs, since the stub doesn't have native code
    315             // and so they are unset.
    316             ai.primaryCpuAbi = donorInfo.primaryCpuAbi;
    317             ai.secondaryCpuAbi = donorInfo.secondaryCpuAbi;
    318         }
    319     }
    320 
    321     private static Context getWebViewContextAndSetProvider() {
    322         Application initialApplication = AppGlobals.getInitialApplication();
    323         try {
    324             WebViewProviderResponse response = null;
    325             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
    326                     "WebViewUpdateService.waitForAndGetProvider()");
    327             try {
    328                 response = getUpdateService().waitForAndGetProvider();
    329             } finally {
    330                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    331             }
    332             if (response.status != LIBLOAD_SUCCESS
    333                     && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
    334                 throw new MissingWebViewPackageException("Failed to load WebView provider: "
    335                         + getWebViewPreparationErrorReason(response.status));
    336             }
    337             // Register to be killed before fetching package info - so that we will be
    338             // killed if the package info goes out-of-date.
    339             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()");
    340             try {
    341                 ActivityManager.getService().addPackageDependency(
    342                         response.packageInfo.packageName);
    343             } finally {
    344                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    345             }
    346             // Fetch package info and verify it against the chosen package
    347             PackageInfo newPackageInfo = null;
    348             PackageManager pm = initialApplication.getPackageManager();
    349             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
    350             try {
    351                 newPackageInfo = pm.getPackageInfo(
    352                     response.packageInfo.packageName,
    353                     PackageManager.GET_SHARED_LIBRARY_FILES
    354                     | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
    355                     // Make sure that we fetch the current provider even if its not
    356                     // installed for the current user
    357                     | PackageManager.MATCH_UNINSTALLED_PACKAGES
    358                     // Fetch signatures for verification
    359                     | PackageManager.GET_SIGNATURES
    360                     // Get meta-data for meta data flag verification
    361                     | PackageManager.GET_META_DATA);
    362             } finally {
    363                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    364             }
    365 
    366             // Validate the newly fetched package info, throws MissingWebViewPackageException on
    367             // failure
    368             verifyPackageInfo(response.packageInfo, newPackageInfo);
    369 
    370             ApplicationInfo ai = newPackageInfo.applicationInfo;
    371             fixupStubApplicationInfo(ai, pm);
    372 
    373             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
    374                     "initialApplication.createApplicationContext");
    375             try {
    376                 // Construct an app context to load the Java code into the current app.
    377                 Context webViewContext = initialApplication.createApplicationContext(
    378                         ai,
    379                         Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
    380                 sPackageInfo = newPackageInfo;
    381                 return webViewContext;
    382             } finally {
    383                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    384             }
    385         } catch (RemoteException | PackageManager.NameNotFoundException e) {
    386             throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
    387         }
    388     }
    389 
    390     private static Class<WebViewFactoryProvider> getProviderClass() {
    391         Context webViewContext = null;
    392         Application initialApplication = AppGlobals.getInitialApplication();
    393 
    394         try {
    395             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
    396                     "WebViewFactory.getWebViewContextAndSetProvider()");
    397             try {
    398                 webViewContext = getWebViewContextAndSetProvider();
    399             } finally {
    400                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    401             }
    402             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
    403                     sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
    404 
    405             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
    406             try {
    407                 initialApplication.getAssets().addAssetPathAsSharedLibrary(
    408                         webViewContext.getApplicationInfo().sourceDir);
    409                 ClassLoader clazzLoader = webViewContext.getClassLoader();
    410 
    411                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
    412                 loadNativeLibrary(clazzLoader, sPackageInfo);
    413                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    414 
    415                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
    416                 try {
    417                     return getWebViewProviderClass(clazzLoader);
    418                 } finally {
    419                     Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    420                 }
    421             } catch (ClassNotFoundException e) {
    422                 Log.e(LOGTAG, "error loading provider", e);
    423                 throw new AndroidRuntimeException(e);
    424             } finally {
    425                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
    426             }
    427         } catch (MissingWebViewPackageException e) {
    428             // If the package doesn't exist, then try loading the null WebView instead.
    429             // If that succeeds, then this is a device without WebView support; if it fails then
    430             // swallow the failure, complain that the real WebView is missing and rethrow the
    431             // original exception.
    432             try {
    433                 return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
    434             } catch (ClassNotFoundException e2) {
    435                 // Ignore.
    436             }
    437             Log.e(LOGTAG, "Chromium WebView package does not exist", e);
    438             throw new AndroidRuntimeException(e);
    439         }
    440     }
    441 
    442     /**
    443      * Perform any WebView loading preparations that must happen in the zygote.
    444      * Currently, this means allocating address space to load the real JNI library later.
    445      */
    446     public static void prepareWebViewInZygote() {
    447         try {
    448             System.loadLibrary("webviewchromium_loader");
    449             long addressSpaceToReserve =
    450                     SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
    451                     CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
    452             sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
    453 
    454             if (sAddressSpaceReserved) {
    455                 if (DEBUG) {
    456                     Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
    457                 }
    458             } else {
    459                 Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
    460                         " bytes of address space failed");
    461             }
    462         } catch (Throwable t) {
    463             // Log and discard errors at this stage as we must not crash the zygote.
    464             Log.e(LOGTAG, "error preparing native loader", t);
    465         }
    466     }
    467 
    468     private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
    469         if (DEBUG) Log.v(LOGTAG, "creating relro files");
    470         int numRelros = 0;
    471 
    472         // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any
    473         // unexpected values will be handled there to ensure that we trigger notifying any process
    474         // waiting on relro creation.
    475         if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
    476             if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
    477             createRelroFile(false /* is64Bit */, nativeLibraryPaths);
    478             numRelros++;
    479         }
    480 
    481         if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
    482             if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
    483             createRelroFile(true /* is64Bit */, nativeLibraryPaths);
    484             numRelros++;
    485         }
    486         return numRelros;
    487     }
    488 
    489     /**
    490      * @hide
    491      */
    492     public static int onWebViewProviderChanged(PackageInfo packageInfo) {
    493         String[] nativeLibs = null;
    494         String originalSourceDir = packageInfo.applicationInfo.sourceDir;
    495         try {
    496             fixupStubApplicationInfo(packageInfo.applicationInfo,
    497                                      AppGlobals.getInitialApplication().getPackageManager());
    498 
    499             nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo);
    500             if (nativeLibs != null) {
    501                 long newVmSize = 0L;
    502 
    503                 for (String path : nativeLibs) {
    504                     if (path == null || TextUtils.isEmpty(path)) continue;
    505                     if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
    506                     File f = new File(path);
    507                     if (f.exists()) {
    508                         newVmSize = Math.max(newVmSize, f.length());
    509                         continue;
    510                     }
    511                     if (path.contains("!/")) {
    512                         String[] split = TextUtils.split(path, "!/");
    513                         if (split.length == 2) {
    514                             try (ZipFile z = new ZipFile(split[0])) {
    515                                 ZipEntry e = z.getEntry(split[1]);
    516                                 if (e != null && e.getMethod() == ZipEntry.STORED) {
    517                                     newVmSize = Math.max(newVmSize, e.getSize());
    518                                     continue;
    519                                 }
    520                             }
    521                             catch (IOException e) {
    522                                 Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e);
    523                             }
    524                         }
    525                     }
    526                     Log.e(LOGTAG, "error sizing load for " + path);
    527                 }
    528 
    529                 if (DEBUG) {
    530                     Log.v(LOGTAG, "Based on library size, need " + newVmSize +
    531                             " bytes of address space.");
    532                 }
    533                 // The required memory can be larger than the file on disk (due to .bss), and an
    534                 // upgraded version of the library will likely be larger, so always attempt to
    535                 // reserve twice as much as we think to allow for the library to grow during this
    536                 // boot cycle.
    537                 newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
    538                 Log.d(LOGTAG, "Setting new address space to " + newVmSize);
    539                 SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
    540                         Long.toString(newVmSize));
    541             }
    542         } catch (Throwable t) {
    543             // Log and discard errors at this stage as we must not crash the system server.
    544             Log.e(LOGTAG, "error preparing webview native library", t);
    545         }
    546 
    547         WebViewZygote.onWebViewProviderChanged(packageInfo, originalSourceDir);
    548 
    549         return prepareWebViewInSystemServer(nativeLibs);
    550     }
    551 
    552     // throws MissingWebViewPackageException
    553     private static String getLoadFromApkPath(String apkPath,
    554                                              String[] abiList,
    555                                              String nativeLibFileName) {
    556         // Search the APK for a native library conforming to a listed ABI.
    557         try (ZipFile z = new ZipFile(apkPath)) {
    558             for (String abi : abiList) {
    559                 final String entry = "lib/" + abi + "/" + nativeLibFileName;
    560                 ZipEntry e = z.getEntry(entry);
    561                 if (e != null && e.getMethod() == ZipEntry.STORED) {
    562                     // Return a path formatted for dlopen() load from APK.
    563                     return apkPath + "!/" + entry;
    564                 }
    565             }
    566         } catch (IOException e) {
    567             throw new MissingWebViewPackageException(e);
    568         }
    569         return "";
    570     }
    571 
    572     // throws MissingWebViewPackageException
    573     private static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo) {
    574         ApplicationInfo ai = packageInfo.applicationInfo;
    575         final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai);
    576 
    577         String path32;
    578         String path64;
    579         boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
    580         if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
    581             // Multi-arch case.
    582             if (primaryArchIs64bit) {
    583                 // Primary arch: 64-bit, secondary: 32-bit.
    584                 path64 = ai.nativeLibraryDir;
    585                 path32 = ai.secondaryNativeLibraryDir;
    586             } else {
    587                 // Primary arch: 32-bit, secondary: 64-bit.
    588                 path64 = ai.secondaryNativeLibraryDir;
    589                 path32 = ai.nativeLibraryDir;
    590             }
    591         } else if (primaryArchIs64bit) {
    592             // Single-arch 64-bit.
    593             path64 = ai.nativeLibraryDir;
    594             path32 = "";
    595         } else {
    596             // Single-arch 32-bit.
    597             path32 = ai.nativeLibraryDir;
    598             path64 = "";
    599         }
    600 
    601         // Form the full paths to the extracted native libraries.
    602         // If libraries were not extracted, try load from APK paths instead.
    603         if (!TextUtils.isEmpty(path32)) {
    604             path32 += "/" + NATIVE_LIB_FILE_NAME;
    605             File f = new File(path32);
    606             if (!f.exists()) {
    607                 path32 = getLoadFromApkPath(ai.sourceDir,
    608                                             Build.SUPPORTED_32_BIT_ABIS,
    609                                             NATIVE_LIB_FILE_NAME);
    610             }
    611         }
    612         if (!TextUtils.isEmpty(path64)) {
    613             path64 += "/" + NATIVE_LIB_FILE_NAME;
    614             File f = new File(path64);
    615             if (!f.exists()) {
    616                 path64 = getLoadFromApkPath(ai.sourceDir,
    617                                             Build.SUPPORTED_64_BIT_ABIS,
    618                                             NATIVE_LIB_FILE_NAME);
    619             }
    620         }
    621 
    622         if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64);
    623         return new String[] { path32, path64 };
    624     }
    625 
    626     private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
    627         final String abi =
    628                 is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
    629 
    630         // crashHandler is invoked by the ActivityManagerService when the isolated process crashes.
    631         Runnable crashHandler = new Runnable() {
    632             @Override
    633             public void run() {
    634                 try {
    635                     Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
    636                     getUpdateService().notifyRelroCreationCompleted();
    637                 } catch (RemoteException e) {
    638                     Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
    639                 }
    640             }
    641         };
    642 
    643         try {
    644             if (nativeLibraryPaths == null
    645                     || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
    646                 throw new IllegalArgumentException(
    647                         "Native library paths to the WebView RelRo process must not be null!");
    648             }
    649             int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
    650                     RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,
    651                     Process.SHARED_RELRO_UID, crashHandler);
    652             if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
    653         } catch (Throwable t) {
    654             // Log and discard errors as we must not crash the system server.
    655             Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
    656             crashHandler.run();
    657         }
    658     }
    659 
    660     private static class RelroFileCreator {
    661         // Called in an unprivileged child process to create the relro file.
    662         public static void main(String[] args) {
    663             boolean result = false;
    664             boolean is64Bit = VMRuntime.getRuntime().is64Bit();
    665             try{
    666                 if (args.length != 2 || args[0] == null || args[1] == null) {
    667                     Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
    668                     return;
    669                 }
    670                 Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " +
    671                         " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
    672                 if (!sAddressSpaceReserved) {
    673                     Log.e(LOGTAG, "can't create relro file; address space not reserved");
    674                     return;
    675                 }
    676                 result = nativeCreateRelroFile(args[0] /* path32 */,
    677                                                args[1] /* path64 */,
    678                                                CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
    679                                                CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
    680                 if (result && DEBUG) Log.v(LOGTAG, "created relro file");
    681             } finally {
    682                 // We must do our best to always notify the update service, even if something fails.
    683                 try {
    684                     getUpdateService().notifyRelroCreationCompleted();
    685                 } catch (RemoteException e) {
    686                     Log.e(LOGTAG, "error notifying update service", e);
    687                 }
    688 
    689                 if (!result) Log.e(LOGTAG, "failed to create relro file");
    690 
    691                 // Must explicitly exit or else this process will just sit around after we return.
    692                 System.exit(0);
    693             }
    694         }
    695     }
    696 
    697     // Assumes that we have waited for relro creation
    698     private static int loadNativeLibrary(ClassLoader clazzLoader, PackageInfo packageInfo) {
    699         if (!sAddressSpaceReserved) {
    700             Log.e(LOGTAG, "can't load with relro file; address space not reserved");
    701             return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
    702         }
    703 
    704         String[] args = getWebViewNativeLibraryPaths(packageInfo);
    705         int result = nativeLoadWithRelroFile(args[0] /* path32 */,
    706                                              args[1] /* path64 */,
    707                                              CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
    708                                              CHROMIUM_WEBVIEW_NATIVE_RELRO_64,
    709                                              clazzLoader);
    710         if (result != LIBLOAD_SUCCESS) {
    711             Log.w(LOGTAG, "failed to load with relro file, proceeding without");
    712         } else if (DEBUG) {
    713             Log.v(LOGTAG, "loaded with relro file");
    714         }
    715         return result;
    716     }
    717 
    718     private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
    719 
    720     /** @hide */
    721     public static IWebViewUpdateService getUpdateService() {
    722         return IWebViewUpdateService.Stub.asInterface(
    723                 ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
    724     }
    725 
    726     private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
    727     private static native boolean nativeCreateRelroFile(String lib32, String lib64,
    728                                                         String relro32, String relro64);
    729     private static native int nativeLoadWithRelroFile(String lib32, String lib64,
    730                                                       String relro32, String relro64,
    731                                                       ClassLoader clazzLoader);
    732 }
    733