• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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