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