/**
   * Adds a ControlPanel for the given objectPath.
   *
   * @param objectPath The object path of the added control panel
   * @param interfaces The interfaces the object path implements
   * @return {@link Unit} this object path belongs to
   * @throws ControlPanelException
   */
  public Unit addControlPanel(String objectPath, String... interfaces)
      throws ControlPanelException {
    if (objectPath == null) {
      throw new ControlPanelException("The objectPath: '" + objectPath + "' is undefined");
    }

    int ifaceMask = CommunicationUtil.getInterfaceMask(interfaces);

    if (!CommunicationUtil.maskIncludes(ifaceMask, ControlPanel.ID_MASK)
        && !CommunicationUtil.maskIncludes(ifaceMask, HTTPControl.ID_MASK)) {
      throw new ControlPanelException(
          "The objectPath: '"
              + objectPath
              + "', doesn't implement any ControlPanel permitted interface");
    }

    return addControlPanel(objectPath, ifaceMask);
  } // addControlPanel
  /**
   * Checks whether the received announcement has ControlPanel interface <br>
   * If has creates ControllableObject and save it in the registry
   *
   * @param args
   */
  private void handleAnnouncement(Map<String, Object> args) {

    String deviceId = (String) args.get("DEVICE_ID");
    String appId = (String) args.get("APP_ID");
    String sender = (String) args.get("SENDER");

    BusObjectDescription[] objDescList = (BusObjectDescription[]) args.get("OBJ_DESC");

    if (deviceId == null || deviceId.length() == 0) {
      Log.e(TAG, "Received a bad Announcement signal, deviceId can't be NULL or empty");
      return;
    }
    if (sender == null || sender.length() == 0) {
      Log.e(TAG, "Received a bad Announcement signal, sender can't be NULL or empty");
      return;
    }
    if (objDescList == null || objDescList.length == 0) {
      Log.e(TAG, "Received a bad Announcement signal, BusObjectDescription array is empty");
      return;
    }

    // The controllable device id should be constructed from the deviceId and the appId
    deviceId = deviceId + "_" + appId;

    boolean newDevice = false; // TRUE if it's a not registered new device
    boolean handledDevice =
        false; // TRUE if for at least one received control panel object path we handled the device

    ControllableDevice device = deviceRegistry.getDevices().get(deviceId);

    // Iterate over the BusObjectDescription objects received from an Announcement signal
    for (BusObjectDescription busObjDesc : objDescList) {
      Log.v(TAG, "Found objPath: '" + busObjDesc.getPath() + "'");

      String[] interfaces = busObjDesc.getInterfaces();

      int ifaceMask = CommunicationUtil.getInterfaceMask(interfaces);
      // Check if found a ControlPanel or HTTPControl interfaces
      if (!CommunicationUtil.maskIncludes(ifaceMask, ControlPanel.ID_MASK)
          && !CommunicationUtil.maskIncludes(ifaceMask, HTTPControl.ID_MASK)) {
        continue;
      }

      String objPath = busObjDesc.getPath();

      Log.d(TAG, "Found ControlPanel object, path: '" + objPath + "'");

      if (!handledDevice) {

        if (device == null) {
          Log.d(
              TAG, "Discovered new device, deviceId: '" + deviceId + "', sender: '" + sender + "'");

          device = new ControllableDevice(deviceId, sender);
          device.subscribeOnFoundLostEvents(); // Listen to events of found | lost adv. name
          newDevice = true;
        } // device == null
        else {
          Log.d(
              TAG,
              "Device with deviceId: '"
                  + deviceId
                  + "' already exists, updating sender to be: '"
                  + sender
                  + "'");
          device.setSender(sender);

          try {
            connMgr.cancelFindAdvertisedName(sender);
          } catch (ControlPanelException cpe) {
            Log.e(
                TAG,
                "Failed to call cancelFindAdvertisedName(), Error: '" + cpe.getMessage() + "'");
            return;
          }
        } // else :: device == null

        device.setReachable(true);
        device.startDeviceFoundVerificationService(); // Start scheduled service before call
        // findAdvName

        Log.d(TAG, "Start findAdvertisedName for sender: '" + sender + "'");
        Status res;

        try {
          res = connMgr.findAdvertisedName(sender);
        } catch (ControlPanelException cpe) {
          Log.e(TAG, "Failed to call findAdvertisedName(), Error: '" + cpe.getMessage() + "'");
          return;
        }

        if (res != Status.OK) {
          Log.d(
              TAG,
              "Failed to start findAdvertisedName for sender: '"
                  + sender
                  + "', Error: '"
                  + res
                  + "'");
          device.stopDeviceActivities();
          return;
        }

        // We handled the discovered device for at least one of the received ControlPanel object
        // paths
        handledDevice = true;
      } // if :: not handledDevice

      try {
        device.addControlPanel(objPath, ifaceMask);
      } catch (ControlPanelException cpe) {
        Log.w(
            TAG,
            "Received a broken object path: '" + objPath + "', Error: '" + cpe.getMessage() + "'");
      }
    } // for :: BusObjectDescription

    if (handledDevice) {
      if (newDevice) {
        deviceRegistry.foundNewDevice(device);
      } else {
        deviceRegistry.reachabilityChanged(device, true);
      }
    }
  } // handleAnnouncement