Formatos y extensiones: qué es este fichero

Antes conviene leer «Anatomía de un APK»

referencia técnica · Actualizado el 25 de septiembre de 2026

1. El problema

Alguien te pasa un fichero para que mires qué hace una aplicación. Se llama com.whatsapp.apks. Ni es un APK ni es lo que bundletool llama un APK Set: es un .xapk de APKCombo al que le cambiaron la extensión, y dentro hay catorce APK distintos de los cuales solo uno lleva el código.

Esto no es una anécdota. De los dieciséis ficheros con extensión .apks del «muestrario», catorce son otra cosa. La «sección 5 · La extensión miente, y está medido» lo mide.

El lío tiene una causa concreta y bastante aburrida. Hasta 2018 una aplicación de Android era un fichero y ese fichero era un APK. Con el Android App Bundle, Google Play dejó de servir un fichero y pasó a servir un conjunto —un base.apk más los split que le tocan a ese teléfono—, pero no publicó ningún formato estándar para meter ese conjunto en algo que se pueda descargar. Así que cada sitio de descargas se inventó el suyo. APKMirror hizo el .apkm, APKCombo y APKPure hicieron el .xapk, y la aplicación Split APKs Installer se limitó a comprimir los APK en un ZIP sin más.

Ninguno de esos tres formatos está especificado en ninguna parte. Los tres son ZIP. Los tres se llaman de forma distinta según quién los exporte. Y encima conviven con los formatos que Google sí especifica: el .aab que sube el desarrollador y el .apks que produce bundletool.

Este documento responde a una sola pregunta —¿qué es este fichero y qué puedo hacer con él?— y remite al resto para el detalle. Para la estructura interna del AAB, del APK Set y de la taxonomía de split, ver «AAB, splits y contenedores»; para el ZIP que hay debajo de todos ellos, «Contenedor ZIP».

2. Tabla maestra

Todo lo que sigue es un ZIP, sin excepción. Lo que cambia es qué hay dentro y quién lo puso ahí.

Extensión Qué es de verdad Quién lo produce ¿Se instala tal cual? Especificado
.apk Una aplicación completa, o un split suelto El compilador de Android, Play, cualquiera Sí, si es completo o si van todos sus split Sí, de facto
.aab El material intermedio del que Play deriva los APK. No es instalable El desarrollador, con Gradle No, en ningún caso Sí, documentado por Google
.apks Un ZIP con todos los APK que Play podría generar, más toc.pb bundletool build-apks Solo mediante bundletool install-apks Sí, vía bundletool
.xapk ZIP plano con el base y sus split, más manifest.json APKCombo, APKPure No. Necesita un instalador de split No
.apkm Como el anterior, más info.json y firma propia del ZIP APKMirror No. Necesita su instalador No
.zip (SAI) ZIP plano, solo APK, sin metadatos Split APKs Installer, o cualquiera No No
.obb Datos de expansión que viajan fuera del APK. Obsoleto El desarrollador, antes de 2021 No. Es un fichero de datos Sí, documentado

Cinco cosas que conviene fijar antes de seguir:

  1. Ninguna de estas extensiones es obligatoria. Ni siquiera .apk: Android identifica el paquete por su contenido, no por su nombre.
  2. .aab es el único que no contiene ningún APK. Es la excepción real de la lista: lo que lleva dentro son .dex sueltos y una tabla de recursos en protobuf, no paquetes.
  3. .apks es el nombre más sobrecargado del ecosistema. La «sección 3.3 · .apks — cuatro cosas distintas con el mismo nombre» enumera las cuatro cosas distintas que se llaman así.
  4. El .apkm es el único que lleva su propia firma. Y esa firma es de APKMirror sobre el envoltorio, no de la aplicación. Confundirlas es un error de análisis frecuente.
  5. Que la extensión no coincida con el contenido no es un caso raro: es lo normal. En el muestrario, catorce de los dieciséis ficheros .apks son .xapk renombrados. La «sección 5 · La extensión miente, y está medido» lo mide y la 4 explica cómo salir de dudas sin descomprimir nada.

3. Los formatos, uno a uno

3.1 .apk — y la trampa de que un split también lo es

Un APK es un ZIP con AndroidManifest.xml y, casi siempre, resources.arsc en la raíz. Eso es todo lo que hace falta para reconocerlo.

Lo que no se ve a simple vista es que un split es también un .apk, y no se instala solo. Un config.es.apk de 300 KB tiene manifiesto, tiene extensión .apk y no tiene código: es un cuarto de aplicación. La diferencia está en un atributo del manifiesto:

BT=~/Library/Android/sdk/build-tools/37.0.0
$BT/aapt2 dump badging config.es.apk | head -1
package: name='com.google.android.keep' versionCode='220671357' versionName='' split='config.es'

split='config.es' y versionName vacío. Un base.apk no trae ni lo uno ni lo otro. La taxonomía completa —config splits, feature modules y las combinaciones de los dos— está en la «sección 4 de AAB, splits y contenedores · Taxonomía de splits».

Intentar instalar solo el base de una aplicación que espera split da un error que vale la pena reconocer de vista, porque es el más frecuente de todos:

adb install com.google.android.keep.apk
Performing Streamed Install
adb: failed to install com.google.android.keep.apk: Failure [INSTALL_FAILED_MISSING_SPLIT: Missing split for com.google.android.keep]

Reproducido el 13 de agosto de 2026 sobre un emulador Android 14 arm64-v8a, con el base.apk extraído de com.google.android.keep.apks del muestrario.

3.2 .aab — el que no se instala

El Android App Bundle es lo que el desarrollador sube a Play. Play no lo distribuye: lo usa para generar los APK de cada dispositivo. Que llegue a manos de un analista es raro y casi siempre significa que alguien se equivocó de fichero.

Tres señales lo delatan de inmediato, y las tres son diferencias con la estructura de un APK:

  • El manifiesto no está en la raíz sino en base/manifest/AndroidManifest.xml.
  • Los .dex no están en la raíz sino en base/dex/.
  • No hay resources.arsc. Hay resources.pb, que es protobuf y no tiene nada que ver.

Más un BundleConfig.pb en la raíz que ningún otro formato lleva.

La consecuencia práctica reparte a las herramientas en dos grupos, y no en el que uno supondría. jadx lo abre sin más: encuentra los .dex en base/dex/ y hasta decodifica el resources.pb. apktool no, y lo peor es cómo no lo hace —devuelve éxito y no extrae nada—; la «sección 6.1 · jadx los abre todos, y apktool ninguno» lo mide. Y un lector de resources.arsc escrito para el formato binario no sirve aquí en absoluto: protobuf no se le parece en nada.

Para instalarlo o para tratarlo como una aplicación, la vía es pasar antes por bundletool build-apks, que produce el .apks de la sección siguiente, o por --mode=universal si lo que se quiere es un APK único.

3.3 .apks — cuatro cosas distintas con el mismo nombre

Esta extensión es el desorden en estado puro. En el muestrario aparece designando cuatro cosas que no tienen nada que ver entre sí:

Lo que dice .apks Lo que es Cómo se distingue
APK Set de bundletool ZIP con toc.pb y los APK bajo splits/, standalones/, instant/… toc.pb en la raíz
.xapk renombrado ZIP plano de APKCombo manifest.json con la clave xapk_version
Exportación de SAI ZIP plano con metadatos propios de la aplicación SAI meta.sai_v1.json o meta.sai_v2.json en la raíz
ZIP plano sin más Solo APK, sin metadatos de ningún tipo No queda ninguna otra evidencia

El APK Set genuino es el único de los cuatro que está documentado. Lo produce bundletool build-apks y su firma estructural es toc.pb, un protobuf android.bundle.BuildApksResult que dice qué APK va a qué dispositivo. Su detalle está en la «sección 3 de AAB, splits y contenedores · El APK Set (.apks)».

La exportación de SAI es la que menos se conoce y la que más fácilmente se clasifica mal. Desde su versión 3.10, SAI añade a los .apks que exporta meta.sai_v1.json y el icono, icon.png, en la raíz; desde la 4.0, también meta.sai_v2.json. El v2 lleva meta_version, package, label, version_code, version_name, export_timestamp, split_apk, min_sdk, target_sdk y un array backup_components. Es un contenedor autodescriptivo y perfectamente utilizable, y un detector que no lo contemple lo tira al cajón de «zip plano» y pierde todo eso.

El muestrario no contiene ningún .apks exportado por SAI, así que esta fila no es una medición propia: sale del META-FORMAT.md del proyecto y del código que escribe esos ficheros, ApksSingleBackupTaskExecutor.java (líneas 80-170 del último commit, 55505d2, SAI 4.5), que los guarda sin comprimir y en este orden —meta.sai_v2.json, meta.sai_v1.json y, solo si consigue extraer el icono, icon.png— antes de los APK. Lo que sí está medido es que ninguno de los ficheros del muestrario lleva meta.sai_v*.json.

3.4 .xapk — APKCombo y APKPure

ZIP plano: el base y los config splits en la raíz, sin subdirectorios. Lo acompañan un manifest.json, un icon.png y —en los de APKCombo— un APKComboInstaller.url.

La clave que identifica el formato sin ambigüedad es xapk_version. El resto del manifest.json es sorprendentemente completo. De com.google.android.keep.apks del muestrario, que es un .xapk con la extensión cambiada:

xapk_version         2
package_name         com.google.android.keep
name                 Keep Notes
locales_name         {'ar': 'Keep Notes', 'de': 'Keep Notes', 'en': 'Keep Notes', …}
version_code         220671357
version_name         5.26.321.01.97
min_sdk_version      32
target_sdk_version   37
permissions          ['android.permission.ACCESS_NETWORK_STATE', …]
total_size           18144742
icon                 icon.png
split_apks           [{'file': 'com.google.android.keep.apk', 'id': 'base'}, …]

split_apks es lo que lo hace valioso: dice el id de cada fichero sin necesidad de abrir ninguno de los APK. Un inventario de un .xapk sale entero del manifest.json.

Dos detalles del nombrado que despistan: el base no se llama base.apk sino <paquete>.apk, y los config splits se llaman config.<lo que sea>.apk sin el prefijo split_. Justo al revés que en el .apkm.

Los .xapk de juegos pueden traer además los .obb de la «sección 3.7 · .obb — el heredado», en una entrada Android/obb/….

3.5 .apkm — APKMirror

La misma idea que el .xapk con dos diferencias que importan.

La primera: firma su propio ZIP. Un .apkm trae META-INF/MANIFEST.MF, META-INF/APKMIRRO.SF y META-INF/APKMIRRO.RSA en la raíz, que son una firma JAR clásica (ver «Esquemas de firma») hecha por APKMirror sobre el contenedor. Acredita de dónde salió la descarga y no tiene ninguna relación con la firma de la aplicación, que está dentro del base.apk. Un verify que se confunda de nivel da un veredicto sobre el envoltorio y lo presenta como si fuera de la aplicación.

La segunda: los nombres van al revés que en el .xapk. Aquí el base sí se llama base.apk y los config splits llevan prefijo: split_config.arm64_v8a.apk.

Su metadato es info.json, con diecisiete claves. De tv.twitch.android.app.apkm del muestrario:

apkm_version         5
apk_title            Twitch: Live Streaming 26.2.4 (120-640dpi) (Android 8.0+)
app_name             Twitch: Live Streaming
release_version      26.2.4
variant              (universal) (120-640dpi) (Android 8.0+)
release_title        Twitch: Live Streaming 26.2.4 (120-640dpi) (Android 8.0+)
versioncode          2602046
pname                tv.twitch.android.app
post_date            2025-09-25 22:42:23
capabilities         []
languages            ['af', 'am', 'ar', 'as', 'az', 'be', 'bg', 'bn', …]
arches               ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']
dpis                 ['120', '160', '213', '240', '320', '480', '640']
min_api              26
accent_color         9044ff
apk_id               10872180
release_id           10872182

apkm_version vale 5 en los ocho .apkm del muestrario y pname es el nombre del paquete. El .url de la raíz también cambia: aquí es APKM_installer.url, y en el .xapk, APKComboInstaller.url. Sirve como confirmación barata cuando el resto no basta.

3.6 El ZIP plano y SAI

El caso más simple y el que peor se identifica: un ZIP con los .apk dentro y nada más. Sin metadatos, sin firma propia, sin ningún fichero que diga qué es.

El del muestrario es com.netflix.mediaclient.sai.zip, con 39 entradas y las 39 son .apk. La ausencia total de ficheros descriptivos es literalmente lo único que lo define, y por eso en el orden de detección de la «sección 4 · Cómo saber en diez segundos qué tienes delante» va el último de los contenedores: se llega a él por descarte.

Conviene no confundir dos cosas que llevan el mismo nombre. SAI es una aplicación de Android, un instalador de split que acepta .apks, .xapk, .apkm y ZIP planos; el ZIP plano se llama «de SAI» por costumbre, porque es lo que SAI consume, no porque SAI lo genere con esa forma. Lo que SAI exporta es un .apks con los metadatos de la «sección 3.3 · .apks — cuatro cosas distintas con el mismo nombre».

La lista de formatos de entrada no está en la documentación de SAI, pero sí en su código: el selector de ficheros del instalador filtra por "zip", "apks", "xapk", "apk" y "apkm" (InstallerXDialogFragment.java, línea 196, en el último commit, 55505d2), es decir, los cuatro contenedores de esta sección más APK sueltos. Su README declara que el proyecto está en mantenimiento mínimo («SAI is kinda dead cause I have no motivation for continuing its development»), y el último commit de su rama principal es del 25 de febrero de 2021.

3.7 .obb — el heredado

Antes del AAB, la forma oficial de superar el límite de tamaño de Play eran los expansion files: hasta dos ficheros de 2 GB que viajaban fuera del APK y se descargaban aparte. El nombre sigue un patrón fijo:

[main|patch].<versionCode-de-la-primera-subida>.<nombre-del-paquete>.obb

Por ejemplo main.314159.com.example.app.obb. Se instalan en <almacenamiento-compartido>/Android/obb/<paquete>/.

Están superados desde agosto de 2021, cuando Play empezó a exigir AAB para las aplicaciones nuevas, pero siguen apareciendo en descargas de juegos antiguos, a veces dentro de un .xapk. Un .obb no es un APK: es un contenedor de datos, normalmente un ZIP o un sistema de ficheros propio del motor del juego.

El muestrario no contiene ninguno; esta sección sale de la documentación oficial, no de una medición.

3.8 Los que no son contenedores de aplicación

Aparecen en el mismo tipo de trabajo y conviene descartarlos rápido:

Extensión Qué es Cómo se reconoce
.dex Bytecode Dalvik suelto, fuera de un APK Magic dex\n035\0 (u otra versión) en el offset 0
.jar ZIP de .class de Java. A veces contiene un classes.dex .class dentro, o META-INF/MANIFEST.MF sin AndroidManifest.xml
.odex, .vdex, .oat Artefactos que produce el dispositivo al instalar, no el desarrollador Solo aparecen extraídos de un teléfono. Ver «Artefactos auxiliares»
.idsig La firma v4, que viaja junto al APK y no dentro Ver «Firma v4»
.apex Módulo actualizable del sistema, no una aplicación apex_payload.img en la raíz

4. Cómo saber en diez segundos qué tienes delante

Se mira el Central Directory del ZIP y ya está. No hace falta descomprimir nada: la lista de nombres basta para decidir, y se lee en milisegundos por grande que sea el fichero.

unzip -Z1 fichero | head -20

Y se compara con esta tabla, en este orden:

Nº Si encuentras… Entonces es…
1 toc.pb en la raíz APK Set genuino de bundletool
2 BundleConfig.pb y base/manifest/AndroidManifest.xml .aab
3 info.json y META-INF/*.RSA en la raíz .apkm de APKMirror
4 manifest.json con la clave xapk_version .xapk de APKCombo o APKPure
5 meta.sai_v1.json o meta.sai_v2.json .apks exportado por SAI
6 Varias entradas *.apk, ningún metadato y ningún AndroidManifest.xml en la raíz ZIP plano de split
7 AndroidManifest.xml en la raíz, con o sin resources.arsc (los split de ABI no lo llevan) APK suelto
8 Lo anterior más un atributo split en el manifiesto Un split, no una aplicación

El orden no es un detalle de eficiencia, es lo que hace que la regla funcione. Las evidencias se solapan: un .apkm tiene entradas *.apk en la raíz, así que también cumple la regla 6; un APK Set las tiene bajo splits/. Un detector que empiece por la regla 6 clasifica .apkm y APK Set como ZIP plano y pierde sus metadatos. Uno que empiece por la 7 no distingue una aplicación de un split.

Sobre el .aab conviene una precisión: el orden que fija la «sección 6.2 de AAB, splits y contenedores · El orden de precedencia» lo pone el último. Aquí va el segundo, y da igual, porque BundleConfig.pb no coincide con ninguna otra evidencia de la lista. Adelantarlo tiene la ventaja de que un .aab se descarta antes de intentar tratarlo como un contenedor de APK, que es lo que no se puede hacer con él.

Comprobado sobre los tres formatos del muestrario:

cd ~/muestras-apk
unzip -Z1 splits/tv.twitch.android.app.apkm | grep -v '\.apk$'
META-INF/MANIFEST.MF
META-INF/APKMIRRO.SF
META-INF/APKMIRRO.RSA
info.json
icon.png
APKM_installer.url
unzip -Z1 splits/com.google.android.keep.apks | grep -v '\.apk$'
manifest.json
APKComboInstaller.url
icon.png
unzip -Z1 splits/com.netflix.mediaclient.sai.zip | grep -v '\.apk$'

El tercero no devuelve nada: 39 entradas y las 39 son .apk. Ese vacío es la evidencia.

5. La extensión miente, y está medido

No es una precaución teórica. Clasificando los 29 contenedores del muestrario por su estructura y comparándolo con lo que dice su nombre:

cd ~/muestras-apk
python3 - <<'PY'
import zipfile, json, glob, os
def detecta(z):
    raiz = [x for x in z.namelist() if '/' not in x.rstrip('/')]
    if 'toc.pb' in raiz: return 'APK Set'
    if 'BundleConfig.pb' in raiz: return '.aab'
    if 'info.json' in raiz and any(x.startswith('META-INF/') and x.endswith('.RSA')
                                   for x in z.namelist()): return '.apkm'
    if 'manifest.json' in raiz:
        if 'xapk_version' in json.loads(z.read('manifest.json')): return '.xapk'
    if 'AndroidManifest.xml' not in raiz and any(x.endswith('.apk') for x in z.namelist()):
        return 'zip plano'
    return '?'
cuenta = {}
for f in sorted(glob.glob('*.xapk') + glob.glob('splits/*')):
    if f.endswith('.DS_Store'): continue
    with zipfile.ZipFile(f) as z:
        clave = (os.path.splitext(f)[1], detecta(z))
    cuenta[clave] = cuenta.get(clave, 0) + 1
print(f"  {'dice ser':<8}   {'es':<10} {'n':>2}")
for (ext, det), n in sorted(cuenta.items()):
    print(f'  {ext:<8} -> {det:<10} {n:>2}')
PY
  dice ser   es          n
  .aab     -> .aab        1
  .apkm    -> .apkm       8
  .apks    -> .xapk      14
  .apks    -> APK Set     2
  .xapk    -> .xapk       3
  .zip     -> zip plano   1

Catorce de los dieciséis .apks no son APK Set. Son .xapk de APKCombo con la extensión cambiada, y lo confirma su manifest.json:

unzip -p splits/com.whatsapp.apks manifest.json | python3 -m json.tool | head -3
{
    "xapk_version": "2",
    "package_name": "com.whatsapp",

Los dos .apks que sí lo son —com.ejemplo.fixture.apks y com.ejemplo.fixture-universal.apks— se generaron localmente con bundletool. Dicho de otra forma: de los contenedores que llegaron de fuera, ninguno con extensión .apks era un APK Set. Un análisis que se fíe del nombre acierta cero veces de catorce.

Los .apkm, los .xapk que sí se llaman .xapk y el ZIP de SAI se clasifican bien por estructura, así que el problema no es que la detección sea difícil: es que la extensión es un dato que aporta el sitio de descargas y no el formato.

Y cuando la estructura contradice a la extensión, lo correcto no es fallar: es continuar y dejarlo escrito en el informe. Un desajuste es información sobre la procedencia del fichero, no un error del fichero.

6. Qué se puede hacer con cada uno

La tabla que resuelve la pregunta práctica. Todo lo que sigue está ejecutado el 13 de agosto de 2026 con bundletool 1.18.3, apktool 3.0.3, jadx 1.5.6, baksmali 3.0.7 y adb 1.0.41.

Operación .apk .aab .apks .xapk / .apkm / ZIP plano
Ver el manifiesto aapt2 dump badging bundletool dump manifest --bundle Extraer el base primero Extraer el base primero
Decompilar a Java jadx -d out app.apk jadx lo abre jadx lo abre jadx lo abre
Desensamblar a smali baksmali disassemble Extraer el DEX primero Extraer el base primero Extraer el base primero
Decodificar recursos apktool d bundletool dump resources --bundle Extraer el base primero Extraer el base primero
Verificar la firma apksigner verify No aplica Sobre cada APK de dentro Sobre cada APK de dentro. No sobre el envoltorio
Instalar adb install No bundletool install-apks adb install-multiple, o un instalador de split
Fusionar en un APK único No aplica bundletool build-apks --mode=universal Requiere fusión de tablas Requiere fusión de tablas

6.1 jadx los abre todos, y apktool ninguno

Esta fila merece detalle porque contradice lo que se suele suponer, y porque las dos herramientas se comportan de forma opuesta ante la misma entrada.

jadx entra en cualquier contenedor sin que se lo pidan. Sobre el .aab del muestrario encuentra el DEX donde está —en base/dex/, no en la raíz— y lo dice en el propio Java que escribe:

/* JADX INFO: loaded from: base/dex/classes.dex */
public class MainActivity extends Activity {

Además decodifica el resources.pb en protobuf a un árbol res/values/ legible, que es exactamente lo que un lector de resources.arsc no puede hacer. Y con los contenedores de terceros no se queda corto: sobre el .xapk de la calculadora de Google escribe 3.468 ficheros .java, con código salido de varios split a la vez, y termina con código 3 por trece métodos que no pudo decompilar.

apktool hace lo contrario, y de la peor manera posible: no falla.

apktool d -f -o salida $MUESTRAS/splits/com.whatsapp.apks ; echo "EXIT=$?"
I: Using Apktool 3.0.3 on com.whatsapp.apks with 8 threads
I: Copying original files...
I: Copying unknown files...
EXIT=0
ls salida ; ls salida/unknown | head -4
apktool.yml
unknown
config.ldpi.apk
config.xxhdpi.apk
icon.png
manifest.json

Código de salida cero, ni un aviso, y cero smali. Lo único que ha hecho es copiar los catorce APK sin abrir a un directorio llamado unknown/. Con un .aab pasa lo mismo: los .pb y el AndroidManifest.xml del módulo acaban en unknown/ y el directorio smali/ ni se crea.

Es el fallo silencioso en estado puro: una herramienta que devuelve éxito habiendo entendido nada. En un pipeline automatizado que compruebe el código de salida, esto pasa por bueno y el error solo aparece más tarde, cuando alguien busca una clase que nunca se extrajo.

6.2 Extraer el base

«Extraer el base primero» quiere decir literalmente eso, y con las herramientas de siempre:

unzip -j contenedor.apks 'com.whatsapp.apk' -d ./trabajo

Recordando el nombrado de la «sección 3.4 · .xapk — APKCombo y APKPure»: en un .xapk el base se llama <paquete>.apk, en un .apkm y en un ZIP plano se llama base.apk.

Analizar solo el base no es lo mismo que analizar la aplicación. El código está casi siempre entero ahí, pero las cadenas traducidas viven en los config.<idioma>.apk, las bibliotecas nativas en los config.<abi>.apk y los módulos de funcionalidad pueden tener DEX propio. Si el análisis busca una cadena o una biblioteca y no la encuentra, la primera sospecha es que está en otro split.

Y sobre instalar el conjunto, dos errores que salen a la primera y conviene reconocer. Con todos los split correctos:

adb install-multiple org.telegram.messenger.apk config.es.apk \
                     config.arm64_v8a.apk config.xxhdpi.apk
Success

Con un juego cuyo config.<abi> no corresponde a la arquitectura del dispositivo:

adb: failed to finalize session
Failure [INSTALL_FAILED_NO_MATCHING_ABIS: INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]

Ambos reproducidos el 13 de agosto de 2026 sobre un emulador Android 14 arm64-v8a. El segundo salió al instalar los split de Google Keep del muestrario, que solo trae config.armeabi_v7a. El catálogo completo de errores de instalación está en «Diagnóstico de fallos».

Un detalle que solo se ve haciéndolo: el sistema renombra los split al instalarlos. Se le pasa config.es.apk y en el dispositivo queda split_config.es.apk, porque el nombre lo deriva del atributo split del manifiesto y no del fichero de entrada:

adb shell pm path org.telegram.messenger
package:/data/app/~~cB6DHo33I…==/org.telegram.messenger-F_5lfNc…==/base.apk
package:/data/app/~~cB6DHo33I…==/org.telegram.messenger-F_5lfNc…==/split_config.arm64_v8a.apk
package:/data/app/~~cB6DHo33I…==/org.telegram.messenger-F_5lfNc…==/split_config.es.apk
package:/data/app/~~cB6DHo33I…==/org.telegram.messenger-F_5lfNc…==/split_config.xxhdpi.apk

7. Árbol de decisión

Primero, qué es:

                       tengo un fichero
                              │
                     ¿abre como ZIP?
              ┌──────────────┴──────────────┐
             no                            sí
              │                             │
    ¿magic dex\n0NN?               mirar el Central Directory
       ┌──────┴──────┐              y aplicar la tabla de §4
      sí            no                      │
       │             │                      ▼
   DEX suelto   no es de aquí     ┌──────────────────────────┐
                (.idsig, .so,     │ 1  toc.pb        APK Set │
                 binario, .obb    │ 2  BundleConfig  .aab    │
                 que no es ZIP)   │ 3  info.json+RSA .apkm   │
                                  │ 4  xapk_version  .xapk   │
                                  │ 5  meta.sai_v*   SAI     │
                                  │ 6  varios *.apk  plano   │
                                  │ 7  manifest      APK     │
                                  └──────────────────────────┘

Un .obb que sea un ZIP («sección 3.7 · .obb — el heredado») entra por la rama del «sí» y no cumple ninguna regla de la tabla: tampoco es de aquí.

Y después, qué hacer con ello:

 APK Set   ──► bundletool extract-apks --device-spec …   para quedarse con lo que toca
               bundletool install-apks                   si hay dispositivo

 .aab      ──► bundletool build-apks --mode=universal    para obtener algo instalable
               jadx lo lee tal cual                      para leer el código

 .apkm     ──► extraer base.apk        · la firma del ZIP es de APKMirror, no de la app
 .xapk     ──► extraer <paquete>.apk   · el base NO se llama base.apk
 SAI/plano ──► extraer base.apk

 APK       ──► jadx · apktool · baksmali · apksigner verify
               pero antes: ¿tiene atributo split en el <manifest>?
               ┌──────────────┴──────────────┐
              sí                            no
               │                             │
        es un trozo de aplicación      es la aplicación
        y le falta el resto            entera

Fuentes

  1. Formato del Android App Bundle — https://developer.android.com/guide/app-bundle/app-bundle-format Consultado el 13 de agosto de 2026. De aquí salen las tres señales que distinguen un .aab de un APK en la «sección 3.2 · .aab — el que no se instala» y su fila de la tabla maestra.
  2. bundletool — documentación oficial — https://developer.android.com/tools/bundletool Consultado el 13 de agosto de 2026. De aquí sale la definición del APK Set, la sintaxis de build-apks, install-apks y extract-apks de las secciones «3.3» y «6», y el modo --mode=universal.
  3. Aefyr/SAI, META-FORMAT.md — https://github.com/Aefyr/SAI/blob/master/META-FORMAT.md Consultado el 13 de agosto y el 25 de septiembre de 2026 vía la API de GitHub. De aquí salen la descripción del .apks exportado por SAI de la «sección 3.3 · .apks — cuatro cosas distintas con el mismo nombre» —meta.sai_v1.json desde la versión 3.10 y meta.sai_v2.json desde la 4.0— y el esquema de meta.sai_v2.json.
  4. Aefyr/SAI, README.md — https://github.com/Aefyr/SAI Consultado el 13 de agosto de 2026 vía la API de GitHub. De aquí sale la naturaleza de SAI como instalador de split y la cita sobre su estado de mantenimiento de la «sección 3.6 · El ZIP plano y SAI».
  5. APK expansion files (OBB) — https://developer.android.com/google/play/expansion-files Consultado el 13 de agosto de 2026. De aquí salen el patrón de nombre, la ruta de instalación, el límite de 2 GB y el estado de obsolescencia de la «sección 3.7 · .obb — el heredado».
  6. adb — documentación oficial — https://developer.android.com/tools/adb Consultado el 13 de agosto de 2026. De aquí sale la sintaxis de adb install-multiple y adb shell pm path de la «sección 6 · Qué se puede hacer con cada uno».
  7. bundletool — ModuleSplit.java — https://github.com/google/bundletool/blob/master/src/main/java/com/android/tools/build/bundletool/model/ModuleSplit.java Consultado el 25 de septiembre de 2026. De aquí sale que los splits de bibliotecas nativas se crean sin tabla de recursos (forNativeLibraries, setResourceTable = false), y por tanto sin resources.arsc («sección 4 · Cómo saber en diez segundos qué tienes delante»).
  8. Aefyr/SAI, ApksSingleBackupTaskExecutor.java y SaiExportedAppMeta2.java, commit 55505d2 (SAI 4.5) — https://github.com/Aefyr/SAI/blob/55505d231b1390e824d1cc0c8f4fa35fd4677105/app/src/main/java/com/aefyr/sai/backup2/impl/storage/ApksSingleBackupTaskExecutor.java Consultado el 25 de septiembre de 2026. De aquí salen el orden, la compresión y la condición del icono de los metadatos del .apks de SAI («sección 3.3 · .apks — cuatro cosas distintas con el mismo nombre»).
  9. Aefyr/SAI, InstallerXDialogFragment.java, commit 55505d2 — https://github.com/Aefyr/SAI/blob/55505d231b1390e824d1cc0c8f4fa35fd4677105/app/src/main/java/com/aefyr/sai/ui/dialogs/InstallerXDialogFragment.java#L196 Consultado el 25 de septiembre de 2026. De aquí sale la lista de extensiones que acepta SAI («sección 3.6 · El ZIP plano y SAI»).