1package com.android.webview.chromium; 2 3import android.app.Application; 4import android.content.Context; 5import android.content.pm.PackageInfo; 6import android.content.res.AssetManager; 7import android.content.res.Resources; 8import android.graphics.Canvas; 9import android.os.Trace; 10import android.util.Log; 11import android.util.SparseArray; 12import android.view.View; 13import android.webkit.WebViewFactory; 14 15import java.lang.reflect.InvocationTargetException; 16import java.lang.reflect.Method; 17 18/** 19 * Factory class for {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate}s. 20 * 21 * <p>{@link WebViewDelegate com.android.webview.chromium.WebViewDelegate}s provide the same 22 * interface as {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} but without 23 * a dependency on the webkit class. Defining our own 24 * {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} in frameworks/webview 25 * allows the WebView apk to be binary compatible with the API 21 version of the framework, in 26 * which {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been 27 * introduced. 28 * 29 * <p>The {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} interface and this 30 * factory class can be removed once we don't longer need to support WebView apk updates to devices 31 * running the API 21 version of the framework. At that point, we should use 32 * {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} directly instead. 33 */ 34class WebViewDelegateFactory { 35 36 /** 37 * Copy of {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}'s interface. 38 * See {@link WebViewDelegateFactory} for the reasons why this copy is needed. 39 */ 40 interface WebViewDelegate { 41 /** @see android.webkit.WebViewDelegate.OnTraceEnabledChangeListener */ 42 interface OnTraceEnabledChangeListener { 43 void onTraceEnabledChange(boolean enabled); 44 } 45 46 /** @see android.webkit.WebViewDelegate#setOnTraceEnabledChangeListener */ 47 void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener); 48 49 /** @see android.webkit.WebViewDelegate#isTraceTagEnabled */ 50 boolean isTraceTagEnabled(); 51 52 /** @see android.webkit.WebViewDelegate#canInvokeDrawGlFunctor */ 53 boolean canInvokeDrawGlFunctor(View containerView); 54 55 /** @see android.webkit.WebViewDelegate#invokeDrawGlFunctor */ 56 void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor, 57 boolean waitForCompletion); 58 59 /** @see android.webkit.WebViewDelegate#callDrawGlFunction */ 60 void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor); 61 62 /** @see android.webkit.WebViewDelegate#detachDrawGlFunctor */ 63 void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor); 64 65 /** @see android.webkit.WebViewDelegate#getPackageId */ 66 int getPackageId(Resources resources, String packageName); 67 68 /** @see android.webkit.WebViewDelegate#getApplication */ 69 Application getApplication(); 70 71 /** @see android.webkit.WebViewDelegate#getErrorString */ 72 String getErrorString(Context context, int errorCode); 73 74 /** @see android.webkit.WebViewDelegate#addWebViewAssetPath */ 75 void addWebViewAssetPath(Context context); 76 } 77 78 /** 79 * Creates a {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} that proxies 80 * requests to the given {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}. 81 * 82 * @return the created delegate 83 */ 84 static WebViewDelegate createProxyDelegate(android.webkit.WebViewDelegate delegate) { 85 return new ProxyDelegate(delegate); 86 } 87 88 /** 89 * Creates a {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} compatible 90 * with the API 21 version of the framework in which 91 * {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been 92 * introduced. 93 * 94 * @return the created delegate 95 */ 96 static WebViewDelegate createApi21CompatibilityDelegate() { 97 return new Api21CompatibilityDelegate(); 98 } 99 100 /** 101 * A {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} that proxies requests 102 * to a {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate}. 103 */ 104 private static class ProxyDelegate implements WebViewDelegate { 105 106 android.webkit.WebViewDelegate delegate; 107 108 ProxyDelegate(android.webkit.WebViewDelegate delegate) { 109 this.delegate = delegate; 110 } 111 112 @Override 113 public void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener) { 114 delegate.setOnTraceEnabledChangeListener( 115 new android.webkit.WebViewDelegate.OnTraceEnabledChangeListener() { 116 @Override 117 public void onTraceEnabledChange(boolean enabled) { 118 listener.onTraceEnabledChange(enabled); 119 ; 120 } 121 }); 122 } 123 124 @Override 125 public boolean isTraceTagEnabled() { 126 return delegate.isTraceTagEnabled(); 127 } 128 129 @Override 130 public boolean canInvokeDrawGlFunctor(View containerView) { 131 return delegate.canInvokeDrawGlFunctor(containerView); 132 } 133 134 @Override 135 public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor, 136 boolean waitForCompletion) { 137 delegate.invokeDrawGlFunctor(containerView, nativeDrawGLFunctor, waitForCompletion); 138 } 139 140 @Override 141 public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) { 142 delegate.callDrawGlFunction(canvas, nativeDrawGLFunctor); 143 } 144 145 @Override 146 public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) { 147 delegate.detachDrawGlFunctor(containerView, nativeDrawGLFunctor); 148 } 149 150 @Override 151 public int getPackageId(Resources resources, String packageName) { 152 return delegate.getPackageId(resources, packageName); 153 } 154 155 @Override 156 public Application getApplication() { 157 return delegate.getApplication(); 158 } 159 160 @Override 161 public String getErrorString(Context context, int errorCode) { 162 return delegate.getErrorString(context, errorCode); 163 } 164 165 @Override 166 public void addWebViewAssetPath(Context context) { 167 delegate.addWebViewAssetPath(context); 168 } 169 } 170 171 /** 172 * A {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} compatible with the 173 * API 21 version of the framework in which 174 * {@link android.webkit.WebViewDelegate android.webkit.WebViewDelegate} had not yet been 175 * introduced. 176 * 177 * <p>This class implements the 178 * {@link WebViewDelegate com.android.webview.chromium.WebViewDelegate} functionality by using 179 * reflection to call into hidden frameworks APIs released in the API-21 version of the 180 * framework. 181 */ 182 private static class Api21CompatibilityDelegate implements WebViewDelegate { 183 /** Copy of Trace.TRACE_TAG_WEBVIEW */ 184 private final static long TRACE_TAG_WEBVIEW = 1L << 4; 185 186 /** Hidden APIs released in the API 21 version of the framework */ 187 private final Method mIsTagEnabledMethod; 188 private final Method mAddChangeCallbackMethod; 189 private final Method mGetViewRootImplMethod; 190 private final Method mInvokeFunctorMethod; 191 private final Method mCallDrawGLFunctionMethod; 192 private final Method mDetachFunctorMethod; 193 private final Method mGetAssignedPackageIdentifiersMethod; 194 private final Method mAddAssetPathMethod; 195 private final Method mCurrentApplicationMethod; 196 private final Method mGetStringMethod; 197 private final Method mGetLoadedPackageInfoMethod; 198 199 Api21CompatibilityDelegate() { 200 try { 201 // Important: This reflection essentially defines a snapshot of some hidden APIs 202 // at version 21 of the framework for compatibility reasons, and the reflection 203 // should not be changed even if those hidden APIs change in future releases. 204 mIsTagEnabledMethod = Trace.class.getMethod("isTagEnabled", long.class); 205 mAddChangeCallbackMethod = Class.forName("android.os.SystemProperties") 206 .getMethod("addChangeCallback", Runnable.class); 207 mGetViewRootImplMethod = View.class.getMethod("getViewRootImpl"); 208 mInvokeFunctorMethod = Class.forName("android.view.ViewRootImpl") 209 .getMethod("invokeFunctor", long.class, boolean.class); 210 mDetachFunctorMethod = Class.forName("android.view.ViewRootImpl") 211 .getMethod("detachFunctor", long.class); 212 mCallDrawGLFunctionMethod = Class.forName("android.view.HardwareCanvas") 213 .getMethod("callDrawGLFunction", long.class); 214 mGetAssignedPackageIdentifiersMethod = AssetManager.class.getMethod( 215 "getAssignedPackageIdentifiers"); 216 mAddAssetPathMethod = AssetManager.class.getMethod( 217 "addAssetPath", String.class); 218 mCurrentApplicationMethod = Class.forName("android.app.ActivityThread") 219 .getMethod("currentApplication"); 220 mGetStringMethod = Class.forName("android.net.http.ErrorStrings") 221 .getMethod("getString", int.class, Context.class); 222 mGetLoadedPackageInfoMethod = Class.forName("android.webkit.WebViewFactory") 223 .getMethod("getLoadedPackageInfo"); 224 } catch (Exception e) { 225 throw new RuntimeException("Invalid reflection", e); 226 } 227 } 228 229 @Override 230 public void setOnTraceEnabledChangeListener(final OnTraceEnabledChangeListener listener) { 231 try { 232 mAddChangeCallbackMethod.invoke(null, new Runnable() { 233 @Override 234 public void run() { 235 listener.onTraceEnabledChange(isTraceTagEnabled()); 236 } 237 }); 238 } catch (Exception e) { 239 throw new RuntimeException("Invalid reflection", e); 240 } 241 } 242 243 @Override 244 public boolean isTraceTagEnabled() { 245 try { 246 return ((Boolean) mIsTagEnabledMethod.invoke(null, TRACE_TAG_WEBVIEW)); 247 } catch (Exception e) { 248 throw new RuntimeException("Invalid reflection", e); 249 } 250 } 251 252 @Override 253 public boolean canInvokeDrawGlFunctor(View containerView) { 254 try { 255 Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView); 256 // viewRootImpl can be null during teardown when window is leaked. 257 return viewRootImpl != null; 258 } catch (Exception e) { 259 throw new RuntimeException("Invalid reflection", e); 260 } 261 } 262 263 @Override 264 public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor, 265 boolean waitForCompletion) { 266 try { 267 Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView); 268 if (viewRootImpl != null) { 269 mInvokeFunctorMethod.invoke(viewRootImpl, nativeDrawGLFunctor, waitForCompletion); 270 } 271 } catch (Exception e) { 272 throw new RuntimeException("Invalid reflection", e); 273 } 274 } 275 276 @Override 277 public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) { 278 try { 279 mCallDrawGLFunctionMethod.invoke(canvas, nativeDrawGLFunctor); 280 } catch (Exception e) { 281 throw new RuntimeException("Invalid reflection", e); 282 } 283 } 284 285 @Override 286 public void detachDrawGlFunctor(View containerView, long nativeDrawGLFunctor) { 287 try { 288 Object viewRootImpl = mGetViewRootImplMethod.invoke(containerView); 289 if (viewRootImpl != null) { 290 mDetachFunctorMethod.invoke(viewRootImpl, nativeDrawGLFunctor); 291 } 292 } catch (Exception e) { 293 throw new RuntimeException("Invalid reflection", e); 294 } 295 } 296 297 @Override 298 public int getPackageId(Resources resources, String packageName) { 299 try { 300 SparseArray packageIdentifiers = 301 (SparseArray) mGetAssignedPackageIdentifiersMethod.invoke( 302 resources.getAssets()); 303 for (int i = 0; i < packageIdentifiers.size(); i++) { 304 final String name = (String) packageIdentifiers.valueAt(i); 305 306 if (packageName.equals(name)) { 307 return packageIdentifiers.keyAt(i); 308 } 309 } 310 } catch (Exception e) { 311 throw new RuntimeException("Invalid reflection", e); 312 } 313 throw new RuntimeException("Package not found: " + packageName); 314 } 315 316 @Override 317 public Application getApplication() { 318 try { 319 return (Application) mCurrentApplicationMethod.invoke(null); 320 } catch (Exception e) { 321 throw new RuntimeException("Invalid reflection", e); 322 } 323 } 324 325 @Override 326 public String getErrorString(Context context, int errorCode) { 327 try { 328 return (String) mGetStringMethod.invoke(null, errorCode, context); 329 } catch (Exception e) { 330 throw new RuntimeException("Invalid reflection", e); 331 } 332 } 333 334 @Override 335 public void addWebViewAssetPath(Context context) { 336 try { 337 PackageInfo info = (PackageInfo) mGetLoadedPackageInfoMethod.invoke(null); 338 mAddAssetPathMethod.invoke(context.getAssets(), info.applicationInfo.sourceDir); 339 } catch (Exception e) { 340 throw new RuntimeException("Invalid reflection", e); 341 } 342 } 343 } 344} 345 346