• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 import dalvik.system.VMRuntime;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.invoke.MethodType;
20 import java.util.function.Consumer;
21 
22 public class ChildClass {
23   enum PrimitiveType {
24     TInteger('I', Integer.TYPE, Integer.valueOf(0)),
25     TLong('J', Long.TYPE, Long.valueOf(0)),
26     TFloat('F', Float.TYPE, Float.valueOf(0)),
27     TDouble('D', Double.TYPE, Double.valueOf(0)),
28     TBoolean('Z', Boolean.TYPE, Boolean.valueOf(false)),
29     TByte('B', Byte.TYPE, Byte.valueOf((byte) 0)),
30     TShort('S', Short.TYPE, Short.valueOf((short) 0)),
31     TCharacter('C', Character.TYPE, Character.valueOf('0'));
32 
PrimitiveType(char shorty, Class klass, Object value)33     PrimitiveType(char shorty, Class klass, Object value) {
34       mShorty = shorty;
35       mClass = klass;
36       mDefaultValue = value;
37     }
38 
39     public char mShorty;
40     public Class mClass;
41     public Object mDefaultValue;
42   }
43 
44   enum Hiddenness {
45     Sdk(PrimitiveType.TShort),
46     Unsupported(PrimitiveType.TBoolean),
47     ConditionallyBlocked(PrimitiveType.TByte),
48     Blocklist(PrimitiveType.TCharacter),
49     BlocklistAndCorePlatformApi(PrimitiveType.TInteger);
50 
Hiddenness(PrimitiveType type)51     Hiddenness(PrimitiveType type) { mAssociatedType = type; }
52     public PrimitiveType mAssociatedType;
53   }
54 
55   enum Visibility {
56     Public(PrimitiveType.TInteger),
57     Package(PrimitiveType.TFloat),
58     Protected(PrimitiveType.TLong),
59     Private(PrimitiveType.TDouble);
60 
Visibility(PrimitiveType type)61     Visibility(PrimitiveType type) { mAssociatedType = type; }
62     public PrimitiveType mAssociatedType;
63   }
64 
65   enum Behaviour {
66     Granted,
67     Warning,
68     Denied,
69   }
70 
71   // This needs to be kept in sync with DexDomain in Main.
72   enum DexDomain {
73     CorePlatform,
74     Platform,
75     Application
76   }
77 
78   private static final boolean booleanValues[] = new boolean[] { false, true };
79 
runTest(String libFileName, int parentDomainOrdinal, int childDomainOrdinal, boolean everythingSdked)80   public static void runTest(String libFileName, int parentDomainOrdinal,
81       int childDomainOrdinal, boolean everythingSdked) throws Exception {
82     System.load(libFileName);
83 
84     parentDomain = DexDomain.values()[parentDomainOrdinal];
85     childDomain = DexDomain.values()[childDomainOrdinal];
86 
87     configMessage = "parentDomain=" + parentDomain.name() + ", childDomain=" + childDomain.name()
88         + ", everythingSdked=" + everythingSdked;
89 
90     // Check expectations about loading into boot class path.
91     boolean isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
92     boolean expectedParentInBoot = (parentDomain != DexDomain.Application);
93     if (isParentInBoot != expectedParentInBoot) {
94       throw new RuntimeException("Expected ParentClass " +
95                                  (expectedParentInBoot ? "" : "not ") + "in boot class path");
96     }
97     boolean isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
98     boolean expectedChildInBoot = (childDomain != DexDomain.Application);
99     if (isChildInBoot != expectedChildInBoot) {
100       throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
101                                  "in boot class path");
102     }
103     ChildClass.everythingSdked = everythingSdked;
104 
105     boolean isSameBoot = (isParentInBoot == isChildInBoot);
106 
107     // For compat reasons, meta-reflection should still be usable by apps if hidden api check
108     // hardening is disabled (i.e. target SDK is Q or earlier). The only configuration where this
109     // workaround used to work is for ChildClass in the Application domain and ParentClass in the
110     // Platform domain, so only test that configuration with hidden api check hardening disabled.
111     boolean testHiddenApiCheckHardeningDisabled =
112         (childDomain == DexDomain.Application) && (parentDomain == DexDomain.Platform);
113 
114     // Run meaningful combinations of access flags.
115     for (Hiddenness hiddenness : Hiddenness.values()) {
116       final Behaviour expected;
117       final boolean invokesMemberCallback;
118       // Warnings are now disabled whenever access is granted, even for
119       // greylisted APIs. This is the behaviour for release builds.
120       if (everythingSdked || hiddenness == Hiddenness.Sdk) {
121         expected = Behaviour.Granted;
122         invokesMemberCallback = false;
123       } else if (parentDomain == DexDomain.CorePlatform && childDomain == DexDomain.Platform) {
124         expected = (hiddenness == Hiddenness.Unsupported
125                            || hiddenness == Hiddenness.BlocklistAndCorePlatformApi)
126                 ? Behaviour.Granted
127                 : Behaviour.Denied;
128         invokesMemberCallback = false;
129       } else if (isSameBoot) {
130         expected = Behaviour.Granted;
131         invokesMemberCallback = false;
132       } else if (hiddenness == Hiddenness.Blocklist ||
133                  hiddenness == Hiddenness.BlocklistAndCorePlatformApi) {
134         expected = Behaviour.Denied;
135         invokesMemberCallback = true;
136       } else {
137         expected = Behaviour.Warning;
138         invokesMemberCallback = true;
139       }
140 
141       for (boolean isStatic : booleanValues) {
142         String suffix = (isStatic ? "Static" : "") + hiddenness.name();
143 
144         for (Visibility visibility : Visibility.values()) {
145           // Test reflection and JNI on methods and fields
146           for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
147             String baseName = visibility.name() + suffix;
148             checkField(klass, "field" + baseName, isStatic, visibility, expected,
149                 invokesMemberCallback, testHiddenApiCheckHardeningDisabled);
150             checkMethod(klass, "method" + baseName, isStatic, visibility, expected,
151                 invokesMemberCallback, testHiddenApiCheckHardeningDisabled);
152           }
153 
154           // Check whether one can use a class constructor.
155           checkConstructor(ParentClass.class, visibility, hiddenness, expected,
156                 testHiddenApiCheckHardeningDisabled);
157 
158           // Check whether one can use an interface default method.
159           String name = "method" + visibility.name() + "Default" + hiddenness.name();
160           checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected,
161               invokesMemberCallback, testHiddenApiCheckHardeningDisabled);
162         }
163 
164         // Test whether static linking succeeds.
165         checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
166         checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
167         checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
168         checkLinking("LinkMethodInterface" + suffix, /*takesParameter*/ false, expected);
169       }
170 
171       // Check whether Class.newInstance succeeds.
172       checkNullaryConstructor(Class.forName("NullaryConstructor" + hiddenness.name()), expected);
173     }
174   }
175 
176   static final class RecordingConsumer implements Consumer<String> {
177       public String recordedValue = null;
178 
179       @Override
accept(String value)180       public void accept(String value) {
181           recordedValue = value;
182       }
183   }
184 
checkMemberCallback(Class<?> klass, String name, boolean isPublic, boolean isField, boolean expectedCallback)185   private static void checkMemberCallback(Class<?> klass, String name,
186           boolean isPublic, boolean isField, boolean expectedCallback) {
187       try {
188           RecordingConsumer consumer = new RecordingConsumer();
189           VMRuntime.setNonSdkApiUsageConsumer(consumer);
190           try {
191               if (isPublic) {
192                   if (isField) {
193                       klass.getField(name);
194                   } else {
195                       klass.getMethod(name);
196                   }
197               } else {
198                   if (isField) {
199                       klass.getDeclaredField(name);
200                   } else {
201                       klass.getDeclaredMethod(name);
202                   }
203               }
204           } catch (NoSuchFieldException|NoSuchMethodException ignored) {
205               // We're not concerned whether an exception is thrown or not - we're
206               // only interested in whether the callback is invoked.
207           }
208 
209           boolean actualCallback = consumer.recordedValue != null &&
210                           consumer.recordedValue.contains(name);
211           if (expectedCallback != actualCallback) {
212               if (expectedCallback) {
213                 throw new RuntimeException("Expected callback for member: " + name);
214               } else {
215                 throw new RuntimeException("Did not expect callback for member: " + name);
216               }
217           }
218       } finally {
219           VMRuntime.setNonSdkApiUsageConsumer(null);
220       }
221   }
222 
checkField(Class<?> klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback, boolean testHiddenApiCheckHardeningDisabled)223   private static void checkField(Class<?> klass, String name, boolean isStatic,
224       Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback,
225       boolean testHiddenApiCheckHardeningDisabled) throws Exception {
226 
227     boolean isPublic = (visibility == Visibility.Public);
228     boolean canDiscover = (behaviour != Behaviour.Denied);
229 
230     if (klass.isInterface() && (!isStatic || !isPublic)) {
231       // Interfaces only have public static fields.
232       return;
233     }
234 
235     // Test discovery with reflection.
236 
237     if (Reflection.canDiscoverWithGetDeclaredField(klass, name) != canDiscover) {
238       throwDiscoveryException(klass, name, true, "getDeclaredField()", canDiscover);
239     }
240 
241     if (Reflection.canDiscoverWithGetDeclaredFields(klass, name) != canDiscover) {
242       throwDiscoveryException(klass, name, true, "getDeclaredFields()", canDiscover);
243     }
244 
245     if (Reflection.canDiscoverWithGetField(klass, name) != (canDiscover && isPublic)) {
246       throwDiscoveryException(klass, name, true, "getField()", (canDiscover && isPublic));
247     }
248 
249     if (Reflection.canDiscoverWithGetFields(klass, name) != (canDiscover && isPublic)) {
250       throwDiscoveryException(klass, name, true, "getFields()", (canDiscover && isPublic));
251     }
252 
253     // Test discovery with JNI.
254 
255     if (JNI.canDiscoverField(klass, name, isStatic) != canDiscover) {
256       throwDiscoveryException(klass, name, true, "JNI", canDiscover);
257     }
258 
259     // Test discovery with MethodHandles.lookup() which is caller
260     // context sensitive.
261 
262     final MethodHandles.Lookup lookup = MethodHandles.lookup();
263     if (JLI.canDiscoverWithLookupFindGetter(lookup, klass, name, int.class)
264         != canDiscover) {
265       throwDiscoveryException(klass, name, true, "MethodHandles.lookup().findGetter()",
266                               canDiscover);
267     }
268     if (JLI.canDiscoverWithLookupFindStaticGetter(lookup, klass, name, int.class)
269         != canDiscover) {
270       throwDiscoveryException(klass, name, true, "MethodHandles.lookup().findStaticGetter()",
271                               canDiscover);
272     }
273 
274     // Test discovery with MethodHandles.publicLookup() which can only
275     // see public fields. Looking up setters here and fields in
276     // interfaces are implicitly final.
277 
278     final MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
279     if (JLI.canDiscoverWithLookupFindSetter(publicLookup, klass, name, int.class)
280         != canDiscover) {
281       throwDiscoveryException(klass, name, true, "MethodHandles.publicLookup().findSetter()",
282                               canDiscover);
283     }
284     if (JLI.canDiscoverWithLookupFindStaticSetter(publicLookup, klass, name, int.class)
285         != canDiscover) {
286       throwDiscoveryException(klass, name, true, "MethodHandles.publicLookup().findStaticSetter()",
287                               canDiscover);
288     }
289 
290     // Check for meta reflection.
291 
292     // With hidden api check hardening enabled, only white and light greylisted fields should be
293     // discoverable.
294     if (Reflection.canDiscoverFieldWithMetaReflection(klass, name, true) != canDiscover) {
295       throwDiscoveryException(klass, name, false,
296           "Meta reflection with hidden api hardening enabled", canDiscover);
297     }
298 
299     if (testHiddenApiCheckHardeningDisabled) {
300       // With hidden api check hardening disabled, all fields should be discoverable.
301       if (Reflection.canDiscoverFieldWithMetaReflection(klass, name, false) != true) {
302         throwDiscoveryException(klass, name, false,
303             "Meta reflection with hidden api hardening enabled", canDiscover);
304       }
305     }
306 
307     if (canDiscover) {
308       // Test that modifiers are unaffected.
309 
310       if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) {
311         throwModifiersException(klass, name, true);
312       }
313 
314       // Test getters and setters when meaningful.
315 
316       if (!Reflection.canGetField(klass, name)) {
317         throwAccessException(klass, name, true, "Field.getInt()");
318       }
319       if (!Reflection.canSetField(klass, name)) {
320         throwAccessException(klass, name, true, "Field.setInt()");
321       }
322       if (!JNI.canGetField(klass, name, isStatic)) {
323         throwAccessException(klass, name, true, "getIntField");
324       }
325       if (!JNI.canSetField(klass, name, isStatic)) {
326         throwAccessException(klass, name, true, "setIntField");
327       }
328     }
329 
330     // Test that callbacks are invoked correctly.
331     checkMemberCallback(klass, name, isPublic, true /* isField */, invokesMemberCallback);
332   }
333 
checkMethod(Class<?> klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback, boolean testHiddenApiCheckHardeningDisabled)334   private static void checkMethod(Class<?> klass, String name, boolean isStatic,
335       Visibility visibility, Behaviour behaviour, boolean invokesMemberCallback,
336       boolean testHiddenApiCheckHardeningDisabled) throws Exception {
337 
338     boolean isPublic = (visibility == Visibility.Public);
339     if (klass.isInterface() && !isPublic) {
340       // All interface members are public.
341       return;
342     }
343 
344     boolean canDiscover = (behaviour != Behaviour.Denied);
345 
346     // Test discovery with reflection.
347 
348     if (Reflection.canDiscoverWithGetDeclaredMethod(klass, name) != canDiscover) {
349       throwDiscoveryException(klass, name, false, "getDeclaredMethod()", canDiscover);
350     }
351 
352     if (Reflection.canDiscoverWithGetDeclaredMethods(klass, name) != canDiscover) {
353       throwDiscoveryException(klass, name, false, "getDeclaredMethods()", canDiscover);
354     }
355 
356     if (Reflection.canDiscoverWithGetMethod(klass, name) != (canDiscover && isPublic)) {
357       throwDiscoveryException(klass, name, false, "getMethod()", (canDiscover && isPublic));
358     }
359 
360     if (Reflection.canDiscoverWithGetMethods(klass, name) != (canDiscover && isPublic)) {
361       throwDiscoveryException(klass, name, false, "getMethods()", (canDiscover && isPublic));
362     }
363 
364     // Test discovery with JNI.
365 
366     if (JNI.canDiscoverMethod(klass, name, isStatic) != canDiscover) {
367       throwDiscoveryException(klass, name, false, "JNI", canDiscover);
368     }
369 
370     // Test discovery with MethodHandles.lookup().
371 
372     final MethodHandles.Lookup lookup = MethodHandles.lookup();
373     final MethodType methodType = MethodType.methodType(int.class);
374     if (JLI.canDiscoverWithLookupFindVirtual(lookup, klass, name, methodType) != canDiscover) {
375       throwDiscoveryException(klass, name, false, "MethodHandles.lookup().findVirtual()",
376                               canDiscover);
377     }
378 
379     if (JLI.canDiscoverWithLookupFindStatic(lookup, klass, name, methodType) != canDiscover) {
380       throwDiscoveryException(klass, name, false, "MethodHandles.lookup().findStatic()",
381                               canDiscover);
382     }
383 
384     // Check for meta reflection.
385 
386     // With hidden api check hardening enabled, only white and light greylisted methods should be
387     // discoverable.
388     if (Reflection.canDiscoverMethodWithMetaReflection(klass, name, true) != canDiscover) {
389       throwDiscoveryException(klass, name, false,
390           "Meta reflection with hidden api hardening enabled", canDiscover);
391     }
392 
393     if (testHiddenApiCheckHardeningDisabled) {
394       // With hidden api check hardening disabled, all methods should be discoverable.
395       if (Reflection.canDiscoverMethodWithMetaReflection(klass, name, false) != true) {
396         throwDiscoveryException(klass, name, false,
397             "Meta reflection with hidden api hardening enabled", canDiscover);
398       }
399     }
400 
401     // Finish here if we could not discover the method.
402 
403     if (canDiscover) {
404       // Test that modifiers are unaffected.
405 
406       if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) {
407         throwModifiersException(klass, name, false);
408       }
409 
410       // Test whether we can invoke the method. This skips non-static interface methods.
411       if (!klass.isInterface() || isStatic) {
412         if (!Reflection.canInvokeMethod(klass, name)) {
413           throwAccessException(klass, name, false, "invoke()");
414         }
415         if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
416           throwAccessException(klass, name, false, "CallMethodA");
417         }
418         if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
419           throwAccessException(klass, name, false, "CallMethodV");
420         }
421       }
422     }
423 
424     // Test that callbacks are invoked correctly.
425     checkMemberCallback(klass, name, isPublic, false /* isField */, invokesMemberCallback);
426   }
427 
checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness, Behaviour behaviour, boolean testHiddenApiCheckHardeningDisabled)428   private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
429       Behaviour behaviour, boolean testHiddenApiCheckHardeningDisabled) throws Exception {
430 
431     boolean isPublic = (visibility == Visibility.Public);
432     String signature = "(" + visibility.mAssociatedType.mShorty +
433                              hiddenness.mAssociatedType.mShorty + ")V";
434     String fullName = "<init>" + signature;
435     Class<?> args[] = new Class[] { visibility.mAssociatedType.mClass,
436                                     hiddenness.mAssociatedType.mClass };
437     Object initargs[] = new Object[] { visibility.mAssociatedType.mDefaultValue,
438                                        hiddenness.mAssociatedType.mDefaultValue };
439     MethodType methodType = MethodType.methodType(void.class, args);
440 
441     boolean canDiscover = (behaviour != Behaviour.Denied);
442 
443     // Test discovery with reflection.
444 
445     if (Reflection.canDiscoverWithGetDeclaredConstructor(klass, args) != canDiscover) {
446       throwDiscoveryException(klass, fullName, false, "getDeclaredConstructor()", canDiscover);
447     }
448 
449     if (Reflection.canDiscoverWithGetDeclaredConstructors(klass, args) != canDiscover) {
450       throwDiscoveryException(klass, fullName, false, "getDeclaredConstructors()", canDiscover);
451     }
452 
453     if (Reflection.canDiscoverWithGetConstructor(klass, args) != (canDiscover && isPublic)) {
454       throwDiscoveryException(
455           klass, fullName, false, "getConstructor()", (canDiscover && isPublic));
456     }
457 
458     if (Reflection.canDiscoverWithGetConstructors(klass, args) != (canDiscover && isPublic)) {
459       throwDiscoveryException(
460           klass, fullName, false, "getConstructors()", (canDiscover && isPublic));
461     }
462 
463     // Test discovery with JNI.
464 
465     if (JNI.canDiscoverConstructor(klass, signature) != canDiscover) {
466       throwDiscoveryException(klass, fullName, false, "JNI", canDiscover);
467     }
468 
469     // Test discovery with MethodHandles.lookup()
470 
471     final MethodHandles.Lookup lookup = MethodHandles.lookup();
472     if (JLI.canDiscoverWithLookupFindConstructor(lookup, klass, methodType) != canDiscover) {
473       throwDiscoveryException(klass, fullName, false, "MethodHandles.lookup().findConstructor",
474                               canDiscover);
475     }
476 
477     final MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
478     if (JLI.canDiscoverWithLookupFindConstructor(publicLookup, klass, methodType) != canDiscover) {
479       throwDiscoveryException(klass, fullName, false,
480                               "MethodHandles.publicLookup().findConstructor",
481                               canDiscover);
482     }
483 
484     // Check for meta reflection.
485 
486     // With hidden api check hardening enabled, only white and light greylisted constructors should
487     // be discoverable.
488     if (Reflection.canDiscoverConstructorWithMetaReflection(klass, args, true) != canDiscover) {
489       throwDiscoveryException(klass, fullName, false,
490           "Meta reflection with hidden api hardening enabled", canDiscover);
491     }
492 
493     if (testHiddenApiCheckHardeningDisabled) {
494       // With hidden api check hardening disabled, all constructors should be discoverable.
495       if (Reflection.canDiscoverConstructorWithMetaReflection(klass, args, false) != true) {
496         throwDiscoveryException(klass, fullName, false,
497             "Meta reflection with hidden api hardening enabled", canDiscover);
498       }
499     }
500 
501     if (canDiscover) {
502       // Test whether we can invoke the constructor.
503 
504       if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
505         throwAccessException(klass, fullName, false, "invoke()");
506       }
507       if (!JNI.canInvokeConstructorA(klass, signature)) {
508         throwAccessException(klass, fullName, false, "NewObjectA");
509       }
510       if (!JNI.canInvokeConstructorV(klass, signature)) {
511         throwAccessException(klass, fullName, false, "NewObjectV");
512       }
513     }
514   }
515 
checkNullaryConstructor(Class<?> klass, Behaviour behaviour)516   private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
517       throws Exception {
518     boolean canAccess = (behaviour != Behaviour.Denied);
519 
520     if (Reflection.canUseNewInstance(klass) != canAccess) {
521       throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
522           "be able to construct " + klass.getName() + ". " + configMessage);
523     }
524   }
525 
checkLinking(String className, boolean takesParameter, Behaviour behaviour)526   private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
527       throws Exception {
528     boolean canAccess = (behaviour != Behaviour.Denied);
529 
530     if (Linking.canAccess(className, takesParameter) != canAccess) {
531       throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
532           "be able to verify " + className + "." + configMessage);
533     }
534   }
535 
throwDiscoveryException(Class<?> klass, String name, boolean isField, String fn, boolean canAccess)536   private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
537       String fn, boolean canAccess) {
538     throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
539         "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
540         configMessage);
541   }
542 
throwAccessException(Class<?> klass, String name, boolean isField, String fn)543   private static void throwAccessException(Class<?> klass, String name, boolean isField,
544       String fn) {
545     throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
546         klass.getName() + "." + name + " using " + fn + ". " + configMessage);
547   }
548 
throwModifiersException(Class<?> klass, String name, boolean isField)549   private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
550     throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
551         "." + name + " to not expose hidden modifiers");
552   }
553 
554   private static DexDomain parentDomain;
555   private static DexDomain childDomain;
556   private static boolean everythingSdked;
557 
558   private static String configMessage;
559 }
560