Skip to content

Commit 36d6def

Browse files
committed
Resolve libbacktrace-native.so from split APKs (AAB/Google Play)
Update CrashHandlerConfiguration to resolve the libbacktrace-native.so container
1 parent 76bd75c commit 36d6def

File tree

1 file changed

+84
-1
lines changed

1 file changed

+84
-1
lines changed

backtrace-library/src/main/java/backtraceio/library/models/nativeHandler/CrashHandlerConfiguration.java

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
import android.text.TextUtils;
55

66
import java.io.File;
7+
import java.io.IOException;
78
import java.util.ArrayList;
89
import java.util.Arrays;
910
import java.util.HashSet;
1011
import java.util.List;
1112
import java.util.Map;
1213
import java.util.Set;
14+
import java.util.zip.ZipEntry;
15+
import java.util.zip.ZipFile;
1316

1417
import backtraceio.library.common.AbiHelper;
1518
import backtraceio.library.services.BacktraceCrashHandlerRunner;
@@ -36,9 +39,42 @@ public String getClassPath() {
3639
}
3740

3841
public List<String> getCrashHandlerEnvironmentVariables(ApplicationInfo applicationInfo) {
39-
return getCrashHandlerEnvironmentVariables(applicationInfo.sourceDir, applicationInfo.nativeLibraryDir, AbiHelper.getCurrentAbi());
42+
final String classPathApk = applicationInfo.sourceDir;
43+
final String nativeLibraryDirPath = applicationInfo.nativeLibraryDir;
44+
final String arch = AbiHelper.getCurrentAbi();
45+
46+
final List<String> environmentVariables = new ArrayList<>();
47+
48+
// system environment variables
49+
for (Map.Entry<String, String> variable : System.getenv().entrySet()) {
50+
environmentVariables.add(String.format("%s=%s", variable.getKey(), variable.getValue()));
51+
}
52+
53+
// LD_LIBRARY_PATH
54+
File nativeLibraryDirectory = new File(nativeLibraryDirPath);
55+
File allNativeLibrariesDirectory = nativeLibraryDirectory.getParentFile();
56+
String allPossibleLibrarySearchPaths = TextUtils.join(File.pathSeparator, new String[]{
57+
nativeLibraryDirPath,
58+
allNativeLibrariesDirectory.getPath(),
59+
System.getProperty("java.library.path"),
60+
"/data/local"
61+
});
62+
63+
final String backtraceNativeLibraryPath = resolveBacktraceNativeLibraryPath(applicationInfo, arch);
64+
65+
environmentVariables.add(String.format("CLASSPATH=%s", classPathApk));
66+
environmentVariables.add(String.format("%s=%s", BACKTRACE_CRASH_HANDLER, backtraceNativeLibraryPath));
67+
environmentVariables.add(String.format("LD_LIBRARY_PATH=%s", allPossibleLibrarySearchPaths));
68+
environmentVariables.add("ANDROID_DATA=/data");
69+
70+
return environmentVariables;
4071
}
4172

73+
/**
74+
* @deprecated Prefer {@link #getCrashHandlerEnvironmentVariables(android.content.pm.ApplicationInfo)} which correctly resolves split APKs on GooglePlay/AAB installs.
75+
* This method may be removed in a future release.
76+
*/
77+
@Deprecated
4278
public List<String> getCrashHandlerEnvironmentVariables(String apkPath, String nativeLibraryDirPath, String arch) {
4379
final List<String> environmentVariables = new ArrayList<>();
4480

@@ -86,4 +122,51 @@ private String getBacktraceNativeLibraryPath(String nativeLibraryDirPath, String
86122
? backtraceNativeLibraryPath
87123
: String.format("%s!/lib/%s/%s", apkPath, arch, BACKTRACE_NATIVE_LIBRARY_NAME);
88124
}
125+
126+
/**
127+
* Resolve native lib container:
128+
* extracted dir if present,
129+
* base.apk if it contains the entry,
130+
* first split that contains the entry,
131+
* fallback to base.apk path format.
132+
*/
133+
private String resolveBacktraceNativeLibraryPath(ApplicationInfo appInfo, String arch) {
134+
final String entry = "lib/" + arch + "/" + BACKTRACE_NATIVE_LIBRARY_NAME;
135+
136+
// extracted dir if present
137+
if (appInfo.nativeLibraryDir != null) {
138+
File extracted = new File(appInfo.nativeLibraryDir, BACKTRACE_NATIVE_LIBRARY_NAME);
139+
if (extracted.exists()) {
140+
return extracted.getAbsolutePath();
141+
}
142+
}
143+
144+
// base.apk if it contains the lib
145+
if (apkContains(appInfo.sourceDir, entry)) {
146+
return appInfo.sourceDir + "!/" + entry;
147+
}
148+
149+
// first split that contains the entry
150+
if (appInfo.splitSourceDirs != null) {
151+
for (String split : appInfo.splitSourceDirs) {
152+
if (apkContains(split, entry)) {
153+
return split + "!/" + entry;
154+
}
155+
}
156+
}
157+
158+
// fallback to base.apk path format
159+
return appInfo.sourceDir + "!/" + entry;
160+
}
161+
162+
private static boolean apkContains(String apkPath, String entry) {
163+
if (apkPath == null || apkPath.isEmpty()) return false;
164+
try (ZipFile zf = new ZipFile(apkPath)) {
165+
ZipEntry ze = zf.getEntry(entry);
166+
return ze != null;
167+
} catch (IOException ignored) {
168+
return false;
169+
}
170+
}
171+
89172
}

0 commit comments

Comments
 (0)