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