1 /* 2 * Copyright (C) 2008 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 com.android.tools.layoutlib.create; 18 19 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 20 import com.android.tools.layoutlib.java.LinkedHashMap_Delegate; 21 import com.android.tools.layoutlib.java.Reference_Delegate; 22 23 import org.objectweb.asm.Opcodes; 24 import org.objectweb.asm.Type; 25 26 import java.lang.ref.Reference; 27 import java.lang.ref.WeakReference; 28 import java.util.Arrays; 29 import java.util.HashSet; 30 import java.util.LinkedHashMap; 31 import java.util.Locale; 32 import java.util.Map; 33 import java.util.Set; 34 35 /** 36 * Describes the work to be done by {@link AsmGenerator}. 37 */ 38 public final class CreateInfo implements ICreateInfo { 39 40 @Override getMethodReplacers()41 public MethodReplacer[] getMethodReplacers() { 42 return METHOD_REPLACERS; 43 } 44 45 @Override getInjectedClasses()46 public Class<?>[] getInjectedClasses() { 47 return INJECTED_CLASSES; 48 } 49 50 @Override getDelegateMethods()51 public String[] getDelegateMethods() { 52 return DELEGATE_METHODS; 53 } 54 55 @Override getDelegateClassNatives()56 public String[] getDelegateClassNatives() { 57 return DELEGATE_CLASS_NATIVES; 58 } 59 60 @Override getDelegateClassNativesToNatives()61 public String[] getDelegateClassNativesToNatives() { 62 return DELEGATE_CLASS_NATIVES_TO_NATIVES; 63 } 64 65 @Override shouldKeepAllNativeClasses()66 public boolean shouldKeepAllNativeClasses() { 67 return false; 68 } 69 70 @Override getKeepClassNatives()71 public String[] getKeepClassNatives() { 72 return KEEP_CLASS_NATIVES; 73 } 74 75 @Override getRenamedClasses()76 public String[] getRenamedClasses() { 77 return RENAMED_CLASSES; 78 } 79 80 @Override getDeleteReturns()81 public String[] getDeleteReturns() { 82 return DELETE_RETURNS; 83 } 84 85 @Override getJavaPkgClasses()86 public String[] getJavaPkgClasses() { 87 return JAVA_PKG_CLASSES; 88 } 89 90 @Override getRefactoredClasses()91 public String[] getRefactoredClasses() { 92 return REFACTOR_CLASSES; 93 } 94 95 @Override getExcludedClasses()96 public String[] getExcludedClasses() { 97 String[] refactoredClasses = getJavaPkgClasses(); 98 int count = refactoredClasses.length / 2 + EXCLUDED_CLASSES.length; 99 Set<String> excludedClasses = new HashSet<>(count); 100 for (int i = 0; i < refactoredClasses.length; i+=2) { 101 excludedClasses.add(refactoredClasses[i]); 102 } 103 excludedClasses.addAll(Arrays.asList(EXCLUDED_CLASSES)); 104 return excludedClasses.toArray(new String[0]); 105 } 106 107 @Override getPromotedFields()108 public String[] getPromotedFields() { 109 return PROMOTED_FIELDS; 110 } 111 112 @Override getPromotedMethods()113 public String[] getPromotedMethods() { 114 return PROMOTED_METHODS; 115 } 116 117 @Override getPromotedClasses()118 public String[] getPromotedClasses() { 119 return PROMOTED_CLASSES; 120 } 121 122 @Override getInjectedMethodsMap()123 public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { 124 return INJECTED_METHODS; 125 } 126 127 @Override getDeferredStaticInitializerClasses()128 public String[] getDeferredStaticInitializerClasses() { 129 return DEFERRED_STATIC_INITIALIZER_CLASSES; 130 } 131 132 //----- 133 134 private static final MethodReplacer[] METHOD_REPLACERS = new MethodReplacer[] { 135 new SystemLoadLibraryReplacer(), 136 new SystemArrayCopyReplacer(), 137 new LocaleGetDefaultReplacer(), 138 new SystemLogReplacer(), 139 new SystemNanoTimeReplacer(), 140 new SystemCurrentTimeMillisReplacer(), 141 new LinkedHashMapEldestReplacer(), 142 new ContextGetClassLoaderReplacer(), 143 new ImageReaderNativeInitReplacer(), 144 new NativeInitPathReplacer(), 145 new AdaptiveIconMaskReplacer(), 146 new ActivityThreadInAnimationReplacer(), 147 new ReferenceRefersToReplacer(), 148 new HtmlApplicationResourceReplacer(), 149 }; 150 151 /** 152 * The list of class from layoutlib_create to inject in layoutlib. 153 */ 154 private final static Class<?>[] INJECTED_CLASSES = new Class<?>[] { 155 OverrideMethod.class, 156 MethodListener.class, 157 MethodAdapter.class, 158 ICreateInfo.class, 159 CreateInfo.class, 160 LayoutlibDelegate.class, 161 InjectMethodRunnable.class, 162 InjectMethodRunnables.class, 163 /* Java package classes */ 164 LinkedHashMap_Delegate.class, 165 Reference_Delegate.class, 166 }; 167 168 /** 169 * The list of methods to rewrite as delegates. 170 */ 171 public final static String[] DELEGATE_METHODS = NativeConfig.DELEGATE_METHODS; 172 173 /** 174 * The list of classes on which to delegate all native methods. 175 */ 176 public final static String[] DELEGATE_CLASS_NATIVES = NativeConfig.DELEGATE_CLASS_NATIVES; 177 178 public final static String[] DELEGATE_CLASS_NATIVES_TO_NATIVES = new String[] {}; 179 180 /** 181 * The list of classes on which NOT to delegate any native method. 182 */ 183 public final static String[] KEEP_CLASS_NATIVES = new String[] { 184 "android.animation.PropertyValuesHolder", 185 "android.content.res.StringBlock", 186 "android.content.res.XmlBlock", 187 "android.graphics.BaseCanvas", 188 "android.graphics.BaseRecordingCanvas", 189 "android.graphics.Bitmap", 190 "android.graphics.BitmapFactory", 191 "android.graphics.BitmapShader", 192 "android.graphics.BlendModeColorFilter", 193 "android.graphics.BlurMaskFilter", 194 "android.graphics.BlurShader", 195 "android.graphics.Camera", 196 "android.graphics.Canvas", 197 "android.graphics.CanvasProperty", 198 "android.graphics.Color", 199 "android.graphics.ColorFilter", 200 "android.graphics.ColorMatrixColorFilter", 201 "android.graphics.ColorSpace$Rgb", 202 "android.graphics.ComposePathEffect", 203 "android.graphics.ComposeShader", 204 "android.graphics.CornerPathEffect", 205 "android.graphics.DashPathEffect", 206 "android.graphics.DiscretePathEffect", 207 "android.graphics.DrawFilter", 208 "android.graphics.EmbossMaskFilter", 209 "android.graphics.FontFamily", 210 "android.graphics.ImageDecoder", 211 "android.graphics.Interpolator", 212 "android.graphics.LightingColorFilter", 213 "android.graphics.LinearGradient", 214 "android.graphics.MaskFilter", 215 "android.graphics.Matrix", 216 "android.graphics.NinePatch", 217 "android.graphics.Paint", 218 "android.graphics.PaintFlagsDrawFilter", 219 "android.graphics.Path", 220 "android.graphics.PathDashPathEffect", 221 "android.graphics.PathEffect", 222 "android.graphics.PathMeasure", 223 "android.graphics.Picture", 224 "android.graphics.PorterDuffColorFilter", 225 "android.graphics.RadialGradient", 226 "android.graphics.RecordingCanvas", 227 "android.graphics.Region", 228 "android.graphics.RegionIterator", 229 "android.graphics.RenderEffect", 230 "android.graphics.RenderNode", 231 "android.graphics.RuntimeShader", 232 "android.graphics.Shader", 233 "android.graphics.SumPathEffect", 234 "android.graphics.SweepGradient", 235 "android.graphics.TableMaskFilter", 236 "android.graphics.Typeface", 237 "android.graphics.YuvImage", 238 "android.graphics.animation.NativeInterpolatorFactory", 239 "android.graphics.animation.RenderNodeAnimator", 240 "android.graphics.drawable.AnimatedVectorDrawable", 241 "android.graphics.drawable.VectorDrawable", 242 "android.graphics.fonts.Font", 243 "android.graphics.fonts.Font$Builder", 244 "android.graphics.fonts.FontFamily", 245 "android.graphics.fonts.FontFamily$Builder", 246 "android.graphics.fonts.FontFileUtil", 247 "android.graphics.fonts.SystemFonts", 248 "android.graphics.text.PositionedGlyphs", 249 "android.graphics.text.LineBreaker", 250 "android.graphics.text.MeasuredText", 251 "android.graphics.text.MeasuredText$Builder", 252 "android.graphics.text.TextRunShaper", 253 "android.os.SystemProperties", 254 "android.os.Trace", 255 "android.text.AndroidCharacter", 256 "android.util.Log", 257 "android.util.PathParser", 258 "android.view.MotionEvent", 259 "android.view.Surface", 260 "com.android.internal.util.VirtualRefBasePtr", 261 }; 262 263 /** 264 * The list of classes to rename, must be an even list: the binary FQCN 265 * of class to replace followed by the new FQCN. 266 */ 267 private final static String[] RENAMED_CLASSES = 268 new String[] { 269 "android.os.ServiceManager", "android.os._Original_ServiceManager", 270 "android.view.textservice.TextServicesManager", "android.view.textservice._Original_TextServicesManager", 271 "android.view.SurfaceView", "android.view._Original_SurfaceView", 272 "android.view.WindowManagerImpl", "android.view._Original_WindowManagerImpl", 273 "android.webkit.WebView", "android.webkit._Original_WebView", 274 }; 275 276 /** 277 * The list of class references to update, must be an even list: the binary 278 * FQCN of class to replace followed by the new FQCN. The classes to 279 * replace are to be excluded from the output. 280 */ 281 private final static String[] JAVA_PKG_CLASSES = 282 new String[] { 283 "sun.misc.Cleaner", "com.android.layoutlib.bridge.libcore.util.Cleaner", 284 }; 285 286 /** 287 * List of classes to refactor. This is similar to combining {@link #getRenamedClasses()} and 288 * {@link #getJavaPkgClasses()}. 289 * Classes included here will be renamed and then all their references in any other classes 290 * will be also modified. 291 * FQCN of class to refactor followed by its new FQCN. 292 */ 293 private final static String[] REFACTOR_CLASSES = 294 new String[] { 295 "android.os.Build", "android.os._Original_Build", 296 }; 297 298 private final static String[] EXCLUDED_CLASSES = 299 new String[] { 300 "android.preference.PreferenceActivity", 301 "java.**", 302 "kotlin.**", 303 "org.kxml2.io.KXmlParser", 304 "org.xmlpull.**", 305 "sun.**", 306 }; 307 308 /** 309 * List of fields for which we will update the visibility to be public. This is sometimes 310 * needed when access from the delegate classes is needed. 311 */ 312 private final static String[] PROMOTED_FIELDS = new String[] { 313 "android.animation.AnimationHandler#mDelayedCallbackStartTime", 314 "android.animation.AnimationHandler#mAnimationCallbacks", 315 "android.animation.AnimationHandler#mCommitCallbacks", 316 "android.animation.AnimatorSet#mLastFrameTime", 317 "android.animation.PropertyValuesHolder#sSetterPropertyMap", 318 "android.animation.PropertyValuesHolder#sGetterPropertyMap", 319 "android.animation.PropertyValuesHolder$IntPropertyValuesHolder#sJNISetterPropertyMap", 320 "android.animation.PropertyValuesHolder$FloatPropertyValuesHolder#sJNISetterPropertyMap", 321 "android.animation.PropertyValuesHolder$MultiFloatValuesHolder#sJNISetterPropertyMap", 322 "android.animation.PropertyValuesHolder$MultiIntValuesHolder#sJNISetterPropertyMap", 323 "android.graphics.ImageDecoder$InputStreamSource#mInputStream", 324 "android.graphics.Typeface#DEFAULT_FAMILY", 325 "android.graphics.Typeface#sDynamicTypefaceCache", 326 "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI#mSet", 327 "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#mPendingAnimationActions", 328 "android.graphics.drawable.AnimatedVectorDrawable#mAnimatorSet", 329 "android.graphics.drawable.DrawableInflater#mRes", 330 "android.hardware.input.InputManagerGlobal#sInstance", 331 "android.view.Choreographer#mCallbackQueues", // required for tests only 332 "android.view.Choreographer$CallbackQueue#mHead", // required for tests only 333 "android.view.ViewRootImpl#mTmpFrames", 334 "com.android.internal.util.ArrayUtils#sCache", 335 }; 336 337 /** 338 * List of methods for which we will update the visibility to be public. 339 */ 340 private final static String[] PROMOTED_METHODS = new String[] { 341 "android.animation.AnimationHandler#doAnimationFrame", 342 "android.content.res.StringBlock#addParagraphSpan", 343 "android.content.res.StringBlock#getColor", 344 "android.graphics.Bitmap#setNinePatchChunk", 345 "android.graphics.Path#nInit", 346 "android.graphics.Typeface$Builder#createAssetUid", 347 "android.hardware.input.InputManagerGlobal#<init>", 348 "android.media.ImageReader#nativeClassInit", 349 "android.view.Choreographer#doFrame", 350 "android.view.Choreographer#postCallbackDelayedInternal", 351 "android.view.Choreographer#removeCallbacksInternal", 352 "android.view.ViewRootImpl#getRootMeasureSpec", 353 }; 354 355 /** 356 * List of classes to be promoted to public visibility. Prefer using PROMOTED_FIELDS to this 357 * if possible. 358 */ 359 private final static String[] PROMOTED_CLASSES = new String[] { 360 "android.content.res.StringBlock$Height", 361 "android.graphics.ImageDecoder$InputStreamSource", 362 "android.graphics.ImageDecoder$ResourceSource", 363 "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI", 364 "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator", 365 "android.view.Choreographer$CallbackQueue", // required for tests only 366 }; 367 368 /** 369 * List of classes for which the methods returning them should be deleted. 370 * The array contains a list of null terminated section starting with the name of the class 371 * to rename in which the methods are deleted, followed by a list of return types identifying 372 * the methods to delete. 373 */ 374 private final static String[] DELETE_RETURNS = 375 new String[] { 376 null }; // separator, for next class/methods list. 377 378 private final static String[] DEFERRED_STATIC_INITIALIZER_CLASSES = 379 NativeConfig.DEFERRED_STATIC_INITIALIZER_CLASSES; 380 381 private final static Map<String, InjectMethodRunnable> INJECTED_METHODS = Map.of( 382 "android.content.Context", InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER); 383 384 public static class LinkedHashMapEldestReplacer implements MethodReplacer { 385 386 private final String VOID_TO_MAP_ENTRY = 387 Type.getMethodDescriptor(Type.getType(Map.Entry.class)); 388 private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class); 389 390 @Override isNeeded(String owner, String name, String desc, String sourceClass)391 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 392 return LINKED_HASH_MAP.equals(owner) && 393 "eldest".equals(name) && 394 VOID_TO_MAP_ENTRY.equals(desc); 395 } 396 397 @Override replace(MethodInformation mi)398 public void replace(MethodInformation mi) { 399 mi.opcode = Opcodes.INVOKESTATIC; 400 mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class); 401 mi.desc = Type.getMethodDescriptor( 402 Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class)); 403 } 404 } 405 406 private static class ContextGetClassLoaderReplacer implements MethodReplacer { 407 // When LayoutInflater asks for a class loader, we must return the class loader that 408 // cannot return app's custom views/classes. This is so that in case of any failure 409 // or exception when instantiating the views, the IDE can replace it with a mock view 410 // and have proper error handling. However, if a custom view asks for the class 411 // loader, we must return a class loader that can find app's custom views as well. 412 // Thus, we rewrite the call to get class loader in LayoutInflater to 413 // getFrameworkClassLoader and inject a new method in Context. This leaves the normal 414 // method: Context.getClassLoader() free to be used by the apps. 415 private final String VOID_TO_CLASS_LOADER = 416 Type.getMethodDescriptor(Type.getType(ClassLoader.class)); 417 418 @Override isNeeded(String owner, String name, String desc, String sourceClass)419 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 420 return owner.equals("android/content/Context") && 421 sourceClass.equals("android/view/LayoutInflater") && 422 name.equals("getClassLoader") && 423 desc.equals(VOID_TO_CLASS_LOADER); 424 } 425 426 @Override replace(MethodInformation mi)427 public void replace(MethodInformation mi) { 428 mi.name = "getFrameworkClassLoader"; 429 } 430 } 431 432 private static class SystemCurrentTimeMillisReplacer implements MethodReplacer { 433 @Override isNeeded(String owner, String name, String desc, String sourceClass)434 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 435 return Type.getInternalName(System.class).equals(owner) && name.equals("currentTimeMillis"); 436 } 437 438 @Override replace(MethodInformation mi)439 public void replace(MethodInformation mi) { 440 mi.name = "currentTimeMillis"; 441 mi.owner = "com/android/internal/lang/System_Delegate"; 442 } 443 } 444 445 private static class SystemNanoTimeReplacer implements MethodReplacer { 446 @Override isNeeded(String owner, String name, String desc, String sourceClass)447 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 448 return Type.getInternalName(System.class).equals(owner) && name.equals("nanoTime"); 449 } 450 451 @Override replace(MethodInformation mi)452 public void replace(MethodInformation mi) { 453 mi.name = "nanoTime"; 454 mi.owner = "com/android/internal/lang/System_Delegate"; 455 } 456 } 457 458 public static class SystemLogReplacer implements MethodReplacer { 459 @Override isNeeded(String owner, String name, String desc, String sourceClass)460 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 461 return Type.getInternalName(System.class).equals(owner) && name.length() == 4 462 && name.startsWith("log"); 463 } 464 465 @Override replace(MethodInformation mi)466 public void replace(MethodInformation mi) { 467 assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V") 468 || mi.desc.equals("(Ljava/lang/String;)V"); 469 mi.name = "log"; 470 mi.owner = "com/android/internal/lang/System_Delegate"; 471 } 472 } 473 474 /** 475 * Platform code should not loadLibrary on its own. Layoutlib loading infrastructure takes case 476 * of loading all the necessary native libraries (having the right paths etc.) 477 */ 478 public static class SystemLoadLibraryReplacer implements MethodReplacer { 479 @Override isNeeded(String owner, String name, String desc, String sourceClass)480 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 481 return Type.getInternalName(System.class).equals(owner) && name.equals("loadLibrary"); 482 } 483 484 @Override replace(MethodInformation mi)485 public void replace(MethodInformation mi) { 486 mi.owner = "com/android/internal/lang/System_Delegate"; 487 } 488 } 489 490 /** 491 * This is to replace a static call to a dummy, so that ImageReader can be loaded and accessed 492 * during JNI loading 493 */ 494 public static class ImageReaderNativeInitReplacer implements MethodReplacer { 495 @Override isNeeded(String owner, String name, String desc, String sourceClass)496 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 497 return "android/media/ImageReader".equals(owner) && name.equals("nativeClassInit"); 498 } 499 500 @Override replace(MethodInformation mi)501 public void replace(MethodInformation mi) { 502 mi.owner = "android/media/ImageReader_Delegate"; 503 mi.opcode = Opcodes.INVOKESTATIC; 504 } 505 } 506 507 private static class LocaleGetDefaultReplacer implements MethodReplacer { 508 509 @Override isNeeded(String owner, String name, String desc, String sourceClass)510 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 511 return Type.getInternalName(Locale.class).equals(owner) 512 && "getDefault".equals(name) 513 && desc.equals(Type.getMethodDescriptor(Type.getType(Locale.class))); 514 } 515 516 @Override replace(MethodInformation mi)517 public void replace(MethodInformation mi) { 518 mi.owner = "com/android/layoutlib/bridge/android/AndroidLocale"; 519 } 520 } 521 522 private static class SystemArrayCopyReplacer implements MethodReplacer { 523 /** 524 * Descriptors for specialized versions {@link System#arraycopy} that are not present on the 525 * Desktop VM. 526 */ 527 private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList( 528 "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V", 529 "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V")); 530 531 @Override isNeeded(String owner, String name, String desc, String sourceClass)532 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 533 return Type.getInternalName(System.class).equals(owner) && "arraycopy".equals(name) && 534 ARRAYCOPY_DESCRIPTORS.contains(desc); 535 } 536 537 @Override replace(MethodInformation mi)538 public void replace(MethodInformation mi) { 539 mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V"; 540 } 541 } 542 543 public static class NativeInitPathReplacer implements MethodReplacer { 544 @Override isNeeded(String owner, String name, String desc, String sourceClass)545 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 546 return "android/graphics/Path".equals(owner) && 547 "nInit".equals(name) && "(J)J".equals(desc); 548 } 549 550 @Override replace(MethodInformation mi)551 public void replace(MethodInformation mi) { 552 mi.owner = "android/graphics/Path_Delegate"; 553 mi.opcode = Opcodes.INVOKESTATIC; 554 } 555 } 556 557 public static class AdaptiveIconMaskReplacer implements MethodReplacer { 558 @Override isNeeded(String owner, String name, String desc, String sourceClass)559 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 560 return "android/graphics/drawable/AdaptiveIconDrawable".equals(sourceClass) && 561 "android/content/res/Resources".equals(owner) && 562 name.equals("getString"); 563 } 564 565 @Override replace(MethodInformation mi)566 public void replace(MethodInformation mi) { 567 mi.owner = "android/graphics/drawable/AdaptiveIconDrawable_Delegate"; 568 mi.name = "getResourceString"; 569 mi.opcode = Opcodes.INVOKESTATIC; 570 mi.desc = "(Landroid/content/res/Resources;I)Ljava/lang/String;"; 571 } 572 } 573 574 public static class ActivityThreadInAnimationReplacer implements MethodReplacer { 575 @Override isNeeded(String owner, String name, String desc, String sourceClass)576 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 577 return ("android/app/ActivityThread").equals(owner) && 578 name.equals("getSystemUiContext") && 579 sourceClass.equals("android/view/animation/Animation"); 580 } 581 582 @Override replace(MethodInformation mi)583 public void replace(MethodInformation mi) { 584 mi.owner = "android/app/ActivityThread_Delegate"; 585 mi.opcode = Opcodes.INVOKESTATIC; 586 mi.desc = "()Landroid/content/Context;"; 587 } 588 } 589 590 public static class ReferenceRefersToReplacer implements MethodReplacer { 591 @Override isNeeded(String owner, String name, String desc, String sourceClass)592 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 593 return Type.getInternalName(WeakReference.class).equals(owner) && 594 "refersTo".equals(name); 595 } 596 597 @Override replace(MethodInformation mi)598 public void replace(MethodInformation mi) { 599 mi.opcode = Opcodes.INVOKESTATIC; 600 mi.owner = Type.getInternalName(Reference_Delegate.class); 601 mi.desc = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Reference.class), 602 Type.getType(Object.class)); 603 } 604 } 605 606 public static class HtmlApplicationResourceReplacer implements MethodReplacer { 607 @Override isNeeded(String owner, String name, String desc, String sourceClass)608 public boolean isNeeded(String owner, String name, String desc, String sourceClass) { 609 return ("android/text/Html".equals(sourceClass) || 610 "android/text/HtmlToSpannedConverter".equals(sourceClass)) && 611 "android/app/Application".equals(owner) && 612 name.equals("getResources"); 613 } 614 615 @Override replace(MethodInformation mi)616 public void replace(MethodInformation mi) { 617 mi.owner = "android/app/Application_Delegate"; 618 mi.name = "getResources"; 619 mi.opcode = Opcodes.INVOKESTATIC; 620 mi.desc = "(Landroid/app/Application;)Landroid/content/res/Resources;"; 621 } 622 } 623 } 624