6.1 KiB
Cómo está hecha la APK de Oasis
Análisis de la APK oficial de epsylon (oasis-v1.3.3.apk, sha256 4e8d60d4…5550d)
con REA 6.7.0 y JADX headless 0.7.1, el 20/10/2026. Lo que se cita sale del código
decompilado de com.solarnethub.oasis.OasisActivity; las salidas completas están
en OASIS_APK/ANALISIS_REA/ (fuera del repo).
Las piezas
La app es una envoltura Android pequeña (cuatro clases propias) alrededor de un servidor Node.js que corre dentro del móvil, con nodejs-mobile.
APK
├── classes.dex OasisActivity pantalla: WebView, extracción, arranque de node
│ OasisService servicio en primer plano (sigue vivo con la app cerrada)
│ OasisAudioBridge, OasisStreamDecoder micro, altavoz y cámara del Teléfono
├── lib/*/libnode.so node para el móvil
└── assets/nodejs-project.zip
└── nodejs-project/ main.js, package.json, src/ (backend, vistas, CSS, node_modules)
La interfaz no es nativa: es HTML que sirve node en http://127.0.0.1:3000 y que la
WebView enseña. Todo lo que vemos (vistas, CSS, textos) va dentro del zip.
El arranque
onCreate:
isExtractionNeeded(): mira si hay que descomprimir.configureWebView()y la pantalla de carga.- En otro hilo:
copyNodeProjectIfNeeded()y despuésstartNode(), que lanzanode nodejs-project/main.jsy el servicio en primer plano. - Cuando node responde en
127.0.0.1:3000, la WebView carga la página.
Cuándo se descomprime el zip
// copyNodeProjectIfNeeded()
File file2 = new File(file, ".version");
String appVersion = getAppVersion(); // versionName + "." + versionCode
String strTrim = file2.exists() ? readFile(file2).trim() : null;
if (file.exists() && appVersion.equals(strTrim)) {
Log.i("OASIS", "Node project already up to date (v" + appVersion + ")");
return;
}
...
stashLegacyState(file3, file4); // copia src/configs a .configs-stash
deleteRecursive(file);
extractZipAsset(ASSET_ZIP, ...);
restoreLegacyState(file4, file3); // vuelve a poner los configs de antes
writeFile(file2, appVersion);
Solo se descomprime si cambia versionName.versionCode. Una APK nueva con el mismo
nombre y número de versión arranca con el código de la vez anterior, aunque dentro
lleve otro. Eso es lo que pasaba con nuestras APK de prueba: el versionCode solo
subía con cada commit, y al instalar una APK hecha sobre el mismo commit (o con
cambios sin commitear) se veía el CSS y las vistas de antes. Desde el 20/10/2026
sobre_oficial.sh pone una huella del código en el versionName.
Al descomprimir se conservan los ficheros de src/configs (la config de cada cual:
tema, idioma, workflow, colores). Consecuencia: si cambiamos la config de serie en
configs/oasis-config.json, solo la ve quien instala de cero; quien actualiza se
queda con la suya.
La WebView no guarda caché
// configureWebView()
settings.setCacheMode(2); // LOAD_NO_CACHE
this.webView.clearCache(true);
Cada página y cada hoja de estilo se piden de nuevo al node del móvil. Si después de instalar se ve CSS viejo, no es la caché: es que node está sirviendo el código viejo (ver el punto anterior).
La WebView solo navega dentro de 127.0.0.1:3000 y localhost:3000. Los enlaces
a una .apk los descarga ella misma y el resto de enlaces http(s) los abre en otra
app (el navegador). Hay rutas que intercepta para el Teléfono (/phone/speaker,
/phone/accept-speaker).
El backend por dentro
rea analyze-javascript-application sobre el nodejs-project oficial, sin
node_modules (463 ficheros JavaScript). Del grafo de imports, contando solo
ficheros del dev:
backend/backend.jslo cose todo: hace 896 de los imports. Rutas, middleware y la llamada a cada modelo y cada vista pasan por él.- Las capas van
backend → views(568 imports),backend ↔ models(218 y 223) ymodels → configs(180). Las vistas casi no importan nada del backend (3). - Lo más importado, y por tanto lo que más rompe si cambia:
views/main_views.js(217),models/typed_log.js(194),configs/config-manager.js(152),views/clearnet_view.js(116),backend/dedupe.js(112).
Cuadra con la lista de lo que no se toca de la guía: un cambio nuestro en
backend.js o en main_views.js tropieza con casi todo lo demás en cada versión.
Por eso lo nuestro va en fork_routes.js, mobile_ui.js y las hojas propias, que
el dev no importa.
Lo que hace nuestra APK encima
android/scripts/sobre_oficial.sh coge esta APK tal cual y cambia:
assets/nodejs-project.zip: nuestrosrc/,main.jsypackage.json, con elnode_modulesdel dev (el podado para el móvil).- El paquete, a
net.laenre.oasis, para que conviva con la oficial. - El icono, por el adaptativo de
android/icono/. versionName, a<versión>-hacklab.<huella del código>.
La parte Android (Java, puente de audio, servicio) es la del dev sin tocar.
Repetir el análisis
npm install --global --prefix ~/.local rea-agents@6.7.0
# JADX headless 0.7.1, comprobando la huella que fija REA
curl -fL -o jadx-headless-mcp-0.7.1-all.jar \
https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | sha256sum -c -
export REA_JADX_MCP_JAR=$PWD/jadx-headless-mcp-0.7.1-all.jar JAVA_HOME=/opt/android-studio/jbr
rea inspect-android-package oasis-v1.3.3.apk
rea inspect-android-class oasis-v1.3.3.apk com.solarnethub.oasis.OasisActivity
rea inspect-android-method oasis-v1.3.3.apk com.solarnethub.oasis.OasisActivity copyNodeProjectIfNeeded
# el backend: sacar nodejs-project del zip y quitarle src/server/node_modules
# (con ellas, 16.500 ficheros, se corta por tiempo)
rea --json analyze-javascript-application /ruta/absoluta/nodejs-project-sin-node_modules
Con cada versión nueva del dev conviene mirar otra vez copyNodeProjectIfNeeded,
configureWebView y el manifiesto: si cambia la forma de extraer o la caché,
sobre_oficial.sh puede tener que cambiar también.