code: protected function saveItemResponses($emptyAllowed = true)
      {
          if ($this->getRequestParameter('itemDefinition') && $this->getRequestParameter('itemResponse')) {
  
              $itemDefinition = $this->getRequestParameter('itemDefinition');
              $serviceContext = $this->getServiceContext();
              $itemResponse = $this->getRequestParameter('itemResponse')
                  ? json_decode($this->getRequestParameter('itemResponse'), true)
                  : null;
  
              if (!is_null($itemResponse) && !empty($itemDefinition)) {
                  $responses = $this
                      ->getRunnerService()
                      ->parsesItemResponse($serviceContext, $itemDefinition, $itemResponse);
  
                  //still verify allowSkipping & empty responses
                  if (
                      !$emptyAllowed
                      && $this->getRunnerService()->getTestConfig()->getConfigValue('enableAllowSkipping')
                      && !TestRunnerUtils::doesAllowSkipping($serviceContext->getTestSession())) {
                      if ($this->getRunnerService()->emptyResponse($serviceContext, $responses)) {
                          throw new QtiRunnerEmptyResponsesException();
                      }
                  }
  
                  return $this->getRunnerService()->storeItemResponse($serviceContext, $itemDefinition, $responses);
              }
          }
          return false;
      }
docs: Save the item responses
  Requires params itemDuration and optionaly consumedExtraTime
  
  @param boolean $emptyAllowed if we allow empty responses
  @return boolean true if saved
  @throws \common_Exception
  @throws QtiRunnerEmptyResponsesException if responses are empty, emptyAllowed is false and no allowSkipping