From 55e79fb70f7b8cb1cbe36e6ee82c45797f9a7852 Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:06:02 +0100 Subject: [PATCH] fix(res): use safe number parsing for android manifest, refactor params object (#2857) --- .../export/gen/AndroidGradleGenerator.java | 11 +- .../src/main/java/jadx/core/utils/Utils.java | 22 +++ .../utils/android/AndroidManifestParser.java | 56 +++---- .../core/utils/android/ApplicationParams.java | 137 +++++++++++------- 4 files changed, 131 insertions(+), 95 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/export/gen/AndroidGradleGenerator.java b/jadx-core/src/main/java/jadx/core/export/gen/AndroidGradleGenerator.java index f44a00f7a..cde2b6c4f 100644 --- a/jadx-core/src/main/java/jadx/core/export/gen/AndroidGradleGenerator.java +++ b/jadx-core/src/main/java/jadx/core/export/gen/AndroidGradleGenerator.java @@ -31,9 +31,6 @@ public class AndroidGradleGenerator implements IExportGradleGenerator { private static final Logger LOG = LoggerFactory.getLogger(AndroidGradleGenerator.class); private static final Pattern ILLEGAL_GRADLE_CHARS = Pattern.compile("[/\\\\:>\"?*|]"); - private static final ApplicationParams UNKNOWN_APP_PARAMS = - new ApplicationParams("UNKNOWN", 0, 0, 0, 0, "UNKNOWN", "UNKNOWN", "UNKNOWN"); - private final RootNode root; private final File projectDir; private final List resources; @@ -83,8 +80,8 @@ public class AndroidGradleGenerator implements IExportGradleGenerator { try { ResourceFile androidManifest = AndroidManifestParser.getAndroidManifest(resources); if (androidManifest == null) { - LOG.warn("AndroidManifest.xml not found, exported files will contains 'UNKNOWN' fields"); - return UNKNOWN_APP_PARAMS; + LOG.warn("AndroidManifest.xml not found, exported files will contains 'null' fields"); + return new ApplicationParams(); } ResContainer strings = null; if (exportApp) { @@ -118,7 +115,7 @@ public class AndroidGradleGenerator implements IExportGradleGenerator { return parser.parse(); } catch (Exception t) { LOG.warn("Failed to parse AndroidManifest.xml", t); - return UNKNOWN_APP_PARAMS; + return new ApplicationParams(); } } @@ -143,7 +140,7 @@ public class AndroidGradleGenerator implements IExportGradleGenerator { private void saveSettingsGradle() throws IOException { TemplateFile tmpl = TemplateFile.fromResources("/export/android/settings.gradle.tmpl"); - String appName = applicationParams.getApplicationName(); + String appName = applicationParams.getApplicationLabel(); String projectName; if (appName != null) { projectName = ILLEGAL_GRADLE_CHARS.matcher(appName).replaceAll(""); diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 1c4f8188a..36c8a8de5 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -672,4 +672,26 @@ public class Utils { } return Integer.parseInt(strValue); } + + public static int safeParseInt(String value, int defValue) { + if (value == null || value.isEmpty()) { + return defValue; + } + try { + return Integer.parseInt(value); + } catch (Exception e) { + return defValue; + } + } + + public static @Nullable Integer safeParseInteger(@Nullable String value) { + if (value == null || value.isEmpty()) { + return null; + } + try { + return Integer.parseInt(value); + } catch (Exception e) { + return null; + } + } } diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java index 9cfaed6a7..1a97fc37f 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java @@ -18,6 +18,8 @@ import jadx.api.security.IJadxSecurity; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.xmlgen.ResContainer; +import static jadx.core.utils.Utils.safeParseInteger; + public class AndroidManifestParser { private final Document androidManifest; private final @Nullable Document appStrings; @@ -41,8 +43,7 @@ public class AndroidManifestParser { return androidManifest != null; } - @Nullable - public static ResourceFile getAndroidManifest(List resources) { + public static @Nullable ResourceFile getAndroidManifest(List resources) { return resources.stream() .filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST) .findFirst() @@ -53,19 +54,11 @@ public class AndroidManifestParser { if (!isManifestFound()) { throw new JadxRuntimeException("AndroidManifest.xml is missing"); } - return parseAttributes(); } private ApplicationParams parseAttributes() { - String applicationLabel = null; - Integer minSdkVersion = null; - Integer targetSdkVersion = null; - Integer compileSdkVersion = null; - Integer versionCode = null; - String versionName = null; - String mainActivity = null; - String application = null; + ApplicationParams appParams = new ApplicationParams(); @Nullable Element manifest = (Element) androidManifest.getElementsByTagName("manifest").item(0); @@ -73,49 +66,47 @@ public class AndroidManifestParser { Element usesSdk = (Element) androidManifest.getElementsByTagName("uses-sdk").item(0); if (parseAttrs.contains(AppAttribute.APPLICATION_LABEL)) { - applicationLabel = getApplicationLabel(); + appParams.setApplicationLabel(getApplicationLabel()); } if (usesSdk != null) { if (parseAttrs.contains(AppAttribute.MIN_SDK_VERSION)) { - minSdkVersion = Integer.valueOf(usesSdk.getAttribute("android:minSdkVersion")); + appParams.setMinSdkVersion(safeParseInteger(usesSdk.getAttribute("android:minSdkVersion"))); } if (parseAttrs.contains(AppAttribute.TARGET_SDK_VERSION)) { String stringTargetSdk = usesSdk.getAttribute("android:targetSdkVersion"); if (!stringTargetSdk.isEmpty()) { - targetSdkVersion = Integer.valueOf(stringTargetSdk); + appParams.setTargetSdkVersion(safeParseInteger(stringTargetSdk)); } else { - if (minSdkVersion == null) { - minSdkVersion = Integer.valueOf(usesSdk.getAttribute("android:minSdkVersion")); + if (appParams.getMinSdkVersion() == null) { + appParams.setMinSdkVersion(safeParseInteger(usesSdk.getAttribute("android:minSdkVersion"))); } - targetSdkVersion = minSdkVersion; + appParams.setTargetSdkVersion(appParams.getMinSdkVersion()); } } if (parseAttrs.contains(AppAttribute.COMPILE_SDK_VERSION)) { String stringCompileSdk = usesSdk.getAttribute("android:compileSdkVersion"); if (!stringCompileSdk.isEmpty()) { - compileSdkVersion = Integer.valueOf(stringCompileSdk); + appParams.setCompileSdkVersion(safeParseInteger(stringCompileSdk)); } else { - compileSdkVersion = targetSdkVersion; + appParams.setCompileSdkVersion(appParams.getTargetSdkVersion()); } } } if (manifest != null) { if (parseAttrs.contains(AppAttribute.VERSION_CODE)) { - versionCode = Integer.valueOf(manifest.getAttribute("android:versionCode")); + appParams.setVersionCode(safeParseInteger(manifest.getAttribute("android:versionCode"))); } if (parseAttrs.contains(AppAttribute.VERSION_NAME)) { - versionName = manifest.getAttribute("android:versionName"); + appParams.setVersionName(manifest.getAttribute("android:versionName")); } } if (parseAttrs.contains(AppAttribute.MAIN_ACTIVITY)) { - mainActivity = getMainActivityName(); + appParams.setMainActivity(getMainActivityName()); } if (parseAttrs.contains(AppAttribute.APPLICATION)) { - application = getApplicationName(); + appParams.setApplication(getApplicationName()); } - - return new ApplicationParams(applicationLabel, minSdkVersion, targetSdkVersion, compileSdkVersion, - versionCode, versionName, mainActivity, application); + return appParams; } private String getApplicationLabel() { @@ -143,11 +134,10 @@ public class AndroidManifestParser { return appLabelName; } } - return "UNKNOWN"; } - private String getMainActivityName() { + private @Nullable String getMainActivityName() { String mainActivityName = getMainActivityNameThroughActivityTag(); if (mainActivityName == null) { mainActivityName = getMainActivityNameThroughActivityAliasTag(); @@ -155,7 +145,7 @@ public class AndroidManifestParser { return mainActivityName; } - private String getApplicationName() { + private @Nullable String getApplicationName() { Element application = (Element) androidManifest.getElementsByTagName("application").item(0); if (application.hasAttribute("android:name")) { return application.getAttribute("android:name"); @@ -163,7 +153,7 @@ public class AndroidManifestParser { return null; } - private String getMainActivityNameThroughActivityAliasTag() { + private @Nullable String getMainActivityNameThroughActivityAliasTag() { NodeList activityAliasNodes = androidManifest.getElementsByTagName("activity-alias"); for (int i = 0; i < activityAliasNodes.getLength(); i++) { Element activityElement = (Element) activityAliasNodes.item(i); @@ -174,7 +164,7 @@ public class AndroidManifestParser { return null; } - private String getMainActivityNameThroughActivityTag() { + private @Nullable String getMainActivityNameThroughActivityTag() { NodeList activityNodes = androidManifest.getElementsByTagName("activity"); for (int i = 0; i < activityNodes.getLength(); i++) { Element activityElement = (Element) activityNodes.item(i); @@ -230,7 +220,7 @@ public class AndroidManifestParser { } } - private Document parseAppStrings(ResContainer appStrings) { + private @Nullable Document parseAppStrings(@Nullable ResContainer appStrings) { if (appStrings == null) { return null; } @@ -238,7 +228,7 @@ public class AndroidManifestParser { return parseXml(content); } - private Document parseAndroidManifest(ResourceFile androidManifest) { + private @Nullable Document parseAndroidManifest(ResourceFile androidManifest) { if (androidManifest == null) { return null; } diff --git a/jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java b/jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java index 8f087053b..3fa1f2ad7 100644 --- a/jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java @@ -1,68 +1,95 @@ package jadx.core.utils.android; +import org.jetbrains.annotations.Nullable; + import jadx.api.JadxDecompiler; import jadx.api.JavaClass; public class ApplicationParams { + private @Nullable String applicationLabel; + private @Nullable Integer minSdkVersion; + private @Nullable Integer targetSdkVersion; + private @Nullable Integer compileSdkVersion; + private @Nullable Integer versionCode; + private @Nullable String versionName; + private @Nullable String mainActivity; + private @Nullable String application; - private final String applicationLabel; - private final Integer minSdkVersion; - private final Integer targetSdkVersion; - private final Integer compileSdkVersion; - private final Integer versionCode; - private final String versionName; - private final String mainActivity; - private final String application; - - public ApplicationParams(String applicationLabel, Integer minSdkVersion, Integer targetSdkVersion, Integer compileSdkVersion, - Integer versionCode, String versionName, String mainActivity, String application) { - this.applicationLabel = applicationLabel; - this.minSdkVersion = minSdkVersion; - this.targetSdkVersion = targetSdkVersion; - this.compileSdkVersion = compileSdkVersion; - this.versionCode = versionCode; - this.versionName = versionName; - this.mainActivity = mainActivity; - this.application = application; - } - - public String getApplicationName() { - return applicationLabel; - } - - public Integer getMinSdkVersion() { - return minSdkVersion; - } - - public Integer getTargetSdkVersion() { - return targetSdkVersion; - } - - public Integer getCompileSdkVersion() { - return compileSdkVersion; - } - - public Integer getVersionCode() { - return versionCode; - } - - public String getVersionName() { - return versionName; - } - - public String getMainActivity() { - return mainActivity; - } - - public JavaClass getMainActivityJavaClass(JadxDecompiler decompiler) { - return decompiler.searchJavaClassByAliasFullName(mainActivity); - } - - public String getApplication() { + public @Nullable String getApplication() { return application; } - public JavaClass getApplicationJavaClass(JadxDecompiler decompiler) { + public void setApplication(@Nullable String application) { + this.application = application; + } + + public @Nullable JavaClass getApplicationJavaClass(JadxDecompiler decompiler) { + if (application == null) { + return null; + } return decompiler.searchJavaClassByAliasFullName(application); } + + public @Nullable String getApplicationLabel() { + return applicationLabel; + } + + public void setApplicationLabel(@Nullable String applicationLabel) { + this.applicationLabel = applicationLabel; + } + + public @Nullable String getMainActivity() { + return mainActivity; + } + + public void setMainActivity(@Nullable String mainActivity) { + this.mainActivity = mainActivity; + } + + public @Nullable JavaClass getMainActivityJavaClass(JadxDecompiler decompiler) { + if (mainActivity == null) { + return null; + } + return decompiler.searchJavaClassByAliasFullName(mainActivity); + } + + public @Nullable Integer getCompileSdkVersion() { + return compileSdkVersion; + } + + public void setCompileSdkVersion(@Nullable Integer compileSdkVersion) { + this.compileSdkVersion = compileSdkVersion; + } + + public @Nullable Integer getMinSdkVersion() { + return minSdkVersion; + } + + public void setMinSdkVersion(@Nullable Integer minSdkVersion) { + this.minSdkVersion = minSdkVersion; + } + + public @Nullable Integer getTargetSdkVersion() { + return targetSdkVersion; + } + + public void setTargetSdkVersion(@Nullable Integer targetSdkVersion) { + this.targetSdkVersion = targetSdkVersion; + } + + public @Nullable Integer getVersionCode() { + return versionCode; + } + + public void setVersionCode(@Nullable Integer versionCode) { + this.versionCode = versionCode; + } + + public @Nullable String getVersionName() { + return versionName; + } + + public void setVersionName(@Nullable String versionName) { + this.versionName = versionName; + } }