Permissions Runtime Behavior
1. Overview
This document explains how the Telematics SDK monitors permission state changes at runtime and how it reacts to telemetry interruptions on iOS.
It describes the limitations imposed by the operating system, the internal state model used by the SDK, and the callback mechanisms exposed to notify the application of relevant events.
2. Detects permission changes only in the foreground
On iOS, an app that moves to the background can remain active or be suspended, depending on whether there is a service that justifies its execution in the background. As long as there is a valid service, such as active location updates, the app remains in an active state in the background, allowing timers and callbacks to run.
When there is no longer a valid background service, the system suspends the app. A suspended app:
- does not execute code
- does not receive callbacks
- cannot react to permission changes
Permission changes are always initiated by the user outside the context of the application, typically in the system settings. iOS does not provide global callbacks or background notifications to inform a suspended application that a permission has been removed or reset. As a result, if the application is suspended at the time of the change, that change will only be observable when the application resumes running.
In practice, this means that:
- Detection of permission changes is only guaranteed when the application returns to the foreground
- Background verification is inherently unreliable
- There is no reliable way to react to a permission reset while the application remains in the background
For this reason, the Telematics SDK checks the permissions state whenever the application enters the foreground. This is the only time when the system guarantees that the application is active and able to execute code, allowing the SDK to consistently detect changes and notify the application in order to react appropriately.
3. How it works in practice
The Telematics SDK reevaluates the status of permissions as soon as the application becomes active again. This point in the lifecycle, associated with applicationDidBecomeActive, is the first moment guaranteed by the system when the SDK can execute code and validate the actual status of permissions.
Whenever the application becomes active, the SDK performs a deterministic evaluation of:
- current permissions
- previously persisted telemetry state
- actual runtime telemetry state
- time since the last collected sample
This evaluation can result in:
- A callback to the application, notifying it of a relevant event
- A reconciliation action, where the SDK automatically aligns runtime telemetry with its intended state
These two outcomes are independent: a reconciliation may happen with or without a callback.
3.1 Internal state vs current runtime state
The SDK distinguishes between two fundamental concepts:
Internal telemetry state
- Represents whether telemetry is intended to be active
- Is persisted across app launches
- Is only changed when the application explicitly starts or stops telemetry
- Reflects user intent
Current runtime state
- Represents whether telemetry sensors are currently running in the process
- Can change implicitly due to:
- permission removal
- app suspension or termination
- system intervention (e.g. SIGKILL)
When these two states diverge, the SDK may attempt to reconcile the runtime state to match the internal state when the app becomes active.
Reconciliation never modifies the internal state.
It only starts or stops runtime telemetry to restore consistency.
In some interruption scenarios, runtime telemetry may stop before collected samples are sent to the server. The SDK includes a recovery mechanism to flush pending data once execution resumes.
3.2 Callbacks exposed by the SDK
The SDK exposes three distinct callbacks, each corresponding to a specific class of events.
Callback 1 — Permissions missing
The application is notified when:
- one or more mandatory permissions are missing
- telemetry is not expected to be running according to the internal state
This callback informs the application that telemetry cannot be started until the missing permissions are restored.
Callback 2 — Permissions lost while running
The application is notified when:
- mandatory permissions are missing
- telemetry was expected to be active
The SDK reports the moment from which telemetry is no longer valid and the list of missing permissions, allowing the application to reason about data gaps and take corrective action.
In this case, the SDK also stops runtime telemetry to prevent inconsistent data collection.
Callback 3 — Telemetry gap detected
The application is notified when:
- telemetry was expected to be active
- permissions are currently valid
- there was a significant interruption in sample collection
The SDK reports:
- when telemetry was interrupted
- when it resumed
This callback may be triggered in scenarios such as:
- the application being terminated while telemetry was active
- permissions being temporarily removed and restored before the app returns to the foreground
- the device being restarted while telemetry was active
This callback is used to signal incomplete or interrupted trips, even if telemetry has already restarted automatically.
3.3 Interruption threshold
The SDK applies a fixed time threshold to distinguish between:
- short, insignificant interruptions
- meaningful breaks in telemetry collection
If the interruption duration is below the threshold, the SDK resumes telemetry silently.
If the interruption exceeds the threshold, the SDK reports a telemetry gap to the application using the 3rd Callback - Telemetry gap detected.
This mechanism prevents unnecessary notifications while still guaranteeing visibility into relevant data loss.
3.4 Reconciliation behavior
During foreground evaluation, the SDK may:
- start telemetry if it should be running but is not
- stop telemetry if it should not be running but is
This process:
- does not modify the internal telemetry state
- does not necessarily trigger a callback
- exists to restore consistency between intent and runtime execution
4. Pending data recovery after process interruption
In some scenarios, telemetry may be interrupted by the system before collected samples are sent to the server.
This can occur when:
- the application is terminated while telemetry is active
- a mandatory permission is revoked, causing a system-level termination
- the process is killed by the system (e.g. SIGKILL)
- the device is restarted while telemetry is active
In these cases, telemetry samples, vehicle positions and attention events may remain stored locally but not yet uploaded, since data transmission is asynchronous and cannot be guaranteed before termination.
4.1 Automatic pending data flush
To handle this, the SDK includes a recovery mechanism that attempts to flush pending data as soon as execution resumes.
The SDK automatically tries to send pending data when initSDK completes successfully and the SDK is authenticated.
This mechanism:
- recovers data left unsent due to crashes or forced termination
- does not require the user to restart telemetry
- does not alter the internal telemetry state
If the SDK is not authenticated (for example, after the session was invalidated), the flush is skipped and retried automatically after the next successful initSDK with new tokens.
Important notes
- Background Modes are not permissions: they are application capabilities that must be correctly configured.
- Motion does not keep the background active: it only complements data collection.
- Location (Always) is the determining factor for continuous background execution.
- Permission changes are not detected in background: the SDK does not guarantee immediate detection of permission removal or restoration if the application is suspended. Permission changes are only reliably evaluated when the application becomes active again.
5. Usage Example
The Telematics SDK exposes three callbacks that allow the application to react to permission changes and telemetry interruptions.
Callback 1 — Permissions missing
Triggered when the SDK evaluates the current permission state and detects whether mandatory permissions are missing or restored, while telemetry is not expected to be running.
This callback is used both to signal missing permissions and to signal that the permission state has become consistent again.
Typical use cases:
- block or unblock UI
- prompt the user to grant permissions
- react to permissions being restored
- prevent telemetry from starting while permissions are missing
- Swift
- Objective-C
sdk.onPermissionsMissing = { [weak self] (missingPermissions: [String]) in
DispatchQueue.main.async {
if missingPermissions.isEmpty {
// Permissions are now consistent with SDK requirements
} else {
// One or more mandatory permissions are missing
}
}
}
sdk.onPermissionsMissing = ^(NSArray<NSString *> *missingPermissions) {
dispatch_async(dispatch_get_main_queue(), ^{
if (missingPermissions.count == 0) {
// Permissions are now consistent with SDK requirements
} else {
// One or more mandatory permissions are missing
// missingPermissions contains the identifiers returned by `checkPermissions()`
}
});
};
Important note: An empty array ([]) explicitly indicates that all mandatory permissions are now granted, and that the application state is once again aligned with the SDK requirements.
Callback 2 — Permissions lost while running
Triggered when mandatory permissions are removed while telemetry was expected to be active.
In this scenario:
- the SDK automatically stops runtime telemetry
- the internal telemetry state remains unchanged (still marked as active)
- the application is informed of when telemetry became invalid
Typical use cases:
- notify the user that telemetry was interrupted
- explicitly stop the telemetry to change the internal state
- request permission authorization again
- Swift
- Objective-C
sdk.onPermissionsLostWhileRunning = { [weak self] (lostAt: Date, missingPermissions: [String]) in
DispatchQueue.main.async {
// Telemetry was interrupted due to permission removal
// lostAt indicates when telemetry became invalid
// missingPermissions contains the permissions that were removed
}
}
sdk.onPermissionsLostWhileRunning = ^(NSDate *lostAt, NSArray<NSString *> *missingPermissions) {
dispatch_async(dispatch_get_main_queue(), ^{
// Telemetry was interrupted due to permission removal
// lostAt indicates when telemetry became invalid (timestamp of the last saved sample)
// missingPermissions contains the permissions that were removed
});
};
Callback 3 — Telemetry gap detected
Triggered when telemetry was expected to be active, permissions are currently valid, and a significant interruption in sample collection was detected.
This callback may occur even if telemetry has already restarted automatically.
Typical use cases:
- be informed about gaps in the data
- Swift
- Objective-C
sdk.onTelemetryGapDetected = { [weak self] (from: Date, to: Date) in
DispatchQueue.main.async {
// A telemetry interruption was detected
// • from: moment telemetry stopped producing samples
// • to: moment telemetry resumed
// Telemetry should be already running again at this point
}
}
sdk.onTelemetryGapDetected = ^(NSDate *from, NSDate *to) {
dispatch_async(dispatch_get_main_queue(), ^{
// A telemetry interruption was detected
// • from: moment telemetry stopped producing samples (timestamp of the last saved sample)
// • to: moment telemetry resumed (timestamp of the app entering the foreground)
// Telemetry should be already running again at this point
});
};