Use tmpdir

This commit is contained in:
LoveSy 2021-06-19 11:57:09 +08:00
parent 2fb8750c75
commit 6a1303f4d6
4 changed files with 228 additions and 274 deletions

View File

@ -1,47 +1,49 @@
package org.lsposed.patch; package org.lsposed.patch;
import static org.apache.commons.io.FileUtils.copyDirectory; import com.android.apksigner.ApkSignerTool;
import static org.apache.commons.io.FileUtils.copyFile;
import com.android.tools.build.apkzlib.zip.StoredEntry; import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile; import com.android.tools.build.apkzlib.zip.ZFile;
import com.beust.jcommander.JCommander; import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.wind.meditor.base.BaseCommand; import com.wind.meditor.core.ManifestEditor;
import com.wind.meditor.core.FileProcesser;
import com.wind.meditor.property.AttributeItem; import com.wind.meditor.property.AttributeItem;
import com.wind.meditor.property.ModificationProperty; import com.wind.meditor.property.ModificationProperty;
import com.wind.meditor.utils.NodeValue; import com.wind.meditor.utils.NodeValue;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.lsposed.lspatch.share.Constants; import org.lsposed.lspatch.share.Constants;
import org.lsposed.patch.task.BuildAndSignApkTask;
import org.lsposed.patch.util.ApkSignatureHelper; import org.lsposed.patch.util.ApkSignatureHelper;
import org.lsposed.patch.util.ManifestParser; import org.lsposed.patch.util.ManifestParser;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.io.InputStream;
import java.text.SimpleDateFormat; import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern;
public class LSPatch { public class LSPatch {
@Parameter(description = "apk path") static class PatchError extends Error {
private String apkPath; PatchError(String message) {
super(message);
}
}
private String unzipApkFilePath; @Parameter(description = "apk")
private String apkPath = null;
@Parameter(names = "--help", help = true, order = 0) @Parameter(names = {"-h", "--help"}, help = true, order = 0, description = "Print this message")
private boolean help = false; private boolean help = false;
@Parameter(names = {"-o", "--output"}, description = "Output apk file") @Parameter(names = {"-o", "--output"}, description = "Output apk file")
@ -53,17 +55,30 @@ public class LSPatch {
@Parameter(names = {"-p", "--proxyname"}, description = "Special proxy app name with full dot path") @Parameter(names = {"-p", "--proxyname"}, description = "Special proxy app name with full dot path")
private String proxyName = "org.lsposed.lspatch.appstub.LSPApplicationStub"; private String proxyName = "org.lsposed.lspatch.appstub.LSPApplicationStub";
@Parameter(names = {"-d", "--debuggable"}, description = "Set true to make the app debuggable, otherwise set 0 (default) to make the app non-debuggable") @Parameter(names = {"-d", "--debuggable"}, description = "Set app to be debuggable")
private boolean debuggableFlag = false; private boolean debuggableFlag = false;
@Parameter(names = {"-l", "--sigbypasslv"}, description = "Signature bypass level. 0 (disable), 1 (pm), 2 (pm+openat). default 0") @Parameter(names = {"-l", "--sigbypasslv"}, description = "Signature bypass level. 0 (disable), 1 (pm), 2 (pm+openat). default 0")
private int sigbypassLevel = 0; private int sigbypassLevel = 0;
@Parameter(names = {"--v1"}, description = "Sign with v1 signature")
private boolean v1 = true;
@Parameter(names = {"--v2"}, description = "Sign with v2 signature")
private boolean v2 = true;
@Parameter(names = {"--v3"}, description = "Sign with v3 signature")
private boolean v3 = true;
@Parameter(names = {"-v", "--verbose"}, description = "Verbose output")
private boolean verbose = false;
private int dexFileCount = 0; private int dexFileCount = 0;
private static final String UNZIP_APK_FILE_NAME = "apk-unzip-files";
private static final String APPLICATION_NAME_ASSET_PATH = "assets/original_application_name.ini"; private static final String APPLICATION_NAME_ASSET_PATH = "assets/original_application_name.ini";
private final static String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini"; private static final String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini";
private static final String ORIGIN_APK_ASSET_PATH = "assets/origin_apk.bin";
private static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml";
private static final String[] APK_LIB_PATH_ARRAY = { private static final String[] APK_LIB_PATH_ARRAY = {
"lib/armeabi-v7a", "lib/armeabi-v7a",
"lib/armeabi", "lib/armeabi",
@ -80,191 +95,238 @@ public class LSPatch {
.addObject(lsPatch) .addObject(lsPatch)
.build(); .build();
jCommander.parse(args); jCommander.parse(args);
lsPatch.doCommandLine(); try {
lsPatch.doCommandLine();
} catch (PatchError e) {
System.err.println(e.getMessage());
}
} }
public void doCommandLine() throws IOException { public void doCommandLine() throws PatchError, IOException {
if (help) {
jCommander.usage();
return;
}
if (apkPath == null || apkPath.isEmpty()) { if (apkPath == null || apkPath.isEmpty()) {
jCommander.usage(); jCommander.usage();
return; return;
} }
File srcApkFile = new File(apkPath); File srcApkFile = new File(apkPath).getAbsoluteFile();
if (!srcApkFile.exists()) { if (!srcApkFile.exists())
System.out.println("The source apk file not exsit, please choose another one."); throw new PatchError("The source apk file does not exit. Please provide a correct path.");
return;
}
File finalApk = new File(String.format("%s-%s-unsigned.apk", srcApkFile.getName(), var workingDir = Files.createTempDirectory("LSPatch").toFile();
Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel)); try {
FileUtils.copyFile(srcApkFile, finalApk); File tmpApk = new File(workingDir, String.format("%s-%s-unsigned.apk", srcApkFile.getName(),
Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel));
if (verbose) {
System.out.println("work dir: " + workingDir);
System.out.println("apk path: " + srcApkFile);
}
ZFile zFile = ZFile.openReadWrite(finalApk); String apkFileName = srcApkFile.getName();
String currentDir = new File(".").getAbsolutePath(); if (outputPath == null || outputPath.length() == 0) {
System.out.println("work dir: " + currentDir); outputPath = String.format("%s-lv%s-xposed-signed.apk", FilenameUtils.getBaseName(apkFileName), sigbypassLevel);
System.out.println("apk path: " + apkPath); }
if (outputPath == null || outputPath.length() == 0) { File outputFile = new File(outputPath);
String sig = Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel; if (outputFile.exists() && !forceOverwrite)
outputPath = String.format("%s-%s-xposed-signed.apk", new File(apkPath).getName(), sig); throw new PatchError(outputPath + " exists. Use --force to overwrite");
}
File outputFile = new File(outputPath); System.out.println("Copying to tmp apk...");
if (outputFile.exists() && !forceOverwrite) {
System.err.println(outputPath + " exists, use --force to overwrite");
return;
}
String outputApkFileParentPath = outputFile.getParent(); FileUtils.copyFile(srcApkFile, tmpApk);
if (outputApkFileParentPath == null) {
String absPath = outputFile.getAbsolutePath();
int index = absPath.lastIndexOf(File.separatorChar);
outputApkFileParentPath = absPath.substring(0, index);
}
String apkFileName = srcApkFile.getName(); System.out.println("Parsing original apk...");
ZFile zFile = ZFile.openReadWrite(tmpApk);
String tempFilePath = outputApkFileParentPath + File.separator + // save the apk original signature info, to support crach signature.
currentTimeStr() + "-tmp" + File.separator; String originalSignature = ApkSignatureHelper.getApkSignInfo(apkPath);
if (originalSignature == null || originalSignature.isEmpty()) {
throw new PatchError("get original signature failed");
}
if (verbose)
System.out.println("Original signature\n" + originalSignature);
try (var is = new ByteArrayInputStream(originalSignature.getBytes(StandardCharsets.UTF_8))) {
zFile.add(SIGNATURE_INFO_ASSET_PATH, is);
} catch (IOException e) {
throw new PatchError("Error when saving signature: " + e);
}
unzipApkFilePath = tempFilePath + apkFileName + "-" + UNZIP_APK_FILE_NAME + File.separator; // get the dex count in the apk zip file
dexFileCount = findDexFileCount(zFile);
// save the apk original signature info, to support crach signature. if (verbose)
String originalSignature = ApkSignatureHelper.getApkSignInfo(apkPath); System.out.println("dexFileCount: " + dexFileCount);
if (originalSignature == null || originalSignature.isEmpty()) {
throw new IllegalStateException("get original signature failed");
}
File osi = new File((unzipApkFilePath + SIGNATURE_INFO_ASSET_PATH).replace("/", File.separator));
FileUtils.write(osi, originalSignature, Charset.defaultCharset());
zFile.add(SIGNATURE_INFO_ASSET_PATH, new FileInputStream(osi));
// get the dex count in the apk zip file // copy out manifest file from zlib
dexFileCount = findDexFileCount(zFile); var manifestEntry = zFile.get(ANDROID_MANIFEST_XML);
if (manifestEntry == null)
throw new PatchError("Provided file is not a valid apk");
System.out.println("dexFileCount: " + dexFileCount); // parse the app main application full name from the manifest file
ManifestParser.Pair pair = ManifestParser.parseManifestFile(manifestEntry.open());
if (pair == null)
throw new PatchError("Failed to parse AndroidManifest.xml");
String applicationName = pair.applicationName == null ? "" : pair.applicationName;
// copy out manifest file from zlib if (verbose)
int copySize = IOUtils.copy(zFile.get("AndroidManifest.xml").open(), new FileOutputStream(unzipApkFilePath + "AndroidManifest.xml.bak")); System.out.println("original application name: " + applicationName);
if (copySize <= 0) {
throw new IllegalStateException("wtf");
}
String manifestFilePath = unzipApkFilePath + "AndroidManifest.xml.bak";
// parse the app main application full name from the manifest file System.out.println("Patching apk...");
ManifestParser.Pair pair = ManifestParser.parseManifestFile(manifestFilePath); // modify manifest
String applicationName = null; try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open()))) {
if (pair != null && pair.applicationName != null) { zFile.add(APPLICATION_NAME_ASSET_PATH, is);
applicationName = pair.applicationName; } catch (IOException e) {
} throw new PatchError("Error when modifying manifest: " + e);
}
System.out.println("original application name: " + applicationName); // save original main application name to asset file even its empty
try (var is = new ByteArrayInputStream(applicationName.getBytes(StandardCharsets.UTF_8))) {
zFile.add(APPLICATION_NAME_ASSET_PATH, is);
} catch (IOException e) {
throw new PatchError("Error when saving signature: " + e);
}
// modify manifest // copy so and dex files into the unzipped apk
modifyManifestFile(manifestFilePath, new File(unzipApkFilePath, "AndroidManifest.xml").getPath()); Set<String> apkArchs = new HashSet<>();
// save original main application name to asset file even its empty if (verbose)
File oan = new File((unzipApkFilePath + APPLICATION_NAME_ASSET_PATH).replace("/", File.separator)); System.out.println("search target apk library arch..");
FileUtils.write(oan, applicationName, Charset.defaultCharset()); for (StoredEntry storedEntry : zFile.entries()) {
zFile.add(APPLICATION_NAME_ASSET_PATH, new FileInputStream(oan)); for (String arch : APK_LIB_PATH_ARRAY) {
if (storedEntry.getCentralDirectoryHeader().getName().startsWith(arch)) {
// copy so and dex files into the unzipped apk apkArchs.add(arch);
Set<String> apkArchs = new HashSet<>(); }
System.out.println("search target apk library arch..");
for (StoredEntry storedEntry : zFile.entries()) {
for (String arch : APK_LIB_PATH_ARRAY) {
if (storedEntry.getCentralDirectoryHeader().getName().startsWith(arch)) {
apkArchs.add(arch);
} }
} }
}
if (apkArchs.isEmpty()) { if (apkArchs.isEmpty()) {
apkArchs.add(APK_LIB_PATH_ARRAY[0]); apkArchs.add(APK_LIB_PATH_ARRAY[0]);
} }
for (String arch : apkArchs) { for (String arch : apkArchs) {
// lib/armeabi-v7a -> armeabi-v7a // lib/armeabi-v7a -> armeabi-v7a
String justArch = arch.substring(arch.indexOf('/')); String justArch = arch.substring(arch.indexOf('/'));
File sod = new File("list-so", justArch); File sod = new File("list-so", justArch);
File[] files = sod.listFiles(); File[] files = sod.listFiles();
if (files == null) { if (files == null) {
System.out.println("Warning: Nothing so file has been copied in " + sod.getPath()); System.err.println("Warning: No so file has been copied in " + sod.getPath());
continue; continue;
}
for (File file : files) {
zFile.add(arch + "/" + file.getName(), new FileInputStream(file));
if (verbose)
System.out.println("add " + file.getPath());
}
}
// copy all dex files in list-dex
File[] files = new File("list-dex").listFiles();
if (files == null || files.length == 0) {
System.err.println("Warning: No dex file has been copied");
return;
} }
for (File file : files) { for (File file : files) {
zFile.add(arch + "/" + file.getName(), new FileInputStream(file)); String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex";
System.out.println("add " + file.getPath()); zFile.add(copiedDexFileName, new FileInputStream(file));
dexFileCount++;
} }
}
// copy all dex files in list-dex // copy origin apk to assets
File[] files = new File("list-dex").listFiles(); // convenient to bypass some check like CRC
if (files == null || files.length == 0) { if (sigbypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) {
System.out.println("Warning: Nothing dex file has been copied"); zFile.add(ORIGIN_APK_ASSET_PATH, new FileInputStream(srcApkFile));
return; }
}
for (File file : files) {
String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex";
zFile.add(copiedDexFileName, new FileInputStream(file));
dexFileCount++;
}
// copy origin apk to assets File[] listAssets = new File("list-assets").listFiles();
// convenient to bypass some check like CRC if (listAssets == null || listAssets.length == 0) {
if (sigbypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) { System.err.println("Warning: No assets file copyied");
zFile.add("assets/origin_apk.bin", new FileInputStream(srcApkFile)); } else {
} for (File f : listAssets) {
if (f.isDirectory()) {
File[] listAssets = new File("list-assets").listFiles(); throw new PatchError("unsupported directory in assets");
if (listAssets == null || listAssets.length == 0) { }
System.out.println("Warning: No assets file copyied"); zFile.add("assets/" + f.getName(), new FileInputStream(f));
}
else {
for (File f : listAssets) {
if (f.isDirectory()) {
throw new IllegalStateException("unsupport directory in assets");
} }
zFile.add("assets/" + f.getName(), new FileInputStream(f));
} }
// save lspatch config to asset..
try (var is = new ByteArrayInputStream("42".getBytes(StandardCharsets.UTF_8))) {
zFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, is);
} catch (IOException e) {
throw new PatchError("Error when saving signature: " + e);
}
zFile.update();
zFile.close();
System.out.println("Signing apk...");
signApkUsingAndroidApksigner(workingDir, tmpApk, outputFile);
System.out.println("Done. Output APK: " + outputFile.getAbsolutePath());
} finally {
FileUtils.deleteDirectory(workingDir);
} }
// save lspatch config to asset..
File sl = new File(unzipApkFilePath, "tmp");
FileUtils.write(sl, "42", Charset.defaultCharset());
zFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, new FileInputStream(sl));
zFile.update();
zFile.close();
new BuildAndSignApkTask(true, unzipApkFilePath, finalApk.getAbsolutePath(), outputPath).run();
System.out.println("Output APK: " + outputPath);
} }
private void modifyManifestFile(String filePath, String dstFilePath) { private byte[] modifyManifestFile(InputStream is) {
ModificationProperty property = new ModificationProperty(); ModificationProperty property = new ModificationProperty();
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag)); property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag));
property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true)); property.addApplicationAttribute(new AttributeItem("extractNativeLibs", true));
property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyName)); property.addApplicationAttribute(new AttributeItem(NodeValue.Application.NAME, proxyName));
FileProcesser.processManifestFile(filePath, dstFilePath, property); var os = new ByteArrayOutputStream();
(new ManifestEditor(is, os, property)).processManifest();
return os.toByteArray();
} }
private int findDexFileCount(ZFile zFile) { private int findDexFileCount(ZFile zFile) {
for (int i = 2; i < 30; i++) { for (int i = 2; ; i++) {
if (zFile.get("classes" + i + ".dex") == null) if (zFile.get("classes" + i + ".dex") == null)
return i - 1; return i - 1;
} }
throw new IllegalStateException("wtf");
} }
// Use the current timestamp as the name of the build file private void signApkUsingAndroidApksigner(File workingDir, File apkPath, File outputPath) throws PatchError, IOException {
@SuppressWarnings("SimpleDateFormat") ArrayList<String> commandList = new ArrayList<>();
private String currentTimeStr() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); var keyStoreFile = new File(workingDir, "keystore");
return df.format(new Date());
try (InputStream is = getClass().getClassLoader().getResourceAsStream("assets/keystore");
FileOutputStream os = new FileOutputStream(keyStoreFile)) {
if (is == null)
throw new PatchError("Fail to save keystore file");
IOUtils.copy(is, os);
}
commandList.add("sign");
commandList.add("--ks");
commandList.add(keyStoreFile.getAbsolutePath());
commandList.add("--ks-key-alias");
commandList.add("key0");
commandList.add("--ks-pass");
commandList.add("pass:" + 123456);
commandList.add("--key-pass");
commandList.add("pass:" + 123456);
commandList.add("--out");
commandList.add(outputPath.getAbsolutePath());
commandList.add("--v1-signing-enabled");
commandList.add(Boolean.toString(v1));
commandList.add("--v2-signing-enabled"); // v2签名不兼容android 6
commandList.add(Boolean.toString(v2));
commandList.add("--v3-signing-enabled"); // v3签名不兼容android 6
commandList.add(Boolean.toString(v3));
commandList.add(apkPath.getAbsolutePath());
try {
ApkSignerTool.main(commandList.toArray(new String[0]));
} catch (Exception e) {
throw new PatchError("Failed to sign apk: " + e.getMessage());
}
} }
} }

View File

@ -1,98 +0,0 @@
package org.lsposed.patch.task;
import com.android.apksigner.ApkSignerTool;
import com.android.tools.build.apkzlib.zip.ZFile;
import org.apache.commons.io.IOUtils;
import org.lsposed.patch.LSPatch;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
/**
* Created by Wind
*/
public class BuildAndSignApkTask implements Runnable {
private final boolean keepUnsignedApkFile;
private final String signedApkPath;
private final String unzipApkFilePath;
private final String unsignedApkPath;
public BuildAndSignApkTask(boolean keepUnsignedApkFile, String unzipApkFilePath, String unsignedApkPath, String signedApkPath) {
this.keepUnsignedApkFile = keepUnsignedApkFile;
this.unsignedApkPath = unsignedApkPath;
this.unzipApkFilePath = unzipApkFilePath;
this.signedApkPath = signedApkPath;
}
@Override
public void run() {
try {
File unzipApkPathFile = new File(unzipApkFilePath);
File keyStoreFile = new File(unzipApkPathFile, "keystore");
String keyStoreAssetPath;
keyStoreAssetPath = "assets/keystore";
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(keyStoreAssetPath);
FileOutputStream out = new FileOutputStream(keyStoreFile)) {
IOUtils.copy(inputStream, out);
}
boolean signResult = signApk(unsignedApkPath, keyStoreFile.getAbsolutePath(), signedApkPath);
File unsignedApkFile = new File(unsignedApkPath);
File signedApkFile = new File(signedApkPath);
// delete unsigned apk file
if (!keepUnsignedApkFile && unsignedApkFile.exists() && signedApkFile.exists() && signResult) {
if (!unsignedApkFile.delete()) {
throw new IllegalStateException("wtf");
}
}
}
catch (Exception err) {
throw new IllegalStateException("wtf", err);
}
}
private boolean signApk(String apkPath, String keyStorePath, String signedApkPath) {
return signApkUsingAndroidApksigner(apkPath, keyStorePath, signedApkPath, "123456");
}
private boolean signApkUsingAndroidApksigner(String apkPath, String keyStorePath, String signedApkPath, String keyStorePassword) {
ArrayList<String> commandList = new ArrayList<>();
commandList.add("sign");
commandList.add("--ks");
commandList.add(keyStorePath);
commandList.add("--ks-key-alias");
commandList.add("key0");
commandList.add("--ks-pass");
commandList.add("pass:" + keyStorePassword);
commandList.add("--key-pass");
commandList.add("pass:" + keyStorePassword);
commandList.add("--out");
commandList.add(signedApkPath);
commandList.add("--v1-signing-enabled");
commandList.add("true");
commandList.add("--v2-signing-enabled"); // v2签名不兼容android 6
commandList.add("false");
commandList.add("--v3-signing-enabled"); // v3签名不兼容android 6
commandList.add("false");
commandList.add(apkPath);
try {
ApkSignerTool.main(commandList.toArray(new String[0]));
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}

View File

@ -72,7 +72,6 @@ public class ApkSignatureHelper {
} }
} }
jarFile.close(); jarFile.close();
System.out.println("getApkSignInfo result: " + certs[0]);
return new String(toChars(certs[0].getEncoded())); return new String(toChars(certs[0].getEncoded()));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -3,6 +3,7 @@ package org.lsposed.patch.util;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import wind.android.content.res.AXmlResourceParser; import wind.android.content.res.AXmlResourceParser;
import wind.v1.XmlPullParser; import wind.v1.XmlPullParser;
@ -13,23 +14,12 @@ import wind.v1.XmlPullParserException;
*/ */
public class ManifestParser { public class ManifestParser {
/** public static Pair parseManifestFile(InputStream is) throws IOException {
* Get the package name and the main application name from the manifest file
* */
public static Pair parseManifestFile(String filePath) {
AXmlResourceParser parser = new AXmlResourceParser(); AXmlResourceParser parser = new AXmlResourceParser();
File file = new File(filePath);
String packageName = null; String packageName = null;
String applicationName = null; String applicationName = null;
if (!file.exists()) {
System.out.println(" manifest file not exist!!! filePath -> " + filePath);
return null;
}
FileInputStream inputStream = null;
try { try {
inputStream = new FileInputStream(file); parser.open(is);
parser.open(inputStream);
while (true) { while (true) {
int type = parser.next(); int type = parser.next();
@ -64,20 +54,21 @@ public class ManifestParser {
} }
} }
} catch (XmlPullParserException | IOException e) { } catch (XmlPullParserException | IOException e) {
e.printStackTrace(); return null;
System.out.println("parseManifestFile failed, reason --> " + e.getMessage());
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
return new Pair(packageName, applicationName); return new Pair(packageName, applicationName);
} }
/**
* Get the package name and the main application name from the manifest file
*/
public static Pair parseManifestFile(String filePath) throws IOException {
File file = new File(filePath);
try (var is = new FileInputStream(file)) {
return parseManifestFile(is);
}
}
public static class Pair { public static class Pair {
public String packageName; public String packageName;
public String applicationName; public String applicationName;