/**
   * @param objPath
   * @param ifaceMask
   * @throws ControlPanelException
   */
  Unit addControlPanel(String objPath, int ifaceMask) throws ControlPanelException {
    Log.d(
        TAG,
        "Creating ControlPanelCollection object for objPath: '"
            + objPath
            + "', device: '"
            + deviceId
            + "'");

    // parse the received object path
    String[] segments = CommunicationUtil.parseObjPath(objPath);
    String unitId = segments[0];
    String panelId = segments[1];

    Unit unit = unitMap.get(unitId);

    if (unit == null) {
      Log.v(TAG, "Found new functional unit: '" + unitId + "', panel: '" + panelId + "'");
      unit = new Unit(this, unitId);
      unitMap.put(unitId, unit); // Store the new unit object
    } else {
      Log.v(TAG, "Found an existent functional unit: '" + unitId + "', panel: '" + panelId + "'");
    }

    if (CommunicationUtil.maskIncludes(ifaceMask, HTTPControl.ID_MASK)) {
      Log.d(TAG, "The objPath: '" + objPath + "' belongs to the HTTPControl interface, setting");
      unit.setHttpControlObjPath(objPath);
    } else {
      unit.createControlPanelCollection(objPath, panelId);
    }

    return unit;
  } // createUnit
  /**
   * Creates a NotificationAction control panel for the given objectPath <br>
   * The method should be used after establishment a session with a controllable device
   *
   * @param objectPath The object path to the created control panel
   * @return {@link ControlPanelCollection}
   * @throws ControlPanelException
   */
  public ControlPanelCollection createNotificationAction(String objectPath)
      throws ControlPanelException {

    if (sessionId == null) {
      throw new ControlPanelException(
          "The session wasn't established, can't create a ControlPanelCollection");
    }

    if (objectPath == null) {
      throw new ControlPanelException("Received an undefined objectPath");
    }

    Log.i(TAG, "Creating a NotificationAction control panel, objectPath: '" + objectPath + "'");

    String[] segments = CommunicationUtil.parseObjPath(objectPath);
    String unitId = segments[0];
    String panelName = segments[1];

    Unit unit = new Unit(this, unitId);
    ControlPanelCollection coll = unit.createControlPanelCollection(objectPath, panelName);
    coll.retrievePanels();
    coll.handleNotificationAction();

    return coll;
  } // addNotificationAction
  /**
   * 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