public function send(Message $message, $registrationIds) {
    if (empty($registrationIds)) {
      throw new \InvalidArgumentException('registrationIds cannot be empty');
    }

    if (!is_array($registrationIds)) {
      $registrationIds = array($registrationIds);
    }

    $chunks = array_chunk($registrationIds, 1000);

    $multicastResults = array();
    foreach($chunks as $chunk) {
      $attempt = 0;
      $backoff = self::BACKOFF_INITIAL_DELAY;
      $results = array();
      $unsentRegistrationIds = array_values($chunk);
      $multicastIds = array();
      do {
        $attempt++;

        try {
          $multicastResult = $this->makeRequest($message, $unsentRegistrationIds);
          $multicastIds[] = $multicastResult->getMulticastId();
          $unsentRegistrationIds = $this->updateStatus($unsentRegistrationIds, $results, $multicastResult);
        } catch (InvalidRequestException $e) {
          if ($attempt >= $this->retries) {
            throw $e;
          }
        }

        $tryAgain = count($unsentRegistrationIds) > 0 && $attempt <= $this->retries;
        if ($tryAgain) {
          $sleepTime = $backoff / 2 + rand(0, $backoff);
          sleep($sleepTime / 1000);
          if (2 * $backoff < self::MAX_BACKOFF_DELAY) {
            $backoff *= 2;
          }
        }
      } while ($tryAgain);

      $success = $failure = $canonicalIds = 0;
      foreach ($results as $result) {
        if (!is_null($result->getMessageId())) {
          $success++;

          if (!is_null($result->getCanonicalRegistrationId())) {
            $canonicalIds++;
          }
        } else {
          $failure++;
        }
      }

      $result = new MulticastResult($success, $failure, $canonicalIds, $multicastIds[0], $multicastIds);
      foreach ($registrationIds as $registrationId) {
        $result->addResult($results[$registrationId]);
      }

      $multicastResults[] = $result;
    }

    return new AggregateResult($multicastResults);
  }