1/*
  2 * SPDX-FileCopyrightText: 2026 Copyright (c) Contributors to the Eclipse Foundation
  3 *
  4 * See the NOTICE file(s) distributed with this work for additional
  5 * information regarding copyright ownership.
  6 *
  7 * This program and the accompanying materials are made available under the
  8 * terms of the Apache License Version 2.0 which is available at
  9 * https://www.apache.org/licenses/LICENSE-2.0
 10 *
 11 * SPDX-License-Identifier: Apache-2.0
 12 */
 13
 14//! Authoritative communication lifecycle operations and failures.
 15//!
[docs] 16//! [[ dimpl~communication-control-operations, Authoritative communication lifecycle operations, dimpl ]]
 17//!
 18//! Part of the communication-access contract (with [`super::access`]), defined
 19//! here so that transport crates and replacement communication plugins can
 20//! depend on it without coupling to the default plugin's crate.
 21
 22/// Why authoritative communication activation was requested.
 23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 24pub enum ActivationCause {
 25    /// Startup requires communication to be available immediately (`Always` mode).
 26    Startup,
 27    /// A diagnostic UDS operation needed communication while it was not ready.
 28    DiagnosticRequest,
 29    /// An application or custom plugin explicitly requested activation.
 30    Explicit,
 31    /// An exclusive disable lease was released.
 32    DisableRelease,
 33}
 34
 35/// Why whole-vehicle (re-)detection was requested via a communication
 36/// plugin's `trigger_detection`.
 37///
 38/// The framework attaches no authorization meaning to a cause. Which ones are
 39/// admitted in a given
 40/// [`CommunicationInitMode`](crate::communication_control::CommunicationInitMode)
 41/// is the selected plugin's policy.
 42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 43pub enum DetectionCause {
 44    /// An application or custom plugin explicitly requested (re-)detection.
 45    Explicit,
 46    // TODO(networkreset-endpoint, #490): add TopologyRediscovery variant for
 47    // the `networkreset(trigger_detection=true)` request once the endpoint
 48    // exists.
 49}
 50
 51/// A lifecycle operation controlled by the communication framework.
 52///
 53/// An activation prepares every registered lifecycle reconfiguration
 54/// participant and hook, publishes their state through one synchronous commit
 55/// barrier, enables the transport, initializes the hooks, then runs
 56/// [`CommunicationVariantDetection`](super::CommunicationVariantDetection).
 57/// Activation-shaped operations differ only in which stages they run:
 58///
 59/// | Operation         | Prepare + commit | Transport                | `initialize` | Detector                                      |
 60/// |-------------------|------------------|--------------------------|--------------|-----------------------------------------------|
 61/// | `Enable`          | yes              | enable                   | yes          | no                                            |
 62/// | `EnableAndDetect` | yes              | enable                   | yes          | yes, subject to `VariantDetectionMode`        |
 63/// | `Detect`          | untouched        | untouched                | untouched    | yes                                           |
 64/// | `Resume`          | yes              | restore displaced state  | if enabled   | whatever the enable it resumes did            |
 65///
 66/// Which of them a caller may request in a given `init_mode` is the selected
 67/// communication plugin's policy.
 68///
 69/// # Stages run only on a transition
 70///
 71/// Every row above applies only when the operation claims a transition. From
 72/// [`Enabled`](super::access::CommunicationState::Enabled), `Enable` and
 73/// `EnableAndDetect` succeed without running any stage. Detecting from there
 74/// requires a separate [`Detect`](Self::Detect).
 75/// `VariantState` remains the readiness signal, because detection settles per
 76/// ECU and asynchronously.
 77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 78pub enum CommunicationOperation {
 79    /// Activate physical transport and run lifecycle hooks, but *not* variant
 80    /// detection. For plugins that own the detection decision themselves.
 81    Enable,
 82    /// Activate physical transport, run lifecycle hooks, then run variant
 83    /// detection.
 84    EnableAndDetect,
 85    /// Register a lifecycle hook with the communication worker.
 86    RegisterLifecycleHook,
 87    /// Register the whole-vehicle variant detector with the communication worker.
 88    RegisterVariantDetection,
 89    /// Disable physical transport under an exclusive lease.
 90    Disable,
 91    /// Resume physical transport after a disable lease is released.
 92    Resume,
 93    /// Finalize pending lifecycle reconfiguration while remaining disabled.
 94    FinishDisableLease,
 95    /// Run whole-vehicle variant detection against an already-live transport.
 96    ///
 97    /// Touches neither the transport nor the lifecycle hooks, and is therefore
 98    /// valid only from
 99    /// [`CommunicationState::Enabled`](super::access::CommunicationState::Enabled).
100    /// From any other state it is refused with
101    /// [`CommunicationOperationFailure::TransitionFailure`].
102    Detect,
103}
104
105impl CommunicationOperation {
106    /// Whether this operation's activation sequence runs variant detection
107    /// after the transport and the lifecycle hooks are up.
108    ///
109    /// Only meaningful for the operations that reach the activation sequence.
110    /// `Resume` never consults this. It repeats the shape of the enable it
111    /// resumes, which only the worker knows.
112    #[must_use]
113    pub const fn runs_detection(self) -> bool {
114        !matches!(self, Self::Enable)
115    }
116}
117
118/// A structured failure of an authoritative lifecycle operation.
119#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
120pub enum CommunicationOperationFailure {
121    #[error(
122        "Permission denied for {operation:?}. Likely the operation was protected by a different \
123         owner."
124    )]
125    PermissionFailure {
126        /// Lifecycle operation that was rejected.
127        operation: CommunicationOperation,
128    },
129
130    /// The physical transport operation failed.
131    #[error("Transport {operation:?} failed: {error}")]
132    TransportFailure {
133        /// Lifecycle operation that invoked the transport.
134        operation: CommunicationOperation,
135        /// Transport error description.
136        error: String,
137    },
138    /// A post-activation initializer failed.
139    #[error("Communication initializer {initializer} failed: {error}")]
140    InitializerFailure {
141        /// Initializer name.
142        initializer: String,
143        /// Initializer error description.
144        error: String,
145    },
146    /// An initializer failed and transport cleanup failed as well.
147    #[error(
148        "Communication initializer {initializer} failed: {initializer_error}; transport cleanup \
149         failed: {cleanup_error}"
150    )]
151    InitializerCleanupFailure {
152        /// Initializer name.
153        initializer: String,
154        /// Initializer error description.
155        initializer_error: String,
156        /// Transport cleanup error description.
157        cleanup_error: String,
158    },
159    /// Variant detection failed.
160    #[error("Variant detection {detector} failed: {error}")]
161    DetectionFailure {
162        /// Detector name.
163        detector: String,
164        /// Detection error description.
165        error: String,
166    },
167    /// Variant detection failed and transport cleanup failed as well.
168    #[error(
169        "Variant detection {detector} failed: {detection_error}; transport cleanup failed: \
170         {cleanup_error}"
171    )]
172    DetectionCleanupFailure {
173        /// Detector name.
174        detector: String,
175        /// Detection error description.
176        detection_error: String,
177        /// Transport cleanup error description.
178        cleanup_error: String,
179    },
180
181    /// The operation was invalid for the current authoritative lifecycle state.
182    ///
183    /// Notably returned for a [`CommunicationOperation::Detect`] requested
184    /// while communication is not `Enabled`. Detection runs against a live
185    /// transport and never brings one up itself.
186    #[error("Cannot {operation:?} in the current lifecycle state")]
187    TransitionFailure {
188        /// Requested operation.
189        operation: CommunicationOperation,
190    },
191
192    /// The operation was rejected because an exclusive disable lease currently owns the state.
193    /// Only the matching lease may re-activate communication.
194    #[error("Cannot {operation:?}: an exclusive disable lease is currently held")]
195    DisableLeaseHeld {
196        /// Requested operation.
197        operation: CommunicationOperation,
198    },
199
200    /// Variant detection was rejected because active communication guards are
201    /// held.
202    ///
203    /// A [`CommunicationGuard`](super::access::CommunicationGuard) is the only
204    /// lock that spans a whole multi-request sequence (e.g. a flash transfer's
205    /// `TransferData` blocks). The per-ECU transport semaphore reopens between
206    /// individual requests, so a detection request could otherwise land between
207    /// two requests of that sequence.
208    ///
209    /// Detection is refused rather than made to wait, so that the decision stays
210    /// synchronous and a minutes-long sequence cannot block it.
211    #[error("Cannot {operation:?}: communication guards are currently held")]
212    GuardsHeld {
213        /// Requested operation.
214        operation: CommunicationOperation,
215    },
216
217    /// The lifecycle worker is shutting down. No new operation may start.
218    #[error("Cannot {operation:?}: the communication framework is shutting down")]
219    ShuttingDown {
220        /// Requested operation.
221        operation: CommunicationOperation,
222    },
223
224    /// The lifecycle worker exited unexpectedly and cannot process the request.
225    #[error("Cannot {operation:?}: the communication worker is unavailable")]
226    WorkerUnavailable {
227        /// Requested operation.
228        operation: CommunicationOperation,
229    },
230
231    /// `init_mode` does not authorize this operation.
232    ///
233    /// Returned by a communication plugin applying its own policy, never by the
234    /// framework's lifecycle handle.
235    #[error(
236        "init_mode does not authorize {operation:?}; communication must be enabled out of band"
237    )]
238    ModeDisabled {
239        /// Requested operation.
240        operation: CommunicationOperation,
241    },
242}