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.InMemoryDexClassLoader;
18 
19 import java.io.InputStream;
20 import java.lang.reflect.Method;
21 import java.nio.ByteBuffer;
22 import java.util.zip.ZipEntry;
23 import java.util.zip.ZipFile;
24 
25 public class Main {
26 
main(String[] args)27   public static void main(String[] args) throws Exception {
28     // Extract Dex file contents from the secondary Jar file.
29     String jarFilename =
30         System.getenv("DEX_LOCATION") + "/656-annotation-lookup-generic-jni-ex.jar";
31     ZipFile zipFile = new ZipFile(jarFilename);
32     ZipEntry zipEntry = zipFile.getEntry("classes.dex");
33     InputStream inputStream = zipFile.getInputStream(zipEntry);
34     int dexFileSize = (int) zipEntry.getSize();
35     byte[] dexFileContents = new byte[dexFileSize];
36     inputStream.read(dexFileContents, 0, dexFileSize);
37 
38     // Create class loader from secondary Dex file.
39     ByteBuffer dexBuffer = ByteBuffer.wrap(dexFileContents);
40     ClassLoader classLoader = createUnquickenedDexClassLoader(dexBuffer);
41 
42     // Load and initialize the Test class.
43     Class<?> testClass = classLoader.loadClass("Test");
44     Method initialize = testClass.getMethod("initialize", String.class);
45     initialize.invoke(null, args[0]);
46 
47     // Invoke Test.nativeMethodWithAnnotation().
48     Method nativeMethodWithAnnotation = testClass.getMethod("nativeMethodWithAnnotation");
49     // Invoking the native method Test.nativeMethodWithAnnotation used
50     // to crash the Generic JNI trampoline during the resolution of
51     // the method's annotations (DummyAnnotation) (see b/38454151).
52     nativeMethodWithAnnotation.invoke(null);
53 
54     zipFile.close();
55     System.out.println("passed");
56   }
57 
58   // Create a class loader loading a Dex file in memory
59   // *without creating an Oat file*. This way, the Dex file won't be
60   // quickened and JNI stubs won't be compiled, thus forcing the use
61   // of Generic JNI when invoking the native method
62   // Test.nativeMethodWithAnnotation.
createUnquickenedDexClassLoader(ByteBuffer dexBuffer)63   static ClassLoader createUnquickenedDexClassLoader(ByteBuffer dexBuffer) {
64     InMemoryDexClassLoader cl = new InMemoryDexClassLoader(dexBuffer, getBootClassLoader());
65     return cl;
66   }
67 
getBootClassLoader()68   static ClassLoader getBootClassLoader() {
69     ClassLoader cl = Main.class.getClassLoader();
70     while (cl.getParent() != null) {
71       cl = cl.getParent();
72     }
73     return cl;
74   }
75 
76 }
77