/**
   * Add several local services to the service bundle. Adding services triggers a service-announce
   * by the local RVI node.
   *
   * @param serviceIdentifiers a list of service identifiers
   */
  public void addLocalServices(ArrayList<String> serviceIdentifiers) {
    for (String serviceIdentifier : serviceIdentifiers)
      mLocalServices.put(
          serviceIdentifier,
          new Service(serviceIdentifier, mDomain, mBundleIdentifier, mLocalNodeIdentifier));

    if (mNode != null) mNode.announceServices();
  }
  /**
   * Add a local service to the service bundle. Adding services triggers a service-announce by the
   * local RVI node.
   *
   * @param serviceIdentifier the identifier of the service
   */
  public void addLocalService(String serviceIdentifier) {
    if (!mLocalServices.containsKey(serviceIdentifier))
      mLocalServices.put(
          serviceIdentifier,
          new Service(serviceIdentifier, mDomain, mBundleIdentifier, mLocalNodeIdentifier));

    if (mNode != null) mNode.announceServices();
  }
  /**
   * Invoke/update a remote service on the remote RVI node
   *
   * @param serviceIdentifier the service identifier
   * @param parameters the parameters
   * @param timeout the timeout, in milliseconds. This is added to the current system time.
   */
  public void invokeService(String serviceIdentifier, Object parameters, Integer timeout) {
    Service service = getRemoteService(serviceIdentifier);

    service.setParameters(parameters);
    service.setTimeout(System.currentTimeMillis() + timeout);

    if (service.hasNodeIdentifier() && mNode != null) // TODO: Check the logic here
    mNode.invokeService(service);
    else queueServiceInvocation(serviceIdentifier, service);
  }
  /**
   * Instantiates a new Service bundle.
   *
   * @param context the Application context. This value cannot be null.
   * @param domain the domain portion of the RVI node's prefix (e.g., "jlr.com"). The domain must
   *     only contain alphanumeric characters, underscores, and/or periods. No other characters or
   *     whitespace are allowed. This value cannot be an empty string or null.
   * @param bundleIdentifier the bundle identifier (e.g., "hvac") The bundle identifier must only
   *     contain alphanumeric characters, underscores, and/or periods. No other characters or
   *     whitespace are allowed. This value cannot be an empty string or null.
   * @param servicesIdentifiers a list of the identifiers for all the local services. The service
   *     identifiers must only contain alphanumeric characters, underscores, and/or periods. No
   *     other characters or whitespace are allowed. This value cannot be an empty string or null.
   * @exception java.lang.IllegalArgumentException Throws an exception when the context is null, or
   *     if the domain, bundle identifier or any of the service identifiers are an empty string,
   *     contain special characters, or are null.
   */
  public ServiceBundle(
      Context context,
      String domain,
      String bundleIdentifier,
      ArrayList<String> servicesIdentifiers)
      throws IllegalArgumentException {
    if (context == null) throw new InvalidParameterException("Input parameter can't be null");

    mDomain = validated(domain);
    mBundleIdentifier = validated(bundleIdentifier);

    mLocalNodeIdentifier = RVINode.getLocalNodeIdentifier(context);

    mLocalServices = makeServices(servicesIdentifiers);
  }
  /**
   * Add a remote service to the service bundle. If there is a pending service invocation with a
   * matching service identifier, this invocation is sent to the remote node.
   *
   * @param serviceIdentifier the identifier of the service
   */
  void addRemoteService(String serviceIdentifier, String remoteNodeIdentifier) {
    if (!mRemoteServices.containsKey(serviceIdentifier))
      mRemoteServices.put(
          serviceIdentifier,
          new Service(serviceIdentifier, mDomain, mBundleIdentifier, remoteNodeIdentifier));

    ArrayList<Service> pendingServiceInvocationList =
        mPendingServiceInvocations.get(serviceIdentifier);

    if (pendingServiceInvocationList != null) {
      for (Service pendingServiceInvocation : pendingServiceInvocationList) {
        if (pendingServiceInvocation.getTimeout() >= System.currentTimeMillis()) {
          pendingServiceInvocation.setNodeIdentifier(remoteNodeIdentifier);
          mNode.invokeService(pendingServiceInvocation);
        }
      }

      mPendingServiceInvocations.remove(serviceIdentifier);
    }
  }
  /**
   * Removes all the local services from the service bundle. Removing services triggers a
   * service-announce by the local RVI node.
   */
  public void removeAllLocalServices() {
    mLocalServices.clear();

    if (mNode != null) mNode.announceServices();
  }
  /**
   * Remote a local service from the service bundle. Removing services triggers a service-announce
   * by the local RVI node.
   *
   * @param serviceIdentifier the identifier of the service
   */
  public void removeLocalService(String serviceIdentifier) {
    mLocalServices.remove(serviceIdentifier);

    if (mNode != null) mNode.announceServices();
  }