|
20 | 20 | import android.os.Debug; |
21 | 21 |
|
22 | 22 | import java.io.File; |
| 23 | +import java.io.FileInputStream; |
23 | 24 | import java.io.FileOutputStream; |
24 | 25 | import java.io.IOException; |
25 | 26 | import java.io.InputStream; |
@@ -62,10 +63,58 @@ class JvmtiAgent { |
62 | 63 | + "by a BaseDexClassLoader"); |
63 | 64 | } |
64 | 65 |
|
65 | | - Debug.attachJvmtiAgent(AGENT_LIB_NAME, null, cl); |
| 66 | + String agentPath = resolveAgentPath(cl); |
| 67 | + Debug.attachJvmtiAgent(agentPath, null, cl); |
66 | 68 | nativeRegisterTransformerHook(); |
67 | 69 | } |
68 | 70 |
|
| 71 | + /** |
| 72 | + * Resolve the agent library to a path that Debug.attachJvmtiAgent can load. |
| 73 | + * |
| 74 | + * <p>Debug.attachJvmtiAgent rejects paths containing '=' (used as options |
| 75 | + * separator). On Android, app install directories may contain '=' in their |
| 76 | + * path (e.g. /data/app/~~K6yx...A==/pkg-axiN...Q==/). When findLibrary |
| 77 | + * returns such a path, or returns null (extractNativeLibs=false), we copy |
| 78 | + * the library to a temp file. |
| 79 | + */ |
| 80 | + private static String resolveAgentPath(ClassLoader cl) throws IOException { |
| 81 | + String path = ((BaseDexClassLoader) cl).findLibrary("dexmakerjvmtiagent"); |
| 82 | + |
| 83 | + if (path != null && !path.contains("=")) { |
| 84 | + return path; |
| 85 | + } |
| 86 | + |
| 87 | + // Copy the library to a temp file with a clean path. |
| 88 | + File copiedAgent = File.createTempFile("org.mockito.android.agent", ".so"); |
| 89 | + copiedAgent.deleteOnExit(); |
| 90 | + |
| 91 | + InputStream is; |
| 92 | + if (path != null) { |
| 93 | + is = new FileInputStream(path); |
| 94 | + } else { |
| 95 | + // findLibrary returned null — try loading from APK resources |
| 96 | + String abi = Build.SUPPORTED_ABIS[0]; |
| 97 | + String resourcePath = "lib/" + abi + "/" + AGENT_LIB_NAME; |
| 98 | + is = cl.getResourceAsStream(resourcePath); |
| 99 | + if (is == null) { |
| 100 | + throw new IOException("Could not find " + AGENT_LIB_NAME |
| 101 | + + " via findLibrary or classloader resources"); |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + try (OutputStream os = new FileOutputStream(copiedAgent)) { |
| 106 | + byte[] buffer = new byte[64 * 1024]; |
| 107 | + int numRead; |
| 108 | + while ((numRead = is.read(buffer)) != -1) { |
| 109 | + os.write(buffer, 0, numRead); |
| 110 | + } |
| 111 | + } finally { |
| 112 | + is.close(); |
| 113 | + } |
| 114 | + |
| 115 | + return copiedAgent.getAbsolutePath(); |
| 116 | + } |
| 117 | + |
69 | 118 | private native void nativeUnregisterTransformerHook(); |
70 | 119 |
|
71 | 120 | @Override |
|
0 commit comments