blob: 2bf6f1479d7a52e3a94ca30f105a081536899e22 [file] [log] [blame]
package com.android.webview.chromium;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.os.Trace;
import android.util.SparseArray;
import android.view.View;
import java.lang.reflect.Method;
/**
* Factory class for {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate}s.
*
* <p>{@link WebViewDelegate com.android.webview.chromium.WebViewDelegate}s provide the same
* interface as {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} but without
* a dependency on the webkit class. Defining our own
* {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} in frameworks/webview
* allows the WebView apk to be binary compatible with the API 21 version of the framework, in
* which {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been
* introduced.
*
* <p>The {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} interface and this
* factory class can be removed once we don't longer need to support WebView apk updates to devices
* running the API 21 version of the framework. At that point, we should use
* {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} directly instead.
*/
class WebViewDelegateFactory {
/**
* Copy of {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}'s interface.
* See {@link WebViewDelegateFactory} for the reasons why this copy is needed.
*/
interface WebViewDelegate {
/** @see android.webkit.WebViewDelegate.OnTraceEnabledChangeListener */
interface OnTraceEnabledChangeListener {
void onTraceEnabledChange(boolean enabled);
}
/** @see android.webkit.WebViewDelegate#setOnTraceEnabledChangeListener */
void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener);
/** @see android.webkit.WebViewDelegate#isTraceTagEnabled */
boolean isTraceTagEnabled();
/** @see android.webkit.WebViewDelegate#canInvokeDrawGlFunctor */
boolean canInvokeDrawGlFunctor(View containerView);
/** @see android.webkit.WebViewDelegate#invokeDrawGlFunctor */
void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
boolean waitForCompletion);
/** @see android.webkit.WebViewDelegate#callDrawGlFunction */
void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor);
/** @see android.webkit.WebViewDelegate#detachDrawGlFunctor */
void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor);
/** @see android.webkit.WebViewDelegate#getPackageId */
int getPackageId(Resources resources, String packageName);
/** @see android.webkit.WebViewDelegate#getApplication */
Application getApplication();
/** @see android.webkit.WebViewDelegate#getErrorString */
String getErrorString(Context context, int errorCode);
/** @see android.webkit.WebViewDelegate#addWebViewAssetPath */
void addWebViewAssetPath(Context context);
}
/**
* Creates a {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} that proxies
* requests to the given {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}.
*
* @return the created delegate
*/
static WebViewDelegate createProxyDelegate(android.webkit.WebViewDelegate delegate) {
return new ProxyDelegate(delegate);
}
/**
* Creates a {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} compatible
* with the API 21 version of the framework in which
* {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been
* introduced.
*
* @return the created delegate
*/
static WebViewDelegate createApi21CompatibilityDelegate() {
return new Api21CompatibilityDelegate();
}
/**
* A {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} that proxies requests
* to a {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}.
*/
private static class ProxyDelegate implements WebViewDelegate {
android.webkit.WebViewDelegate delegate;
ProxyDelegate(android.webkit.WebViewDelegate delegate) {
this.delegate = delegate;
}
@Override
public void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener) {
delegate.setOnTraceEnabledChangeListener(
new android.webkit.WebViewDelegate.OnTraceEnabledChangeListener() {
@Override
public void onTraceEnabledChange(boolean enabled) {
listener.onTraceEnabledChange(enabled);
;
}
});
}
@Override
public boolean isTraceTagEnabled() {
return delegate.isTraceTagEnabled();
}
@Override
public boolean canInvokeDrawGlFunctor(View containerView) {
return delegate.canInvokeDrawGlFunctor(containerView);
}
@Override
public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
boolean waitForCompletion) {
delegate.invokeDrawGlFunctor(containerView, nativeDrawGLFunctor, waitForCompletion);
}
@Override
public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
delegate.callDrawGlFunction(canvas, nativeDrawGLFunctor);
}
@Override
public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) {
delegate.detachDrawGlFunctor(containerView, nativeDrawGLFunctor);
}
@Override
public int getPackageId(Resources resources, String packageName) {
return delegate.getPackageId(resources, packageName);
}
@Override
public Application getApplication() {
return delegate.getApplication();
}
@Override
public String getErrorString(Context context, int errorCode) {
return delegate.getErrorString(context, errorCode);
}
@Override
public void addWebViewAssetPath(Context context) {
delegate.addWebViewAssetPath(context);
}
}
/**
* A {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} compatible with the
* API 21 version of the framework in which
* {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been
* introduced.
*
* <p>This class implements the
* {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} functionality by using
* reflection to call into hidden frameworks APIs released in the API-21 version of the
* framework.
*/
private static class Api21CompatibilityDelegate implements WebViewDelegate {
/** Copy of Trace.TRACE_TAG_WEBVIEW */
private final static long TRACE_TAG_WEBVIEW = 1L << 4;
/** Hidden APIs released in the API 21 version of the framework */
private final Method mIsTagEnabledMethod;
private final Method mAddChangeCallbackMethod;
private final Method mGetViewRootImplMethod;
private final Method mInvokeFunctorMethod;
private final Method mCallDrawGLFunctionMethod;
private final Method mDetachFunctorMethod;
private final Method mGetAssignedPackageIdentifiersMethod;
private final Method mAddAssetPathMethod;
private final Method mCurrentApplicationMethod;
private final Method mGetStringMethod;
private final Method mGetLoadedPackageInfoMethod;
Api21CompatibilityDelegate() {
try {
// Important: This reflection essentially defines a snapshot of some hidden APIs
// at version 21 of the framework for compatibility reasons, and the reflection
// should not be changed even if those hidden APIs change in future releases.
mIsTagEnabledMethod = Trace.class.getMethod("isTagEnabled", long.class);
mAddChangeCallbackMethod = Class.forName("android.os.SystemProperties")
.getMethod("addChangeCallback", Runnable.class);
mGetViewRootImplMethod = View.class.getMethod("getViewRootImpl");
mInvokeFunctorMethod = Class.forName("android.view.ViewRootImpl")
.getMethod("invokeFunctor", long.class, boolean.class);
mDetachFunctorMethod = Class.forName("android.view.ViewRootImpl")
.getMethod("detachFunctor", long.class);
mCallDrawGLFunctionMethod = Class.forName("android.view.HardwareCanvas")
.getMethod("callDrawGLFunction", long.class);
mGetAssignedPackageIdentifiersMethod = AssetManager.class.getMethod(
"getAssignedPackageIdentifiers");
mAddAssetPathMethod = AssetManager.class.getMethod(
"addAssetPath", String.class);
mCurrentApplicationMethod = Class.forName("android.app.ActivityThread")
.getMethod("currentApplication");
mGetStringMethod = Class.forName("android.net.http.ErrorStrings")
.getMethod("getString", int.class, Context.class);
mGetLoadedPackageInfoMethod = Class.forName("android.webkit.WebViewFactory")
.getMethod("getLoadedPackageInfo");
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener) {
try {
mAddChangeCallbackMethod.invoke(null, new Runnable() {
@Override
public void run() {
listener.onTraceEnabledChange(isTraceTagEnabled());
}
});
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public boolean isTraceTagEnabled() {
try {
return ((Boolean) mIsTagEnabledMethod.invoke(null, TRACE_TAG_WEBVIEW));
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public boolean canInvokeDrawGlFunctor(View containerView) {
try {
Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView);
// viewRootImpl can be null during teardown when window is leaked.
return viewRootImpl != null;
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
boolean waitForCompletion) {
try {
Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView);
if (viewRootImpl != null) {
mInvokeFunctorMethod.invoke(viewRootImpl, nativeDrawGLFunctor, waitForCompletion);
}
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
try {
mCallDrawGLFunctionMethod.invoke(canvas, nativeDrawGLFunctor);
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) {
try {
Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView);
if (viewRootImpl != null) {
mDetachFunctorMethod.invoke(viewRootImpl, nativeDrawGLFunctor);
}
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public int getPackageId(Resources resources, String packageName) {
try {
SparseArray packageIdentifiers =
(SparseArray) mGetAssignedPackageIdentifiersMethod.invoke(
resources.getAssets());
for (int i = 0; i < packageIdentifiers.size(); i++) {
final String name = (String) packageIdentifiers.valueAt(i);
if (packageName.equals(name)) {
return packageIdentifiers.keyAt(i);
}
}
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
throw new RuntimeException("Package not found: " + packageName);
}
@Override
public Application getApplication() {
try {
return (Application) mCurrentApplicationMethod.invoke(null);
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public String getErrorString(Context context, int errorCode) {
try {
return (String) mGetStringMethod.invoke(null, errorCode, context);
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
@Override
public void addWebViewAssetPath(Context context) {
try {
PackageInfo info = (PackageInfo) mGetLoadedPackageInfoMethod.invoke(null);
mAddAssetPathMethod.invoke(context.getAssets(), info.applicationInfo.sourceDir);
} catch (Exception e) {
throw new RuntimeException("Invalid reflection", e);
}
}
}
}