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