WebViewFactory.java revision fc424478a9e6c253238afe22ca174789d72e0682
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
17package android.webkit;
18
19import android.annotation.SystemApi;
20import android.app.ActivityManagerInternal;
21import android.app.AppGlobals;
22import android.app.Application;
23import android.content.Context;
24import android.content.pm.ApplicationInfo;
25import android.content.pm.PackageInfo;
26import android.content.pm.PackageManager;
27import android.content.res.XmlResourceParser;
28import android.os.Build;
29import android.os.Process;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.os.StrictMode;
33import android.os.SystemProperties;
34import android.os.Trace;
35import android.provider.Settings;
36import android.provider.Settings.Secure;
37import android.text.TextUtils;
38import android.util.AndroidRuntimeException;
39import android.util.Log;
40
41import com.android.internal.util.XmlUtils;
42import com.android.server.LocalServices;
43
44import dalvik.system.VMRuntime;
45
46import java.io.File;
47import java.io.IOException;
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.List;
51import java.util.zip.ZipEntry;
52import java.util.zip.ZipFile;
53
54import org.xmlpull.v1.XmlPullParserException;
55
56/**
57 * Top level factory, used creating all the main WebView implementation classes.
58 *
59 * @hide
60 */
61@SystemApi
62public final class WebViewFactory {
63
64    private static final String CHROMIUM_WEBVIEW_FACTORY =
65            "com.android.webview.chromium.WebViewChromiumFactoryProvider";
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_WEBVIEW_BEING_REPLACED = 8;
106    public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9;
107
108    private static String getWebViewPreparationErrorReason(int error) {
109        switch (error) {
110            case LIBLOAD_FAILED_WAITING_FOR_RELRO:
111                return "Time out waiting for Relro files being created";
112            case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
113                return "No WebView installed";
114            case LIBLOAD_WEBVIEW_BEING_REPLACED:
115                return "Time out waiting for WebView to be replaced";
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    private static String TAG_START = "webviewproviders";
131    private static String TAG_WEBVIEW_PROVIDER = "webviewprovider";
132    private static String TAG_PACKAGE_NAME = "packageName";
133    private static String TAG_DESCRIPTION = "description";
134    private static String TAG_SIGNATURE = "signature";
135
136    /**
137     * Reads all signatures at the current depth (within the current provider) from the XML parser.
138     */
139    private static String[] readSignatures(XmlResourceParser parser) throws IOException,
140            XmlPullParserException {
141        List<String> signatures = new ArrayList<String>();
142        int outerDepth = parser.getDepth();
143        while(XmlUtils.nextElementWithin(parser, outerDepth)) {
144            if (parser.getName().equals(TAG_SIGNATURE)) {
145                // Parse the value within the signature tag
146                String signature = parser.nextText();
147                signatures.add(signature);
148            } else {
149                Log.e(LOGTAG, "Found an element in a webview provider that is not a signature");
150            }
151        }
152        return signatures.toArray(new String[signatures.size()]);
153    }
154
155    /**
156     * Returns all packages declared in the framework resources as potential WebView providers.
157     * @hide
158     * */
159    public static WebViewProviderInfo[] getWebViewPackages() {
160        XmlResourceParser parser = null;
161        List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
162        try {
163            parser = AppGlobals.getInitialApplication().getResources().getXml(
164                    com.android.internal.R.xml.config_webview_packages);
165            XmlUtils.beginDocument(parser, TAG_START);
166            while(true) {
167                XmlUtils.nextElement(parser);
168                String element = parser.getName();
169                if (element == null) {
170                    break;
171                }
172                if (element.equals(TAG_WEBVIEW_PROVIDER)) {
173                    String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME);
174                    if (packageName == null) {
175                        throw new MissingWebViewPackageException(
176                                "WebView provider in framework resources missing package name");
177                    }
178                    String description = parser.getAttributeValue(null, TAG_DESCRIPTION);
179                    if (description == null) {
180                        throw new MissingWebViewPackageException(
181                                "WebView provider in framework resources missing description");
182                    }
183                    webViewProviders.add(
184                            new WebViewProviderInfo(packageName, description,
185                                readSignatures(parser)));
186                }
187                else {
188                    Log.e(LOGTAG, "Found an element that is not a webview provider");
189                }
190            }
191        } catch(XmlPullParserException e) {
192            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
193        } catch(IOException e) {
194            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
195        } finally {
196            if (parser != null) parser.close();
197        }
198        return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
199    }
200
201
202    // TODO (gsennton) remove when committing webview xts test change
203    public static String getWebViewPackageName() {
204        WebViewProviderInfo[] providers = getWebViewPackages();
205        return providers[0].packageName;
206    }
207
208    /**
209     * @hide
210     */
211    public static String getWebViewLibrary(ApplicationInfo ai) {
212        if (ai.metaData != null)
213            return ai.metaData.getString("com.android.webview.WebViewLibrary");
214        return null;
215    }
216
217    public static PackageInfo getLoadedPackageInfo() {
218        return sPackageInfo;
219    }
220
221    /**
222     * Load the native library for the given package name iff that package
223     * name is the same as the one providing the webview.
224     */
225    public static int loadWebViewNativeLibraryFromPackage(String packageName) {
226        int ret = waitForProviderAndSetPackageInfo();
227        if (ret != LIBLOAD_SUCCESS) {
228            return ret;
229        }
230        if (!sPackageInfo.packageName.equals(packageName))
231            return LIBLOAD_WRONG_PACKAGE_NAME;
232
233        return loadNativeLibrary();
234    }
235
236    static WebViewFactoryProvider getProvider() {
237        synchronized (sProviderLock) {
238            // For now the main purpose of this function (and the factory abstraction) is to keep
239            // us honest and minimize usage of WebView internals when binding the proxy.
240            if (sProviderInstance != null) return sProviderInstance;
241
242            final int uid = android.os.Process.myUid();
243            if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID) {
244                throw new UnsupportedOperationException(
245                        "For security reasons, WebView is not allowed in privileged processes");
246            }
247
248            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
249            try {
250                Class<WebViewFactoryProvider> providerClass = getProviderClass();
251
252                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
253                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
254                try {
255                    sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
256                            .newInstance(new WebViewDelegate());
257                    if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
258                    return sProviderInstance;
259                } catch (Exception e) {
260                    Log.e(LOGTAG, "error instantiating provider", e);
261                    throw new AndroidRuntimeException(e);
262                } finally {
263                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
264                    StrictMode.setThreadPolicy(oldPolicy);
265                }
266            } finally {
267                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
268            }
269        }
270    }
271
272    private static Class<WebViewFactoryProvider> getProviderClass() {
273        try {
274            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
275                    "WebViewFactory.waitForProviderAndSetPackageInfo()");
276            try {
277                // First fetch the package info so we can log the webview package version.
278                int res = waitForProviderAndSetPackageInfo();
279                if (res != LIBLOAD_SUCCESS) {
280                    throw new MissingWebViewPackageException(
281                            "Failed to load WebView provider, error: "
282                            + getWebViewPreparationErrorReason(res));
283                }
284            } finally {
285                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
286            }
287            Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
288                sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
289
290            Application initialApplication = AppGlobals.getInitialApplication();
291            Context webViewContext = null;
292            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "initialApplication.createPackageContext()");
293            try {
294                // Construct a package context to load the Java code into the current app.
295                // This is done as early as possible since by constructing a package context we
296                // register the WebView package as a dependency for the current application so that
297                // when the WebView package is updated this application will be killed.
298                webViewContext = initialApplication.createPackageContext(
299                        sPackageInfo.packageName,
300                        Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
301            } catch (PackageManager.NameNotFoundException e) {
302                throw new MissingWebViewPackageException(e);
303            } finally {
304                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
305            }
306
307            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
308            loadNativeLibrary();
309            Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
310
311            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
312            try {
313                initialApplication.getAssets().addAssetPathAsSharedLibrary(
314                        webViewContext.getApplicationInfo().sourceDir);
315                ClassLoader clazzLoader = webViewContext.getClassLoader();
316                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
317                try {
318                    return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
319                            true, clazzLoader);
320                } finally {
321                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
322                }
323            } catch (ClassNotFoundException e) {
324                Log.e(LOGTAG, "error loading provider", e);
325                throw new AndroidRuntimeException(e);
326            } finally {
327                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
328            }
329        } catch (MissingWebViewPackageException e) {
330            // If the package doesn't exist, then try loading the null WebView instead.
331            // If that succeeds, then this is a device without WebView support; if it fails then
332            // swallow the failure, complain that the real WebView is missing and rethrow the
333            // original exception.
334            try {
335                return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
336            } catch (ClassNotFoundException e2) {
337                // Ignore.
338            }
339            Log.e(LOGTAG, "Chromium WebView package does not exist", e);
340            throw new AndroidRuntimeException(e);
341        }
342    }
343
344    /**
345     * Perform any WebView loading preparations that must happen in the zygote.
346     * Currently, this means allocating address space to load the real JNI library later.
347     */
348    public static void prepareWebViewInZygote() {
349        try {
350            System.loadLibrary("webviewchromium_loader");
351            long addressSpaceToReserve =
352                    SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
353                    CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
354            sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
355
356            if (sAddressSpaceReserved) {
357                if (DEBUG) {
358                    Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
359                }
360            } else {
361                Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
362                        " bytes of address space failed");
363            }
364        } catch (Throwable t) {
365            // Log and discard errors at this stage as we must not crash the zygote.
366            Log.e(LOGTAG, "error preparing native loader", t);
367        }
368    }
369
370    private static int prepareWebViewInSystemServer(String[] nativeLibraryPaths) {
371        if (DEBUG) Log.v(LOGTAG, "creating relro files");
372        int numRelros = 0;
373
374        // We must always trigger createRelRo regardless of the value of nativeLibraryPaths. Any
375        // unexpected values will be handled there to ensure that we trigger notifying any process
376        // waiting on relro creation.
377        if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
378            if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
379            createRelroFile(false /* is64Bit */, nativeLibraryPaths);
380            numRelros++;
381        }
382
383        if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
384            if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
385            createRelroFile(true /* is64Bit */, nativeLibraryPaths);
386            numRelros++;
387        }
388        return numRelros;
389    }
390
391    /**
392     * @hide
393     */
394    public static int onWebViewProviderChanged(PackageInfo packageInfo) {
395        String[] nativeLibs = null;
396        try {
397            nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo);
398            if (nativeLibs != null) {
399                long newVmSize = 0L;
400
401                for (String path : nativeLibs) {
402                    if (path == null || TextUtils.isEmpty(path)) continue;
403                    if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
404                    File f = new File(path);
405                    if (f.exists()) {
406                        newVmSize = Math.max(newVmSize, f.length());
407                        continue;
408                    }
409                    if (path.contains("!/")) {
410                        String[] split = TextUtils.split(path, "!/");
411                        if (split.length == 2) {
412                            try (ZipFile z = new ZipFile(split[0])) {
413                                ZipEntry e = z.getEntry(split[1]);
414                                if (e != null && e.getMethod() == ZipEntry.STORED) {
415                                    newVmSize = Math.max(newVmSize, e.getSize());
416                                    continue;
417                                }
418                            }
419                            catch (IOException e) {
420                                Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e);
421                            }
422                        }
423                    }
424                    Log.e(LOGTAG, "error sizing load for " + path);
425                }
426
427                if (DEBUG) {
428                    Log.v(LOGTAG, "Based on library size, need " + newVmSize +
429                            " bytes of address space.");
430                }
431                // The required memory can be larger than the file on disk (due to .bss), and an
432                // upgraded version of the library will likely be larger, so always attempt to
433                // reserve twice as much as we think to allow for the library to grow during this
434                // boot cycle.
435                newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
436                Log.d(LOGTAG, "Setting new address space to " + newVmSize);
437                SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
438                        Long.toString(newVmSize));
439            }
440        } catch (Throwable t) {
441            // Log and discard errors at this stage as we must not crash the system server.
442            Log.e(LOGTAG, "error preparing webview native library", t);
443        }
444        return prepareWebViewInSystemServer(nativeLibs);
445    }
446
447    // throws MissingWebViewPackageException
448    private static String getLoadFromApkPath(String apkPath,
449                                             String[] abiList,
450                                             String nativeLibFileName) {
451        // Search the APK for a native library conforming to a listed ABI.
452        try (ZipFile z = new ZipFile(apkPath)) {
453            for (String abi : abiList) {
454                final String entry = "lib/" + abi + "/" + nativeLibFileName;
455                ZipEntry e = z.getEntry(entry);
456                if (e != null && e.getMethod() == ZipEntry.STORED) {
457                    // Return a path formatted for dlopen() load from APK.
458                    return apkPath + "!/" + entry;
459                }
460            }
461        } catch (IOException e) {
462            throw new MissingWebViewPackageException(e);
463        }
464        return "";
465    }
466
467    // throws MissingWebViewPackageException
468    private static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo) {
469        ApplicationInfo ai = packageInfo.applicationInfo;
470        final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai);
471
472        String path32;
473        String path64;
474        boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
475        if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
476            // Multi-arch case.
477            if (primaryArchIs64bit) {
478                // Primary arch: 64-bit, secondary: 32-bit.
479                path64 = ai.nativeLibraryDir;
480                path32 = ai.secondaryNativeLibraryDir;
481            } else {
482                // Primary arch: 32-bit, secondary: 64-bit.
483                path64 = ai.secondaryNativeLibraryDir;
484                path32 = ai.nativeLibraryDir;
485            }
486        } else if (primaryArchIs64bit) {
487            // Single-arch 64-bit.
488            path64 = ai.nativeLibraryDir;
489            path32 = "";
490        } else {
491            // Single-arch 32-bit.
492            path32 = ai.nativeLibraryDir;
493            path64 = "";
494        }
495
496        // Form the full paths to the extracted native libraries.
497        // If libraries were not extracted, try load from APK paths instead.
498        if (!TextUtils.isEmpty(path32)) {
499            path32 += "/" + NATIVE_LIB_FILE_NAME;
500            File f = new File(path32);
501            if (!f.exists()) {
502                path32 = getLoadFromApkPath(ai.sourceDir,
503                                            Build.SUPPORTED_32_BIT_ABIS,
504                                            NATIVE_LIB_FILE_NAME);
505            }
506        }
507        if (!TextUtils.isEmpty(path64)) {
508            path64 += "/" + NATIVE_LIB_FILE_NAME;
509            File f = new File(path64);
510            if (!f.exists()) {
511                path64 = getLoadFromApkPath(ai.sourceDir,
512                                            Build.SUPPORTED_64_BIT_ABIS,
513                                            NATIVE_LIB_FILE_NAME);
514            }
515        }
516
517        if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64);
518        return new String[] { path32, path64 };
519    }
520
521    private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
522        final String abi =
523                is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
524
525        // crashHandler is invoked by the ActivityManagerService when the isolated process crashes.
526        Runnable crashHandler = new Runnable() {
527            @Override
528            public void run() {
529                try {
530                    Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
531                    getUpdateService().notifyRelroCreationCompleted();
532                } catch (RemoteException e) {
533                    Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
534                }
535            }
536        };
537
538        try {
539            if (nativeLibraryPaths == null
540                    || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
541                throw new IllegalArgumentException(
542                        "Native library paths to the WebView RelRo process must not be null!");
543            }
544            int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
545                    RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,
546                    Process.SHARED_RELRO_UID, crashHandler);
547            if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
548        } catch (Throwable t) {
549            // Log and discard errors as we must not crash the system server.
550            Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
551            crashHandler.run();
552        }
553    }
554
555    private static class RelroFileCreator {
556        // Called in an unprivileged child process to create the relro file.
557        public static void main(String[] args) {
558            boolean result = false;
559            boolean is64Bit = VMRuntime.getRuntime().is64Bit();
560            try{
561                if (args.length != 2 || args[0] == null || args[1] == null) {
562                    Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
563                    return;
564                }
565                Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " +
566                        " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
567                if (!sAddressSpaceReserved) {
568                    Log.e(LOGTAG, "can't create relro file; address space not reserved");
569                    return;
570                }
571                result = nativeCreateRelroFile(args[0] /* path32 */,
572                                               args[1] /* path64 */,
573                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
574                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
575                if (result && DEBUG) Log.v(LOGTAG, "created relro file");
576            } finally {
577                // We must do our best to always notify the update service, even if something fails.
578                try {
579                    getUpdateService().notifyRelroCreationCompleted();
580                } catch (RemoteException e) {
581                    Log.e(LOGTAG, "error notifying update service", e);
582                }
583
584                if (!result) Log.e(LOGTAG, "failed to create relro file");
585
586                // Must explicitly exit or else this process will just sit around after we return.
587                System.exit(0);
588            }
589        }
590    }
591
592    private static int waitForProviderAndSetPackageInfo() {
593        WebViewProviderResponse response = null;
594        try {
595            response =
596                getUpdateService().waitForAndGetProvider();
597            if (response.status == WebViewFactory.LIBLOAD_SUCCESS)
598                sPackageInfo = response.packageInfo;
599        } catch (RemoteException e) {
600            Log.e(LOGTAG, "error waiting for relro creation", e);
601            return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
602        }
603        return response.status;
604    }
605
606    // Assumes that we have waited for relro creation and set sPackageInfo
607    private static int loadNativeLibrary() {
608        if (!sAddressSpaceReserved) {
609            Log.e(LOGTAG, "can't load with relro file; address space not reserved");
610            return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
611        }
612
613        String[] args = getWebViewNativeLibraryPaths(sPackageInfo);
614        int result = nativeLoadWithRelroFile(args[0] /* path32 */,
615                                                 args[1] /* path64 */,
616                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
617                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
618        if (result != LIBLOAD_SUCCESS) {
619            Log.w(LOGTAG, "failed to load with relro file, proceeding without");
620        } else if (DEBUG) {
621            Log.v(LOGTAG, "loaded with relro file");
622        }
623        return result;
624    }
625
626    private static IWebViewUpdateService getUpdateService() {
627        return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
628    }
629
630    private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
631    private static native boolean nativeCreateRelroFile(String lib32, String lib64,
632                                                        String relro32, String relro64);
633    private static native int nativeLoadWithRelroFile(String lib32, String lib64,
634                                                          String relro32, String relro64);
635}
636