Diagnóstico de fallos

referencia técnica · Revisado el 12 de agosto de 2026

Es un índice de síntomas. Se entra por el mensaje literal que se tiene delante, no por el concepto, porque cuando algo falla lo único que se tiene es una cadena de texto y ninguna pista de a qué capa pertenece. Cada entrada tiene la misma forma —síntoma literal → qué significa → causa raíz → qué comprobar → cómo se arregla— y una convención de honestidad: (reproducido) marca lo provocado en la máquina de referencia, con la cadena copiada de la salida real; (fuente) marca lo copiado del código que lo emite —AOSP, jadx o apktool— con el fichero identificado pero sin reproducir aquí; y ⚠️ marca lo que no está verificado, diciendo por qué.

Dos avisos generales. Los mensajes de error mienten sobre la causa con frecuencia: el caso de manual está en §4.2, donde un zipalign ejecutado un paso tarde produce Signature stripped?, que suena a ataque y es un error de orden. Y el error real casi nunca está en la salida de la orden que falló: en instalación está en logcat; en apktool, en el log de aapt2 y no en la excepción; en jadx, en el .java generado y no en la consola.

2. Instalación: los códigos INSTALL_*

2.1 De dónde salen y cómo se leen

Están enumerados en frameworks/base/core/java/android/content/pm/PackageManager.java de AOSP. Todos los valores son negativos, todos los símbolos están marcados @hide —no son API pública— y adb install los devuelve como texto, sin el número. Dos familias que llevan a sitios distintos: INSTALL_PARSE_FAILED_* (−100 a −109, más −124 y −125) significa que el paquete no se pudo leer —el fallo está en el fichero y lo produce el analizador—; INSTALL_FAILED_* (−1 a −29 y −110 en adelante) significa que el paquete se leyó bien y el sistema lo rechaza por su relación con lo instalado, con el dispositivo o con la política. Y una regla operativa: el motivo concreto está en logcat, no en la salida de adb install — el instalador devuelve un código seco, y el mensaje que dice qué entrada, qué certificado o qué split falla lo escribe PackageManager en el log del sistema.

⚠️ Ninguno de estos códigos se ha reproducido: no hay dispositivo Android ni emulador conectado al entorno de referencia y adb no está en el PATH. Los valores, las descripciones y los mensajes internos proceden del código de AOSP en la rama main, leído directamente (?format=TEXT, decodificado) el 12 de agosto de 2026. Nada de esta sección es una salida observada.

2.2 Fallos de análisis del paquete

Código Valor Qué significa Dónde se lanza y con qué condición
..._NOT_APK −100 La ruta no es un fichero o no acaba en .apk ApkLiteParseUtils: directorio sin paquetes (No packages found in split), o ApkAssets.loadFromFd lanza IOException
..._BAD_MANIFEST −101 No se pudo recuperar el manifiesto ApkLiteParseUtils: splits que discrepan del primero (Inconsistent package…, Inconsistent version…), nombre de split repetido, o Missing base APK in <dir>
..._UNEXPECTED_EXCEPTION −102 Excepción inesperada parseApkLiteInner(): catch general de XmlPullParserException | IOException | RuntimeException. Cajón de sastre: no dice nada por sí mismo, el motivo está en logcat
..._NO_CERTIFICATES −103 Sin certificados utilizables §2.3
..._INCONSISTENT_CERTIFICATES −104 Certificados incoherentes §2.3
..._CERTIFICATE_ENCODING · ..._BAD_SHARED_USER_ID · ..._MANIFEST_EMPTY −105 · −107 · −109 CertificateEncodingException en algún fichero · sharedUserId inválido · ni <application> ni <instrumentation> ApkSignatureVerifier · ApkLiteParseUtils · ParsingPackageUtils
..._BAD_PACKAGE_NAME −106 Nombre de paquete inválido o ausente en el manifiesto §2.3
..._MANIFEST_MALFORMED −108 Problema estructural en el manifiesto §2.3
..._RESOURCES_ARSC_COMPRESSED −124 resources.arsc comprimido o desalineado §2.3

2.3 Los cinco que hay que conocer al detalle

INSTALL_PARSE_FAILED_NO_CERTIFICATES. El paquete no tiene firma utilizable, o no la que el rango de API exige. Lo produce ApkSignatureVerifier.java en varios sitios con mensajes distintos: cuando el esquema mínimo requerido es más nuevo que cualquiera de los presentes —"No signature found in package of version … or newer for package …"—, cuando se captura SignatureNotFoundException y no se puede caer a un esquema anterior —"No APK Signature Scheme v4/v3/v2 signature in package …"— y, en el camino de v1, "Package … has no certificates at entry AndroidManifest.xml". En la práctica, tres orígenes: se olvidó firmar —apktool, APKEditor y bundletool build-apks sin --ks producen APK sin firma por diseño—, se firmó solo con v1 teniendo un targetSdk que exige v2 (§3.3), o se pasó zipalign después de firmar (§4.2). Comprobar: apksigner verify --verbose --min-sdk-version <el minSdk real> app.apk, y el inventario físico de META-INF/. Arreglo: firmar en el orden correcto —alinear y después apksigner sign con v2 y v3 (Pipeline de modificación, §7.4).

INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES. Dentro del paquete —o del conjunto de splits— no todo está firmado por lo mismo. Dos sitios: ApkSignatureVerifier.java en la verificación completa de v1, cuando una entrada del ZIP tiene firmas distintas de las del manifiesto ("Package … has mismatched certificates at entry …"); y ParsingPackageUtils.getSigningDetails() / FrameworkParsingPackageUtils, cuando !Signature.areExactMatch(...) entre un split y el base ("<baseCodePath> has mismatched certificates"). El segundo es el frecuente, y aparece al instalar splits refirmados por separado o al mezclar splits de dos descargas. Comprobar: un bucle de apksigner verify --print-certs sobre todos los splits tiene que devolver un solo certificate SHA-256 digest (Pipeline de análisis, §5.4). Arreglo: refirmar todos con la misma clave, cada uno alineado antes.

INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME. Lo produce FrameworkParsingPackageUtils.validateName(), cuyas reglas están implementadas ahí literalmente y no en ninguna especificación. Recorre carácter a carácter con dos banderas, front y hasSep: [a-zA-Z] siempre vale y pone front = false; [0-9] y _ valen solo si front == false, o sea que un segmento no puede empezar por dígito ni por subrayado; . pone hasSep = true y front = true; y cualquier otro carácter da "bad character '<c>'". Con requireFilename añade "Invalid filename" y "the length of the name is greater than 223"; y al final, con requireSeparator y sin ningún punto, "must have at least one '.' separator". El paquete se valida con requireSeparator=true, requireFilename=true; los nombres de split con false, false; los tipos con false, true. Aparece casi siempre tras un --rename-manifest-package con un guion, un segmento que empieza por dígito (com.ejemplo.2app) o ningún punto.

INSTALL_PARSE_FAILED_MANIFEST_MALFORMED. Varios sitios de ApkLiteParseUtils.java, con mensajes muy informativos si se llegan a ver en logcat: "No start tag found", "No <manifest> tag", "Invalid manifest split types: ", un <uses-split> sin android:name, o un <uses-sdk-library> / <uses-static-library> mal formado o duplicado. Al fusionar splits la causa habitual es otra —quedaron atributos de split en el manifiesto del resultado (Fusión de splits, §5)—, y sobre un APK fusionado el aapt2 dump xmltree … | grep -E "split=|splitTypes|isSplitRequired|isFeatureSplit|configForSplit" no debe devolver nada. Si el manifiesto ni siquiera se puede leer, el error está un nivel más abajo y aapt2 lo dice con precisión: §8.3.

INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED — con la trampa reproducida. Significa que el resources.arsc de alguno de los APK va comprimido o no está alineado a 4 bytes, así que el sistema no lo puede mapear con mmap. Lo produce ParsingPackageUtils.parseBaseApk(): tras un análisis correcto del manifiesto comprueba assets.containsAllocatedTable() —la tabla tuvo que reservarse en RAM en vez de mapearse— y emite un error diferido con el identificador de cambio de comportamiento RESOURCES_ARSC_COMPRESSED = 132742131, anotado @EnabledAfter(targetSdkVersion = Q): solo se activa para targetSdk 30 y superior; con 29 o menos, el mismo APK instala. El texto en AOSP es "Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary".

Y aquí está la trampa: las herramientas locales no lo detectan. Sobre un APK construido a propósito con resources.arsc comprimido, aapt2 dump badging responde con normalidad (package: name='org.fossify.math' versionCode='10' …) y zipalign -c -v 4 lo marca 3322768 resources.arsc (OK - compressed) saliendo con 0. La comprobación hay que hacerla a mano —zipfile.ZipFile(apk).getinfo('resources.arsc').compress_type == 0— y el arreglo es reempaquetar forzando stored en esa entrada.

2.4 Fallos de instalación

Código Valor Qué significa
INSTALL_FAILED_INVALID_APK −2 El archivo del paquete es inválido
INSTALL_FAILED_INSUFFICIENT_STORAGE −4 No hay espacio suficiente en el dispositivo
INSTALL_FAILED_UPDATE_INCOMPATIBLE −7 Ya hay un paquete con ese nombre y firma distinta, y sus datos no se borraron
INSTALL_FAILED_DEXOPT −11 Falló la optimización o la validación de los DEX
INSTALL_FAILED_OLDER_SDK −12 El minSdkVersion es mayor que la versión del dispositivo
INSTALL_FAILED_CPU_ABI_INCOMPATIBLE −16 Trae código nativo y ninguna ABI es compatible
INSTALL_FAILED_VERSION_DOWNGRADE −25 El versionCode es menor que el instalado
INSTALL_FAILED_MISSING_SPLIT −28 Requiere al menos un split y no se aportó
INSTALL_FAILED_INTERNAL_ERROR −110 Fallo del sistema
INSTALL_FAILED_NO_MATCHING_ABIS −113 El código nativo no coincide con ninguna ABI del sistema
INSTALL_FAILED_ABORTED −115 Sin Javadoc en AOSP: solo /** {@hide} */

⚠️ INSTALL_FAILED_MULTI_ARCH_NOT_SUPPORTED no existe. Circula en foros, pero no aparece en PackageManager.java de la rama main. La constante real más cercana es INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS = -131, para aplicaciones con android:multiArch="true" cuyo código nativo no cubre todas las ABIs del sistema.

INSTALL_FAILED_UPDATE_INCOMPATIBLE. InstallPackageHelper.preparePackage(), en el camino de reemplazo, cuando las credenciales de firma nuevas no tienen CertCapabilities.INSTALLED_DATA respecto a las instaladas y no es un rollback válido a un ancestro del linaje: "New package has a different signature: " + pkgName. Hay otras dos rutas al mismo código —fallo de checkUpgradeKeySetLocked, y un cambio de SDK en una biblioteca sin subir su versionMajor— y una cuarta que AOSP reconoce como reutilización indebida: en scanInstallPackages() se devuelve el mismo código cuando la actualización cambia PROPERTY_NO_APP_DATA_STORAGE, con un TODO en el fuente admitiendo que no corresponde. Es decir: no siempre es de firma. En la práctica aparece siempre que se instala un APK refirmado encima del original, lo cual es inevitable (Pipeline de modificación, §7.3), y el único arreglo es adb uninstall con pérdida de los datos.

INSTALL_FAILED_VERSION_DOWNGRADE. La comprobación real es PackageManagerServiceUtils.checkDowngrade(), más fina de lo que sugiere el nombre: falla si el versionCode nuevo es menor, o si son iguales y el baseRevisionCode nuevo es menor, o si el splitRevisionCode de cualquier split coincidente es menor — salvo que isDowngradePermitted() lo autorice, lo que ocurre con adb install -d sobre una compilación depurable. Ojo con haber tocado el versionCode al reempaquetar sin querer: apktool lo repuebla desde apktool.yml.

INSTALL_FAILED_MISSING_SPLIT. PackageInstallerSession.validateApkInstallLocked(), en dos sitios y los dos con el mensaje "Missing split for " + mPackageName: en instalación completa, cuando baseApk.isSplitRequired() y stagedSplits.size() <= 1 o !stagedSplitTypes.containsAll(requiredSplitTypes); y heredando, cuando se han quitado todos los splits o los tipos aportados no cubren los requeridos. Hay que mirar los dos mecanismos que conviven —el booleano isSplitRequired y el par requiredSplitTypes/splitTypes—, porque el corpus tiene aplicaciones con cada uno (Fusión de splits, §5.2). Arreglo: adb install-multiple con el conjunto completo, o fusionar y sanear el manifiesto.

INSTALL_FAILED_NO_MATCHING_ABIS y INSTALL_FAILED_CPU_ABI_INCOMPATIBLE. Casi siempre al fusionar splits cogiendo de menos. Es el único de los tres ejes que produce un fallo duro —densidad e idioma degradan en silencio (Fusión de splits, §3.1)—, y se comprueba comparando las ABIs bajo lib/ con adb shell getprop ro.product.cpu.abilist.

3. Firma

3.1 DOES NOT VERIFY — el prefijo común

apksigner verify responde siempre con esa línea, sale con código 1, y el motivo va en las líneas ERROR: siguientes. Los cinco que se ven de verdad:

Línea ERROR: Causa
Missing META-INF/MANIFEST.MF No hay firma ninguna, o se destruyó el APK Signing Block y no había v1 · §3.2
JAR signature META-INF/X.SF indicates the APK is signed using APK Signature Scheme v2 but no such signature was found. Signature stripped? Había v1 y v2/v3, y el bloque desapareció · §4.2
APK integrity check failed. CHUNKED_SHA256 digest mismatch. El contenido cambió después de firmar · §3.2
Target SDK version 36 requires a minimum of signature scheme v2; the APK is not signed with this or a later signature scheme Se firmó solo con v1 · §3.3
Verified using vN scheme: false sin DOES NOT VERIFY No es un error: es la trampa del rango de SDK · §3.4

3.2 Bloque desaparecido frente a contenido modificado (reproducido)

Es la distinción que más tiempo ahorra, y las dos familias se confunden constantemente.

ERROR: Missing META-INF/MANIFEST.MF significa que el verificador no encontró ningún bloque de firma y cayó al camino de v1, donde lo primero que busca es MANIFEST.MF. Dos causas: el APK nunca se firmó —apktool, APKEditor y bundletool build-apks sin --ks producen APK sin firma por diseño; reproducido sobre un APK del corpus sin META-INF/ y sobre el universal.apk de bundletool --mode=universal— o se ejecutó zipalign después de firmar un APK que solo tenía v2/v3 (§4.2). Si unzip -l app.apk | grep META-INF no devuelve nada y el APK mide menos que antes, es el segundo caso: hay que firmar el de antes de alinear.

CHUNKED_SHA256 digest mismatch significa lo contrario: el bloque sigue ahí y es legible, y lo que no cuadra es el digest. Cambiando un solo byte dentro de resources.arsc de un APK ya firmado, sin mover ningún offset —el fichero mide exactamente lo mismo antes y después—:

DOES NOT VERIFY
ERROR: APK Signature Scheme v3 signer #1: APK integrity check failed. CHUNKED_SHA256 digest mismatch. Expected: <655a9adc…cafe8d6c>, actual: <bf95b77b…39f57b21>

La regla: digest mismatch es contenido modificado —«alguien tocó el fichero»—; Signature stripped? y Missing META-INF/MANIFEST.MF son bloque desaparecido —casi siempre, «se ejecutó una herramienta en el orden equivocado»—. El arreglo, en los dos casos, es volver a firmar (§3.5).

3.3 Target SDK version 36 requires a minimum of signature scheme v2 (reproducido)

Firmando una copia del corpus con --v1-signing-enabled true y las demás a false:

DOES NOT VERIFY
ERROR: Target SDK version 36 requires a minimum of signature scheme v2; the APK is not signed with this or a later signature scheme
WARNING: META-INF/com/android/build/gradle/app-metadata.properties not protected by signature. …
WARNING: META-INF/version-control-info.textproto not protected by signature. …

Firmar solo con v1 hoy es no firmar: la firma es criptográficamente correcta y el APK no se instala, porque Android exige un esquema mínimo según el targetSdkVersion. Cuatro de las trece herramientas de Comparativa y selección firman solo con v1, y sus APK parecen correctos hasta el último paso. Los WARNING son el otro dato: la firma JAR no cubre las entradas de META-INF/, así que cualquiera puede modificarlas sin invalidarla — la debilidad que motivó v2. Arreglo: --v2-signing-enabled true --v3-signing-enabled true; v1 solo si minSdkVersion < 24.

3.4 Verified using v1 scheme: false con el MANIFEST.MF dentro (reproducido)

No hay fallo. apksigner verify no responde «¿está presente este esquema?» sino «¿se usó este esquema para verificar en el rango de API que me has dado?». El mismo APK firmado con v1+v2+v3, con dos rangos distintos, da Verified using v1 scheme (JAR signing): false con --min-sdk-version 24 y true con 21 — y META-INF/PROC.SF, META-INF/PROC.RSA y META-INF/MANIFEST.MF están dentro en los dos casos. Para saber qué esquemas hay, se mira el ZIP y el APK Signing Block; para saber si instalará, se ejecuta verify con el minSdkVersion real de la aplicación.

3.5 Fallos al firmar (reproducidos)

Síntoma Causa Arreglo
Failed to load signer "signer #1" + java.io.IOException: keystore password was incorrect Contraseña del keystore incorrecta Revisar --ks-pass; preferir env: o file: sobre pass:
Failed to load signer "signer #1": x.p12 entry "ALIAS" does not contain a key El alias no existe keytool -list -v -keystore x.p12
com.android.apksig.apk.ApkFormatException: Malformed APK: not a ZIP archive No es un ZIP §8.1
com.android.apksig.apk.ApkFormatException: Missing AndroidManifest.xml Es un ZIP pero no un APK Se cogió el contenedor en vez del APK de dentro

Y la imposibilidad de fondo: la firma original nunca se puede conservar tras modificar el contenido. No es una limitación de las herramientas, es la propiedad que hace útil a la firma (Pipeline de modificación, §7.3).

4. Alineación

4.1 (BAD - n) de zipalign -c (reproducido)

Sobre split_config.arm64_v8a.apk del SAI de Netflix del corpus:

    4096 lib/arm64-v8a/libavif_android.so (BAD - 4096)
  851968 lib/arm64-v8a/libbugsnag-ndk.so (OK)
 2039808 lib/arm64-v8a/libbugsnag-plugin-android-anr.so (BAD - 8192)
 7974912 lib/arm64-v8a/libtensorflowlite_jni_gms_client.so (BAD - 12288)
Verification FAILED

El n no es un código de error: es el resto de dividir el offset de los datos entre la alineación exigida. libavif_android.so empieza en el byte 4.096, y 4096 mod 16384 = 4096: o sea, cuántos bytes sobran. Causa raíz: el APK está alineado a 4 KiB y se le exige 16 KiB, y el mismo fichero con zipalign -c 4 sale con 0 —las mismas entradas, dos veredictos opuestos, porque son dos preguntas distintas—. Esos seis splits de Netflix son el único incumplimiento de 16 KB en los 717 APK del corpus. Arreglo: zipalign -P 16 -f 4 entrada.apk salida.apk, antes de firmar.

Los tres veredictos por entrada: (OK) es stored y alineada; (OK - compressed) es comprimida y la alineación no le aplica; (BAD - n) es stored desalineada por n bytes.

4.2 La trampa mayor: zipalign después de firmar (reproducido)

El fallo de manual del ecosistema, y el que produce el diagnóstico más engañoso.

$BT/apksigner verify --verbose firmado.apk | head -4      # v1 + v2 + v3, todo correcto
$BT/zipalign -p -f 4 firmado.apk roto.apk; echo "zipalign exit=$?"
$BT/apksigner verify --verbose roto.apk; echo "verify exit=$?"
zipalign exit=0
DOES NOT VERIFY
ERROR: JAR signer PROC.RSA: JAR signature META-INF/PROC.SF indicates the APK is signed using APK Signature Scheme v2 but no such signature was found. Signature stripped?
ERROR: JAR signer PROC.RSA: JAR signature META-INF/PROC.SF indicates the APK is signed using APK Signature Scheme v3 but no such signature was found. Signature stripped?
verify exit=1

zipalign no invalida la firma: la borra. Reescribe el ZIP entrada por entrada a un fichero nuevo, con Central Directory y EOCD nuevos, y el hueco donde vivía el APK Signing Block no se reproduce. El .SF de v1 sí sobrevive —es una entrada normal del ZIP— y sigue declarando X-Android-APK-Signed: 2, 3; el verificador ve esa declaración, no encuentra los bloques, y acusa de un stripping que no hubo. Y si el APK no tenía v1, el mensaje es aún más desconcertante —mismo experimento firmando solo con v2+v3: DOES NOT VERIFY / ERROR: Missing META-INF/MANIFEST.MF—: un fichero que nunca tuvo MANIFEST.MF, acusado de que le falta.

La señal delatora: zipalign devolvió 0. Para él la operación fue un éxito. Y el APK resultante mide algunos kilobytes menos que el firmado: la diferencia es el bloque desaparecido más el relleno recalculado. Arreglo: recuperar el APK de antes de firmar, alinearlo y firmar después.

Orden Resultado
construir → alinear → firmar Correcto
construir → firmar → alinear Firma destruida
construir → firmar → tocar un fichero digest mismatch (§3.2)
alinear → firmar → zipalign -c Correcto: -c solo lee

4.3 zipalign -c pasa y el APK sigue siendo incompatible con 16 KB

zipalign -c responde a «¿están alineadas las entradas que debían estarlo?», no a «¿cumple este APK la regla de 16 KB?». Un APK con extractNativeLibs="true" comprime sus .so, no tiene ninguna entrada a la que la regla aplique, y pasa siempre. Y hay un segundo requisito que zipalign no mira: los segmentos LOAD del propio ELF tienen que estar alineados a 2**14. Los dos controles son necesarios y ninguno basta (Alineación, §3.1, y Código nativo y ELF).

5. apktool

⚠️ Nada de esta sección está reproducido: apktool no está instalado y estas reglas prohíben instalarlo. Las cadenas literales proceden del código fuente del proyecto, leído directamente en el commit 79b6338 del 10 de agosto de 2026 —rama main, que es la 3.x— y en los tags v2.7.0 y v2.9.3 para las variantes antiguas.

5.1 El aviso de versión, que es la mitad del problema

Las cadenas cambiaron entre 2.x y 3.x, y las que circulan por los foros son las antiguas: si se busca un mensaje y no aparece, casi siempre es esto.

2.x 3.x (main)
Paquete brut.androlib.err · clase CantFindFrameworkResException Paquete brut.androlib.exceptions · clase FrameworkNotFoundException
Can't find framework resources for package of id: 1 Could not find framework resources for package ID 1. + salto de línea + You must install proper framework files, see project website for more info.
Could not decode arsc file (sin punto) Could not decode arsc file. (con punto)
Invalid config flags detected. Dropping resources: Invalid resource config detected. Dropping resources: %s %s

⚠️ El texto de ayuda de --keep-broken-res en el Main.java de 3.x sigue citando la redacción 2.x del mensaje al que remite: está desactualizado en el propio proyecto.

5.2 Las excepciones de brut.androlib (3.x)

Clase Mensaje literal
AndrolibException Clase base, sin mensaje fijo. Extiende brut.common.BrutException
FrameworkNotFoundException Could not find framework resources for package ID <id>. + You must install proper framework files, see project website for more info.
InFileNotFoundException · OutDirExistsException · RawXmlEncounteredException · NinePatchNotFoundException Input file (<ruta>) was not found or was not readable. · Destination directory (<ruta>) already exists. Use -f option if you want to overwrite it. · Could not decode XML. · Could not find nine patch chunk.

InFileNotFoundException, OutDirExistsException y FrameworkNotFoundException se imprimen sin traza de pila y con salida 1; el resto, con traza. Y falta una más, UndefinedResObjectException, cuyo mensaje es estructurado —entry: pkgId=0x%02x, typeId=0x%02x, entryId=0x%04x, config=%s, overlayable: pkgId=0x%02x, name=%s— y dice exactamente qué identificador no resolvió.

Could not find framework resources for package ID 1. apktool no tiene el APK de framework para resolver los identificadores del espacio 0x01....... Trae empotrado el framework estándar, así que con aplicaciones normales de Play no pasa: pasa con las de sistema y de fabricante —Samsung, MIUI, HTC—. Arreglo: apktool if framework-res.apk, o apktool if -t miui framework-miui-res.apk y luego apktool d -t miui app.apk.

Could not decode arsc file. El lector de resources.arsc de apktool se atascó, y las issues cerradas identifican el patrón: el tamaño declarado de un chunk no corresponde al contenido. iBotPeaches en la #3036: «what appears to be happening is the reported size of a chunk is wrong … the chunks we are reading are exceeding the size that was reported of that chunk»; en la #2989: «The header chunk is lying about its reported size.» Segunda familia: las tablas sparse (#3199, #3298), donde el criterio correcto no es el orden de los chunks sino el propio flag de sparse, con el umbral de AOSP de al menos 1.000 recursos.

⚠️ --force-manual-size no existe y nunca ha existido en apktool: se buscó en los 2.376 commits de todas las ramas del repositorio, desde 2010, y en la documentación oficial, con cero apariciones. El control de sparse es la clave sparseResources de apktool.yml, no un flag; si una guía lo menciona, viene de un fork.

Qué comprobar: si aapt2 dump resources lee el fichero, el problema es de apktool y no de la tabla. Arreglos por coste: apktool d -r, --keep-broken-res, o APKEditor, que usa otro lector.

Invalid resource config detected. Dropping resources: … es un aviso, no un error: apktool ha encontrado una configuración que no entiende y está descartando esos recursos; la ejecución continúa y el resultado está incompleto. --keep-broken-res los conserva, y hay que arreglarlos a mano antes de reconstruir.

5.3 Fallos de reconstrucción por aapt2

Aquí está el detalle que hace perder más tiempo: apktool no aporta ningún mensaje propio. El envoltorio de aapt2 captura la BrutException y la reenvuelve en un AndrolibException(ex) sin mensaje, solo con la causa, así que lo que se ve es lo que produce brut.util.OS.exec: Execution failed (exit code = 1): [.../aapt2, link, -o, ..., --manifest, ...]. Los errores reales de aapt2 —los error: resource … is private, los Multiple resources— llegan por un camino distinto: un reenviador de flujos que los registra en el log a nivel warn. Por eso aparecen en el log y nunca en el texto de la excepción, y por eso buscar «Multiple resources» en el código de apktool no devuelve nada. Hay que subir el nivel de log y leer las líneas de aapt2.

Error de aapt2 Causa Arreglo
resource … is private El APK original referencia un recurso del framework marcado como privado. Es legal en un binario construido y aapt2 lo rechaza al enlazar, porque su trabajo es impedirlo apktool d -r, o APKEditor
Multiple resources / símbolos duplicados Identificadores que apktool no supo resolver y sustituyó por @null. Issue #2836: «This works for the first resource, but as you see becomes duplicate symbols» Actualizar apktool, o APKEditor

Dos datos más: apktool siempre pasa --legacy a aapt2 —comentario del fuente: «Treats error that used to be valid in aapt1 as warnings in aapt2»— y aapt1 ya no existe, Main.java responde Legacy aapt is no longer supported. desde 2025.

5.4 Lo que apktool no hace, y hay que saber

  • No firma. La FAQ oficial: «Apktool builds unsigned APKs. This means some directories and files like META-INF are missing.»
  • No alinea. Verificado por inspección del código fuente de la rama main: no hay ninguna referencia a zipalign, a alineación de página ni a 16.384. Registra qué entradas van stored en doNotCompress y las escribe sin comprimir, pero nunca las alinea: zipalign es estrictamente externo.
  • --copy-original solo sirve para v1 (FAQ), y --match-original impide reconstruir: la ayuda dice literalmente Keep files closest to original as possible (prevents rebuild).
  • Hay APK que no soporta: los construidos con herramientas modificadas para OEM concretos «are not built with regular AOSP tools and are not compatible with Apktool».

6. jadx

⚠️ Nada de esta sección está reproducido: jadx no está instalado y estas reglas prohíben instalarlo. Las cadenas literales proceden del código fuente del proyecto, leído directamente en el commit e738a26 del 5 de agosto de 2026, con fichero y línea identificados.

6.1 Corrección previa: dos cadenas que circulan y no existen

Cadena que circula Realidad
// decompilation failed No existe en jadx. No aparece en ninguna parte del árbol. Lo más parecido es "Class decompilation failed", mensaje de una JadxRuntimeException del modo --single-class, no un comentario del código generado
// JADX WARNING: inconsistent code La forma real es /* JADX WARN: <mensaje> */. Y no se puede buscar en el fuente: se compone en ejecución como "JADX " + nivel.name() + ": " en JadxCommentsAttr, así que grepear el repositorio no devuelve nada y lleva a concluir que es falsa

⚠️ La sección 3.5 de Decompiladores a Java cita las dos formas incorrectas. Los modos de fallo que describe son correctos; los literales, no.

6.2 Los marcadores reales, con su fichero

Literal Dónde se emite Qué significa
/* JADX ERROR: <msg>*/ (con dos espacios tras /*) codegen/utils/CodeGenUtils.java:48 Error duro en ese nodo. Si hay causa, la traza de pila Java completa va incrustada dentro del comentario
/* JADX WARN: … */, /* JADX INFO: … */, /* JADX DEBUG: … */ JadxCommentsAttr + CodeGenUtils.addComments Aviso del nivel correspondiente. Por defecto --comments-level es info: WARN e INFO se emiten, DEBUG no
Code decompiled incorrectly, please refer to instructions dump. + To view partially-correct add '--show-bad-code' argument codegen/MethodGen.java:114-127 El peligroso. Ver §6.3. La falta de la palabra «code» en el segundo está en el fuente, no es errata de este documento
throw new UnsupportedOperationException("Method not decompiled: …") MethodGen.java:365-373 El método no se reconstruyó; se emite un lanzamiento para que el fichero siga siendo parseable
Method dump skipped, instruction units count: <n> + To view this dump add '--comments-level debug' option MethodGen.java:381 y :385 El volcado se omitió por tamaño; el umbral es más de 200 unidades de instrucción
// Can't load method instructions: <msg> / // Can't load method instructions. · // Blocks not ready for simple mode, using fallback MethodGen.java:402, :410 · :308 No se pudieron decodificar las instrucciones · cayó al modo de respaldo
Load error · Code generation failed RootNode.java:192 · ClassNode.java:409 Fallo al cargar la entrada · ver §6.4

Mensajes representativos tras JADX WARN:, verbatim del fuente: Multi-variable type inference failed, Type inference incomplete: some casts might be missing, Unreachable blocks removed: , Failed to restore switch over string. Please report as a decompilation issue.

6.3 Code decompiled incorrectly — el que hay que tomarse en serio

No es un comentario de una línea: es un bloque de tres.

/*
    Code decompiled incorrectly, please refer to instructions dump.
    To view partially-correct add '--show-bad-code' argument
*/

Significa que jadx llegó a producir Java y luego detectó que su propia reconstrucción no es coherente con el grafo de partida; por defecto no lo enseña y sustituye el cuerpo por el volcado.

El mecanismo, que aclara la confusión habitual: ClassGen.addMethodCode tiene tres niveles. Aviso (addWarn) pone la bandera INCONSISTENT_CODE, añade el comentario JADX WARN y suma al contador — un solo aviso ya basta para que el método caiga al modo de respaldo. Error (addError) añade el atributo JADX_ERROR y fuerza el respaldo igualmente. El fallo de clase entera es §6.4. Y --show-bad-code no arregla nada: solo cambia la bandera para que se imprima el Java parcialmente reconstruido —que puede ser incorrecto— en lugar del volcado. Es la opción correcta para auditar y la peligrosa para confiar.

Y el dato que corrige un error extendido: jadx no emite smali en el fichero .java. Lo que pone en el modo de respaldo es su volcado de instrucciones interno, en su propia representación; el smali de verdad existe solo como superficie de API —JavaClass.getSmali()—, alimenta la pestaña «Smali» de jadx-gui y nunca aparece en la salida de la CLI.

6.4 Un fichero .java cuyo contenido entero es una traza de pila

La clase entera falló. ClassNode.generateClassCode() captura StackOverflowError | Exception, registra "Code generation failed" y devuelve como contenido del fichero la traza de pila en crudo. Un pipeline que cuente .java producidos concluirá que la clase se decompiló.

6.5 Salida vacía, cuelgues y OutOfMemoryError

Causa raíz, con explicación del mantenedor. La issue #2676, cerrada: «errors during instructions decoding handled per single instruction, but there are several big methods (~100K instruction units) with errors at almost all instructions, so all these errors are collected to be added into generated code later (JADX_ERROR attribute). This is very slow and eats a lot of memory.» Es decir: un DEX con anti-análisis produce un error por instrucción, y acumularlos mata el proceso. La #2744 es análoga para código muy ofuscado. Arreglos: JAVA_OPTS="-Xmx8g", -j 4, excluir paquetes, jadx-gui con su caché en disco, o -m fallback.

6.6 Los códigos de salida, que sí sirven para automatizar

Código Condición
0 Éxito
1 JadxArgsValidateException (Incorrect arguments: {}) o cualquier Throwable (Process error:)
2 No se cargó ninguna clase y se pasó --no-res: Load failed! No classes for decompile!
3 Decompiló, pero con errores. Imprime el informe y luego finished with errors, count: {}

El 3 es el que importa: hay salida, y parte de ella son trazas de pila y volcados de instrucciones. Un 0, en cambio, no garantiza cobertura completa: los avisos no cuentan como error y ya bastan para que un método caiga al modo de respaldo. El informe previo lo emite ErrorsCounter.printReport() con los formatos "{} errors occurred in following nodes:" y "{} warnings in {} nodes".

7. Fusión de splits

Síntoma Causa raíz Qué comprobar
NoClassDefFoundError en ejecución Falta un DEX, la renumeración dejó un hueco, o el DEX de un feature module quedó delante de los del base y sombreó una clase unzip -l final.apk | grep -oE "classes[0-9]*\.dex" | sort -V — consecutiva y sin huecos
Los textos salen en inglés; los iconos, borrosos La tabla de recursos no se fusionó. Los ficheros de res/ se copiaron y ninguna entrada apunta a ellos aapt2 dump badging final.apk | grep -E "^(locales|densities|native-code)". Si locales es solo '--_--', no se fusionó
Un módulo de funcionalidad no está · dos Package con el mismo id= Se descartaron los splits con isFeatureSplit=true o su paquete propio · colisión real de package ID aapt2 dump resources final.apk | grep "^Package" debe listar dos paquetes con id distintos. La colisión es un aborto, no un aviso
INSTALL_FAILED_MISSING_SPLIT sobre el fusionado Quedaron atributos de split en el manifiesto §2.3 y Fusión de splits, §5
Instala y muere en System.loadLibrary extractNativeLibs="false" con las .so comprimidas zipalign -c -P 16 4, y comprobar compress_type de las entradas lib/
Arranca y se cierra sin traza La aplicación consulta sus propios splits en ejecución §9.1. No tiene arreglo dentro del alcance de este corpus

El dato que acota el problema: cruzando todas las rutas de todos los splits de cuatro contenedores del corpus, los conflictos reales son como mucho seis ficheros —manifiesto, tabla, los tres de firma y classes.dex con feature modules—; nada de res/, lib/ ni assets/ colisiona con contenido distinto (Fusión de splits, §2.2).

8. Formatos

8.1 El fichero no es lo que dice ser (reproducido)

Qué pasa aapt2 dice apksigner dice
No es un ZIP error: failed opening zip: Invalid file. ApkFormatException: Malformed APK: not a ZIP archive
Es un ZIP sin AndroidManifest.xml error: could not identify format of APK. ApkFormatException: Missing AndroidManifest.xml

La segunda fila aparece constantemente al pasar el contenedor —un .xapk, un .apkm— a una herramienta que espera un APK; la detección estructural del paso 0 de Pipeline de análisis lo resuelve antes de llegar aquí.

8.2 resources.arsc que no parsea (reproducido)

Provocado inflando el campo size del chunk raíz, y truncando el fichero:

size_grande.apk: error: corrupt resources.arsc: chunk's data extends past the end of the document (type=02 header_size=12 size=4572644).
truncado.apk:   error: corrupt resources.arsc: chunk's data extends past the end of the document (type=02 header_size=12 size=3572644).

Un chunk declara un tamaño mayor que lo que queda de fichero: la misma familia de fallo que las issues de apktool de §5.2, y el mensaje da los tres campos de la cabecera, que es lo que hace falta para localizarlo con xxd. Lo que aapt2 tolera y sorprende: con packageCount = 99 en un fichero de un paquete, aapt2 dump resources lo lee entero sin protestar —su lector no confía en ese contador, recorre hasta que se acaban los datos—. Un lector propio que sí confíe en él leerá fuera de rango.

El caso legítimo que rompe lectores estrictos: cinco splits del SAI de Netflix traen un resources.arsc de 40 bytes exactos:

00000000: 0200 0c00 2800 0000 0000 0000 0100 1c00  ....(...........
00000010: 1c00 0000 0000 0000 0000 0000 0001 0000  ................
00000020: 1c00 0000 0000 0000                      ........

RES_TABLE_TYPE = 0x0002, headerSize = 12, size = 40, packageCount = 0, seguido de un string pool vacío de 28 bytes. Es una tabla válida: aapt2 dump resources responde solo Binary APK y sale con 0. Pero rompe cualquier lector que asuma al menos un paquete. Y en el mismo contenedor, split_voip.apk no tiene resources.arsc en absoluto.

8.3 AXML malformado (reproducido)

Los tres mensajes salen del mismo código de AOSP (ResourceType.cpp) que usa el dispositivo, así que reproducirlos con aapt2 predice lo que dirá el instalador:

Provocado Salida literal
size del chunk raíz mayor que el fichero W ResourceType: Bad XML block: header size 8 or total size 25572 is larger than data size 21476
Fichero truncado a la mitad W ResourceType: Bad XML block: header size 8 or total size 21476 is larger than data size 10738
Tipo del chunk raíz cambiado a 0x00FF W ResourceType: Bad XML block: expected root block type 3, got 255

Los tres van seguidos de <fichero>: error: failed to parse binary AndroidManifest.xml: failed to initialize ResXMLTree., que en el dispositivo es INSTALL_PARSE_FAILED_BAD_MANIFEST o …_MALFORMED.

8.4 DEX con checksum inválido (reproducido)

Corrompiendo los bytes 8 a 11 —el Adler-32— de un classes.dex del corpus y volviendo a ejecutar dexdump -f:

E dexdump : dexdump.cc:2037 Failure to verify dex file 'checksum_malo.dex': Bad checksum (73027441, expected efbeadde)

Cómo se lee. El primer número es el checksum calculado sobre el contenido; el segundo, precedido de expected, es el que estaba escrito en la cabecera — la redacción es contraintuitiva, porque expected es lo que el fichero afirma, no lo que debería ser.

El detalle que enseña la estructura. Corrompiendo en cambio la signature SHA-1 —bytes 12 a 31— el mensaje es también de checksum, Bad checksum (709a6bcb, expected 73027441), porque el Adler-32 cubre todo lo que va detrás del byte 11, incluida la signature: tocar la firma rompe primero el checksum. Y con la magic rota, Expected valid zip or dex file. Aparece al editar un DEX a mano con un editor hexadecimal sin recalcular la cabecera — el ensamblador smali la recalcula solo; un sed sobre el binario, no.

9. Síntomas de protección activa que se confunden con errores de herramienta

Los cuatro comparten una propiedad: el pipeline no falla en ningún paso y el problema aparece en ejecución. Aquí se detectan y se informan; neutralizarlos está fuera del alcance de este corpus, y el mecanismo de cada uno está en Anti-análisis y hardening y Packers.

9.1 · La aplicación instala, arranca y se cierra. No es un fallo del reempaquetado: el APK verifica, alinea e instala. Suele ser una comprobación de integridad en ejecución: la aplicación compara la huella SHA-256 de su propio certificado —vía PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES)— contra una constante, o el digest de una entrada del ZIP, o consulta una atestación remota. Refirmar cambia la huella por construcción, así que la comprobación falla siempre, y que cambió se ve con apksigner verify --print-certs sobre el original y sobre el modificado. Otra variante: un APK fusionado en el que la aplicación consulta sus propios splits (ApplicationInfo.splitSourceDirs, splitNames, SplitInstallManager) y los encuentra vacíos —la issue #211 de APKEditor, abierta desde septiembre de 2025 sin respuesta, recoge una veintena de aplicaciones fusionadas desde Play que caen con segfault en libpairipcore.so en Android 14 y 15.

9.2 · Funciona en emulador y no en dispositivo, o al revés. Detección de entorno: anti-emuladorro.product.model, ro.kernel.qemu, /dev/qemu_pipe, el número de serie, los sensores— o, al revés, anti-debug/anti-root que solo se dispara en el dispositivo real. Los indicadores sobreviven a la ofuscación porque son cadenas que llegan al sistema, así que dexdump -d classes.dex | grep -oE "ro\.kernel\.qemu|/dev/qemu_pipe|goldfish|TracerPid" basta.

9.3 · Decompila, pero el código no tiene sentido. Cuatro causas que hay que separar antes de sacar conclusiones:

Lo que se ve Causa Cómo se distingue
Métodos con Code decompiled incorrectly o JADX ERROR Límite del decompilador El marcador está ahí y se ve. Es §6
La aplicación habla por red y no hay ni una URL en el DEX Cifrado de strings de un ofuscador comercial dexdump -d | grep -oE 'https?://…' no devuelve casi nada
Todo el flujo es un switch gigante dentro de un while Aplanamiento de control de flujo La estructura es uniforme en muchos métodos a la vez
classes.dex minúsculo, un .so grande y un assets/ opaco Packer: lo decompilado es el stub §6.4 de Pipeline de análisis

La regla. Antes de concluir «este código es raro» hay que descartar que el problema sea el decompilador con el recuento de §6.6: un método marcado como incoherente no es evidencia de nada.

9.4 · Funciona la primera vez y luego no. Un packer que descifra el payload y lo cachea, o una atestación remota que ya ha reportado el dispositivo. Lo relevante es lo negativo: no es un fallo de reempaquetado, porque un APK mal reempaquetado falla de forma determinista.

10. Árbol de diagnóstico

   ¿QUÉ HA PASADO?
        │
        ├─▶ NO INSTALA ───────────────────────────────────────────────────
        │     ¿el mensaje empieza por INSTALL_PARSE_FAILED_?
        │       sí → el fichero no se pudo leer                      §2.2
        │            NO_CERTIFICATES / INCONSISTENT_CERT ─▶ firma     §3
        │            MANIFEST_MALFORMED ─▶ ¿atributos de split?       §2.3
        │            RESOURCES_ARSC_COMPRESSED ─▶ nada local lo ve    §2.3
        │       no → el sistema lo rechaza                            §2.4
        │            UPDATE_INCOMPATIBLE ─▶ refirmado: desinstalar
        │            MISSING_SPLIT ─▶ install-multiple, o sanear manifiesto
        │            NO_MATCHING_ABIS ─▶ falta el split de ABI
        │     ¿ni siquiera llega a un código INSTALL_*? ─▶ apksigner  §3.1
        │
        ├─▶ INSTALA Y PETA ───────────────────────────────────────────────
        │     ¿arranca y se cierra sin traza?     ─▶ integridad en ejec. §9.1
        │     ¿muere en System.loadLibrary?       ─▶ .so comprimidas     §7
        │     ¿NoClassDefFoundError?              ─▶ DEX mal renumerado   §7
        │     ¿falla solo la pantalla parcheada?  ─▶ verificación de ART:
        │                                            banco de registros
        │                                            (02-modificación §5.2)
        │     ¿faltan textos o iconos?            ─▶ tabla sin fusionar   §7
        │     ¿va en emulador y no en real?       ─▶ detección de entorno §9.2
        │
        ├─▶ NO DECOMPILA / NO DESEMPAQUETA ───────────────────────────────
        │     apktool "Could not find framework resources" ─▶ apktool if §5.2
        │             "Could not decode arsc file"         ─▶ -r, o APKEditor
        │             "Execution failed (exit code = 1)"    ─▶ log de aapt2 §5.3
        │     jadx    exit 2, "No classes for decompile"    ─▶ ¿es un APK? §8.1
        │             OutOfMemoryError o cuelgue            ─▶ -Xmx, -j    §6.5
        │     aapt2   "could not identify format of APK" ─▶ es el contenedor §8.1
        │             "corrupt resources.arsc" §8.2 · "ResXMLTree" §8.3
        │
        └─▶ DECOMPILA MAL ────────────────────────────────────────────────
              PRIMERO: contar los marcadores                    §6.2, §6.6
              ¿jadx salió con 3? → hay huecos, y sabe cuántos
              ¿"Code decompiled incorrectly"? → ese Java puede mentir §6.3
              ¿el .java entero es una traza de pila? → clase fallida  §6.4
              descartado el decompilador:
              ¿ni una URL en el DEX?          ─▶ strings cifrados     §9.3
              ¿switch gigante en todos lados? ─▶ flujo aplanado       §9.3
              ¿DEX minúsculo + .so grande?    ─▶ packer: es el stub   §9.3
              ¿nombres a.b.c?                 ─▶ R8: buscar mapping.txt

Fuentes

Todas las URL se consultaron el 12 de agosto de 2026. Los ficheros de AOSP son de la rama main y cuelgan de https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/, descargados con ?format=TEXT y decodificados.

  1. AOSP — core/java/android/content/pm/PackageManager.java · https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/java/android/content/pm/PackageManager.java · íntegras las tablas de §2.2 y §2.4 con sus valores y descripciones, la marca @hide de todos, y la comprobación de que INSTALL_FAILED_MULTI_ARCH_NOT_SUPPORTED no existe.
  2. AOSP — los cinco ficheros que producen los errores de §2.3 y §2.4, bajo la misma raíz: core/java/android/content/pm/parsing/ApkLiteParseUtils.java (sitios y mensajes de NOT_APK, BAD_MANIFEST, UNEXPECTED_EXCEPTION y MANIFEST_MALFORMED); core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java (las reglas de validateName() y el límite de 223 caracteres); core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java junto a core/java/android/content/pm/parsing/result/ParseInput.java (el error diferido RESOURCES_ARSC_COMPRESSED, su ChangeId 132742131 con @EnabledAfter(targetSdkVersion = Q) y el texto del error); core/java/android/util/apk/ApkSignatureVerifier.java (NO_CERTIFICATES e INCONSISTENT_CERTIFICATES); y services/core/java/com/android/server/pm/ con PackageInstallerSession.java, InstallPackageHelper.java y PackageManagerServiceUtils.java (MISSING_SPLIT, UPDATE_INCOMPATIBLE y VERSION_DOWNGRADE, incluido el TODO del propio AOSP sobre la reutilización del segundo).
  3. jadx — código fuente, commit e738a26 del 5 de agosto de 2026, repositorio clonado y leído directamente: codegen/MethodGen.java, codegen/utils/CodeGenUtils.java, dex/attributes/nodes/JadxCommentsAttr.java, dex/nodes/ClassNode.java, utils/ErrorsCounter.java y jadx-cli/…/JadxCLI.java · https://github.com/skylot/jadx/blob/master/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java · todos los literales de §6.2, el mecanismo de tres niveles de §6.3, el comportamiento de clase fallida de §6.4 y los códigos de salida de §6.6.
  4. jadx — issues cerradas #2676 y #2744 · https://github.com/skylot/jadx/issues/2676 · las explicaciones de causa raíz de skylot sobre el consumo de memoria por error-por-instrucción y el desbordamiento de pila en la inferencia de tipos (§6.5), citadas literalmente.
  5. Apktool — código fuente, commit 79b6338 del 10 de agosto de 2026 (rama main, 3.x), repositorio clonado · https://github.com/iBotPeaches/Apktool/tree/main/brut.apktool/apktool-lib/src/main/java/brut/androlib/exceptions y brut.j.util/src/main/java/brut/util/OS.java · la tabla de §5.2, el mensaje Execution failed (exit code = …) de §5.3, el uso de --legacy, Legacy aapt is no longer supported. y la comprobación por inspección de que apktool no alinea (§5.4).
  6. Apktool — tags v2.7.0 y v2.9.3 · https://raw.githubusercontent.com/iBotPeaches/Apktool/v2.7.0/brut.apktool/apktool-lib/src/main/java/brut/androlib/err/CantFindFrameworkResException.java · las cadenas 2.x de §5.1.
  7. Apktool — issues cerradas #3036, #2989, #2824, #2836, #3199 y #3298 · https://github.com/iBotPeaches/Apktool/issues/3036 · las explicaciones de causa raíz de iBotPeaches sobre tamaños de chunk mentirosos, entradas duplicadas y tablas sparse (§5.2, §5.3). La ausencia de --force-manual-size se verificó sobre los 2.376 commits de todas las ramas y sobre https://apktool.org/docs/cli-parameters/.
  8. Apktool — FAQ oficial · https://apktool.org/docs/faq/ · las citas literales de §5.4.
  9. Ejecuciones locales sobre el corpus — 12 de agosto de 2026 con build-tools 37.0.0 (apksigner 0.9, zipalign, aapt2 2.20-15087165, dexdump), OpenJDK 21.0.10 y Python 3.12.10 sobre macOS Darwin 25.5.0. De aquí salen todos los mensajes marcados (reproducido): §2.3, §3.2 a §3.5, §4.1, §4.2 y §8.1 a §8.4. Provocados sobre copias de org.fossify.calculator_1.4.0.apk y sobre com.netflix.mediaclient.sai.zip en el scratchpad. En ningún momento se ha escrito en el corpus.
  10. Documentos de este corpus · Alineación y zipalign (§4.1, §4.3), Esquemas v1, v2 y v3 (§3.3), Formato DEX (§8.4), Tabla de recursos (§8.2) y Decompiladores a Java (§6.1, cuya sección 3.5 este documento corrige) y Comparativa y selección (§4.3, el reparto de esquemas de firma entre las trece herramientas, citado en §3.3).