@Override
  public void unregisterService(ServiceState serviceState) {
    List<ServiceName> serviceNames = serviceState.getServiceNames();
    log.debug("Unregister service: " + serviceNames);

    AbstractBundle serviceOwner = serviceState.getServiceOwner();

    // This event is synchronously delivered before the service has completed unregistering.
    eventsPlugin.fireServiceEvent(serviceOwner, ServiceEvent.UNREGISTERING, serviceState);

    // Remove from using bundles
    for (AbstractBundle bundleState : serviceState.getUsingBundlesInternal()) {
      while (ungetService(bundleState, serviceState)) ;
    }

    // Remove from owner bundle
    serviceOwner.removeRegisteredService(serviceState);

    // Unregister name associations
    for (ServiceName serviceName : serviceNames) {
      String[] clazzes = (String[]) serviceState.getProperty(Constants.OBJECTCLASS);
      for (String clazz : clazzes) unregisterNameAssociation(clazz, serviceName);
    }

    // Remove from controller
    ServiceName rootServiceName = serviceNames.get(0);
    try {
      ServiceController<?> controller = serviceContainer.getService(rootServiceName);
      controller.setMode(Mode.REMOVE);
    } catch (RuntimeException ex) {
      log.error("Cannot remove service: " + rootServiceName, ex);
    }
  }
  @Override
  public boolean ungetService(AbstractBundle bundleState, ServiceState serviceState) {
    serviceState.ungetScopedValue(bundleState);

    int useCount = bundleState.removeServiceInUse(serviceState);
    if (useCount == 0) serviceState.removeUsingBundle(bundleState);

    return useCount >= 0;
  }
  /*
   * The FindHook is called when a target bundle searches the service registry
   * with the getServiceReference or getServiceReferences methods. A registered
   * FindHook service gets a chance to inspect the returned set of service
   * references and can optionally shrink the set of returned services. The order
   * in which the find hooks are called is the reverse compareTo ordering of
   * their Service References.
   */
  private List<ServiceState> processFindHooks(
      AbstractBundle bundle,
      String clazz,
      String filterStr,
      boolean checkAssignable,
      List<ServiceState> serviceStates) {
    BundleContext context = bundle.getBundleContext();
    List<ServiceState> hookRefs =
        getServiceReferencesInternal(bundle, FindHook.class.getName(), null, true);
    if (hookRefs.isEmpty()) return serviceStates;

    // Event and Find Hooks can not be used to hide the services from the framework.
    if (clazz != null && clazz.startsWith(FindHook.class.getPackage().getName()))
      return serviceStates;

    // The order in which the find hooks are called is the reverse compareTo ordering of
    // their ServiceReferences. That is, the service with the highest ranking number must be called
    // first.
    List<ServiceReference> sortedHookRefs = new ArrayList<ServiceReference>(hookRefs);
    Collections.reverse(sortedHookRefs);

    List<FindHook> hooks = new ArrayList<FindHook>();
    for (ServiceReference hookRef : sortedHookRefs)
      hooks.add((FindHook) context.getService(hookRef));

    Collection<ServiceReference> hookParam = new ArrayList<ServiceReference>();
    for (ServiceState aux : serviceStates) hookParam.add(aux.getReference());

    hookParam = new RemoveOnlyCollection<ServiceReference>(hookParam);
    for (FindHook hook : hooks) {
      try {
        hook.find(context, clazz, filterStr, !checkAssignable, hookParam);
      } catch (Exception ex) {
        log.warn("Error while calling FindHook: " + hook, ex);
      }
    }

    List<ServiceState> result = new ArrayList<ServiceState>();
    for (ServiceReference aux : hookParam) {
      ServiceState serviceState = ServiceState.assertServiceState(aux);
      result.add(serviceState);
    }

    return result;
  }
  @Override
  public Object getService(AbstractBundle bundleState, ServiceState serviceState) {
    // If the service has been unregistered, null is returned.
    if (serviceState.isUnregistered()) return null;

    // Add the given service ref to the list of used services
    bundleState.addServiceInUse(serviceState);
    serviceState.addUsingBundle(bundleState);

    Object value = serviceState.getScopedValue(bundleState);

    // If the factory returned an invalid value
    // restore the service usage counts
    if (value == null) {
      bundleState.removeServiceInUse(serviceState);
      serviceState.removeUsingBundle(bundleState);
    }

    return value;
  }
 @Override
 public Set<AbstractBundle> getUsingBundles(ServiceState serviceState) {
   return serviceState.getUsingBundlesInternal();
 }
  public List<ServiceState> getServiceReferencesInternal(
      AbstractBundle bundleState, String clazz, Filter filter, boolean checkAssignable) {
    if (bundleState == null) throw new IllegalArgumentException("Null bundleState");

    List<ServiceName> serviceNames;
    if (clazz != null) {
      serviceNames = serviceNameMap.get(clazz);
      if (serviceNames == null) serviceNames = new ArrayList<ServiceName>();

      // Add potentially registered xservcie
      ServiceName xserviceName = ServiceName.of(ModuleContext.XSERVICE_PREFIX, clazz);
      ServiceController<?> xservice = serviceContainer.getService(xserviceName);
      if (xservice != null) serviceNames.add(xserviceName);
    } else {
      // [MSC-9] Add ability to query the ServiceContainer
      Set<ServiceName> allServiceNames = new HashSet<ServiceName>();
      for (List<ServiceName> auxList : serviceNameMap.values()) allServiceNames.addAll(auxList);

      serviceNames = new ArrayList<ServiceName>(allServiceNames);
    }

    if (serviceNames.isEmpty()) return Collections.emptyList();

    if (filter == null) filter = NoFilter.INSTANCE;

    List<ServiceState> result = new ArrayList<ServiceState>();
    for (ServiceName serviceName : serviceNames) {
      ServiceController<?> controller = serviceContainer.getService(serviceName);
      if (controller == null)
        throw new IllegalStateException("Cannot obtain service for: " + serviceName);

      Object value = controller.getValue();

      // Create the ServiceState on demand for an XService instance
      // [TODO] This should be done eagerly to keep the serviceId constant
      // [TODO] service events for XService lifecycle changes
      // [MSC-17] Canonical ServiceName string representation
      if (value instanceof ServiceState == false
          && serviceName.toString().contains(ModuleContext.XSERVICE_PREFIX)) {
        long serviceId = getNextServiceId();
        Bundle bundle = packageAdmin.getBundle(value.getClass());
        AbstractBundle owner = AbstractBundle.assertBundleState(bundle);
        value =
            new ServiceState(
                owner,
                serviceId,
                new ServiceName[] {serviceName},
                new String[] {clazz},
                value,
                null);
      }

      ServiceState serviceState = (ServiceState) value;
      if (filter.match(serviceState) == false) continue;

      Object rawValue = serviceState.getRawValue();

      checkAssignable &= (clazz != null);
      checkAssignable &= (bundleState.getBundleId() != 0);
      checkAssignable &= !(rawValue instanceof ServiceFactory);
      if (checkAssignable == false || serviceState.isAssignableTo(bundleState, clazz)) {
        result.add(serviceState);
      }
    }

    // Sort the result
    Collections.sort(result, ServiceReferenceComparator.getInstance());
    Collections.reverse(result);

    return Collections.unmodifiableList(result);
  }