• 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.NonNull;
20 import android.annotation.SystemApi;
21 import android.annotation.UptimeMillisLong;
22 import android.app.ActivityManager;
23 import android.app.AppGlobals;
24 import android.app.Application;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.content.Context;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.Signature;
31 import android.os.Build;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.SystemClock;
35 import android.os.Trace;
36 import android.util.AndroidRuntimeException;
37 import android.util.ArraySet;
38 import android.util.Log;
39 
40 import java.io.File;
41 import java.lang.reflect.Method;
42 
43 /**
44  * Top level factory, used creating all the main WebView implementation classes.
45  *
46  * @hide
47  */
48 @SystemApi
49 public final class WebViewFactory {
50 
51     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
52     /** @hide */
53     private static final String CHROMIUM_WEBVIEW_FACTORY =
54             "com.android.webview.chromium.WebViewChromiumFactoryProviderForT";
55 
56     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
57 
58     private static final String LOGTAG = "WebViewFactory";
59 
60     private static final boolean DEBUG = false;
61 
62     // Cache the factory both for efficiency, and ensure any one process gets all webviews from the
63     // same provider.
64     @UnsupportedAppUsage
65     private static WebViewFactoryProvider sProviderInstance;
66     private static final Object sProviderLock = new Object();
67     @UnsupportedAppUsage
68     private static PackageInfo sPackageInfo;
69     private static Boolean sWebViewSupported;
70     private static boolean sWebViewDisabled;
71     private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
72 
73     // Error codes for loadWebViewNativeLibraryFromPackage
74     public static final int LIBLOAD_SUCCESS = 0;
75     public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
76     public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2;
77 
78     // error codes for waiting for WebView preparation
79     public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3;
80     public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
81 
82     // native relro loading error codes
83     public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5;
84     public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6;
85     public static final int LIBLOAD_FAILED_JNI_CALL = 7;
86 
87     // more error codes for waiting for WebView preparation
88     public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8;
89 
90     // error for namespace lookup
91     public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10;
92 
93     /**
94      * Stores the timestamps at which various WebView startup events occurred in this process.
95      */
96     public static class StartupTimestamps {
97         long mWebViewLoadStart;
98         long mCreateContextStart;
99         long mCreateContextEnd;
100         long mAddAssetsStart;
101         long mAddAssetsEnd;
102         long mGetClassLoaderStart;
103         long mGetClassLoaderEnd;
104         long mNativeLoadStart;
105         long mNativeLoadEnd;
106         long mProviderClassForNameStart;
107         long mProviderClassForNameEnd;
108 
StartupTimestamps()109         StartupTimestamps() {}
110 
111         /** When the overall WebView provider load began. */
112         @UptimeMillisLong
getWebViewLoadStart()113         public long getWebViewLoadStart() {
114             return mWebViewLoadStart;
115         }
116 
117         /** Before creating the WebView APK Context. */
118         @UptimeMillisLong
getCreateContextStart()119         public long getCreateContextStart() {
120             return mCreateContextStart;
121         }
122 
123         /** After creating the WebView APK Context. */
124         @UptimeMillisLong
getCreateContextEnd()125         public long getCreateContextEnd() {
126             return mCreateContextEnd;
127         }
128 
129         /** Before adding WebView assets to AssetManager. */
130         @UptimeMillisLong
getAddAssetsStart()131         public long getAddAssetsStart() {
132             return mAddAssetsStart;
133         }
134 
135         /** After adding WebView assets to AssetManager. */
136         @UptimeMillisLong
getAddAssetsEnd()137         public long getAddAssetsEnd() {
138             return mAddAssetsEnd;
139         }
140 
141         /** Before creating the WebView ClassLoader. */
142         @UptimeMillisLong
getGetClassLoaderStart()143         public long getGetClassLoaderStart() {
144             return mGetClassLoaderStart;
145         }
146 
147         /** After creating the WebView ClassLoader. */
148         @UptimeMillisLong
getGetClassLoaderEnd()149         public long getGetClassLoaderEnd() {
150             return mGetClassLoaderEnd;
151         }
152 
153         /** Before preloading the WebView native library. */
154         @UptimeMillisLong
getNativeLoadStart()155         public long getNativeLoadStart() {
156             return mNativeLoadStart;
157         }
158 
159         /** After preloading the WebView native library. */
160         @UptimeMillisLong
getNativeLoadEnd()161         public long getNativeLoadEnd() {
162             return mNativeLoadEnd;
163         }
164 
165         /** Before looking up the WebView provider class. */
166         @UptimeMillisLong
getProviderClassForNameStart()167         public long getProviderClassForNameStart() {
168             return mProviderClassForNameStart;
169         }
170 
171         /** After looking up the WebView provider class. */
172         @UptimeMillisLong
getProviderClassForNameEnd()173         public long getProviderClassForNameEnd() {
174             return mProviderClassForNameEnd;
175         }
176     }
177 
178     static final StartupTimestamps sTimestamps = new StartupTimestamps();
179 
180     @NonNull
getStartupTimestamps()181     static StartupTimestamps getStartupTimestamps() {
182         return sTimestamps;
183     }
184 
getWebViewPreparationErrorReason(int error)185     private static String getWebViewPreparationErrorReason(int error) {
186         switch (error) {
187             case LIBLOAD_FAILED_WAITING_FOR_RELRO:
188                 return "Time out waiting for Relro files being created";
189             case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES:
190                 return "No WebView installed";
191             case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN:
192                 return "Crashed for unknown reason";
193         }
194         return "Unknown";
195     }
196 
197     static class MissingWebViewPackageException extends Exception {
MissingWebViewPackageException(String message)198         public MissingWebViewPackageException(String message) { super(message); }
MissingWebViewPackageException(Exception e)199         public MissingWebViewPackageException(Exception e) { super(e); }
200     }
201 
isWebViewSupported()202     private static boolean isWebViewSupported() {
203         // No lock; this is a benign race as Boolean's state is final and the PackageManager call
204         // will always return the same value.
205         if (sWebViewSupported == null) {
206             sWebViewSupported = AppGlobals.getInitialApplication().getPackageManager()
207                     .hasSystemFeature(PackageManager.FEATURE_WEBVIEW);
208         }
209         return sWebViewSupported;
210     }
211 
212     /**
213      * @hide
214      */
disableWebView()215     static void disableWebView() {
216         synchronized (sProviderLock) {
217             if (sProviderInstance != null) {
218                 throw new IllegalStateException(
219                         "Can't disable WebView: WebView already initialized");
220             }
221             sWebViewDisabled = true;
222         }
223     }
224 
225     /**
226      * @hide
227      */
setDataDirectorySuffix(String suffix)228     static void setDataDirectorySuffix(String suffix) {
229         synchronized (sProviderLock) {
230             if (sProviderInstance != null) {
231                 throw new IllegalStateException(
232                         "Can't set data directory suffix: WebView already initialized");
233             }
234             if (suffix.indexOf(File.separatorChar) >= 0) {
235                 throw new IllegalArgumentException("Suffix " + suffix
236                                                    + " contains a path separator");
237             }
238             sDataDirectorySuffix = suffix;
239         }
240     }
241 
242     /**
243      * @hide
244      */
getDataDirectorySuffix()245     static String getDataDirectorySuffix() {
246         synchronized (sProviderLock) {
247             return sDataDirectorySuffix;
248         }
249     }
250 
251     /**
252      * @hide
253      */
getWebViewLibrary(ApplicationInfo ai)254     public static String getWebViewLibrary(ApplicationInfo ai) {
255         if (ai.metaData != null)
256             return ai.metaData.getString("com.android.webview.WebViewLibrary");
257         return null;
258     }
259 
getLoadedPackageInfo()260     public static PackageInfo getLoadedPackageInfo() {
261         synchronized (sProviderLock) {
262             return sPackageInfo;
263         }
264     }
265 
266     /**
267      * @hide
268      */
getWebViewProviderClass(ClassLoader clazzLoader)269     public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader)
270             throws ClassNotFoundException {
271         return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
272                 true, clazzLoader);
273     }
274 
275     /**
276      * Load the native library for the given package name if that package
277      * name is the same as the one providing the webview.
278      */
loadWebViewNativeLibraryFromPackage(String packageName, ClassLoader clazzLoader)279     public static int loadWebViewNativeLibraryFromPackage(String packageName,
280                                                           ClassLoader clazzLoader) {
281         if (!isWebViewSupported()) {
282             return LIBLOAD_WRONG_PACKAGE_NAME;
283         }
284 
285         WebViewProviderResponse response = null;
286         try {
287             response = getUpdateService().waitForAndGetProvider();
288         } catch (RemoteException e) {
289             Log.e(LOGTAG, "error waiting for relro creation", e);
290             return LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN;
291         }
292 
293 
294         if (response.status != LIBLOAD_SUCCESS
295                 && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
296             return response.status;
297         }
298         if (!response.packageInfo.packageName.equals(packageName)) {
299             return LIBLOAD_WRONG_PACKAGE_NAME;
300         }
301 
302         PackageManager packageManager = AppGlobals.getInitialApplication().getPackageManager();
303         String libraryFileName;
304         try {
305             PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
306                     PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
307             libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
308         } catch (PackageManager.NameNotFoundException e) {
309             Log.e(LOGTAG, "Couldn't find package " + packageName);
310             return LIBLOAD_WRONG_PACKAGE_NAME;
311         }
312 
313         int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, libraryFileName);
314         // If we failed waiting for relro we want to return that fact even if we successfully
315         // load the relro file.
316         if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
317         return loadNativeRet;
318     }
319 
320     @UnsupportedAppUsage
getProvider()321     static WebViewFactoryProvider getProvider() {
322         synchronized (sProviderLock) {
323             // For now the main purpose of this function (and the factory abstraction) is to keep
324             // us honest and minimize usage of WebView internals when binding the proxy.
325             if (sProviderInstance != null) return sProviderInstance;
326 
327             sTimestamps.mWebViewLoadStart = SystemClock.uptimeMillis();
328             final int uid = android.os.Process.myUid();
329             if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
330                     || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
331                     || uid == android.os.Process.BLUETOOTH_UID) {
332                 throw new UnsupportedOperationException(
333                         "For security reasons, WebView is not allowed in privileged processes");
334             }
335 
336             if (!isWebViewSupported()) {
337                 // Device doesn't support WebView; don't try to load it, just throw.
338                 throw new UnsupportedOperationException();
339             }
340 
341             if (sWebViewDisabled) {
342                 throw new IllegalStateException(
343                         "WebView.disableWebView() was called: WebView is disabled");
344             }
345 
346             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
347             try {
348                 Class<WebViewFactoryProvider> providerClass = getProviderClass();
349                 Method staticFactory = providerClass.getMethod(
350                         CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
351 
352                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
353                 try {
354                     sProviderInstance = (WebViewFactoryProvider)
355                             staticFactory.invoke(null, new WebViewDelegate());
356                     if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
357                     return sProviderInstance;
358                 } finally {
359                     Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
360                 }
361             } catch (Exception e) {
362                 Log.e(LOGTAG, "error instantiating provider", e);
363                 throw new AndroidRuntimeException(e);
364             } finally {
365                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
366             }
367         }
368     }
369 
370     /**
371      * Returns {@code true} if the signatures match, {@code false} otherwise
372      */
signaturesEquals(Signature[] s1, Signature[] s2)373     private static boolean signaturesEquals(Signature[] s1, Signature[] s2) {
374         if (s1 == null) {
375             return s2 == null;
376         }
377         if (s2 == null) return false;
378 
379         ArraySet<Signature> set1 = new ArraySet<>();
380         for(Signature signature : s1) {
381             set1.add(signature);
382         }
383         ArraySet<Signature> set2 = new ArraySet<>();
384         for(Signature signature : s2) {
385             set2.add(signature);
386         }
387         return set1.equals(set2);
388     }
389 
390     // Throws MissingWebViewPackageException on failure
verifyPackageInfo(PackageInfo chosen, PackageInfo toUse)391     private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse)
392             throws MissingWebViewPackageException {
393         if (!chosen.packageName.equals(toUse.packageName)) {
394             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
395                     + "packageName mismatch, expected: "
396                     + chosen.packageName + " actual: " + toUse.packageName);
397         }
398         if (chosen.getLongVersionCode() > toUse.getLongVersionCode()) {
399             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
400                     + "version code is lower than expected: " + chosen.getLongVersionCode()
401                     + " actual: " + toUse.getLongVersionCode());
402         }
403         if (getWebViewLibrary(toUse.applicationInfo) == null) {
404             throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
405                     + toUse.packageName);
406         }
407         if (!signaturesEquals(chosen.signatures, toUse.signatures)) {
408             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
409                     + "signature mismatch");
410         }
411     }
412 
413     @UnsupportedAppUsage
getWebViewContextAndSetProvider()414     private static Context getWebViewContextAndSetProvider() throws MissingWebViewPackageException {
415         Application initialApplication = AppGlobals.getInitialApplication();
416         try {
417             WebViewProviderResponse response = null;
418             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
419                     "WebViewUpdateService.waitForAndGetProvider()");
420             try {
421                 response = getUpdateService().waitForAndGetProvider();
422             } finally {
423                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
424             }
425             if (response.status != LIBLOAD_SUCCESS
426                     && response.status != LIBLOAD_FAILED_WAITING_FOR_RELRO) {
427                 throw new MissingWebViewPackageException("Failed to load WebView provider: "
428                         + getWebViewPreparationErrorReason(response.status));
429             }
430             // Register to be killed before fetching package info - so that we will be
431             // killed if the package info goes out-of-date.
432             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()");
433             try {
434                 ActivityManager.getService().addPackageDependency(
435                         response.packageInfo.packageName);
436             } finally {
437                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
438             }
439             // Fetch package info and verify it against the chosen package
440             PackageInfo newPackageInfo = null;
441             PackageManager pm = initialApplication.getPackageManager();
442             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
443             try {
444                 newPackageInfo = pm.getPackageInfo(
445                     response.packageInfo.packageName,
446                     PackageManager.GET_SHARED_LIBRARY_FILES
447                     | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
448                     // Make sure that we fetch the current provider even if its not
449                     // installed for the current user
450                     | PackageManager.MATCH_UNINSTALLED_PACKAGES
451                     // Fetch signatures for verification
452                     | PackageManager.GET_SIGNATURES
453                     // Get meta-data for meta data flag verification
454                     | PackageManager.GET_META_DATA);
455             } finally {
456                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
457             }
458 
459             // Validate the newly fetched package info, throws MissingWebViewPackageException on
460             // failure
461             verifyPackageInfo(response.packageInfo, newPackageInfo);
462 
463             ApplicationInfo ai = newPackageInfo.applicationInfo;
464 
465             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
466                     "initialApplication.createApplicationContext");
467             sTimestamps.mCreateContextStart = SystemClock.uptimeMillis();
468             try {
469                 // Construct an app context to load the Java code into the current app.
470                 Context webViewContext = initialApplication.createApplicationContext(
471                         ai,
472                         Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
473                 sPackageInfo = newPackageInfo;
474                 return webViewContext;
475             } finally {
476                 sTimestamps.mCreateContextEnd = SystemClock.uptimeMillis();
477                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
478             }
479         } catch (RemoteException | PackageManager.NameNotFoundException e) {
480             throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
481         }
482     }
483 
484     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getProviderClass()485     private static Class<WebViewFactoryProvider> getProviderClass() {
486         Context webViewContext = null;
487         Application initialApplication = AppGlobals.getInitialApplication();
488 
489         try {
490             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
491                     "WebViewFactory.getWebViewContextAndSetProvider()");
492             try {
493                 webViewContext = getWebViewContextAndSetProvider();
494             } finally {
495                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
496             }
497             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
498                     sPackageInfo.versionName + " (code " + sPackageInfo.getLongVersionCode() + ")");
499 
500             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
501             try {
502                 sTimestamps.mAddAssetsStart = SystemClock.uptimeMillis();
503                 for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
504                     initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
505                 }
506                 sTimestamps.mAddAssetsEnd = sTimestamps.mGetClassLoaderStart =
507                         SystemClock.uptimeMillis();
508                 ClassLoader clazzLoader = webViewContext.getClassLoader();
509                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
510                 sTimestamps.mGetClassLoaderEnd = sTimestamps.mNativeLoadStart =
511                         SystemClock.uptimeMillis();
512                 WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
513                         getWebViewLibrary(sPackageInfo.applicationInfo));
514                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
515                 Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
516                 sTimestamps.mNativeLoadEnd = sTimestamps.mProviderClassForNameStart =
517                         SystemClock.uptimeMillis();
518                 try {
519                     return getWebViewProviderClass(clazzLoader);
520                 } finally {
521                     sTimestamps.mProviderClassForNameEnd = SystemClock.uptimeMillis();
522                     Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
523                 }
524             } catch (ClassNotFoundException e) {
525                 Log.e(LOGTAG, "error loading provider", e);
526                 throw new AndroidRuntimeException(e);
527             } finally {
528                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
529             }
530         } catch (MissingWebViewPackageException e) {
531             Log.e(LOGTAG, "Chromium WebView package does not exist", e);
532             throw new AndroidRuntimeException(e);
533         }
534     }
535 
536     /**
537      * Perform any WebView loading preparations that must happen in the zygote.
538      * Currently, this means allocating address space to load the real JNI library later.
539      */
prepareWebViewInZygote()540     public static void prepareWebViewInZygote() {
541         try {
542             WebViewLibraryLoader.reserveAddressSpaceInZygote();
543         } catch (Throwable t) {
544             // Log and discard errors at this stage as we must not crash the zygote.
545             Log.e(LOGTAG, "error preparing native loader", t);
546         }
547     }
548 
549     /**
550      * @hide
551      */
onWebViewProviderChanged(PackageInfo packageInfo)552     public static int onWebViewProviderChanged(PackageInfo packageInfo) {
553         int startedRelroProcesses = 0;
554         try {
555             startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo);
556         } catch (Throwable t) {
557             // Log and discard errors at this stage as we must not crash the system server.
558             Log.e(LOGTAG, "error preparing webview native library", t);
559         }
560 
561         WebViewZygote.onWebViewProviderChanged(packageInfo);
562 
563         return startedRelroProcesses;
564     }
565 
566     private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
567 
568     /** @hide */
569     @UnsupportedAppUsage
getUpdateService()570     public static IWebViewUpdateService getUpdateService() {
571         if (isWebViewSupported()) {
572             return getUpdateServiceUnchecked();
573         } else {
574             return null;
575         }
576     }
577 
578     /** @hide */
getUpdateServiceUnchecked()579     static IWebViewUpdateService getUpdateServiceUnchecked() {
580         return IWebViewUpdateService.Stub.asInterface(
581                 ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
582     }
583 }
584