view deps/Platinum/Source/Core/PltDeviceData.cpp @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
line wrap: on
line source

/*****************************************************************
|
|   Platinum - Device Data
|
| Copyright (c) 2004-2008, Plutinosoft, LLC.
| All rights reserved.
| http://www.plutinosoft.com
|
| This program is free software; you can redistribute it and/or
| modify it under the terms of the GNU General Public License
| as published by the Free Software Foundation; either version 2
| of the License, or (at your option) any later version.
|
| OEMs, ISVs, VARs and other distributors that combine and 
| distribute commercially licensed software with Platinum software
| and do not wish to distribute the source code for the commercially
| licensed software under version 2, or (at your option) any later
| version, of the GNU General Public License (the "GPL") must enter
| into a commercial license agreement with Plutinosoft, LLC.
| 
| This program is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with this program; see the file LICENSE.txt. If not, write to
| the Free Software Foundation, Inc., 
| 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
| http://www.gnu.org/licenses/gpl-2.0.html
|
****************************************************************/

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include "PltDeviceData.h"
#include "PltService.h"
#include "PltUPnP.h"
#include "PltXmlHelper.h"

NPT_SET_LOCAL_LOGGER("platinum.core.devicedata")

/*----------------------------------------------------------------------
|   PLT_DeviceData::PLT_DeviceData
+---------------------------------------------------------------------*/
PLT_DeviceData::PLT_DeviceData(NPT_HttpUrl      description_url,
                               const char*      uuid,
                               NPT_TimeInterval lease_time,
                               const char*      device_type,
                               const char*      friendly_name) :
    m_UUID(uuid),
    m_URLDescription(description_url),
    m_DeviceType(device_type),
    m_FriendlyName(friendly_name)
{
    if (uuid == NULL || strlen(uuid) == 0) {
        PLT_UPnPMessageHelper::GenerateGUID(m_UUID);
    }

    SetLeaseTime(lease_time);
    SetURLBase(m_URLDescription);
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::~PLT_DeviceData
+---------------------------------------------------------------------*/
PLT_DeviceData::~PLT_DeviceData()
{
    Cleanup();
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::Cleanup
+---------------------------------------------------------------------*/
void
PLT_DeviceData::Cleanup()
{
    m_Services.Apply(NPT_ObjectDeleter<PLT_Service>());
    m_Services.Clear();
    m_EmbeddedDevices.Clear();
    m_Icons.Clear();
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::SetDescriptionUrl
+---------------------------------------------------------------------*/
/*NPT_Result
PLT_DeviceData::SetDescriptionUrl(NPT_HttpUrl& url)
{
    NPT_CHECK_FATAL(SetURLBase(url));
    m_URLDescription = url;
    return NPT_SUCCESS;
}*/

/*----------------------------------------------------------------------
|   PLT_DeviceData::GetDescriptionUrl
+---------------------------------------------------------------------*/
NPT_String
PLT_DeviceData::GetDescriptionUrl(const char* bind_addr)
{
    NPT_HttpUrl url = m_URLDescription;

    // override host
    if (bind_addr) url.SetHost(bind_addr);
    return url.ToString();
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::SetURLBase
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::SetURLBase(NPT_HttpUrl& url) 
{
    // we're assuming param passed to be relative (Host/port not different than description)
    m_URLBasePath = url.GetPath();

    // remove trailing file according to RFC 2396
    if (!m_URLBasePath.EndsWith("/")) {
        int index = m_URLBasePath.ReverseFind('/');
        if (index < 0) return NPT_FAILURE;
        m_URLBasePath.SetLength(index+1);
    }

    return NPT_SUCCESS;
}    

/*----------------------------------------------------------------------
|   PLT_DeviceData::GetURLBase
+---------------------------------------------------------------------*/
NPT_HttpUrl
PLT_DeviceData::GetURLBase()
{
    return NPT_HttpUrl(m_URLDescription.GetHost(),
                       m_URLDescription.GetPort(),
                       m_URLBasePath);
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::GetIconUrl
+---------------------------------------------------------------------*/
NPT_String
PLT_DeviceData::GetIconUrl(const char* mimetype, 
                           NPT_Int32   maxsize, 
                           NPT_Int32   maxdepth)
{
    PLT_DeviceIcon icon;

    for (NPT_Cardinal i=0; i<m_Icons.GetItemCount(); i++) {
        if ((mimetype && m_Icons[i].m_MimeType != mimetype) ||
            (maxsize  && m_Icons[i].m_Width > maxsize)      ||
            (maxsize  && m_Icons[i].m_Height > maxsize)     ||
            (maxdepth && m_Icons[i].m_Depth > maxdepth))
            continue;

        // pick the biggest and better resolution we can
        if (icon.m_Width  >= m_Icons[i].m_Width  ||
            icon.m_Height >= m_Icons[i].m_Height ||
            icon.m_Depth  >= m_Icons[i].m_Depth  ||
            m_Icons[i].m_UrlPath.IsEmpty())
            continue;

        icon = m_Icons[i];
    }

    if (icon.m_UrlPath == "") return "";

    // make absolut path from url base if necessary
    NPT_HttpUrl url(icon.m_UrlPath);
    if (!url.IsValid()) {
        url = GetURLBase();
        if (icon.m_UrlPath.StartsWith("/")) {
            url.SetPathPlus(icon.m_UrlPath);
        } else {
            url.SetPathPlus(url.GetPath() + icon.m_UrlPath);
        }
    }

    return url.ToString();
}
/*----------------------------------------------------------------------
|   PLT_DeviceData::SetLeaseTime
+---------------------------------------------------------------------*/
NPT_Result   
PLT_DeviceData::SetLeaseTime(NPT_TimeInterval lease_time) 
{
    m_LeaseTime = lease_time;
    NPT_System::GetCurrentTimeStamp(m_LeaseTimeLastUpdate);
    return NPT_SUCCESS;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::GetLeaseTimeLastUpdate
+---------------------------------------------------------------------*/
NPT_TimeStamp 
PLT_DeviceData::GetLeaseTimeLastUpdate() 
{
    return m_LeaseTimeLastUpdate;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::ToLog
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::ToLog(int level /* = NPT_LOG_LEVEL_FINE */)
{
    NPT_COMPILER_UNUSED(level);

    NPT_StringOutputStreamReference stream(new NPT_StringOutputStream);
    stream->WriteString("Device GUID: ");
    stream->WriteString((const char*)m_UUID);

    stream->WriteString("Device Type: ");
    stream->WriteString((const char*)m_DeviceType);

    stream->WriteString("Device Base Url: ");
    stream->WriteString((const char*)GetURLBase().ToString());

    stream->WriteString("Device Friendly Name: ");
    stream->WriteString((const char*)m_FriendlyName);

    NPT_LOG_1(level, "%s", (const char*)stream->GetString());
    return NPT_SUCCESS;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::AddDevice
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::AddDevice(PLT_DeviceDataReference& device)
{
    device->m_ParentUUID = m_UUID;
    m_EmbeddedDevices.Add(device);
    return NPT_SUCCESS;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::AddService
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::AddService(PLT_Service* service)
{
    if (service->m_ServiceType == "" ||
        service->m_ServiceID   == "" ||
        service->m_SCPDURL     == "" ||
        service->m_ControlURL  == "" ||
        service->m_EventSubURL == "") {
        return NPT_ERROR_INVALID_PARAMETERS;
    }

    return m_Services.Add(service);
}

/*----------------------------------------------------------------------
|   PLT_GetDescriptionIterator class
+---------------------------------------------------------------------*/
template <class T>
class PLT_GetDescriptionIterator
{
public:
    PLT_GetDescriptionIterator<T>(NPT_XmlElementNode* parent) :
      m_Parent(parent) {}

      NPT_Result operator()(T& data) const {
          return data->GetDescription(m_Parent);
      }

private:
    NPT_XmlElementNode* m_Parent;
};

/*----------------------------------------------------------------------
|   PLT_DeviceData::ToXML
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::GetDescription(NPT_XmlElementNode* root, NPT_XmlElementNode** device_out)
{
    NPT_XmlElementNode* device = new NPT_XmlElementNode("device");
    if (device_out) {
        *device_out = device;
    }

    NPT_CHECK_SEVERE(root->AddChild(device));

    // device properties
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "deviceType", m_DeviceType));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "friendlyName", m_FriendlyName));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "manufacturer", m_Manufacturer));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "manufacturerURL", m_ManufacturerURL));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "modelDescription", m_ModelDescription));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "modelName", m_ModelName));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "modelURL", m_ModelURL));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "modelNumber", m_ModelNumber));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "serialNumber", m_SerialNumber));
    NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "UDN", "uuid:" + m_UUID));
    
    if (!m_PresentationURL.IsEmpty()) {
        NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(device, "presentationURL", m_PresentationURL));
    }
    
    // Extra info not UPnP specs
    NPT_CHECK(OnAddExtraInfo(device));

    // PS3 support
    if (!m_DlnaDoc.IsEmpty()) {
        NPT_XmlElementNode* dlnadoc = new NPT_XmlElementNode("dlna", "X_DLNADOC");
        dlnadoc->AddText(m_DlnaDoc);
        device->AddChild(dlnadoc);
    }
    if (!m_DlnaCap.IsEmpty()) {
        NPT_XmlElementNode* dlnacap = new NPT_XmlElementNode("dlna", "X_DLNACAP");
        dlnacap->AddText(m_DlnaCap);
        device->AddChild(dlnacap);
    }

    // icons
    if (m_Icons.GetItemCount()) {
        NPT_XmlElementNode* icons = new NPT_XmlElementNode("iconList");
        NPT_CHECK_SEVERE(device->AddChild(icons));
        for (NPT_Cardinal i=0; i<m_Icons.GetItemCount(); i++) {
            NPT_XmlElementNode* icon = new NPT_XmlElementNode("icon");
            NPT_CHECK_SEVERE(icons->AddChild(icon));
            NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(icon, "mimetype", m_Icons[i].m_MimeType));
            NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(icon, "width", NPT_String::FromInteger(m_Icons[i].m_Width)));
            NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(icon, "height", NPT_String::FromInteger(m_Icons[i].m_Height)));
            NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(icon, "depth", NPT_String::FromInteger(m_Icons[i].m_Depth)));
            NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(icon, "url", m_Icons[i].m_UrlPath));
        }
    }

    // services
    NPT_XmlElementNode* services = new NPT_XmlElementNode("serviceList");
    NPT_CHECK_SEVERE(device->AddChild(services));
    NPT_CHECK_SEVERE(m_Services.ApplyUntil(PLT_GetDescriptionIterator<PLT_Service*>(services), 
                                           NPT_UntilResultNotEquals(NPT_SUCCESS)));

    // PS3 support
    NPT_XmlElementNode* aggr = new NPT_XmlElementNode("av", "aggregationFlags");
    NPT_CHECK_SEVERE(aggr->SetNamespaceUri("av", "urn:schemas-sonycom:av"));
    aggr->AddText("10");
    device->AddChild(aggr);

    // embedded devices
    if (m_EmbeddedDevices.GetItemCount()) {
        NPT_XmlElementNode* deviceList = new NPT_XmlElementNode("deviceList");
        NPT_CHECK_SEVERE(device->AddChild(deviceList));

        NPT_CHECK_SEVERE(m_EmbeddedDevices.ApplyUntil(
            PLT_GetDescriptionIterator<PLT_DeviceDataReference>(deviceList), 
            NPT_UntilResultNotEquals(NPT_SUCCESS)));
    }

    return NPT_SUCCESS;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::GetDescription
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::GetDescription(NPT_String& desc)
{
    NPT_Result res;
    NPT_XmlElementNode* spec = NULL;
    NPT_XmlElementNode* root = new NPT_XmlElementNode("root");

    NPT_CHECK_LABEL_SEVERE(res = root->SetNamespaceUri("", "urn:schemas-upnp-org:device-1-0"), cleanup);
    NPT_CHECK_LABEL_SEVERE(res = root->SetNamespaceUri("dlna", "urn:schemas-dlna-org:device-1-0"), cleanup);

    // add spec version
    spec = new NPT_XmlElementNode("specVersion");
    NPT_CHECK_LABEL_SEVERE(res = root->AddChild(spec), cleanup);
    NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::AddChildText(spec, "major", "1"), cleanup);
    NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::AddChildText(spec, "minor", "0"), cleanup);

    // get device xml
    NPT_CHECK_LABEL_SEVERE(res = GetDescription(root), cleanup);

    // serialize node
    NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::Serialize(*root, desc), cleanup);

cleanup:
    delete root;
    return res;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::SetDescription
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::SetDescription(const char*          description, 
                               const NPT_IpAddress& local_iface_ip)
{
    NPT_XmlParser       parser;
    NPT_XmlNode*        tree = NULL;
    NPT_Result          res;
    NPT_XmlElementNode* root = NULL;
    NPT_String          URLBase;
    
    res = parser.Parse(description, tree);
    NPT_CHECK_LABEL_SEVERE(res, cleanup);

    root = tree->AsElementNode();
    if (!root || 
        root->GetTag() != "root" || 
        !root->GetNamespace() || 
        *root->GetNamespace() != "urn:schemas-upnp-org:device-1-0") {
        NPT_LOG_INFO_1("root namespace is invalid: %s", 
            (root&&root->GetNamespace())?root->GetNamespace()->GetChars():"null");
        NPT_CHECK_LABEL_SEVERE(NPT_FAILURE, cleanup);
    }

    // look for optional URLBase element
    if (NPT_SUCCEEDED(PLT_XmlHelper::GetChildText(root, "URLBase", URLBase))) {
        NPT_HttpUrl url(URLBase);
        
        if (!url.IsValid()) {
            NPT_CHECK_LABEL_SEVERE(NPT_FAILURE, cleanup);
        }

        SetURLBase(url);
    }

    // at least one root device child element is required
    NPT_XmlElementNode* device;
    if (!(device = PLT_XmlHelper::GetChild(root, "device"))) {
        NPT_CHECK_LABEL_SEVERE(NPT_FAILURE, cleanup);
    }

    res = SetDescriptionDevice(device);

cleanup:
    // delete the tree
    delete tree;

    m_LocalIfaceIp = local_iface_ip;
    return res;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::SetDescriptionDevice
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::SetDescriptionDevice(NPT_XmlElementNode* device_node)
{
    NPT_Result res;

    NPT_CHECK_SEVERE(PLT_XmlHelper::GetChildText(device_node, "deviceType", m_DeviceType));
    NPT_CHECK_SEVERE(PLT_XmlHelper::GetChildText(device_node, "UDN", m_UUID));

    // jump uuid:, we should probably check first
    m_UUID = ((const char*)m_UUID)+5;

    // optional attributes
    PLT_XmlHelper::GetChildText(device_node, "friendlyName", m_FriendlyName);
    PLT_XmlHelper::GetChildText(device_node, "manufacturer", m_Manufacturer);
    PLT_XmlHelper::GetChildText(device_node, "manufacturerURL", m_ManufacturerURL);
    PLT_XmlHelper::GetChildText(device_node, "modelDescription", m_ModelDescription);
    PLT_XmlHelper::GetChildText(device_node, "modelName", m_ModelName);
    PLT_XmlHelper::GetChildText(device_node, "modelNumber", m_ModelNumber);
    PLT_XmlHelper::GetChildText(device_node, "serialNumber", m_SerialNumber);

    // enumerate icons
    NPT_XmlElementNode* iconList = PLT_XmlHelper::GetChild(device_node, "iconList");
    if (iconList) {
        NPT_Array<NPT_XmlElementNode*> icons;
        PLT_XmlHelper::GetChildren(iconList, icons, "icon");

        for (NPT_Cardinal k=0 ; k<icons.GetItemCount(); k++) {
            PLT_DeviceIcon icon;
            NPT_String integer, height, depth;

            PLT_XmlHelper::GetChildText(icons[k], "mimetype", icon.m_MimeType);
            PLT_XmlHelper::GetChildText(icons[k], "url", icon.m_UrlPath);

            if(NPT_SUCCEEDED(PLT_XmlHelper::GetChildText(icons[k], "width", integer)))
                NPT_ParseInteger32(integer, icon.m_Width);
            if(NPT_SUCCEEDED(PLT_XmlHelper::GetChildText(icons[k], "height", integer)))
                NPT_ParseInteger32(integer, icon.m_Height);
            if(NPT_SUCCEEDED(PLT_XmlHelper::GetChildText(icons[k], "depth", integer)))
                NPT_ParseInteger32(integer, icon.m_Depth);

            m_Icons.Add(icon);
        }
    }

    // enumerate device services
    NPT_XmlElementNode* serviceList = PLT_XmlHelper::GetChild(device_node, "serviceList");
    if (serviceList) {
        NPT_Array<NPT_XmlElementNode*> services;
        PLT_XmlHelper::GetChildren(serviceList, services, "service");
        for( int k = 0 ; k < (int)services.GetItemCount(); k++) {    
            PLT_Service* service = new PLT_Service(this);
            PLT_XmlHelper::GetChildText(services[k], "serviceType", service->m_ServiceType);
            PLT_XmlHelper::GetChildText(services[k], "serviceId", service->m_ServiceID);
            PLT_XmlHelper::GetChildText(services[k], "SCPDURL", service->m_SCPDURL);
            PLT_XmlHelper::GetChildText(services[k], "controlURL", service->m_ControlURL);
            PLT_XmlHelper::GetChildText(services[k], "eventSubURL", service->m_EventSubURL);
            if (NPT_FAILED(res = AddService(service))) {
                delete service;
                return res;
            }
        }
    }

    // enumerate embedded devices
    NPT_XmlElementNode* deviceList = PLT_XmlHelper::GetChild(device_node, "deviceList");
    if (deviceList) {
        NPT_Array<NPT_XmlElementNode*> devices;
        PLT_XmlHelper::GetChildren(deviceList, devices, "device");
        for( int k = 0 ; k < (int)devices.GetItemCount(); k++) {    
            PLT_DeviceDataReference device(new PLT_DeviceData(m_URLDescription, "", m_LeaseTime));
            NPT_CHECK_SEVERE(device->SetDescriptionDevice(devices[k]));
            AddDevice(device);
        }
    }

    return NPT_SUCCESS;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::FindEmbeddedDeviceByType
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::FindEmbeddedDeviceByType(const char*              type, 
                                         PLT_DeviceDataReference& device)
{
    return NPT_ContainerFind(m_EmbeddedDevices, 
                             PLT_DeviceDataFinderByType(type), 
                             device);
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::FindServiceById
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::FindServiceById(const char* id, PLT_Service*& service)
{
    return NPT_ContainerFind(m_Services, 
                             PLT_ServiceIDFinder(id),
                             service);
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::FindServiceByType
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::FindServiceByType(const char* type, PLT_Service*& service)
{
    return NPT_ContainerFind(m_Services, 
                             PLT_ServiceTypeFinder(type), 
                             service);
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::FindServiceByDescriptionURI
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::FindServiceByDescriptionURI(const char*   uri, 
                                            PLT_Service*& service)
{
    if (NPT_SUCCEEDED(NPT_ContainerFind(m_Services, 
                                        PLT_ServiceSCPDURLFinder(uri), 
                                        service))) {
        return NPT_SUCCESS;
    }

    for (int i=0; i < (int)m_EmbeddedDevices.GetItemCount(); i++) {
        if (NPT_SUCCEEDED(NPT_ContainerFind(m_EmbeddedDevices[i]->m_Services, 
                                            PLT_ServiceSCPDURLFinder(uri), 
                                            service)))
            return NPT_SUCCESS;
    }
    return NPT_FAILURE;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::FindServiceByControlURI
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::FindServiceByControlURI(const char*   uri, 
                                        PLT_Service*& service)
{
    if (NPT_SUCCEEDED(NPT_ContainerFind(m_Services, 
                                        PLT_ServiceControlURLFinder(uri), 
                                        service))) {
        return NPT_SUCCESS;
    }

    for (int i=0; i < (int)m_EmbeddedDevices.GetItemCount(); i++) {
        if (NPT_SUCCEEDED(NPT_ContainerFind(m_EmbeddedDevices[i]->m_Services, 
                                            PLT_ServiceControlURLFinder(uri), 
                                            service)))
            return NPT_SUCCESS;
    }
    return NPT_FAILURE;
}

/*----------------------------------------------------------------------
|   PLT_DeviceData::FindServiceByEventSubURI
+---------------------------------------------------------------------*/
NPT_Result
PLT_DeviceData::FindServiceByEventSubURI(const char*   uri, 
                                         PLT_Service*& service)
{       
    if (NPT_SUCCEEDED(NPT_ContainerFind(m_Services, 
                                        PLT_ServiceEventSubURLFinder(uri), 
                                        service))) {
        return NPT_SUCCESS;
    }

    for (int i=0; i < (int)m_EmbeddedDevices.GetItemCount(); i++) {
        if (NPT_SUCCEEDED(NPT_ContainerFind(m_EmbeddedDevices[i]->m_Services, 
                                            PLT_ServiceEventSubURLFinder(uri), 
                                            service)))
            return NPT_SUCCESS;
    }
    return NPT_FAILURE;
}