static void prvOTAUpdateTask()

in device_firmware/libraries/freertos_plus/aws/ota/src/aws_iot_ota_agent.c [1536:1797]


static void prvOTAUpdateTask( void * pvUnused )
{
    DEFINE_OTA_METHOD_NAME( "prvOTAUpdateTask" );

    EventBits_t uxBits;
    OTA_FileContext_t * C = NULL;
    OTA_Err_t xErr;
    OTA_PubMsg_t * pxMsgMetaData;
    uint32_t ulNumOfBlocksToReceive = otaconfigMAX_NUM_BLOCKS_REQUEST;

    ( void ) pvUnused;

    /* Subscribe to the OTA job notification topic. */
    if( prvSubscribeToJobNotificationTopics() == ( bool_t ) pdTRUE )
    {
        xOTA_Agent.xOTA_EventFlags = xEventGroupCreate();

        if( xOTA_Agent.xOTA_EventFlags != NULL )
        {
            /* Check if the firmware image is in self test mode. If so, enable the self test timer. */
            ( void ) OTA_CheckForSelfTest();

            /*Save the self-test mode in a flag. This flag is cleared when self-test is successful.*/
            xInSelfTest = prvInSelftest();

            /* Send a request to the job service for the latest available job. */
            ( void ) OTA_CheckForUpdate();

            /* Put the OTA agent in the ready state. */
            xOTA_Agent.eState = eOTA_AgentState_Ready;

            for( ; ; )
            {
                uxBits = xEventGroupWaitBits(
                    xOTA_Agent.xOTA_EventFlags, /* The event group being tested. */
                    OTA_EVT_MASK_ALL_EVENTS,    /* The bits within the event group to wait for. */
                    pdTRUE,                     /* Bits should be cleared before returning. */
                    pdFALSE,                    /* Any bit set will do. */
                    ( TickType_t ) ~( 0U ) );   /* Wait forever. */

                /* Check for the shutdown event. */
                if( ( ( uint32_t ) uxBits & OTA_EVT_MASK_SHUTDOWN ) != 0U )
                {
                    OTA_LOG_L1( "[%s] Received shutdown event.\r\n", OTA_METHOD_NAME );
                    break; /* Break, so we stop all OTA processing. */
                }

                /* Check for a user initiated abort. */
                if( ( ( ( uint32_t ) uxBits & OTA_EVT_MASK_USER_ABORT ) != 0U ) && ( C != NULL ) )
                {
                    OTA_LOG_L1( "[%s] Received user abort event.\r\n", OTA_METHOD_NAME );
                    ( void ) prvSetImageStateWithReason( eOTA_ImageState_Aborted, kOTA_Err_UserAbort );
                    ( void ) prvOTA_Close( C ); /* Ignore false result since we're setting the pointer to null on the next line. */
                    C = NULL;
                }

                /* On OTA request timer timeout, publish the stream request if we have context. */
                if( ( ( uxBits & OTA_EVT_MASK_REQ_TIMEOUT ) != 0U ) && ( C != NULL ) )
                {
                    if( C->ulBlocksRemaining > 0U )
                    {
                        ulNumOfBlocksToReceive = otaconfigMAX_NUM_BLOCKS_REQUEST;

                        xErr = prvPublishGetStreamMessage( C );

                        if( xErr != kOTA_Err_None )
                        {                               /* Abort the current OTA. */
                            ( void ) prvSetImageStateWithReason( eOTA_ImageState_Aborted, xErr );
                            ( void ) prvOTA_Close( C ); /* Ignore false result since we're setting the pointer to null on the next line. */
                            C = NULL;
                        }
                    }
                    else
                    {
                        /* If there are no blocks remaining, this may have been in flight when the
                         * last block was received. Just ignore it since it will be cleaned up by
                         * the agent as required. */
                    }
                }

                /* Check if a MQTT message is ready for us to process. */
                if( ( uxBits & OTA_EVT_MASK_MSG_READY ) != 0U )
                {
                    if( ( xOTA_Agent.eState == eOTA_AgentState_Ready ) || ( xOTA_Agent.eState == eOTA_AgentState_Active ) )
                    {
                        while( xQueueReceive( xOTA_Agent.xOTA_MsgQ, &pxMsgMetaData, 0 ) != pdFALSE )
                        {
                            /* Check for OTA update job messages. */
                            if( ( pxMsgMetaData->eMsgType == eOTA_PubMsgType_Job ) && ( xOTA_Agent.eState == eOTA_AgentState_Ready ) )
                            {
                                if( C != NULL )
                                {
                                    ( void ) prvOTA_Close( C ); /* Abort the existing OTA and ignore impossible false result by design. */
                                }

                                C = prvProcessOTAJobMsg( ( const char * ) pxMsgMetaData->pxPubData.vData, /*lint !e9079 pointer to void is OK to cast to the real type. */
                                                         pxMsgMetaData->pxPubData.ulDataLength );

                                /* A null context here could either mean we didn't receive a valid job or it could
                                 * signify that we're in the self test phase (where the OTA file transfer is already
                                 * completed and we've reset the device and are now running the new firmware). We
                                 * will check the state to determine which case we're in. */
                                if( C == NULL )
                                {
                                    /* If the OTA job is in the self_test state, alert the application layer. */
                                    if( OTA_GetImageState() == eOTA_ImageState_Testing )
                                    {
                                        /* Check the platform's OTA update image state. It should also be in self test. */
                                        if( OTA_CheckForSelfTest() == pdTRUE )
                                        {
                                            xOTA_Agent.xPALCallbacks.xCompleteCallback( eOTA_JobEvent_StartTest );
                                        }
                                        else
                                        {
                                            /* The job is in self test but the platform image state is not so it could be
                                             * an attack on the platform image state. Reject the update (this should also
                                             * cause the image to be erased), aborting the job and reset the device. */
                                            OTA_LOG_L1( "[%s] Job in self test but platform state is not!\r\n", OTA_METHOD_NAME );
                                            ( void ) prvSetImageStateWithReason( eOTA_ImageState_Rejected, kOTA_Err_ImageStateMismatch );
                                            ( void ) prvResetDevice(); /* Ignore return code since there's nothing we can do if we can't force reset. */
                                        }
                                    }
                                    else
                                    {
                                        /* If the job context returned NULL and the image state is not in the self_test state,
                                         * then an error occurred parsing the OTA job message.  Abort the OTA job with a parse error.
                                         *
                                         * If there is a valid job id, then a job status update will be sent.
                                         */
                                        ( void ) prvSetImageStateWithReason( eOTA_ImageState_Aborted, kOTA_Err_JobParserError );
                                    }
                                }
                                else
                                {
                                    xOTA_Agent.eState = eOTA_AgentState_Active;
                                }
                            }
                            /* It's not a job message, maybe it's a data stream message... */
                            else if( pxMsgMetaData->eMsgType == eOTA_PubMsgType_Stream )
                            {
                                /* Ingest data blocks if the platform is not in self-test. */
                                if( ( C != NULL ) && ( xInSelfTest == false ) )
                                {
                                    OTA_Err_t xCloseResult;
                                    IngestResult_t xResult = prvIngestDataBlock( C,
                                                                                 ( const char * ) pxMsgMetaData->pxPubData.vData, /*lint !e9079 pointer to void is OK to cast to the real type. */
                                                                                 pxMsgMetaData->pxPubData.ulDataLength,
                                                                                 &xCloseResult );

                                    if( xResult < eIngest_Result_Accepted_Continue )
                                    {
                                        /* Negative result codes mean we should stop the OTA process
                                         * because we are either done or in an unrecoverable error state.
                                         * We don't want to hang on to the resources. */

                                        if( xResult == eIngest_Result_FileComplete )
                                        {
                                            /* File receive is complete and authenticated. Update the job status with the self_test ready identifier. */
                                            prvUpdateJobStatus( C, eJobStatus_InProgress, ( int32_t ) eJobReason_SigCheckPassed, ( int32_t ) NULL );
                                        }
                                        else
                                        {
                                            OTA_LOG_L1( "[%s] Aborting due to IngestResult_t error %d\r\n", OTA_METHOD_NAME, ( int32_t ) xResult );
                                            /* Call the platform specific code to reject the image. */
                                            xErr = xOTA_Agent.xPALCallbacks.xSetPlatformImageState( xOTA_Agent.ulServerFileID, eOTA_ImageState_Rejected );

                                            if( xErr != kOTA_Err_None )
                                            {
                                                OTA_LOG_L2( "[%s] Error trying to set platform image state (0x%08x)\r\n", OTA_METHOD_NAME, ( int32_t ) xErr );
                                            }
                                            else
                                            {
                                                /* Nothing special to do on success. */
                                            }

                                            prvUpdateJobStatus( C, eJobStatus_FailedWithVal, ( int32_t ) xCloseResult, ( int32_t ) xResult );
                                        }

                                        /* Release all remaining resources of the OTA file. */
                                        ( void ) prvOTA_Close( C ); /* Ignore false result since we're setting the pointer to null on the next line. */
                                        C = NULL;

                                        /* Let main application know of our result. */
                                        xOTA_Agent.xPALCallbacks.xCompleteCallback( ( xResult == eIngest_Result_FileComplete ) ? eOTA_JobEvent_Activate : eOTA_JobEvent_Fail );

                                        /* Free any remaining string memory holding the job name since this job is done. */
                                        if( xOTA_Agent.pcOTA_Singleton_ActiveJobName != NULL )
                                        {
                                            vPortFree( xOTA_Agent.pcOTA_Singleton_ActiveJobName );
                                            xOTA_Agent.pcOTA_Singleton_ActiveJobName = NULL;
                                        }
                                    }
                                    else
                                    {
                                        if( xResult == eIngest_Result_Accepted_Continue )
                                        {
                                            /* We're actively receiving a file so update the job status as needed. */
                                            /* First reset the momentum counter since we received a good block. */
                                            C->ulRequestMomentum = 0;
                                            prvUpdateJobStatus( C, eJobStatus_InProgress, ( int32_t ) eJobReason_Receiving, ( int32_t ) NULL );

                                            /* Check if we have received expected number of blocks for the current request. */
                                            if( ulNumOfBlocksToReceive > 1 )
                                            {
                                                ulNumOfBlocksToReceive--;
                                            }
                                            else
                                            {
                                                /* Received number of data blocks requested so restart the request timer.*/
                                                prvStartRequestTimer( C );

                                                /* Send the event to request next set of data blocks.*/
                                                if( xOTA_Agent.xOTA_EventFlags != NULL )
                                                {
                                                    ( void ) xEventGroupSetBits( xOTA_Agent.xOTA_EventFlags, OTA_EVT_MASK_REQ_TIMEOUT );
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                /* Ignore unknown message types. */
                                OTA_LOG_L2( "[%s] Ignoring unknown message type %d.\r\n", OTA_METHOD_NAME, xMsgMetaData.lMsgType );
                            }

                            if( pxMsgMetaData->pxPubData.vData != NULL )
                            {
                                xOTA_Agent.xStatistics.ulOTA_PacketsProcessed++;
                                /* Free the MQTT buffer since we're done with it (even if we ignored the message). */
                                prvOTAPubMessageFree( pxMsgMetaData );
                            }
                            else
                            {
                                /* Safety ignore messages not associated with an MQTT buffer. */
                            }
                        }
                    }
                }

                if( C == NULL )
                { /* Any event that releases the context structure tells us we're not active anymore. */
                    xOTA_Agent.eState = eOTA_AgentState_Ready;
                }
            }

            /* If we're here, we're shutting down the OTA agent. Free up all resources and quit. */
            prvAgentShutdownCleanup();
            vEventGroupDelete( xOTA_Agent.xOTA_EventFlags );
        }
    }

    /* Clear the entire agent context. This includes the OTA agent state. */
    memset( &xOTA_Agent, 0, sizeof( xOTA_Agent ) );

    /* Reset the image and agent states. Possibly redundant but safer.
     * Finally, self destruct. */
    xOTA_Agent.eImageState = eOTA_ImageState_Unknown;
    xOTA_Agent.eState = eOTA_AgentState_NotReady;
    vTaskDelete( NULL );
}