Skip to content
CARP Docs
Ask AISystemLightDark
On this page

App usage

The APP_USAGE measure, which records the foreground time of each app over a time window from Android usage statistics.

Copy MarkdownOpen inOpen in GitHubView as MarkdownOpen in Scira AIOpen in ChatGPTOpen in ClaudeOpen in Cursor

dk.cachet.carp.appusage is a one-time measure that runs on the phone, on Android only, through the app_usage plugin. Add it as Measure(type: AppsSamplingPackage.APP_USAGE). Each time its trigger fires, it collects the usage of a time window that ends at that moment.

Add the measure to a protocol

Give the measure its own HistoricSamplingConfiguration, so that each window starts where the previous one ended, also after an app restart. Add it with a trigger that repeats:

PeriodicTriggerAppLifecycleTrigger

A PeriodicTrigger collects when sampling starts and then once per period:

protocol.addTaskControl(
  PeriodicTrigger(period: const Duration(days: 1)),
  BackgroundTask(
    measures: [
      Measure(
        type: AppsSamplingPackage.APP_USAGE,
        samplingConfiguration: HistoricSamplingConfiguration( 
          past: const Duration(days: 1),
        ),
      ),
    ],
  ),
  phone,
);

An AppLifecycleTrigger collects each time the app returns to the foreground. AppLifecycleState comes from package:flutter/widgets.dart:

protocol.addTaskControl(
  AppLifecycleTrigger({AppLifecycleState.resumed}),
  BackgroundTask(
    measures: [
      Measure(
        type: AppsSamplingPackage.APP_USAGE,
        samplingConfiguration: HistoricSamplingConfiguration( 
          past: const Duration(days: 1),
        ),
      ),
    ],
  ),
  phone,
);

Collect app usage with one measure and one trigger. Two app usage measures keep separate windows, which overlap.

Configuration

HistoricSamplingConfiguration has these properties:

PropertyTypeDefaultDescription
pastDuration1 dayHow far back the window reaches when the measure has not collected before.
futureDuration1 dayNot used by this measure.
lastTimeDateTime?nullWhen the measure last collected. CARP Mobile Sensing (CAMS) sets it after each collection.

Without a configuration on the measure, the package default applies, with a past of 1 day.

Time window

Each collection covers a window from start to end:

  1. start is lastTime if the measure collected before, otherwise the current time minus past.
  2. end is the current time.
  3. After the collection, CAMS sets lastTime to the current time, so the next window starts where this one ended.

Known issue

Without its own configuration, the measure keeps lastTime in memory only. After an app restart, the first window therefore starts past before the current time again and overlaps data that was already collected. Give the measure its own HistoricSamplingConfiguration, as in the examples above, so that CAMS saves lastTime with the deployment.

Precision

The app_usage plugin states that Android's usage statistics are precise to a day only. The usage of a window therefore comes from Android's statistics for the periods that overlap the window, which can be longer than the window. Each app's startDate and endDate show the period that its statistics cover.

Use windows of a day or longer. Do not add up the usage of consecutive windows: a period that overlaps two windows counts in both.

Usage access

The measure needs the participant to turn on Usage access for the app, which CAMS does not ask for. When access is missing, the app_usage plugin opens the Usage access settings screen each time the measure collects. The measurement then has an empty usage map.

For how to prepare participants for this setting, see Installation.

Limitations

  • In usage, name is the last part of the package name, not the app name. To get app names, join with the installed apps on packageName.
  • lastForeground is not the last time the app was in the foreground. See Data.
  • start and end are in UTC, while the dates in usage are in local time without an offset.
  • Usage is counted in whole seconds, and apps without foreground time are left out.
  • If the phone's clock is set back to before lastTime, each collection fails until the clock passes lastTime again. The data manager then stores an error measurement instead of usage data.
  • The package has no privacy transformer, so package names and usage times are stored as collected.
  • The measure does not run on iOS.

Data

Each measurement holds an AppUsage object with the __type dk.cachet.carp.appusage:

FieldTypeDescription
startDateTimeStart of the window, in UTC.
endDateTimeEnd of the window, in UTC.
usageMap<String, AppUsageInfo>The usage of each app with foreground time, keyed by package name. Empty when usage access is missing.

Each value in usage is an AppUsageInfo:

FieldTypeDescription
nameStringThe last part of the package name, such as music for com.spotify.music. Not the app name.
packageNameStringThe full package name.
usageDurationTotal time in the foreground, in whole seconds. In JSON, an integer number of microseconds.
startDateDateTimeStart of the period that Android's statistics for the app cover, in local time without an offset.
endDateDateTimeEnd of that period, in local time without an offset.
lastForegroundDateTimeOn Android 10 or later, the last time that one of the app's foreground services was used. On Android 9 and lower, the epoch (1970-01-01) in local time, which may also appear for apps without foreground services.

The measurement's sensorStartTime and sensorEndTime hold the window in microseconds since the epoch. The data looks like this:

{
  "__type": "dk.cachet.carp.appusage",
  "start": "2026-10-25T09:00:04.700012Z",
  "end": "2026-10-26T09:00:04.812345Z",
  "usage": {
    "com.spotify.music": {
      "name": "music",
      "packageName": "com.spotify.music",
      "usage": 2712000000,
      "startDate": "2026-10-25T00:00:00.000",
      "endDate": "2026-10-26T09:58:12.000",
      "lastForeground": "2026-10-26T08:41:27.000"
    },
    "com.instagram.android": {
      "name": "android",
      "packageName": "com.instagram.android",
      "usage": 1440000000,
      "startDate": "2026-10-25T00:00:00.000",
      "endDate": "2026-10-26T09:31:40.000",
      "lastForeground": "1970-01-01T01:00:00.000"
    }
  }
}

For the measurement envelope around the data, see Measurements and data.

Quick links

Page · DocumentationCARP DocumentationPage · DocumentationIntroductionPage · DocumentationQuickstartPage · DocumentationTerminologyPage · DocumentationArchitecture