diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp index ec9819d1..2e682af7 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.cpp @@ -83,20 +83,22 @@ void GattAudioService::init() // add all the states and the super state groupings m_stateMachine.addState(IdleState, "Idle"); m_stateMachine.addState(ReadyState, "Ready"); + m_stateMachine.addState(EnableNotificationsState, "EnableNotifications"); m_stateMachine.addState(StreamingSuperState, "StreamingSuperState"); - m_stateMachine.addState(StreamingSuperState, EnableNotificationsState, "EnableNotifications"); m_stateMachine.addState(StreamingSuperState, StartStreamingState, "StartStreaming"); m_stateMachine.addState(StreamingSuperState, StreamingState, "Streaming"); m_stateMachine.addState(StreamingSuperState, StopStreamingState, "StopStreaming"); // add the transitions: From State -> Event -> To State - m_stateMachine.addTransition(IdleState, StartServiceRequestEvent, ReadyState); - m_stateMachine.addTransition(ReadyState, StopServiceRequestEvent, IdleState); - m_stateMachine.addTransition(ReadyState, StartStreamingRequestEvent, EnableNotificationsState); + m_stateMachine.addTransition(IdleState, StartServiceRequestEvent, EnableNotificationsState); + m_stateMachine.addTransition(EnableNotificationsState, RetryStartNotifyEvent, EnableNotificationsState); + m_stateMachine.addTransition(EnableNotificationsState, StopServiceRequestEvent, IdleState); + m_stateMachine.addTransition(EnableNotificationsState, NotificationsEnabledEvent, ReadyState); - m_stateMachine.addTransition(EnableNotificationsState, NotificationsEnabledEvent, StartStreamingState); + m_stateMachine.addTransition(ReadyState, StopServiceRequestEvent, IdleState); + m_stateMachine.addTransition(ReadyState, StartStreamingRequestEvent, StartStreamingState); m_stateMachine.addTransition(StartStreamingState, StreamingStartedEvent, StreamingState); @@ -591,7 +593,8 @@ void GattAudioService::stopStreaming(uint32_t audioDuration, PendingReply<> &&re { // check the service is ready if (m_stateMachine.state() != StreamingState) { - reply.setError("Service not currently streaming"); + // Don't treat this as an error. The intent is to stop streaming, and if we're not streaming then the intent has been satisfied. + XLOGD_DEBUG("service not currently streaming"); reply.finish(); return; } @@ -738,6 +741,16 @@ void GattAudioService::stateMachinePostEvent(const Event::Type event) m_stateMachine.postEvent(event); } +void GattAudioService::stateMachinePostDelayedEvent(const Event::Type event, const int delay) +{ + m_stateMachine.postDelayedEvent(event, delay); +} + +void GattAudioService::stateMachineCancelDelayedEvents(const Event::Type eventType) +{ + m_stateMachine.cancelDelayedEvents(eventType); +} + bool GattAudioService::stateMachineIsIdle() { return m_stateMachine.inState(IdleState); diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.h b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.h index 24864c06..254eef66 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.h +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice.h @@ -71,6 +71,9 @@ class GattAudioService : public BleRcuAudioService void updateFrameCountSupport(bool frameCountSupported); void stateMachinePostEvent(const Event::Type event); + void stateMachinePostDelayedEvent(const Event::Type event, const int delay); + void stateMachineCancelDelayedEvents(const Event::Type eventType); + bool stateMachineIsIdle(); void setLastError(BleRcuAudioService::StreamingError error); @@ -159,15 +162,17 @@ class GattAudioService : public BleRcuAudioService static const Event::Type StopStreamingRequestEvent = Event::Type(Event::User + 4); static const Event::Type NotificationsEnabledEvent = Event::Type(Event::User + 5); + static const Event::Type RetryStartNotifyEvent = Event::Type(Event::User + 6); + + static const Event::Type StreamingStartedEvent = Event::Type(Event::User + 7); + static const Event::Type StreamingStoppedEvent = Event::Type(Event::User + 8); - static const Event::Type StreamingStartedEvent = Event::Type(Event::User + 6); - static const Event::Type StreamingStoppedEvent = Event::Type(Event::User + 7); + static const Event::Type GattErrorEvent = Event::Type(Event::User + 9); + static const Event::Type OutputPipeCloseEvent = Event::Type(Event::User + 10); - static const Event::Type GattErrorEvent = Event::Type(Event::User + 8); - static const Event::Type OutputPipeCloseEvent = Event::Type(Event::User + 9); + static const Event::Type AudioInfoTimeoutEvent = Event::Type(Event::User + 11); + static const Event::Type AudioLastFrameTimeoutEvent = Event::Type(Event::User + 12); - static const Event::Type AudioInfoTimeoutEvent = Event::Type(Event::User + 10); - static const Event::Type AudioLastFrameTimeoutEvent = Event::Type(Event::User + 11); }; #endif // !defined(GATT_AUDIOSERVICE_H) diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp index 80897f10..d2954f76 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_audioservice_rdk.cpp @@ -111,6 +111,9 @@ void GattAudioServiceRdk::stop() } void GattAudioServiceRdk::onEnteredIdle() { + stateMachineCancelDelayedEvents(RetryStartNotifyEvent); + stateMachineCancelDelayedEvents(GattErrorEvent); + if (m_audioDataCharacteristic) { XLOGD_INFO("Disabling notifications for m_audioDataCharacteristic"); m_audioDataCharacteristic->disableNotifications(); @@ -141,14 +144,11 @@ void GattAudioServiceRdk::onEnteredEnableNotificationsState() auto replyHandlerData = [this](PendingReply<> *reply) { if (reply->isError()) { - // this is bad if this happens as we won't get updates, so we install a timer to - // retry enabling notifications in a couple of seconds time - XLOGD_ERROR("failed to enable audio data notifications due to <%s>", - reply->errorMessage().c_str()); - - setLastError(StreamingError::InternalError); - stateMachinePostEvent(GattErrorEvent); - + XLOGD_ERROR("failed to enable audio data notifications due to <%s>", reply->errorMessage().c_str()); + + // notifications are required for audio streaming, so if this fails we need to retry in a couple of seconds + stateMachineCancelDelayedEvents(RetryStartNotifyEvent); + stateMachinePostDelayedEvent(RetryStartNotifyEvent, 2000); } else { // notifications enabled so post an event to the state machine stateMachinePostEvent(NotificationsEnabledEvent); @@ -199,8 +199,7 @@ void GattAudioServiceRdk::onEnteredStartStreamingState() auto replyHandler = [this](PendingReply<> *reply) { if (reply->isError()) { - XLOGD_ERROR("failed to write audio control characteristic due to <%s>", - reply->errorMessage().c_str()); + XLOGD_ERROR("failed to write audio control characteristic due to <%s>", reply->errorMessage().c_str()); setLastError(StreamingError::InternalError); stateMachinePostEvent(GattErrorEvent); @@ -211,12 +210,16 @@ void GattAudioServiceRdk::onEnteredStartStreamingState() }; - // the first byte is the codec to use, the second byte is to enable voice - const vector value({ 0x01, 0x01 }); - - // send a write request to write the control characteristic - m_audioCtrlCharacteristic->writeValueWithoutResponse(value, PendingReply<>(getIsAlivePtr(), replyHandler)); - + if (!m_audioDataCharacteristic->notificationsEnabled()) { + XLOGD_ERROR("audio data notifications not enabled, cannot continue with audio stream"); + setLastError(StreamingError::InternalError); + stateMachineCancelDelayedEvents(GattErrorEvent); + stateMachinePostDelayedEvent(GattErrorEvent, 10); // needs to be delayed to avoid re-entrancy issues with the state machine + } else { + // the first byte is the codec to use, the second byte is to enable voice + const vector value({ 0x01, 0x01 }); + m_audioCtrlCharacteristic->writeValueWithoutResponse(value, PendingReply<>(getIsAlivePtr(), replyHandler)); + } GattAudioService::onEnteredStartStreamingState(); } @@ -237,8 +240,7 @@ void GattAudioServiceRdk::onEnteredStopStreamingState() { // check for errors if (reply->isError()) { - XLOGD_ERROR("failed to write audio control characteristic due to <%s>", - reply->errorMessage().c_str()); + XLOGD_ERROR("failed to write audio control characteristic due to <%s>", reply->errorMessage().c_str()); setLastError(StreamingError::InternalError); stateMachinePostEvent(GattErrorEvent); diff --git a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp index 2ec23b0a..d7bda94b 100644 --- a/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp +++ b/src/ble/hal/blercu/bleservices/gatt/gatt_remotecontrolservice.cpp @@ -244,8 +244,10 @@ void GattRemoteControlService::onEnteredState(int state) requestAdvConfig(); requestAdvConfigCustomList(); requestUnpairReason(); - requestRebootReason(); requestRawBatteryVoltage(); + + // requestRebootReason() posts InitialValuesRetrievedEvent in its reply handler, so queue it last after the other initial reads. + requestRebootReason(); m_readySlots.invoke();