WebViewGlueCommunicator.java
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.webkit.internal;
import android.os.Build;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import org.chromium.support_lib_boundary.WebViewProviderFactoryBoundaryInterface;
import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Utility class for calling into the WebView APK.
*/
public class WebViewGlueCommunicator {
private static final String GLUE_FACTORY_PROVIDER_FETCHER_CLASS =
"org.chromium.support_lib_glue.SupportLibReflectionUtil";
private static final String GLUE_FACTORY_PROVIDER_FETCHER_METHOD =
"createWebViewProviderFactory";
/**
* Fetch the one global support library WebViewProviderFactory from the WebView glue layer.
*/
@NonNull
public static WebViewProviderFactory getFactory() {
return LAZY_FACTORY_HOLDER.INSTANCE;
}
@NonNull
public static WebkitToCompatConverter getCompatConverter() {
return LAZY_COMPAT_CONVERTER_HOLDER.INSTANCE;
}
private static class LAZY_FACTORY_HOLDER {
static final WebViewProviderFactory INSTANCE =
WebViewGlueCommunicator.createGlueProviderFactory();
}
private static class LAZY_COMPAT_CONVERTER_HOLDER {
static final WebkitToCompatConverter INSTANCE = new WebkitToCompatConverter(
WebViewGlueCommunicator.getFactory().getWebkitToCompatConverter());
}
private static InvocationHandler fetchGlueProviderFactoryImpl() throws IllegalAccessException,
InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
Class<?> glueFactoryProviderFetcherClass = Class.forName(
GLUE_FACTORY_PROVIDER_FETCHER_CLASS, false, getWebViewClassLoader());
Method createProviderFactoryMethod = glueFactoryProviderFetcherClass.getDeclaredMethod(
GLUE_FACTORY_PROVIDER_FETCHER_METHOD);
return (InvocationHandler) createProviderFactoryMethod.invoke(null);
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
@NonNull
static WebViewProviderFactory createGlueProviderFactory() {
// We do not support pre-L devices since their WebView APKs cannot be updated.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return new IncompatibleApkWebViewProviderFactory();
}
InvocationHandler invocationHandler;
try {
invocationHandler = fetchGlueProviderFactoryImpl();
// The only way we should fail to fetch the provider-factory is if the class we are
// calling into doesn't exist - any other kind of failure is unexpected and should cause
// a run-time exception.
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
// If WebView APK support library glue entry point doesn't exist then return a Provider
// factory that declares that there are no features available.
return new IncompatibleApkWebViewProviderFactory();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
return new WebViewProviderFactoryAdapter(BoundaryInterfaceReflectionUtil.castToSuppLibClass(
WebViewProviderFactoryBoundaryInterface.class, invocationHandler));
}
/**
* Load the WebView code from the WebView APK and return the classloader containing that code.
*/
@NonNull
public static ClassLoader getWebViewClassLoader() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return ApiHelperForP.getWebViewClassLoader();
} else {
return getWebViewProviderFactory().getClass().getClassLoader();
}
}
@SuppressWarnings({"JavaReflectionMemberAccess", "PrivateApi"})
private static Object getWebViewProviderFactory() {
try {
Method getFactoryMethod = WebView.class.getDeclaredMethod("getFactory");
getFactoryMethod.setAccessible(true);
return getFactoryMethod.invoke(null);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private WebViewGlueCommunicator() {
}
}