Portal Mini v2: ADB permanente sin Meta y control total desde Home Assistant

En el post anterior monte el Portal Mini como wallpanel de Home Assistant, funcionaba, pero me quedaron dos cosas pendientes: el ADB se apagaba en cada reinicio y habia que reactivarlo a mano, y el debloat de Meta era un campo de minas donde un paso en falso te dejaba sin acceso. Desde entonces he dado con un flujo mucho mas limpio y ademas he integrado el Portal en HA como dispositivo nativo via MQTT.

Aviso de entrada: aquí se elimina la cuenta de Facebook del dispositivo y desvincula el Portal de Meta.

Todo lo que sigue esta ejecutado sobre un Portal Mini real (DT90GB, Android 10 / SDK 29) con adb.

Por que el ADB se apagaba: el entitlement de Meta

Lo primero es entender el problema de raiz, en los firmwares recientes aparece un aviso que invita a activar ADB en Settings > Debug > ADB Enabled, pero para la mayoría ese submenu no existe. El motivo: el permiso de ADB es un entitlement que Meta gestiona desde sus servidores y solo habilita cuando hay cuenta vinculada, en fastboot se ve como una linea ADB allowed: false.

Si el dispositivo no tiene una cuenta de Facebook vinculada, no hay identidad contra la que consultar ese entitlement y ADB deja de apagarse solo. No es magia, es quedarse sin la cuenta que Meta usa para decidir si tener ADB o no.

Un detalle importante que confunde a mucha gente: si haces adb devices y el Portal sale como unauthorized, ADB ya esta activo. Sin ADB, el dispositivo no aparece en absoluto. unauthorized solo significa que falta el handshake de la clave RSA; se resuelve autorizando en pantalla, o si el host ya tiene su adbkey de antes, con un simple:

adb reconnect offline
adb devices -l

Fase 1: ADB permanente y fuera Meta con el portal-kit

En vez del debloat manual del post anterior, aqui se usa el paquete de provisionamiento del proyecto starbrightlab/immortal. Ojo con esto: hay que usar el portal-kit completo, no el APK suelto. El APK se instala como app normal; el kit hace la comunicacion a nivel de sistema via ADB (launcher, screensaver, permisos, verifier) y, sobre todo, activa development_settings_enabled 1, que es lo que hace aparecer el menú de ajustes de Android que Meta esconde. Sin ese cableado no tienes atajo a los ajustes de cuentas, que es lo que necesitas para el paso siguiente.

Descarga y verificacion del kit (release v1.64):

curl -sL -o portal-kit.zip \
  https://github.com/starbrightlab/immortal/releases/download/v1.64/portal-kit.zip
shasum -a 256 portal-kit.zip

El provision.sh hace dos preguntas interactivas (bloquear OTA, restaurar Alexa). Para una ejecucion limpia y no interactiva conviene fijarlas en config.env (no por variables de entorno, porque el script pisa el entorno al cargar la config):

DISABLE_OTA=true
RESTORE_ALEXA=false

Y se lanza:

ANDROID_SERIAL=819LCM01Z100NK11 ./provision.sh < /dev/null

En un par de minutos deja: Immortal como launcher, screensaver de marco de fotos, Shizuku instalado, el verifier de instalacion de Meta desactivado, las OTA bloqueadas (para que una actualizacion futura no deshaga el montaje) y el snapshot del launcher/screensaver originales guardado para poder revertir.

Aqui va la diferencia con el metodo manual del post 1: este flujo no toca com.facebook.portal.webview ni los paquetes de ajustes. El kit sabe que no debe, así que te ahorra los dos factory resets que me comi la primera vez. El WebView de Meta sigue intacto (HA lo necesita para renderizar), y el acceso a los settings de Android lo consigues por el development_settings_enabled en vez de a base de arriesgarte con el debloat.

Quitar la cuenta de Facebook

Con los ajustes de Android ya accesibles, se llega al menu de cuentas. Por ADB es mas reproducible que ir por la UI de Immortal:

adb shell am start -a android.settings.SYNC_SETTINGS

Eso abre el AccountDashboardActivity de Android (no la UI de Portal), donde salen cuatro cuentas aloha. Sin root no hay comando para borrarlas (AccountManager no expone nada al shell), asi que hay que conducir la UI: pulsar la cuenta, "Quitar cuenta", confirmar. Como la lista se recompone tras cada borrado lo suyo es leer el arbol de vistas con uiautomator dump en cada pasada y resolver las coordenadas desde el XML. En el material hay un script en Python que automatiza las cuatro, releyendo las cuentas entre borrado y borrado.

El hallazgo: no es quedarse sin cuentas, es quedarse sin UNA cuenta

Y aqui esta la parte que no habia visto documentada en ningun sitio. Tras reiniciar, Meta regenera tres de las cuatro cuentas. Las que vuelven son cuentas de servicio locales sin identidad (aloha_hw_user, aloha_pl_user, aloha_device_user). La que no vuelve es la clave: 1244553216 / com.facebook.aloha.privowner, cuyo nombre es un ID numerico de Facebook, o sea la cuenta real vinculada al dispositivo, la que lleva el entitlement de servidor.

Es decir, el mecanismo real del truco no es "quedarse sin cuentas", es quedarse sin la cuenta privilegiada. Lo cual sugiere que el paso 4 podria reducirse a borrar unicamente privowner, aunque yo borre las cuatro tal como indica el flujo original y no lo he aislado.

dumpsys account | grep -c privowner devuelve 1 aunque la cuenta ya no este, porque el RegisteredServicesCache sigue listando el authenticator. Hay que anclar el grep a las lineas de cuenta reales:

adb shell dumpsys account | grep -E "^\s+Account \{"

La prueba de fuego

adb reboot
adb wait-for-device
adb devices -l

Y el Portal vuelve como device, ya autorizado, sin pasar por ningun menu de depuracion:

adb shell getprop init.svc.adbd        # running
adb shell settings get global adb_enabled   # 1
adb shell getprop persist.sys.usb.config    # adb

ADB persiste al reinicio del Portal.

Fase 2: el Portal como dispositivo nativo de Home Assistant

Con ADB permanente, la integracion en HA da un salto respecto al wallpanel del post 1. En vez de solo mostrar un dashboard, el proyecto RoadRunner-1024/portal-ha-bridge convierte el Portal en un dispositivo de HA completo via MQTT auto-discovery (sin YAML para las entidades).

Lo que expone, entre otras cosas: pantalla (dormir/despertar), camara e interruptor maestro, streaming RTSP H.264, deteccion de movimiento, sensor de presencia (usando la deteccion facial propia de Meta), luz ambiente, nivel de sonido, brillo, volumen y mute, silenciar microfono y botones de tono/timbre. Ademas trae intercom Portal-a-Portal por el mismo broker.

Requisitos: el Portal con ADB (fase 1), un broker MQTT (el add-on Mosquitto de HA va perfecto) y tu instancia de HA.

Instalacion:

curl -fsSL https://raw.githubusercontent.com/RoadRunner-1024/portal-ha-bridge/main/provision.sh -o provision.sh
chmod +x provision.sh
./provision.sh --serial 819LCM01Z100NK11

El provisionador concede por ADB una serie de permisos que no se pueden dar desde la UI del Portal: WRITE_SECURE_SETTINGS (para auto-activar el servicio de accesibilidad que duerme la pantalla), CAMERA, RECORD_AUDIO, READ_LOGS y varios app-ops. Los siete checks salieron en verde a la primera (release v1.18.0).

Un detalle de diseño interesante: el sensor de presencia no implementa deteccion propia. Hace tail del logcat buscando el heartbeat que el PresenceManager de Meta emite cada ~30 segundos, pero solo mientras hay alguien delante. Beats frescos = presente; ~50 segundos sin beat = ausente. Por eso pide READ_LOGS. Y por eso es importante que en la fase 1 NO desactivaras los paquetes de presencia de Meta: si los tumbas, el sensor se queda sin heartbeat que seguir, yo los deje activos a propósito.

Ruido esperado en el log (y una confirmacion bonita)

Al arrancar veras errores MQTT con tag aloha.*. No son del bridge: son los servicios de Meta intentando hablar con los servidores de Facebook sin la cuenta privowner que borramos en la fase 1:

aloha.UserMqttConnection: connection/lost; reason=FAILED_CONNECTION_REFUSED_NOT_AUTHORIZED
aloha.VoipStackUtils: Going to kill all active call ... due to MQTT auth error.

Es inofensivo (el bridge usa su propio cliente contra tu broker local) y de paso confirma por una via independiente que las llamadas de Portal están muertas: la propia pila de Meta las mata por error de autenticacion. Al depurar, distingue tags: PortalHA es el bridge, aloha.* es Meta.