@@ -15,9 +15,13 @@ import picocli.CommandLine.Help.Visibility.ALWAYS
1515import picocli.CommandLine.Model.CommandSpec
1616import picocli.CommandLine.Spec
1717import java.io.File
18+ import java.io.FileOutputStream
1819import java.io.PrintWriter
1920import java.io.StringWriter
2021import java.util.logging.Logger
22+ import java.util.zip.ZipEntry
23+ import java.util.zip.ZipFile
24+ import java.util.zip.ZipOutputStream
2125
2226@CommandLine.Command (
2327 name = " patch" ,
@@ -181,6 +185,19 @@ internal object PatchCommand : Runnable {
181185 )
182186 private var purge: Boolean = false
183187
188+ @CommandLine.Option (
189+ names = [" --rip-lib" ],
190+ description = [" Remove native libraries for specific architectures (e.g., x86, x86_64)." ],
191+ )
192+ private var ripLibs: List <String > = emptyList()
193+
194+ @CommandLine.Option (
195+ names = [" --unsigned" ],
196+ description = [" Don't sign the patched APK file." ],
197+ showDefaultValue = ALWAYS ,
198+ )
199+ private var unsigned: Boolean = false
200+
184201 @CommandLine.Parameters (
185202 description = [" APK file to patch." ],
186203 arity = " 1" ,
@@ -332,7 +349,12 @@ internal object PatchCommand : Runnable {
332349 apk.copyTo(temporaryFilesPath.resolve(apk.name), overwrite = true ).apply {
333350 patcherResult.applyTo(this )
334351 }.let { patchedApkFile ->
335- if (! mount) {
352+
353+ if (ripLibs.isNotEmpty()) {
354+ ripLibraries(patchedApkFile, ripLibs)
355+ }
356+
357+ if (! mount && ! unsigned) {
336358 ApkUtils .signApk(
337359 patchedApkFile,
338360 outputFilePath,
@@ -450,4 +472,42 @@ internal object PatchCommand : Runnable {
450472 }
451473 logger.info(result)
452474 }
475+
476+ private fun ripLibraries (apkFile : File , archsToRemove : List <String >) {
477+ if (archsToRemove.isEmpty()) return
478+
479+ logger.info(" Ripping libraries for architectures: $archsToRemove " )
480+
481+ val tempFile = File .createTempFile(" temp-rip" , " .apk" , apkFile.parentFile)
482+
483+ ZipFile (apkFile).use { zip ->
484+ ZipOutputStream (FileOutputStream (tempFile)).use { out ->
485+ val entries = zip.entries()
486+ while (entries.hasMoreElements()) {
487+ val entry = entries.nextElement()
488+ var shouldRemove = false
489+
490+ for (arch in archsToRemove) {
491+ if (entry.name.startsWith(" lib/$arch /" )) {
492+ shouldRemove = true
493+ break
494+ }
495+ }
496+
497+ if (! shouldRemove) {
498+ out .putNextEntry(ZipEntry (entry.name))
499+ zip.getInputStream(entry).copyTo(out )
500+ out .closeEntry()
501+ }
502+ }
503+ }
504+ }
505+
506+ if (apkFile.delete()) {
507+ tempFile.renameTo(apkFile)
508+ } else {
509+ tempFile.copyTo(apkFile, overwrite = true )
510+ tempFile.delete()
511+ }
512+ }
453513}
0 commit comments