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