Gestión y monitoreo del espacio de almacenamiento en Android: DeviceStorageMonitorService

En el ecosistema de Android, el agotamiento del espacio de almacenamiento es un estado crítico que puede comprometer la estabilidad del sistema, impidiendo la sincronización de bases de datos o la actualización de aplicaciones. Para mitigar estos riesgos, el sistema operativo implementa un mecanismo de vigilancia activa a través del DeviceStorageMonitorService.

Este servicio, ubicado en frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java, se encarga de supervisar el espacio libre en las particiones de datos. Cuando el almacenamiento disponible cae por debajo de ciertos umbrales predefinidos (generalmente el 10% del total), el sistema dispara alertas al usuario medianet la notificación "Poco espacio de almacenamiento" y limita ciertas funciones del sistema.

Inicialización del servicio

El servicio es registrado por el SystemServer durante la fase de arranque de los servicios secundarios del framework. La integración se realiza de la siguiente manera:

// Dentro de SystemServer.java
private void iniciarServiciosDeAlmacenamiento(SystemServiceManager ssm) {
    ssm.startService(DeviceStorageMonitorService.class);
}

Al iniciarse, el constructor configura un hilo secundario (HandlerThread) para ejecutar las tareas de verificación sin bloquear el hilo principal del sistema:

public DeviceStorageMonitorService(Context ctx) {
    super(ctx);
    
    // Configuración de un hilo dedicado para el monitoreo
    HandlerThread storageThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
    storageThread.start();

    mHandler = new Handler(storageThread.getLooper()) {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_CHECK_STORAGE) {
                verificarEspacio();
            }
        }
    };
}

Lógica de verificación y umbrales

El método principal check() (o verificarEspacio() en nuestro ejemplo) recorre todos los volúmenes privados montados. Android utiliza el StorageManager para determinar qué cantidad de bytes se consideran "bajos" o "críticos".

Si el espacio utilizable es inferior al 150% del umbral de "espacio bajo", el servicio intenta liberar espacio automáticamente eliminando archivos de caché a través del PackageManagerService.

private void verificarEspacio() {
    StorageManager storageSrv = getContext().getSystemService(StorageManager.class);
    
    for (VolumeInfo vol : storageSrv.getWritablePrivateVolumes()) {
        File ruta = vol.getPath();
        long limiteCritico = storageSrv.getStorageFullBytes(ruta);
        long limiteBajo = storageSrv.getStorageLowBytes(ruta);
        long espacioDisponible = ruta.getUsableSpace();

        // Intento de limpieza automática si nos acercamos al límite bajo
        if (espacioDisponible < (limiteBajo * 1.5)) {
            IPackageManager pms = IPackageManager.Stub.asInterface(
                    ServiceManager.getService("package"));
            try {
                // Solicitar al sistema que libere caché para alcanzar el doble del umbral bajo
                pms.freeStorage(vol.getFsUuid(), limiteBajo * 2, 0);
            } catch (Exception e) {
                Log.e(TAG, "Error al intentar liberar caché", e);
            }
        }

        evaluarCambioDeEstado(vol, espacioDisponible, limiteBajo, limiteCritico);
    }

    // Programar la siguiente verificación
    if (!mHandler.hasMessages(MSG_CHECK_STORAGE)) {
        mHandler.sendEmptyMessageDelayed(MSG_CHECK_STORAGE, INTERVALO_VERIFICACION);
    }
}

Cálculo de límites en StorageManager

Los umbrales no son estáticos; se calculan dinámicamente basándose en el tamaño total del disco y en configuraciones globales del sistema. El StorageManager define estos valores consultando Settings.Global.

  • Umbral Bajo: Por defecto, es el 10% de la capacidad total, con un tope máximo (usualmente 500 MB) para evitar reservas excesivas en dispositivos de gran capacidad.
  • Umbral Crítico (Full): Es el espacio mínimo para evitar errores de SQLite, generalmente configurado en 1 MB.
// Lógica simplificada de StorageManager para calcular el umbral bajo
public long obtenerBytesBajos(File path) {
    int porcentajeDefecto = Settings.Global.getInt(mResolver,
            Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, 10);
    long bytesPorPorcentaje = (path.getTotalSpace() * porcentajeDefecto) / 100;

    long bytesMaximos = Settings.Global.getLong(mResolver,
            Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, 500 * 1024 * 1024);

    return Math.min(bytesPorPorcentaje, bytesMaximos);
}

Ajuste de parámetros mediante comandos Shell

Es posible modiifcar el comportamiento del monitor de almacenamiento en tiempo de ejecución utilizando el comando settings a través de ADB. Esto es útil para pruebas de rendimeinto o depuración en entornos de desarrollo:

# Cambiar el porcentaje de umbral al 5%
adb shell settings put global sys_storage_threshold_percentage 5

# Ajustar el límite máximo de reserva a 1GB (en bytes)
adb shell settings put global sys_storage_threshold_max_bytes 1000000000

Estos valores persisten en la base de datos de SettingsProvider y son consultados por el servicio cada vez que realiza una ronda de verificación de almacenamiento.

Etiquetas: Android Framework DeviceStorageMonitorService StorageManager Android Internals java

Publicado el 10-4 06:37