On this page Startup and recoveryFirst connectionSelection, cancellation and unbindingCommands and stateConcurrencyAndroid: recover, then read
APP SDK / Android · iOS

Vehicles and recovery

Connect through discovery, authorization, pairing and binding; handle selection, unbinding and recovery after cancellation.

Startup and recovery

After privacy consent and a platform session are available, call recover to reconcile saved pending results, then list for the cloud vehicle list. cached is for display only, not proof of current authorization or binding. Handle recovery failures by their error type; do not delete records to bypass a failure.

Android supports typed methods or execute(action, parametersJson, callback). iOS uses execute:parameters:resolve:reject:. Both platforms share these cloud action names and object parameters:

Action Parameters and purpose
recover, list, cached Empty object; recovery, cloud list or cached list
add Empty object; enter the add-vehicle flow
bootstrap discoveryId, optional expectedTerminal; obtain bootstrap authorization
bind terminalId; submit binding proof and restore target connection authorization
select, report vehicleId, deviceId, terminalId; select or report a connected terminal
restoreSelection terminalId; restore the prior selection after cancelling discovery
clearBootstrap Empty object; clear temporary bootstrap authorization
release terminalId; revoke nearby terminal authorization
unbind vehicle: the complete entry from list, including vehicle.bindingVersion and terminals
authorizePairingRecovery terminalId, nonce; return proof for continuing PIN recovery through the device client
adoptLegacy Empty object; claim only after verifying the legacy origin; see upgrade guidance

First connection

  1. Save the existing selection, call add and begin device scanning.
  2. Read discoveryId from discovery and call bootstrap. Include expectedTerminal when the target is known.
  3. Use the device pairing API for the secure handshake, then submit the PIN shown on the actual terminal.
  4. After pairing, call bind. Refresh the vehicle list and target state after cloud binding completes.
  5. Prepare and connect vehicle Wi-Fi as needed. Verify authentication and connection probes for the expected terminalId.
  6. Clear bootstrap authorization after success, failure or cancellation. Retain SDK-managed pending receipts for recover.

A Bluetooth connection, successful system pairing, matching SSID or Wi-Fi join callback alone does not prove an authenticated vehicle connection. Do not prematurely display successful binding or vehicle control.

Selection, cancellation and unbinding

Use select for an authorized vehicle. When cancelling an add flow, stop discovery and temporary pairing, wait for old operations to settle, then call restoreSelection for the previous vehicle. Check the active operation and target on every asynchronous callback so a late result from the previous vehicle cannot overwrite the current state.

Call cancel() and wait for resolve/reject. Cancellation does not roll back facts already confirmed by the terminal or server. Do not switch or remove vehicles during navigation. On NAVIGATION_ACTIVE, ask the user to end navigation first.

release revokes nearby terminal authorization; unbind performs cloud unbinding with a binding version. If the unbind result is unknown, retry with the same bindingVersion. Recovery resumes pending work after the original account signs in. Do not manually clear pendingReceipt, pendingChallenge or pendingUnbind.

Commands and state

Show controls according to actual terminal capabilities and user authorization. Send a vehicle command once for each explicit user action and wait for its receipt. Never automatically replay it on reconnect, UI recreation or timeout.

State reads and command receipts are different results. Show execution success based on the corresponding receipt and valid subsequent state, not stale cached state. Ongoing navigation, media and background connectivity also depend on host extensions, permissions and terminal support.

Concurrency

The SDK maintains one device business session. When mutually exclusive actions such as binding, selection or unbinding return CLOUD_BUSY, wait for the active operation to settle before refreshing. Android 0.2.1 coalesces same-scope list and recovery requests. Each caller cancels only its own result, without affecting other subscribers. If all callers cancel, shared work may still finish or time out so confirmed receipts remain recoverable. During another exclusive operation, recover returns CLOUD_BUSY; retry after it settles. Recreate clients after account or token changes.

Android: recover, then read

Use each coordinator once, creating, starting and cancelling it on the main thread after SDK configuration and session installation. The host’s isCurrent checks the account, vehicle and UI operation generation. Invalidate it before cancel() when leaving the page or changing session. Retain the coordinator until settlement.

Success reaches result.resolve; failure reaches result.reject for error handling. Cancellation withdraws this subscriber’s result; shared SDK recovery already in progress may still finish.

import net.oou.mobile.CloudOperation
import net.oou.mobile.CloudVehicles
import net.oou.mobile.ResultCallback

class VehicleRefresh(
    private val vehicles: CloudVehicles,
    private val isCurrent: () -> Boolean,
    private val result: ResultCallback
) {
    private var recovery: CloudOperation? = null
    private var listing: CloudOperation? = null
    private var started = false
    private var cancelled = false
    private fun current() = !cancelled && isCurrent()

    private val listResult = object : ResultCallback {
        override fun resolve(value: Any?) {
            if (current()) result.resolve(value)
        }
        override fun reject(code: String, message: String?, cause: Throwable?) {
            if (current()) result.reject(code, message, cause)
        }
    }

    fun start() {
        check(!started)
        started = true
        if (!current()) return
        recovery = vehicles.recover(object : ResultCallback {
            override fun resolve(value: Any?) {
                if (current()) listing = vehicles.list(listResult)
            }
            override fun reject(code: String, message: String?, cause: Throwable?) {
                listResult.reject(code, message, cause)
            }
        })
    }

    fun cancel() {
        cancelled = true
        recovery?.cancel()
        listing?.cancel()
    }
}