Home | History | Annotate | Download | only in internal
      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