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