1 /* 2 * Copyright 2018 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 androidx.webkit.internal; 18 19 import android.os.Build; 20 import android.webkit.WebView; 21 22 import androidx.core.os.BuildCompat; 23 24 import org.chromium.support_lib_boundary.WebViewProviderFactoryBoundaryInterface; 25 import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil; 26 27 import java.lang.reflect.InvocationHandler; 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 31 /** 32 * Utility class for calling into the WebView APK. 33 */ 34 public class WebViewGlueCommunicator { 35 private static final String GLUE_FACTORY_PROVIDER_FETCHER_CLASS = 36 "org.chromium.support_lib_glue.SupportLibReflectionUtil"; 37 private static final String GLUE_FACTORY_PROVIDER_FETCHER_METHOD = 38 "createWebViewProviderFactory"; 39 40 /** 41 * Fetch the one global support library WebViewProviderFactory from the WebView glue layer. 42 */ 43 public static WebViewProviderFactory getFactory() { 44 return LAZY_FACTORY_HOLDER.INSTANCE; 45 } 46 47 public static WebkitToCompatConverter getCompatConverter() { 48 return LAZY_COMPAT_CONVERTER_HOLDER.INSTANCE; 49 } 50 51 private static class LAZY_FACTORY_HOLDER { 52 private static final WebViewProviderFactory INSTANCE = 53 WebViewGlueCommunicator.createGlueProviderFactory(); 54 } 55 56 private static class LAZY_COMPAT_CONVERTER_HOLDER { 57 static final WebkitToCompatConverter INSTANCE = new WebkitToCompatConverter( 58 WebViewGlueCommunicator.getFactory().getWebkitToCompatConverter()); 59 } 60 61 private static InvocationHandler fetchGlueProviderFactoryImpl() throws IllegalAccessException, 62 InvocationTargetException, ClassNotFoundException, NoSuchMethodException { 63 Class<?> glueFactoryProviderFetcherClass = Class.forName( 64 GLUE_FACTORY_PROVIDER_FETCHER_CLASS, false, getWebViewClassLoader()); 65 Method createProviderFactoryMethod = glueFactoryProviderFetcherClass.getDeclaredMethod( 66 GLUE_FACTORY_PROVIDER_FETCHER_METHOD); 67 return (InvocationHandler) createProviderFactoryMethod.invoke(null); 68 } 69 70 private static WebViewProviderFactory createGlueProviderFactory() { 71 // We do not support pre-L devices since their WebView APKs cannot be updated. 72 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 73 return new IncompatibleApkWebViewProviderFactory(); 74 } 75 InvocationHandler invocationHandler; 76 try { 77 invocationHandler = fetchGlueProviderFactoryImpl(); 78 // The only way we should fail to fetch the provider-factory is if the class we are 79 // calling into doesn't exist - any other kind of failure is unexpected and should cause 80 // a run-time exception. 81 } catch (IllegalAccessException e) { 82 throw new RuntimeException(e); 83 } catch (InvocationTargetException e) { 84 throw new RuntimeException(e); 85 } catch (ClassNotFoundException e) { 86 // If WebView APK support library glue entry point doesn't exist then return a Provider 87 // factory that declares that there are no features available. 88 return new IncompatibleApkWebViewProviderFactory(); 89 } catch (NoSuchMethodException e) { 90 throw new RuntimeException(e); 91 } 92 return new WebViewProviderFactoryAdapter(BoundaryInterfaceReflectionUtil.castToSuppLibClass( 93 WebViewProviderFactoryBoundaryInterface.class, invocationHandler)); 94 } 95 96 /** 97 * Load the WebView code from the WebView APK and return the classloader containing that code. 98 */ 99 @SuppressWarnings("NewApi") 100 public static ClassLoader getWebViewClassLoader() { 101 if (BuildCompat.isAtLeastP()) { 102 return WebView.getWebViewClassLoader(); 103 } else { 104 return getWebViewProviderFactory().getClass().getClassLoader(); 105 } 106 } 107 108 private static Object getWebViewProviderFactory() { 109 try { 110 Method getFactoryMethod = WebView.class.getDeclaredMethod("getFactory"); 111 getFactoryMethod.setAccessible(true); 112 return getFactoryMethod.invoke(null); 113 } catch (NoSuchMethodException e) { 114 throw new RuntimeException(e); 115 } catch (InvocationTargetException e) { 116 throw new RuntimeException(e); 117 } catch (IllegalAccessException e) { 118 throw new RuntimeException(e); 119 } 120 } 121 122 private WebViewGlueCommunicator() { 123 } 124 } 125