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