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