# How Omi's Foreground Task System Enables Background Recording on Mobile

> Discover how Omi's foreground task system enables reliable background audio recording on mobile by preventing OS termination and fetching location data.

- Repository: [omi/omi](https://github.com/basedhardware/omi)
- Tags: internals
- Published: 2026-02-26

---

**Omi uses the `flutter_foreground_task` plugin to maintain a persistent Android foreground service that prevents the operating system from terminating the app during background audio recording, while simultaneously fetching location data every five minutes.**

The basedhardware/omi repository implements a sophisticated foreground task architecture to ensure uninterrupted audio capture and location tracking on mobile devices. When users minimize the app or lock their screen, standard Flutter applications typically suspend execution, which would terminate ongoing recording sessions. By leveraging a dedicated foreground service defined in `app/lib/utils/audio/foreground.dart`, Omi maintains an active process that communicates bidirectionally with the main isolate while respecting Android's background execution limits.

## Service Initialization and Platform Configuration

The foreground task lifecycle begins in `app/lib/pages/home/page.dart`, where the `HomePage` widget initializes the service immediately after rendering its first frame. The initialization sequence checks the platform type, requests notification permissions, and configures Android to ignore battery optimizations.

```dart
if (!PlatformService.isDesktop) {
  await ForegroundUtil.initializeForegroundService();
  await ForegroundUtil.startForegroundTask();
}

```

Inside `app/lib/utils/audio/foreground.dart`, the `ForegroundUtil` class handles platform-specific setup on lines 75–92. This includes requesting the foreground service permission and creating a persistent notification channel required by Android for all foreground services. The utility ensures the service can run indefinitely without being killed by the OS's power management features.

## Implementing the Background Task Handler

The core logic resides in `_ForegroundFirstTaskHandler`, a private class implementing the plugin's `TaskHandler` interface at lines 17–69 of `foreground.dart`. This handler defines three critical lifecycle methods that manage the background execution:

- **`onStart`**: Invoked when the service launches, immediately triggering the first location fetch via `_locationInBackground()`
- **`onRepeatEvent`**: Executes every 5 minutes (configured via `ForegroundTaskEventAction.repeat(60 * 1000 * 5)` on lines 33–34)
- **`onReceiveData`**: Processes messages sent from the main isolate back to the background task

```dart
class _ForegroundFirstTaskHandler extends TaskHandler {
  @override
  Future<void> onStart(DateTime timestamp, TaskStarter taskStarter) async {
    Logger.debug("Starting foreground task");
    _locationInBackground();
  }

  @override
  void onRepeatEvent(DateTime timestamp) async {
    Logger.debug("Foreground repeat event triggered");
    _locationInBackground();
  }

  @override
  void onReceiveData(Object data) async {
    Logger.debug('onReceiveData: $data');
    await _locationInBackground();
  }
}

```

## Inter-Isolate Communication and Location Updates

The background task fetches geolocation data using `Geolocator.getCurrentPosition()` and transmits results to the UI isolate via `FlutterForegroundTask.sendDataToMain()`. This occurs at line 39 of `foreground.dart`, where the handler serializes location data into a map containing latitude, longitude, accuracy, altitude, and timestamp.

The main isolate receives this data through a callback registered in `HomePage.initState` at line 53:

```dart
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);

```

The `_onReceiveTaskData` handler parses the location payload and persists it through the app's geolocation service:

```dart
void _onReceiveTaskData(dynamic data) async {
  if (data is! Map<String, dynamic>) return;
  if (!(data.containsKey('latitude') && data.containsKey('longitude'))) return;
  
  await updateUserGeolocation(
    geolocation: Geolocation(
      latitude: data['latitude'],
      longitude: data['longitude'],
      accuracy: data['accuracy'],
      altitude: data['altitude'],
      time: DateTime.parse(data['time']).toUtc(),
    ),
  );
}

```

## Lifecycle Management and Resource Cleanup

Proper lifecycle management prevents resource leaks and unnecessary battery drain. When the `HomePage` widget disposes, the service stops explicitly at line 30:

```dart
ForegroundUtil.stopForegroundTask();

```

This termination method unregisters the task handler, removes the foreground notification, and releases the wake lock, ensuring the app returns to a normal background state when the user navigates away from the recording interface.

## Why Foreground Services Are Required for Background Recording

Android's Doze mode and App Standby buckets aggressively kill background processes to conserve battery. Standard background execution limits prevent apps from accessing the microphone continuously while the screen is off. By promoting the process to a foreground service with a persistent notification, Omi receives the foreground service permission level, granting it CPU priority and exemption from standard background restrictions.

This architecture enables the app to record audio continuously while periodically updating location metadata, even during extended background sessions. The `flutter_foreground_task` dependency declared in [`pubspec.yaml`](https://github.com/basedhardware/omi/blob/main/pubspec.yaml), combined with the service declaration in [`android/app/src/main/AndroidManifest.xml`](https://github.com/basedhardware/omi/blob/main/android/app/src/main/AndroidManifest.xml), completes the configuration required for this capability.

## Summary

- **ForegroundUtil** in `app/lib/utils/audio/foreground.dart` encapsulates service initialization, permission requests at lines 75–92, and the task handler implementation.
- The **_ForegroundFirstTaskHandler** class executes location updates every 5 minutes via `onRepeatEvent`, keeping geolocation metadata current during recording sessions.
- **Inter-isolate communication** uses `sendDataToMain()` and `addTaskDataCallback()` to transmit GPS coordinates from the background service to the UI layer.
- **Lifecycle synchronization** between `HomePage.initState` (lines 91–93) and `HomePage.dispose` (line 30) ensures the service starts when the app loads and stops when the user leaves the recording interface.
- **Android foreground service permissions** prevent the OS from killing the recording process, enabling continuous audio capture in the background.

## Frequently Asked Questions

### What plugin does Omi use to manage foreground tasks?

Omi uses the **`flutter_foreground_task`** plugin, declared in the [`pubspec.yaml`](https://github.com/basedhardware/omi/blob/main/pubspec.yaml) file, which wraps Android's `ForegroundService` API and provides Dart APIs for task scheduling and inter-isolate communication.

### How frequently does the background task fetch location data?

The task handler triggers location updates **every 5 minutes** (300,000 milliseconds), configured through `ForegroundTaskEventAction.repeat(60 * 1000 * 5)` at lines 33–34 of `foreground.dart`.

### Where is the foreground service stopped in Omi's codebase?

The service stops in **`app/lib/pages/home/page.dart`** within the `dispose()` method at line 30, which calls `ForegroundUtil.stopForegroundTask()` to release resources and remove the persistent notification.

### Why does Omi need to request battery optimization exemptions?

Android's Doze mode and background execution limits would otherwise terminate the audio recording process when the screen locks. By requesting the user to ignore battery optimizations in lines 75–92 of `foreground.dart`, Omi ensures the foreground service maintains CPU priority and microphone access during extended background recording sessions.