diff --git a/src/contacts/contacts.pro b/src/contacts/contacts.pro
index d4100f8a449f49bfb665188e28ebe948d2563982..55226af73893c9c7d32b792bca6d4183a1b29e00 100644
--- a/src/contacts/contacts.pro
+++ b/src/contacts/contacts.pro
@@ -19,6 +19,9 @@ PUBLIC_HEADERS += \
     qcontactactionfactory.h \
     qcontactactiontarget.h \
     qcontactchangeset.h \
+    qcontactcollection.h \
+    qcontactcollectionchangeset.h \
+    qcontactcollectionid.h \
     qcontactdetail.h \
     qcontactfetchhint.h \
     qcontactfilter.h \
@@ -39,6 +42,8 @@ PRIVATE_HEADERS += \
     qcontactactionmanager_p.h \
     qcontactactiontarget_p.h \
     qcontactchangeset_p.h \
+    qcontactcollection_p.h \
+    qcontactcollectionchangeset_p.h \
     qcontactdetail_p.h \
     qcontactfetchhint_p.h \
     qcontactfilter_p.h \
@@ -56,6 +61,9 @@ SOURCES += \
     qcontactactionmanager_p.cpp \
     qcontactactiontarget.cpp \
     qcontactchangeset.cpp \
+    qcontactcollection.cpp \
+    qcontactcollectionchangeset.cpp \
+    qcontactcollectionid.cpp \
     qcontactdetail.cpp \
     qcontactfetchhint.cpp \
     qcontactfilter.cpp \
diff --git a/src/contacts/filters/filters.pri b/src/contacts/filters/filters.pri
index 0c017cab0713f73d9902020683a2f1684c05a312..19a8e8d75a1afccb3861e8a158b91d64234e38cf 100644
--- a/src/contacts/filters/filters.pri
+++ b/src/contacts/filters/filters.pri
@@ -3,6 +3,7 @@ INCLUDEPATH += filters
 PUBLIC_HEADERS += \
     filters/qcontactactionfilter.h \
     filters/qcontactchangelogfilter.h \
+    filters/qcontactcollectionfilter.h \
     filters/qcontactdetailfilter.h \
     filters/qcontactdetailrangefilter.h \
     filters/qcontactfilters.h \
@@ -15,6 +16,7 @@ PUBLIC_HEADERS += \
 PRIVATE_HEADERS += \
     filters/qcontactactionfilter_p.h \
     filters/qcontactchangelogfilter_p.h \
+    filters/qcontactcollectionfilter_p.h \
     filters/qcontactdetailfilter_p.h \
     filters/qcontactdetailrangefilter_p.h \
     filters/qcontactidfilter_p.h \
@@ -25,6 +27,7 @@ PRIVATE_HEADERS += \
 SOURCES += \
     filters/qcontactactionfilter.cpp \
     filters/qcontactchangelogfilter.cpp \
+    filters/qcontactcollectionfilter.cpp \
     filters/qcontactdetailfilter.cpp \
     filters/qcontactdetailrangefilter.cpp \
     filters/qcontactidfilter.cpp \
diff --git a/src/contacts/filters/qcontactcollectionfilter.cpp b/src/contacts/filters/qcontactcollectionfilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..56bb9de215a3dec8f1da7ca4ae70b880203f12b7
--- /dev/null
+++ b/src/contacts/filters/qcontactcollectionfilter.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtContacts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollectionfilter.h"
+#include "qcontactcollectionfilter_p.h"
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollectionFilter
+    \brief The QContactCollectionFilter class provides a filter based around the collection one
+           contact belongs to.
+    \inmodule QtContacts
+    \ingroup contacts-filters
+
+    It may be used to select contacts belonging to certain collections.
+ */
+
+Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactCollectionFilter)
+
+/*!
+    \fn QContactCollectionFilter::QContactCollectionFilter(const QContactCollectionFilter &other)
+
+    Constructs a copy of \a other if possible, otherwise constructs a new contact collection filter.
+ */
+
+/*!
+    Constructs a new contact collection filter.
+ */
+QContactCollectionFilter::QContactCollectionFilter()
+    : QContactFilter(new QContactCollectionFilterPrivate)
+{
+}
+
+/*!
+    Sets the \a id of the collection, which the contacts should belong to.
+ */
+void QContactCollectionFilter::setCollectionId(const QContactCollectionId &id)
+{
+    Q_D(QContactCollectionFilter);
+    d->m_ids.clear();
+    d->m_ids.insert(id);
+}
+
+/*!
+    Sets the list of collection \a ids, which the contacts should belong to.
+ */
+void QContactCollectionFilter::setCollectionIds(const QSet<QContactCollectionId> &ids)
+{
+    Q_D(QContactCollectionFilter);
+    d->m_ids = ids;
+}
+
+/*!
+    Returns the list of collection IDs of contacts should belong to.
+ */
+QSet<QContactCollectionId> QContactCollectionFilter::collectionIds() const
+{
+    Q_D(const QContactCollectionFilter);
+    return d->m_ids;
+}
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/filters/qcontactcollectionfilter.h b/src/contacts/filters/qcontactcollectionfilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..da6cdede49d417964feaa969337eec3f47a48190
--- /dev/null
+++ b/src/contacts/filters/qcontactcollectionfilter.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONFILTER_H
+#define QCONTACTCOLLECTIONFILTER_H
+
+#include <QtCore/qset.h>
+
+#include <QtContacts/qcontactcollectionid.h>
+#include <QtContacts/qcontactfilter.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionFilterPrivate;
+
+/* Leaf class */
+
+class Q_CONTACTS_EXPORT QContactCollectionFilter : public QContactFilter
+{
+public:
+    QContactCollectionFilter();
+    QContactCollectionFilter(const QContactFilter &other);
+
+    void setCollectionId(const QContactCollectionId &id);
+    void setCollectionIds(const QSet<QContactCollectionId> &ids);
+    QSet<QContactCollectionId> collectionIds() const;
+
+private:
+    Q_DECLARE_CONTACTFILTER_PRIVATE(QContactCollectionFilter)
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONFILTER_H
diff --git a/src/contacts/filters/qcontactcollectionfilter_p.h b/src/contacts/filters/qcontactcollectionfilter_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..db06be82696771d156ddc6638bd5270a42fde619
--- /dev/null
+++ b/src/contacts/filters/qcontactcollectionfilter_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONFILTER_P_H
+#define QCONTACTCOLLECTIONFILTER_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtContacts/qcontactcollectionfilter.h>
+#include <QtContacts/private/qcontactfilter_p.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionFilterPrivate : public QContactFilterPrivate
+{
+public:
+    QContactCollectionFilterPrivate()
+        : QContactFilterPrivate()
+    {
+    }
+
+    QContactCollectionFilterPrivate(const QContactCollectionFilterPrivate &other)
+        : QContactFilterPrivate(other), m_ids(other.m_ids)
+    {
+    }
+
+    virtual bool compare(const QContactFilterPrivate *other) const
+    {
+        const QContactCollectionFilterPrivate *od = static_cast<const QContactCollectionFilterPrivate *>(other);
+        if (od)
+            return m_ids == od->m_ids;
+        return false;
+    }
+
+#ifndef QT_NO_DATASTREAM
+    QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const
+    {
+        if (formatVersion == 1)
+            stream << m_ids;
+        return stream;
+    }
+
+    QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion)
+    {
+        if (formatVersion == 1)
+            stream >> m_ids;
+        return stream;
+    }
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+    QDebug &debugStreamOut(QDebug &dbg) const
+    {
+        dbg.nospace() << "QContactCollectionFilter(collectionIds=";
+        QList<QContactCollectionId> ids(m_ids.toList());
+        std::sort(ids.begin(), ids.end());
+        dbg.nospace() << ids;
+        dbg.nospace() << ")";
+        return dbg.maybeSpace();
+    }
+#endif // QT_NO_DEBUG_STREAM
+
+    Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactCollectionFilter, QContactFilter::CollectionFilter)
+
+    QSet<QContactCollectionId> m_ids;
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONFILTER_P_H
diff --git a/src/contacts/filters/qcontactfilters.h b/src/contacts/filters/qcontactfilters.h
index ccae2927614258f953c188b425fe282d1ebafd44..8d8618a7b70e81dbb7e7ddaba5fe26d9a5ba2282 100644
--- a/src/contacts/filters/qcontactfilters.h
+++ b/src/contacts/filters/qcontactfilters.h
@@ -47,6 +47,7 @@
 
 #include <QtContacts/qcontactactionfilter.h>
 #include <QtContacts/qcontactchangelogfilter.h>
+#include <QtContacts/qcontactcollectionfilter.h>
 #include <QtContacts/qcontactdetailfilter.h>
 #include <QtContacts/qcontactdetailrangefilter.h>
 #include <QtContacts/qcontactidfilter.h>
diff --git a/src/contacts/qcontact.cpp b/src/contacts/qcontact.cpp
index 931fbd6ae37a9e1437869a6de63f35edf6184f87..d2ebadf33e4fe2c7902143cec6b48062cc50b241 100644
--- a/src/contacts/qcontact.cpp
+++ b/src/contacts/qcontact.cpp
@@ -670,6 +670,16 @@ QList<QContactId> QContact::relatedContacts(const QString& relationshipType, QCo
     return retn;
 }
 
+QContactCollectionId QContact::collectionId() const
+{
+    return d->m_collectionId;
+}
+
+void QContact::setCollectionId(const QContactCollectionId &collectionId)
+{
+    d->m_collectionId = collectionId;
+}
+
 /*!
  * Return a list of descriptors for the actions available to be performed on this contact.
  *
diff --git a/src/contacts/qcontact.h b/src/contacts/qcontact.h
index ef895dd3943af899585f95cbc80721a15fa22607..05da340eddbc137aaaf270636aec392996ced62a 100644
--- a/src/contacts/qcontact.h
+++ b/src/contacts/qcontact.h
@@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE_CONTACTS
 class QContactActionDescriptor;
 class QContactId;
 class QContactManager;
+class QContactCollectionId;
 
 class QContactData;
 class Q_CONTACTS_EXPORT QContact
@@ -115,6 +116,9 @@ public:
     QList<QContactRelationship> relationships(const QString& relationshipType = QString()) const;
     QList<QContactId> relatedContacts(const QString& relationshipType = QString(), QContactRelationship::Role role = QContactRelationship::Either) const;
 
+    QContactCollectionId collectionId() const;
+    void setCollectionId(const QContactCollectionId &collectionId);
+
     /* Actions available to be performed on this contact */
     QList<QContactActionDescriptor> availableActions(const QString& serviceName = QString()) const;
 
diff --git a/src/contacts/qcontact_p.h b/src/contacts/qcontact_p.h
index eba3dfe931b291894328d40442e5f9792efa65ec..460c0bcef54ccb155a834c5e4263b657346493c0 100644
--- a/src/contacts/qcontact_p.h
+++ b/src/contacts/qcontact_p.h
@@ -61,6 +61,7 @@
 #include <QtContacts/qcontactdetail.h>
 #include <QtContacts/qcontactid.h>
 #include <QtContacts/qcontactrelationship.h>
+#include <QtContacts/qcontactcollectionid.h>
 
 QT_BEGIN_NAMESPACE_CONTACTS
 
@@ -75,6 +76,7 @@ public:
     QContactData(const QContactData& other)
         : QSharedData(other),
         m_id(other.m_id),
+        m_collectionId(other.m_collectionId),
         m_details(other.m_details),
         m_relationshipsCache(other.m_relationshipsCache),
         m_preferences(other.m_preferences)
@@ -84,6 +86,7 @@ public:
     ~QContactData() {}
 
     QContactId m_id;
+    QContactCollectionId m_collectionId;
     QList<QContactDetail> m_details;
     QList<QContactRelationship> m_relationshipsCache;
     QMap<QString, int> m_preferences;
diff --git a/src/contacts/qcontactabstractrequest.h b/src/contacts/qcontactabstractrequest.h
index e8dec93168bdbd12bfc824e9d6ac1c99928cf2d2..0e2ea3b95a8ab9744474ec93702aa8a3f9f2714a 100644
--- a/src/contacts/qcontactabstractrequest.h
+++ b/src/contacts/qcontactabstractrequest.h
@@ -84,7 +84,10 @@ public:
         RelationshipFetchRequest,
         RelationshipRemoveRequest,
         RelationshipSaveRequest,
-        ContactFetchByIdRequest
+        ContactFetchByIdRequest,
+        CollectionFetchRequest,
+        CollectionRemoveRequest,
+        CollectionSaveRequest,
     };
 
     RequestType type() const;
diff --git a/src/contacts/qcontactcollection.cpp b/src/contacts/qcontactcollection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d7affe5614c19291af9c4d014a8e59fe9963e17
--- /dev/null
+++ b/src/contacts/qcontactcollection.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollection.h"
+#include "qcontactcollection_p.h"
+
+#ifndef QT_NO_DATASTREAM
+#include <QtCore/qdatastream.h>
+#endif
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollection
+    \brief The QContactCollection class represents a collection of contacts in a manager.
+    \inmodule QtContacts
+    \ingroup contacts-main
+
+    A collection has an ID and optionally some metadata, and contains zero or more contacts.
+    Each different manager will have different requirements before a collection may be saved
+    in it.  Some managers do not allow collections to be saved at all, while others may require
+    a collection to have some minimal amount of metadata defined in it prior to save.
+    For example, most managers require a valid value for the QContactCollection::KeyName
+    meta data key to be set prior to save.
+
+    Every QContact is contained within a collection when stored in a manager.
+    To save an contact in a collection, the client should call QContact::setCollectionId()
+    on the contact, passing in the ID of the destination collection as the argument, and then
+    save the contact in the manager.  To move an contact from one collection to another, the client
+    must fetch the contact from the manager, set the collection ID in the contact to the ID of the
+    collection to which the client wishes the contact to be moved, and then resave the contact in the
+    manager.  That is, the collection which a contact is part of is treated as a property of the
+    contact.
+ */
+
+/*!
+    \enum QContactCollection::MetaDataKey
+
+    This enumeration describes the key of the contact collection metadata.
+
+    \value KeyName         This metadata describes the name of the collection.
+    \value KeyDescription  This metadata gives a description of the collection.
+    \value KeyColor        This metadata describes the color of the collection.
+    \value KeySecondaryColor  This metadata describes the secondary color of the collection.
+    \value KeyImage        This metadata describes the image of the collection.
+    \value KeyExtended     This is an extened metadata, which is stored as a QVariantMap.
+ */
+
+/*!
+    Constructs a new collection.
+ */
+QContactCollection::QContactCollection()
+    : d(new QContactCollectionData)
+{
+}
+
+/*!
+    Cleans up any memory in use by the collection.
+ */
+QContactCollection::~QContactCollection()
+{
+}
+
+/*!
+    Constructs a new copy of the \a other collection.
+ */
+QContactCollection::QContactCollection(const QContactCollection &other)
+    : d(other.d)
+{
+}
+
+/*!
+    Assigns this collection to be equal to the \a other collection.
+ */
+QContactCollection &QContactCollection::operator=(const QContactCollection &other)
+{
+    d = other.d;
+    return *this;
+}
+
+/*!
+    Returns true if the collection is the same as that of the \a other collection, false if either
+    the ID or any of the stored metadata are not the same.
+ */
+bool QContactCollection::operator==(const QContactCollection &other) const
+{
+    if (d == other.d)
+        return true;
+
+    if (d->m_id != other.d->m_id
+        || d->m_metaData.size() != other.d->m_metaData.size()) {
+        return false;
+    }
+
+    QMap<QContactCollection::MetaDataKey, QVariant>::const_iterator i = d->m_metaData.constBegin();
+    while (i != d->m_metaData.constEnd()) {
+        if (i.value() != other.d->m_metaData.value(i.key()))
+            return false;
+        ++i;
+    }
+
+    return true;
+}
+
+/*!
+    \fn QContactCollection::operator!=(const QContactCollection &other) const
+
+    Returns true if the collection is not the same as the \a other collection.
+ */
+
+/*!
+    Returns the ID of the collection.
+ */
+QContactCollectionId QContactCollection::id() const
+{
+    return d->m_id;
+}
+
+/*!
+    Sets the ID of the collection to \a id.
+
+    If the ID is set to a null (default-constructed) ID, saving the collection will cause the manager
+    to save the collection as a new collection.
+ */
+void QContactCollection::setId(const QContactCollectionId &id)
+{
+    d->m_id = id;
+}
+
+/*!
+    Sets the metadata of the collection to be \a metaData.
+ */
+void QContactCollection::setMetaData(const QMap<QContactCollection::MetaDataKey, QVariant> &metaData)
+{
+    d->m_metaData = metaData;
+}
+
+/*!
+    Returns the meta data of the collection.
+ */
+QMap<QContactCollection::MetaDataKey, QVariant> QContactCollection::metaData() const
+{
+    return d->m_metaData;
+}
+
+/*!
+    Sets the meta data of the collection for the given \a key to the given \a value.
+ */
+void QContactCollection::setMetaData(MetaDataKey key, const QVariant &value)
+{
+    d->m_metaData.insert(key, value);
+}
+
+/*!
+    Sets the value of the extended metadata with the given \a key to \a value.
+ */
+void QContactCollection::setExtendedMetaData(const QString &key, const QVariant &value)
+{
+    QVariantMap variantMap = d->m_metaData.value(QContactCollection::KeyExtended).toMap();
+    variantMap.insert(key, value);
+    d->m_metaData.insert(QContactCollection::KeyExtended, variantMap);
+}
+
+/*!
+    Returns the value of extended metadata with the given \a key.
+ */
+QVariant QContactCollection::extendedMetaData(const QString &key) const
+{
+    return d->m_metaData.value(QContactCollection::KeyExtended).toMap().value(key);
+}
+
+/*!
+    Returns the meta data of the collection for the given \a key.
+ */
+QVariant QContactCollection::metaData(MetaDataKey key) const
+{
+    return d->m_metaData.value(key);
+}
+
+/*!
+    \relates QContactCollection
+    Returns the hash value for \a key.
+ */
+Q_CONTACTS_EXPORT uint qHash(const QContactCollection &key)
+{
+    uint hash = qHash(key.id());
+    QMap<QContactCollection::MetaDataKey, QVariant>::const_iterator i = key.d->m_metaData.constBegin();
+    while (i != key.d->m_metaData.constEnd()) {
+        if (i.key() == QContactCollection::KeyExtended) {
+            QVariantMap variantMap = i.value().toMap();
+            QVariantMap::const_iterator j = variantMap.constBegin();
+            while (j != variantMap.constEnd()) {
+                hash += QT_PREPEND_NAMESPACE(qHash)(j.key()) + QT_PREPEND_NAMESPACE(qHash)(j.value().toString());
+                ++j;
+            }
+        } else {
+            hash += QT_PREPEND_NAMESPACE(qHash)(i.key()) + QT_PREPEND_NAMESPACE(qHash)(i.value().toString());
+        }
+        ++i;
+    }
+    return hash;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+    \relates QContactCollection
+    Streams the \a collection to the given debug stream \a dbg, and returns the stream.
+ */
+QDebug operator<<(QDebug dbg, const QContactCollection& collection)
+{
+    dbg.nospace() << "QContactCollection(id=" << collection.id();
+
+    QMap<QContactCollection::MetaDataKey, QVariant> metaData = collection.metaData();
+    QMap<QContactCollection::MetaDataKey, QVariant>::const_iterator i = metaData.constBegin();
+    while (i != metaData.constEnd()) {
+        dbg.nospace() << ", " << i.key() << '=' << i.value();
+        ++i;
+    }
+    dbg.nospace() << ')';
+    return dbg.maybeSpace();
+}
+#endif // QT_NO_DEBUG_STREAM
+
+#ifndef QT_NO_DATASTREAM
+/*!
+    \relates QContactCollection
+    Writes \a collection to the stream \a out.
+ */
+QDataStream &operator<<(QDataStream &out, const QContactCollection &collection)
+{
+    quint8 formatVersion = 1;
+    return out << formatVersion
+               << collection.id().toString()
+               << collection.metaData();
+}
+
+/*!
+    \relates QContactCollection
+    Reads an organizer collection from stream \a in into \a collection.
+ */
+QDataStream &operator>>(QDataStream &in, QContactCollection &collection)
+{
+    quint8 formatVersion;
+    in >> formatVersion;
+    if (formatVersion == 1) {
+        QString idString;
+        QMap<int, QVariant> values;
+        in >> idString >> values;
+
+        collection = QContactCollection();
+        collection.setId(QContactCollectionId::fromString(idString));
+
+        QMap<int, QVariant>::const_iterator i = values.constBegin();
+        while (i != values.constEnd()) {
+            collection.setMetaData(static_cast<QContactCollection::MetaDataKey>(i.key()), i.value());
+            ++i;
+        }
+    } else {
+        in.setStatus(QDataStream::ReadCorruptData);
+    }
+    return in;
+}
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/qcontactcollection.h b/src/contacts/qcontactcollection.h
new file mode 100644
index 0000000000000000000000000000000000000000..2278db565d3812c93cf9b865a90cc17fb05b8265
--- /dev/null
+++ b/src/contacts/qcontactcollection.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTION_H
+#define QCONTACTCOLLECTION_H
+
+#include <QtCore/qmap.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvariant.h>
+
+#include <QtContacts/qcontactcollectionid.h>
+#include <QtContacts/qcontactid.h>
+#include <QtContacts/qcontactdetail.h>
+#include <QtContacts/qcontacttype.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactManagerEngine;
+
+class QContactCollectionData;
+class Q_CONTACTS_EXPORT QContactCollection
+{
+public:
+    enum MetaDataKey {
+        KeyName = 0,
+        KeyDescription,
+        KeyColor,
+        KeySecondaryColor,
+        KeyImage,
+        KeyExtended
+    };
+
+    QContactCollection();
+    ~QContactCollection();
+
+    QContactCollection(const QContactCollection &other);
+    QContactCollection &operator=(const QContactCollection &other);
+
+    bool operator==(const QContactCollection &other) const;
+    bool operator!=(const QContactCollection &other) const {return !(other == *this);}
+
+    QContactCollectionId id() const;
+    void setId(const QContactCollectionId &id);
+
+    void setMetaData(MetaDataKey key, const QVariant &value);
+    QVariant metaData(MetaDataKey key) const;
+
+    void setMetaData(const QMap<QContactCollection::MetaDataKey, QVariant> &metaData);
+    QMap<QContactCollection::MetaDataKey, QVariant> metaData() const;
+
+    void setExtendedMetaData(const QString &key, const QVariant &value);
+    QVariant extendedMetaData(const QString &key) const;
+
+private:
+    friend Q_CONTACTS_EXPORT uint qHash(const QContactCollection &key);
+    friend class QContactManagerEngine;
+    QSharedDataPointer<QContactCollectionData> d;
+};
+
+Q_CONTACTS_EXPORT uint qHash(const QContactCollection &key);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactCollection &collection);
+#endif // QT_NO_DEBUG_STREAM
+
+#ifndef QT_NO_DATASTREAM
+Q_CONTACTS_EXPORT QDataStream &operator<<(QDataStream &out, const QContactCollection &collection);
+Q_CONTACTS_EXPORT QDataStream &operator>>(QDataStream &in, QContactCollection &collection);
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE_CONTACTS
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactCollection), Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+#endif // QCONTACTCOLLECTION_H
diff --git a/src/contacts/qcontactcollection_p.h b/src/contacts/qcontactcollection_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..eaf5da74852272d1e8ccb9b7574b49c2296d97dc
--- /dev/null
+++ b/src/contacts/qcontactcollection_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTION_P_H
+#define QCONTACTCOLLECTION_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qmap.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvariant.h>
+
+#include <QtContacts/qcontactcollection.h>
+#include <QtContacts/qcontactcollectionid.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionData : public QSharedData
+{
+public:
+    QContactCollectionData()
+        : QSharedData()
+    {
+    }
+
+    QContactCollectionData(const QContactCollectionData &other)
+        : QSharedData(other), m_metaData(other.m_metaData), m_id(other.m_id)
+    {
+    }
+
+    ~QContactCollectionData()
+    {
+    }
+
+    QMap<QContactCollection::MetaDataKey, QVariant> m_metaData;
+    QContactCollectionId m_id;
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTION_P_H
diff --git a/src/contacts/qcontactcollectionchangeset.cpp b/src/contacts/qcontactcollectionchangeset.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f9af68052740da1a9ca55d44d61143b582f6d37
--- /dev/null
+++ b/src/contacts/qcontactcollectionchangeset.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollectionchangeset.h"
+#include "qcontactcollectionchangeset_p.h"
+
+#include "qcontactmanagerengine.h"
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollectionChangeSet
+    \brief The QContactCollectionChangeSet class provides a simple API to simplify the emission
+           of state-change signals for collections from QContactManagerEngine implementations.
+    \inmodule QtContacts
+    \ingroup contacts-main
+
+    This class should only be used by backend developers.
+ */
+
+/*!
+    Constructs a new change set.
+ */
+QContactCollectionChangeSet::QContactCollectionChangeSet()
+    : d(new QContactCollectionChangeSetData)
+{
+}
+
+/*!
+    Constructs a copy of the \a other change set.
+ */
+QContactCollectionChangeSet::QContactCollectionChangeSet(const QContactCollectionChangeSet &other)
+    : d(other.d)
+{
+}
+
+/*!
+    Frees the memory used by this change set.
+ */
+QContactCollectionChangeSet::~QContactCollectionChangeSet()
+{
+}
+
+/*!
+    Assigns this change set to be equal to \a other.
+ */
+QContactCollectionChangeSet &QContactCollectionChangeSet::operator=(const QContactCollectionChangeSet &other)
+{
+    d = other.d;
+    return *this;
+}
+
+/*!
+    Sets the data changed flag to \a dataChanged.
+
+    If this is set to true prior to calling emitSignals(), only the QContactManagerEngine::dataChanged()
+    signal will be emitted; otherwise, the appropriate finer-grained signals will be emitted.
+ */
+void QContactCollectionChangeSet::setDataChanged(bool dataChanged)
+{
+    d->m_dataChanged = dataChanged;
+}
+
+/*!
+    Returns the value of the data changed flag.
+ */
+bool QContactCollectionChangeSet::dataChanged() const
+{
+    return d->m_dataChanged;
+}
+
+/*!
+    Returns the set of IDs of collections which have been added to the database.
+ */
+QSet<QContactCollectionId> QContactCollectionChangeSet::addedCollections() const
+{
+    return d->m_addedCollections;
+}
+
+/*!
+    Inserts the given \a collectionId into the set of IDs of collections which have been added to
+    the database.
+ */
+void QContactCollectionChangeSet::insertAddedCollection(const QContactCollectionId &collectionId)
+{
+    d->m_addedCollections.insert(collectionId);
+    d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(collectionId, QContactManager::Add));
+}
+
+/*!
+    Inserts each of the given \a collectionIds into the set of IDs of collections which have been
+    added to the database.
+ */
+void QContactCollectionChangeSet::insertAddedCollections(const QList<QContactCollectionId> &collectionIds)
+{
+    foreach (const QContactCollectionId &id, collectionIds) {
+        d->m_addedCollections.insert(id);
+        d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(id, QContactManager::Add));
+    }
+}
+
+/*!
+    Clears the set of IDs of collections which have been added to the database.
+ */
+void QContactCollectionChangeSet::clearAddedCollections()
+{
+    d->m_addedCollections.clear();
+}
+
+/*!
+    Returns the set of IDs of collections which have been changed in the database.
+ */
+QSet<QContactCollectionId> QContactCollectionChangeSet::changedCollections() const
+{
+    return d->m_changedCollections;
+}
+
+/*!
+    Inserts the given \a collectionId into the set of IDs of collections which have been changed in
+    the database.
+ */
+void QContactCollectionChangeSet::insertChangedCollection(const QContactCollectionId &collectionId)
+{
+    d->m_changedCollections.insert(collectionId);
+    d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(collectionId, QContactManager::Change));
+}
+
+/*!
+    Inserts each of the given \a collectionIds into the set of IDs of collections which have been
+    changed in the database.
+ */
+void QContactCollectionChangeSet::insertChangedCollections(const QList<QContactCollectionId> &collectionIds)
+{
+    foreach (const QContactCollectionId &id, collectionIds) {
+        d->m_changedCollections.insert(id);
+        d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(id, QContactManager::Change));
+    }
+}
+
+/*!
+    Clears the set of IDs of collections which have been changed in the database.
+ */
+void QContactCollectionChangeSet::clearChangedCollections()
+{
+    d->m_changedCollections.clear();
+}
+
+/*!
+    Returns the set of IDs of collections which have been removed from the database.
+ */
+QSet<QContactCollectionId> QContactCollectionChangeSet::removedCollections() const
+{
+    return d->m_removedCollections;
+}
+
+/*!
+    Inserts the given \a collectionId into the set of IDs of collections which have been removed from
+    the database.
+ */
+void QContactCollectionChangeSet::insertRemovedCollection(const QContactCollectionId &collectionId)
+{
+    d->m_removedCollections.insert(collectionId);
+    d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(collectionId, QContactManager::Remove));
+}
+
+/*!
+    Inserts each of the given \a collectionIds into the set of IDs of collections which have been
+    removed from the database.
+ */
+void QContactCollectionChangeSet::insertRemovedCollections(const QList<QContactCollectionId> &collectionIds)
+{
+    foreach (const QContactCollectionId &id, collectionIds) {
+        d->m_removedCollections.insert(id);
+        d->m_modifiedCollections.append(QPair<QContactCollectionId, QContactManager::Operation>(id, QContactManager::Remove));
+    }
+}
+
+/*!
+    Clears the set of ids of collections which have been removed from the database.
+ */
+void QContactCollectionChangeSet::clearRemovedCollections()
+{
+    d->m_removedCollections.clear();
+}
+
+/*!
+    Returns the list of ids of organizer collections which have been added, changed or removed from
+    the database. The list includes information about which database operation was done. The ids and
+    operations are ordered so that the first operation is first in the list.
+ */
+QList<QPair<QContactCollectionId, QContactManager::Operation> > QContactCollectionChangeSet::modifiedCollections() const
+{
+    return d->m_modifiedCollections;
+}
+
+/*!
+    Clears the list of ids of organizer collections which have been added, changed or removed from the database
+ */
+void QContactCollectionChangeSet::clearModifiedCollections()
+{
+    d->m_modifiedCollections.clear();
+}
+
+/*!
+    Clears all flags and sets of IDs in this change set.
+ */
+void QContactCollectionChangeSet::clearAll()
+{
+    d->m_dataChanged = false;
+    d->m_addedCollections.clear();
+    d->m_changedCollections.clear();
+    d->m_removedCollections.clear();
+    d->m_modifiedCollections.clear();
+}
+
+/*!
+    Emits the appropriate signals from the given \a engine given the state of the change set. Note
+    that the flags and sets of IDs are not cleared after signals are emitted.
+ */
+void QContactCollectionChangeSet::emitSignals(QContactManagerEngine *engine) const
+{
+    if (!engine)
+        return;
+
+    if (d->m_dataChanged) {
+        emit engine->dataChanged();
+    } else {
+        if (!d->m_addedCollections.isEmpty())
+            emit engine->collectionsAdded(d->m_addedCollections.toList());
+        if (!d->m_changedCollections.isEmpty())
+            emit engine->collectionsChanged(d->m_changedCollections.toList());
+        if (!d->m_removedCollections.isEmpty())
+            emit engine->collectionsRemoved(d->m_removedCollections.toList());
+        if (!d->m_modifiedCollections.isEmpty())
+            emit engine->collectionsModified(d->m_modifiedCollections);
+
+    }
+}
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/qcontactcollectionchangeset.h b/src/contacts/qcontactcollectionchangeset.h
new file mode 100644
index 0000000000000000000000000000000000000000..8cb392cd8f3231bb4d2f89f1eed7fbeb88791ad3
--- /dev/null
+++ b/src/contacts/qcontactcollectionchangeset.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONCHANGESET_H
+#define QCONTACTCOLLECTIONCHANGESET_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qset.h>
+#include <QtCore/qshareddata.h>
+
+#include <QtContacts/qcontactcollectionid.h>
+#include <QtContacts/qcontactmanager.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactManagerEngine;
+
+class QContactCollectionChangeSetData;
+class Q_CONTACTS_EXPORT QContactCollectionChangeSet
+{
+public:
+    QContactCollectionChangeSet();
+    QContactCollectionChangeSet(const QContactCollectionChangeSet &other);
+    ~QContactCollectionChangeSet();
+
+    QContactCollectionChangeSet &operator=(const QContactCollectionChangeSet &other);
+
+    void setDataChanged(bool dataChanged);
+    bool dataChanged() const;
+
+    QSet<QContactCollectionId> addedCollections() const;
+    void insertAddedCollection(const QContactCollectionId &collectionId);
+    void insertAddedCollections(const QList<QContactCollectionId> &collectionIds);
+    void clearAddedCollections();
+
+    QSet<QContactCollectionId> changedCollections() const;
+    void insertChangedCollection(const QContactCollectionId &collectionId);
+    void insertChangedCollections(const QList<QContactCollectionId> &collectionIds);
+    void clearChangedCollections();
+
+    QSet<QContactCollectionId> removedCollections() const;
+    void insertRemovedCollection(const QContactCollectionId &collectionId);
+    void insertRemovedCollections(const QList<QContactCollectionId> &collectionIds);
+    void clearRemovedCollections();
+
+    QList<QPair<QContactCollectionId, QContactManager::Operation> > modifiedCollections() const;
+    void clearModifiedCollections();
+
+    void clearAll();
+
+    void emitSignals(QContactManagerEngine *engine) const;
+
+private:
+    QSharedDataPointer<QContactCollectionChangeSetData> d;
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONCHANGESET_H
diff --git a/src/contacts/qcontactcollectionchangeset_p.h b/src/contacts/qcontactcollectionchangeset_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..b8e372f372394874b742bc8b091667135adf671a
--- /dev/null
+++ b/src/contacts/qcontactcollectionchangeset_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONCHANGESET_P_H
+#define QCONTACTCOLLECTIONCHANGESET_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qlist.h>
+#include <QtCore/qset.h>
+#include <QtCore/qshareddata.h>
+
+#include <QtContacts/qcontactcollectionid.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionChangeSetData : public QSharedData
+{
+public:
+    QContactCollectionChangeSetData()
+        : QSharedData(), m_dataChanged(false)
+    {
+    }
+
+    QContactCollectionChangeSetData(const QContactCollectionChangeSetData& other)
+        : QSharedData(other),
+          m_dataChanged(other.m_dataChanged),
+          m_addedCollections(other.m_addedCollections),
+          m_changedCollections(other.m_changedCollections),
+          m_removedCollections(other.m_removedCollections),
+          m_modifiedCollections(other.m_modifiedCollections)
+    {
+    }
+
+    ~QContactCollectionChangeSetData()
+    {
+    }
+
+    bool m_dataChanged;
+    QSet<QContactCollectionId> m_addedCollections;
+    QSet<QContactCollectionId> m_changedCollections;
+    QSet<QContactCollectionId> m_removedCollections;
+    QList<QPair<QContactCollectionId, QContactManager::Operation> > m_modifiedCollections;
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONCHANGESET_P_H
diff --git a/src/contacts/qcontactcollectionid.cpp b/src/contacts/qcontactcollectionid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82b4f92437ab5b09d816e546a039b5f0d27e011b
--- /dev/null
+++ b/src/contacts/qcontactcollectionid.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollectionid.h"
+
+#ifndef QT_NO_DATASTREAM
+#include <QtCore/qdatastream.h>
+#endif
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
+#include "qcontactmanager_p.h"
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollectionId
+    \brief The QContactCollectionId class provides information that uniquely identifies a collection
+           in a particular manager.
+    \inmodule QtContact
+    \ingroup contact-main
+
+    It consists of a manager URI which identifies the manager which contains the collection,
+    and the engine specific ID of the collection in that manager.
+
+    An invalid QContactCollectionId has an empty manager URI.
+*/
+
+/*!
+    \fn QContactCollectionId::QContactCollectionId()
+
+    Constructs a new, invalid collection ID.
+*/
+
+// TODO: Document and remove internal once the correct signature has been determined
+/*!
+    \fn QContactCollectionId::QContactCollectionId(const QString &managerUri, const QByteArray &localId)
+    \internal
+
+    Constructs an ID from the supplied manager URI \a managerUri and the engine
+    specific \a localId string.
+*/
+
+/*!
+    \fn bool QContactCollectionId::operator==(const QContactCollectionId &other) const
+
+    Returns true if this collection ID has the same manager URI and
+    engine specific ID as \a other. Returns true also, if both IDs are null.
+*/
+
+/*!
+    \fn bool QContactCollectionId::operator!=(const QContactCollectionId &other) const
+
+    Returns true if either the manager URI or engine specific ID of this
+    collection ID is different to that of \a other.
+*/
+
+/*!
+    \fn bool operator<(const QContactCollectionId &id1, const QContactCollectionId &id2)
+    \relates QContactCollectionId
+
+    Returns true if the collection ID \a id1 will be considered less than
+    the collection ID \a id2 if the manager URI of ID \a id1 is alphabetically
+    less than the manager URI of the \a id2 ID. If both IDs have the same
+    manager URI, ID \a id1 will be considered less than the ID \a id2
+    if the the engine specific ID of \a id1 is less than the engine specific ID of \a id2.
+
+    The invalid, null collection ID consists of an empty manager URI and a null engine ID,
+    and hence will be less than any valid, non-null collection ID.
+
+    This operator is provided primarily to allow use of a QContactCollectionId as a key in a QMap.
+*/
+
+/*!
+    \fn uint qHash(const QContactCollectionId &id)
+    \relates QContactCollectionId
+
+    Returns the hash value for \a id.
+*/
+
+/*!
+    \fn bool QContactCollectionId::isValid() const
+
+    Returns true if the manager URI part is not null; returns false otherwise.
+
+    Note that valid ID may be null at the same time, which means new collection.
+
+    \sa isNull()
+*/
+
+/*!
+    \fn bool QContactCollectionId::isNull() const
+
+    Returns true if the engine specific ID part is a null (default constructed);
+    returns false otherwise.
+
+    \sa isValid()
+*/
+
+/*!
+    \fn QString QContactCollectionId::managerUri() const
+
+    Returns the URI of the manager which contains the collection identified by this ID.
+
+    \sa localId()
+*/
+
+/*!
+    \fn QByteArray QContactCollectionId::localId() const
+
+    Returns the collection's engine specific ID part.
+
+    \sa managerUri()
+*/
+
+/*!
+    Serializes the collection ID to a string. The format of the string will be:
+    "qtorganizer:managerName:params:localId", where localId is encoded binary data
+    formatted as hexadecimal to ensure it is in a printable form.
+
+    \sa fromString(), toByteArray()
+*/
+QString QContactCollectionId::toString() const
+{
+    if (!isNull()) {
+        // Ensure the localId component has a valid string representation by hex encoding
+        const QByteArray encodedLocalId(m_localId.toHex());
+        return QString::fromUtf8(QContactManagerData::buildIdData(m_managerUri, encodedLocalId));
+    }
+
+    return QString();
+}
+
+/*!
+    Deserializes the given \a idString. Returns a default-constructed (null)
+    collection ID if the given \a idString is not a valid, serialized collection ID.
+
+    \sa toString(), fromByteArray()
+*/
+QContactCollectionId QContactCollectionId::fromString(const QString &idString)
+{
+    QString managerUri;
+    QByteArray localId;
+
+    if (QContactManagerData::parseIdData(idString.toUtf8(), 0, 0, &managerUri, &localId)) {
+        // The localId component must be decoded from hex
+        return QContactCollectionId(managerUri, QByteArray::fromHex(localId));
+    }
+
+    return QContactCollectionId();
+}
+
+/*!
+    Serializes the collection ID to a byte array.
+
+    \sa fromByteArray(), toString()
+*/
+QByteArray QContactCollectionId::toByteArray() const
+{
+    if (!isNull())
+        return QContactManagerData::buildIdData(m_managerUri, m_localId);
+
+    return QByteArray();
+}
+
+/*!
+    Deserializes the given \a idData. Returns a default-constructed (null)
+    collection ID if the given \a idData does not contain a valid, serialized collection ID.
+
+    \sa toByteArray(), fromString()
+*/
+QContactCollectionId QContactCollectionId::fromByteArray(const QByteArray &idData)
+{
+    QString managerUri;
+    QByteArray localId;
+
+    if (QContactManagerData::parseIdData(idData, 0, 0, &managerUri, &localId))
+        return QContactCollectionId(managerUri, localId);
+
+    return QContactCollectionId();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+    \relates QContactCollectionId
+    Outputs \a id to the debug stream \a dbg.
+*/
+QDebug operator<<(QDebug dbg, const QContactCollectionId &id)
+{
+    dbg.nospace() << "QContactCollectionId(" << id.toString().toUtf8().constData() << ")";
+    return dbg.maybeSpace();
+}
+#endif // QT_NO_DEBUG_STREAM
+
+#ifndef QT_NO_DATASTREAM
+/*!
+    \relates QContactCollectionId
+    Streams \a id to the data stream \a out.
+*/
+QDataStream &operator<<(QDataStream &out, const QContactCollectionId &id)
+{
+    out << id.toByteArray();
+    return out;
+}
+
+/*!
+    \relates QContactCollectionId
+    Streams \a id in from the data stream \a in.
+*/
+QDataStream &operator>>(QDataStream &in, QContactCollectionId &id)
+{
+    QByteArray idData;
+    in >> idData;
+    id = QContactCollectionId::fromByteArray(idData);
+    return in;
+}
+#endif // QT_NO_DATASTREAM
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/qcontactcollectionid.h b/src/contacts/qcontactcollectionid.h
new file mode 100644
index 0000000000000000000000000000000000000000..8df82109137271904c5a19dd10e3dd7f3f32fc14
--- /dev/null
+++ b/src/contacts/qcontactcollectionid.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONID_H
+#define QCONTACTCOLLECTIONID_H
+
+#include <QtCore/qvariant.h>
+
+#include <QtContacts/qcontactsglobal.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactManagerEngine;
+
+class Q_CONTACTS_EXPORT QContactCollectionId
+{
+public:
+    inline QContactCollectionId() {}
+    inline QContactCollectionId(const QString &managerUri, const QByteArray &localId)
+        : m_managerUri(localId.isEmpty() ? QString() : managerUri),
+          m_localId(m_managerUri.isEmpty() ? QByteArray() : localId)
+    {}
+    // compiler-generated dtor and copy/move ctors/assignment operators are fine!
+
+    inline bool operator==(const QContactCollectionId &other) const
+    { return localId() == other.localId() && managerUri() == other.managerUri(); }
+    inline bool operator!=(const QContactCollectionId &other) const
+    { return !operator==(other); }
+
+    inline bool isNull() const { return m_localId.isEmpty(); }
+
+    inline QString managerUri() const { return m_managerUri; }
+    inline QByteArray localId() const { return m_localId; }
+
+    QString toString() const;
+    static QContactCollectionId fromString(const QString &idString);
+
+    QByteArray toByteArray() const;
+    static QContactCollectionId fromByteArray(const QByteArray &idData);
+
+private:
+    QString m_managerUri;
+    QByteArray m_localId;
+};
+
+inline bool operator<(const QContactCollectionId &id1, const QContactCollectionId &id2)
+{ return id1.managerUri() != id2.managerUri() ? id1.managerUri() < id2.managerUri() : id1.localId() < id2.localId(); }
+
+inline uint qHash(const QContactCollectionId &id)
+{ return qHash(id.localId()); }
+
+#ifndef QT_NO_DATASTREAM
+Q_CONTACTS_EXPORT QDataStream &operator<<(QDataStream &out, const QContactCollectionId &id);
+Q_CONTACTS_EXPORT QDataStream &operator>>(QDataStream &in, QContactCollectionId &id);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactCollectionId &id);
+#endif
+
+QT_END_NAMESPACE_CONTACTS
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactCollectionId), Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QTCONTACTS_PREPEND_NAMESPACE(QContactCollectionId))
+
+#endif // QCONTACTCOLLECTIONID_H
diff --git a/src/contacts/qcontactfilter.h b/src/contacts/qcontactfilter.h
index fdcddf77f55817a23d95035eeb8eab619bff7271..50fb81c9c0d283336936f45bd2b84ff09ebc4d7b 100644
--- a/src/contacts/qcontactfilter.h
+++ b/src/contacts/qcontactfilter.h
@@ -74,7 +74,8 @@ public:
         IntersectionFilter,
         UnionFilter,
         IdFilter,
-        DefaultFilter
+        DefaultFilter,
+        CollectionFilter
     };
 
     FilterType type() const;
diff --git a/src/contacts/qcontactmanager.cpp b/src/contacts/qcontactmanager.cpp
index 805d4203f7cd97e2b25c0e64d27f00cd16197156..2730d29e309b0677aa8546679142b15cf7568605 100644
--- a/src/contacts/qcontactmanager.cpp
+++ b/src/contacts/qcontactmanager.cpp
@@ -811,6 +811,74 @@ QList<QContactDetail::DetailType> QContactManager::supportedContactDetailTypes()
     return d->m_engine->supportedContactDetailTypes();
 }
 
+/*!
+    Returns the default collection managed by this manager. There is always only one default collection
+    for each backend.
+ */
+QContactCollection QContactManager::defaultCollection()
+{
+    QContactManagerSyncOpErrorHolder h(this);
+    return d->m_engine->defaultCollection(&h.error);
+}
+
+/*!
+  Returns the collection identified by the given \a collectionId which is managed by this manager.
+ */
+QContactCollection QContactManager::collection(const QContactCollectionId& collectionId)
+{
+    QContactManagerSyncOpErrorHolder h(this);
+    return d->m_engine->collection(collectionId, &h.error);
+}
+
+/*!
+  Returns a list of all of the collections managed by this manager.
+ */
+QList<QContactCollection> QContactManager::collections()
+{
+    QContactManagerSyncOpErrorHolder h(this);
+    return d->m_engine->collections(&h.error);
+}
+
+/*!
+    Saves the given \a collection to the backend, and returns true on success or false otherwise.
+
+    Note that certain backends may not allow modification nor adding new collections, in such cases
+    the operation will fail and result a QContactManager::PermissionsError error.
+
+    A new collection will be created in the backend store if the collection ID of it is null.
+    Otherwise, an existing collection with the same ID will be updated. If the given collection ID
+    does not exist in the backend, it will result a QContactManager::DoesNotExistError error.
+
+    Note that upon successful saving, the backend may update the collection, e.g. collection ID for
+    newly saved collections.
+ */
+bool QContactManager::saveCollection(QContactCollection* collection)
+{
+    QContactManagerSyncOpErrorHolder h(this);
+    if (collection) {
+        return d->m_engine->saveCollection(collection, &h.error);
+    } else {
+        h.error = QContactManager::BadArgumentError;
+        return false;
+    }
+}
+
+/*!
+    Removes the collection identified by the given \a collectionId (and all items in the collection)
+    from the manager. Returns true on success, false on failure.
+
+    If the given \a collectionId does not exist, the operation will fail and QContactManager::DoesNotExistError
+    will be returned when calling error().
+
+    If the given \a collectionId refers to the default collection, the operation will fail and
+    QContactManager::PermissionsError will be returned when calling error().
+ */
+bool QContactManager::removeCollection(const QContactCollectionId &collectionId)
+{
+    QContactManagerSyncOpErrorHolder h(this);
+    return d->m_engine->removeCollection(collectionId, &h.error);
+}
+
 /*!
   Returns the engine backend implementation version number
  */
diff --git a/src/contacts/qcontactmanager.h b/src/contacts/qcontactmanager.h
index 9ccf3e6caa32a9b5cbbb7866dedc81e8301de6bd..c50288ed1a64a30b3ebc0d4f8f634f6768204170 100644
--- a/src/contacts/qcontactmanager.h
+++ b/src/contacts/qcontactmanager.h
@@ -47,6 +47,7 @@
 #include <QtCore/qstringlist.h>
 
 #include <QtContacts/qcontact.h>
+#include <QtContacts/qcontactcollection.h>
 #include <QtContacts/qcontactid.h>
 #include <QtContacts/qcontactfetchhint.h>
 #include <QtContacts/qcontactrelationship.h>
@@ -110,6 +111,12 @@ public:
         MissingPlatformRequirementsError
     };
 
+    enum Operation {
+        Add,
+        Change,
+        Remove
+    };
+
     /* Error reporting */
     QContactManager::Error error() const;
     QMap<int, QContactManager::Error> errorMap() const;
@@ -150,6 +157,13 @@ public:
     QList<QContactType::TypeValues> supportedContactTypes() const;
     QList<QContactDetail::DetailType> supportedContactDetailTypes() const;
 
+    // collections
+    QContactCollection defaultCollection();
+    QContactCollection collection(const QContactCollectionId& collectionId);
+    QList<QContactCollection> collections();
+    bool saveCollection(QContactCollection* collection);
+    bool removeCollection(const QContactCollectionId& collectionId);
+
     /* return a list of available backends for which a QContactManager can be constructed. */
     static QStringList availableManagers();
 
@@ -161,6 +175,10 @@ Q_SIGNALS:
     void relationshipsAdded(const QList<QContactId>& affectedContactIds);
     void relationshipsRemoved(const QList<QContactId>& affectedContactIds);
     void selfContactIdChanged(const QContactId& oldId, const QContactId& newId); // need both? or just new?
+    void collectionsAdded(const QList<QContactCollectionId> &collectionIds);
+    void collectionsChanged(const QList<QContactCollectionId> &collectionIds);
+    void collectionsRemoved(const QList<QContactCollectionId> &collectionIds);
+    void collectionsModified(const QList<QPair<QContactCollectionId, QContactManager::Operation> > &collectionIds);
 
 protected:
     void connectNotify(const QMetaMethod &signal);
diff --git a/src/contacts/qcontactmanagerengine.cpp b/src/contacts/qcontactmanagerengine.cpp
index 5c41a499ce604bc4294060ab8f66bc12b2488546..ba2919d5fcfe8c02d9b98fc5cc2b2807ed5b02ee 100644
--- a/src/contacts/qcontactmanagerengine.cpp
+++ b/src/contacts/qcontactmanagerengine.cpp
@@ -451,6 +451,79 @@ bool QContactManagerEngine::removeRelationships(const QList<QContactRelationship
     return false;
 }
 
+/*!
+    This function should be reimplemented to support synchronous calls to fetch the default collection.
+    Any errors encountered during this operation should be stored to \a error.
+*/
+QContactCollection QContactManagerEngine::defaultCollection(QContactManager::Error* error)
+{
+    *error = QContactManager::NotSupportedError;
+    return QContactCollection();
+}
+
+/*!
+    This function should be reimplemented to support synchronous calls to fetch a collection based
+    on its ID. Any errors encountered during this operation should be stored to \a error. If the
+    given \a collectionId does not specify a valid collection, \a error will be set to
+    \c QContactManager::DoesNotExistError.
+
+*/
+QContactCollection QContactManagerEngine::collection(const QContactCollectionId& collectionId, QContactManager::Error* error)
+{
+    Q_UNUSED(collectionId);
+    *error = QContactManager::NotSupportedError;
+    return QContactCollection();
+}
+
+/*!
+    This function should be reimplemented to support synchronous calls to fetch all the collections
+    managed by this backend. Any errors encountered during this operation should be stored to \a error.
+ */
+QList<QContactCollection> QContactManagerEngine::collections(QContactManager::Error* error)
+{
+    *error = QContactManager::NotSupportedError;
+    return QList<QContactCollection>();
+}
+
+/*!
+    This function should be reimplemented to support synchronous calls to save a collection.
+
+    This function is supposed to save the given \a collection to the backend, and returns true on
+    success or false otherwise. Any errors encountered during this operation should be stored to
+    \a error.
+
+    A new collection will be created in the backend store if the collection ID of it is null.
+    Otherwise, an existing collection with the same ID will be updated. If the given collection ID
+    does not exist in the backend, it will result a QContactManager::DoesNotExistError error.
+
+    Note that upon successful saving, the backend may update the collection, e.g. collection ID for
+    newly saved collections.
+*/
+bool QContactManagerEngine::saveCollection(QContactCollection* collection, QContactManager::Error* error)
+{
+    Q_UNUSED(collection);
+
+    *error = QContactManager::NotSupportedError;
+    return false;
+}
+
+/*!
+    This function should be reimplemented to support synchronous calls to remove a collection.
+
+    This function is supposed to remove the collection identified by the given \a collectionId, and
+    all items in the collection. Returns true on success, or false otherwise. Any errors encountered
+    during this operation should be stored to \a error.
+
+    Note that removing the default collection should not be allowed and should result a
+    QContactManager::PermissionsError error.
+*/
+bool QContactManagerEngine::removeCollection(const QContactCollectionId& collectionId, QContactManager::Error* error)
+{
+    Q_UNUSED(collectionId);
+
+    *error = QContactManager::NotSupportedError;
+    return false;
+}
 
 /*!
   Given an input \a filter, returns the canonical version of the filter.
@@ -1416,6 +1489,15 @@ bool QContactManagerEngine::testFilter(const QContactFilter &filter, const QCont
                 // Fall through to end
             }
             break;
+
+        case QContactFilter::CollectionFilter:
+            {
+                const QContactCollectionFilter cf(filter);
+                const QSet<QContactCollectionId>& ids = cf.collectionIds();
+                if (ids.contains(contact.collectionId()))
+                    return true;
+                return false;
+            }
     }
     return false;
 }
@@ -1921,6 +2003,116 @@ void QContactManagerEngine::updateRelationshipFetchRequest(QContactRelationshipF
 #endif
 }
 
+/*!
+  Updates the given QContactCollectionFetchRequest \a req with the latest results \a result and an operation error \a error.
+  In addition, the state of the request will be changed to \a newState.
+
+  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
+  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
+ */
+void QContactManagerEngine::updateCollectionFetchRequest(QContactCollectionFetchRequest* req, const QList<QContactCollection>& result, QContactManager::Error error, QContactAbstractRequest::State newState)
+{
+    Q_ASSERT(req);
+    QContactCollectionFetchRequestPrivate* rd = static_cast<QContactCollectionFetchRequestPrivate*>(req->d_ptr);
+    QMutexLocker ml(&rd->m_mutex);
+    bool emitState = rd->m_state != newState;
+    rd->m_collections = result;
+    rd->m_error = error;
+    rd->m_state = newState;
+    ml.unlock();
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    QPointer<QContactAbstractRequest> guard(req);
+#endif
+    Qt::ConnectionType connectionType = Qt::DirectConnection;
+#ifdef QT_NO_THREAD
+    if (req->thread() != QThread::currentThread())
+        connectionType = Qt::BlockingQueuedConnection;
+#endif
+    QMetaObject::invokeMethod(req, "resultsAvailable", connectionType);
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    Q_ASSERT(guard);
+#endif
+    if (emitState)
+        QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState));
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    Q_ASSERT(guard);
+#endif
+}
+
+/*!
+  Updates the given QContactCollectionRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap.
+  In addition, the state of the request will be changed to \a newState.
+
+  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
+  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
+ */
+void QContactManagerEngine::updateCollectionRemoveRequest(QContactCollectionRemoveRequest* req, QContactManager::Error error, const QMap<int, QContactManager::Error>& errorMap, QContactAbstractRequest::State newState)
+{
+    Q_ASSERT(req);
+    QContactCollectionRemoveRequestPrivate* rd = static_cast<QContactCollectionRemoveRequestPrivate*>(req->d_ptr);
+    QMutexLocker ml(&rd->m_mutex);
+    bool emitState = rd->m_state != newState;
+    rd->m_errors = errorMap;
+    rd->m_error = error;
+    rd->m_state = newState;
+    ml.unlock();
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    QPointer<QContactAbstractRequest> guard(req);
+#endif
+    Qt::ConnectionType connectionType = Qt::DirectConnection;
+#ifdef QT_NO_THREAD
+    if (req->thread() != QThread::currentThread())
+        connectionType = Qt::BlockingQueuedConnection;
+#endif
+    QMetaObject::invokeMethod(req, "resultsAvailable", connectionType);
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    Q_ASSERT(guard);
+#endif
+    if (emitState)
+        QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState));
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    Q_ASSERT(guard);
+#endif
+}
+
+/*!
+  Updates the given QContactCollectionSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap.
+  In addition, the state of the request will be changed to \a newState.
+
+  It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress.
+  If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request.
+ */
+void QContactManagerEngine::updateCollectionSaveRequest(QContactCollectionSaveRequest* req, const QList<QContactCollection>& result, QContactManager::Error error, const QMap<int, QContactManager::Error>& errorMap, QContactAbstractRequest::State newState)
+{
+    Q_ASSERT(req);
+    QContactCollectionSaveRequestPrivate* rd = static_cast<QContactCollectionSaveRequestPrivate*>(req->d_ptr);
+    QMutexLocker ml(&rd->m_mutex);
+    bool emitState = rd->m_state != newState;
+    rd->m_collections = result;
+    rd->m_errors = errorMap;
+    rd->m_error = error;
+    rd->m_state = newState;
+    ml.unlock();
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    QPointer<QContactAbstractRequest> guard(req);
+#endif
+    Qt::ConnectionType connectionType = Qt::DirectConnection;
+#ifdef QT_NO_THREAD
+    if (req->thread() != QThread::currentThread())
+        connectionType = Qt::BlockingQueuedConnection;
+#endif
+    QMetaObject::invokeMethod(req, "resultsAvailable", connectionType);
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    Q_ASSERT(guard);
+#endif
+    if (emitState)
+        QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState));
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    Q_ASSERT(guard);
+#endif
+}
+
+
 /*!
   For each contact in \a contacts, either add it to the database or update an existing one.
 
diff --git a/src/contacts/qcontactmanagerengine.h b/src/contacts/qcontactmanagerengine.h
index c438772d7c255843001c6c946088651853ef486b..146b4a48d1cec29983d5d890a9c77ebdea38b1f3 100644
--- a/src/contacts/qcontactmanagerengine.h
+++ b/src/contacts/qcontactmanagerengine.h
@@ -56,6 +56,9 @@
 #include <QtContacts/qcontactmanager.h>
 #include <QtContacts/qcontactrequests.h>
 #include <QtContacts/qcontactsortorder.h>
+#include <QtContacts/qcontactcollectionfetchrequest.h>
+#include <QtContacts/qcontactcollectionsaverequest.h>
+#include <QtContacts/qcontactcollectionremoverequest.h>
 
 QT_BEGIN_NAMESPACE_CONTACTS
 
@@ -77,6 +80,8 @@ public:
 
     inline QContactId contactId(const QByteArray &localId) const
     { return QContactId(managerUri(), localId); }
+    inline QContactCollectionId collectionId(const QByteArray &localId) const
+    { return QContactCollectionId(managerUri(), localId); }
 
     /* Filtering */
     virtual QList<QContactId> contactIds(const QContactFilter &filter, const QList<QContactSortOrder> &sortOrders, QContactManager::Error *error) const;
@@ -102,6 +107,13 @@ public:
     virtual bool saveRelationships(QList<QContactRelationship> *relationships, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error *error);
     virtual bool removeRelationships(const QList<QContactRelationship> &relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error);
 
+    // collections
+    virtual QContactCollection defaultCollection(QContactManager::Error *error);
+    virtual QContactCollection collection(const QContactCollectionId &collectionId, QContactManager::Error *error);
+    virtual QList<QContactCollection> collections(QContactManager::Error *error);
+    virtual bool saveCollection(QContactCollection *collection, QContactManager::Error *error);
+    virtual bool removeCollection(const QContactCollectionId &collectionId, QContactManager::Error *error);
+
     /* Validation for saving */
     virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const;
 
@@ -126,6 +138,10 @@ Q_SIGNALS:
     void relationshipsAdded(const QList<QContactId> &affectedContactIds);
     void relationshipsRemoved(const QList<QContactId> &affectedContactIds);
     void selfContactIdChanged(const QContactId &oldId, const QContactId &newId);
+    void collectionsAdded(const QList<QContactCollectionId> &collectionIds);
+    void collectionsChanged(const QList<QContactCollectionId> &collectionIds);
+    void collectionsRemoved(const QList<QContactCollectionId> &collectionIds);
+    void collectionsModified(const QList<QPair<QContactCollectionId, QContactManager::Operation> > &collectionIds);
 
 public:
     // Async update functions
@@ -140,6 +156,11 @@ public:
     static void updateRelationshipRemoveRequest(QContactRelationshipRemoveRequest *req, QContactManager::Error error, const QMap<int, QContactManager::Error> &errorMap, QContactAbstractRequest::State);
     static void updateRelationshipFetchRequest(QContactRelationshipFetchRequest *req, const QList<QContactRelationship> &result, QContactManager::Error error, QContactAbstractRequest::State);
 
+    // collections
+    static void updateCollectionFetchRequest(QContactCollectionFetchRequest *request, const QList<QContactCollection> &result, QContactManager::Error error, QContactAbstractRequest::State newState);
+    static void updateCollectionRemoveRequest(QContactCollectionRemoveRequest *request, QContactManager::Error error, const QMap<int, QContactManager::Error> &errorMap, QContactAbstractRequest::State newState);
+    static void updateCollectionSaveRequest(QContactCollectionSaveRequest *request, const QList<QContactCollection> &result, QContactManager::Error error, const QMap<int, QContactManager::Error> &errorMap, QContactAbstractRequest::State newState);
+
     // Other protected area update functions
     static void setDetailAccessConstraints(QContactDetail *detail, QContactDetail::AccessConstraints constraints);
     static void setContactRelationships(QContact *contact, const QList<QContactRelationship> &relationships);
diff --git a/src/contacts/requests/qcontactcollectionfetchrequest.cpp b/src/contacts/requests/qcontactcollectionfetchrequest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..adf09a6a340d4ed7ddf627f0eed31128b16bca47
--- /dev/null
+++ b/src/contacts/requests/qcontactcollectionfetchrequest.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollectionfetchrequest.h"
+
+#include "qcontactrequests_p.h"
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollectionFetchRequest
+    \brief The QContactCollectionFetchRequest class allows a client to asynchronously fetch collections
+           from a backend.
+    \inmodule QtContacts
+    \ingroup contacts-requests
+
+    This request will fetch all the collections stored in the given backend.
+ */
+
+/*!
+    Constructs a new contact fetch request whose parent is the specified \a parent.
+*/
+QContactCollectionFetchRequest::QContactCollectionFetchRequest(QObject *parent)
+    : QContactAbstractRequest(new QContactCollectionFetchRequestPrivate, parent)
+{
+}
+
+/*!
+    Frees memory in use by this request.
+*/
+QContactCollectionFetchRequest::~QContactCollectionFetchRequest()
+{
+}
+
+/*!
+    Returns the collections retrieved by this request.
+*/
+QList<QContactCollection> QContactCollectionFetchRequest::collections() const
+{
+    Q_D(const QContactCollectionFetchRequest);
+    QMutexLocker ml(&d->m_mutex);
+    return d->m_collections;
+}
+
+#include "moc_qcontactcollectionfetchrequest.cpp"
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/requests/qcontactcollectionfetchrequest.h b/src/contacts/requests/qcontactcollectionfetchrequest.h
new file mode 100644
index 0000000000000000000000000000000000000000..db08499f513dcfc5fc8349541361438d5ab415ea
--- /dev/null
+++ b/src/contacts/requests/qcontactcollectionfetchrequest.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONFETCHREQUEST_H
+#define QCONTACTCOLLECTIONFETCHREQUEST_H
+
+#include <QtCore/qlist.h>
+
+#include <QtContacts/qcontactabstractrequest.h>
+#include <QtContacts/qcontactcollection.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionFetchRequestPrivate;
+
+/* Leaf class */
+
+class Q_CONTACTS_EXPORT QContactCollectionFetchRequest : public QContactAbstractRequest
+{
+    Q_OBJECT
+
+public:
+    QContactCollectionFetchRequest(QObject *parent = 0);
+    ~QContactCollectionFetchRequest();
+
+    QList<QContactCollection> collections() const;
+
+private:
+    Q_DISABLE_COPY(QContactCollectionFetchRequest)
+    friend class QContactManagerEngine;
+    Q_DECLARE_PRIVATE_D(d_ptr, QContactCollectionFetchRequest)
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONFETCHREQUEST_H
diff --git a/src/contacts/requests/qcontactcollectionremoverequest.cpp b/src/contacts/requests/qcontactcollectionremoverequest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9bd3a03ade2e678b398fdfe240d8d92720dacb0a
--- /dev/null
+++ b/src/contacts/requests/qcontactcollectionremoverequest.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollectionremoverequest.h"
+
+#include "qcontactrequests_p.h"
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollectionRemoveRequest
+    \brief The QContactCollectionRemoveRequest class allows a client to asynchronously remove
+           collections from a backend.
+    \inmodule QtContacts
+    \ingroup contact-requests
+ */
+
+/*!
+    Constructs a new collection remove request whose parent is the specified \a parent.
+*/
+QContactCollectionRemoveRequest::QContactCollectionRemoveRequest(QObject *parent)
+    : QContactAbstractRequest(new QContactCollectionRemoveRequestPrivate, parent)
+{
+}
+
+/*!
+    Frees memory in use by this request.
+*/
+QContactCollectionRemoveRequest::~QContactCollectionRemoveRequest()
+{
+}
+
+/*!
+    Sets the ID of collection which will be removed by this request to  \a collectionId.
+*/
+void QContactCollectionRemoveRequest::setCollectionId(const QContactCollectionId &collectionId)
+{
+    Q_D(QContactCollectionRemoveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    d->m_collectionIds.clear();
+    d->m_collectionIds.append(collectionId);
+}
+
+/*!
+    Sets the list of IDs of collections which will be removed by this request to \a collectionIds.
+*/
+void QContactCollectionRemoveRequest::setCollectionIds(const QList<QContactCollectionId> &collectionIds)
+{
+    Q_D(QContactCollectionRemoveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    d->m_collectionIds = collectionIds;
+}
+
+/*!
+    Returns the list of IDs of collections which will be removed by this request.
+*/
+QList<QContactCollectionId> QContactCollectionRemoveRequest::collectionIds() const
+{
+    Q_D(const QContactCollectionRemoveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    return d->m_collectionIds;
+}
+
+/*!
+    Returns any errors which occurred during the request.
+*/
+QMap<int, QContactManager::Error> QContactCollectionRemoveRequest::errorMap() const
+{
+    Q_D(const QContactCollectionRemoveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    return d->m_errors;
+}
+
+#include "moc_qcontactcollectionremoverequest.cpp"
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/requests/qcontactcollectionremoverequest.h b/src/contacts/requests/qcontactcollectionremoverequest.h
new file mode 100644
index 0000000000000000000000000000000000000000..54b2e75380dc31a7aaad4809a52ce7b66cbcf997
--- /dev/null
+++ b/src/contacts/requests/qcontactcollectionremoverequest.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONREMOVEREQUEST_H
+#define QCONTACTCOLLECTIONREMOVEREQUEST_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+
+#include <QtContacts/qcontactabstractrequest.h>
+#include <QtContacts/qcontactcollectionid.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionRemoveRequestPrivate;
+
+/* Leaf class */
+
+class Q_CONTACTS_EXPORT QContactCollectionRemoveRequest : public QContactAbstractRequest
+{
+    Q_OBJECT
+
+public:
+    QContactCollectionRemoveRequest(QObject *parent = 0);
+    ~QContactCollectionRemoveRequest();
+
+    void setCollectionId(const QContactCollectionId &collectionId);
+    void setCollectionIds(const QList<QContactCollectionId> &collectionIds);
+    QList<QContactCollectionId> collectionIds() const;
+
+    QMap<int, QContactManager::Error> errorMap() const;
+
+private:
+    Q_DISABLE_COPY(QContactCollectionRemoveRequest)
+    friend class QOrganizerManagerEngine;
+    Q_DECLARE_PRIVATE_D(d_ptr, QContactCollectionRemoveRequest)
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONREMOVEREQUEST_H
diff --git a/src/contacts/requests/qcontactcollectionsaverequest.cpp b/src/contacts/requests/qcontactcollectionsaverequest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39877f7c9c628b9ec64a2c13b8e3bcf02885dcec
--- /dev/null
+++ b/src/contacts/requests/qcontactcollectionsaverequest.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactcollectionsaverequest.h"
+
+#include "qcontactrequests_p.h"
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+/*!
+    \class QContactCollectionSaveRequest
+    \brief The QContactCollectionSaveRequest class allows a client to asynchronously save collections
+           to a backend.
+    \inmodule QtContacts
+    \ingroup contact-requests
+ */
+
+/*!
+    Constructs a new collection save request whose parent is the specified \a parent.
+*/
+QContactCollectionSaveRequest::QContactCollectionSaveRequest(QObject *parent)
+    : QContactAbstractRequest(new QContactCollectionSaveRequestPrivate, parent)
+{
+}
+
+/*!
+    Frees memory in use by this request.
+*/
+QContactCollectionSaveRequest::~QContactCollectionSaveRequest()
+{
+}
+
+/*!
+    Sets the collection which will be saved to \a collection.
+*/
+void QContactCollectionSaveRequest::setCollection(const QContactCollection &collection)
+{
+    Q_D(QContactCollectionSaveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    d->m_collections.clear();
+    d->m_collections.append(collection);
+}
+
+/*!
+    Sets the list of collections which will be saved to \a collections.
+*/
+void QContactCollectionSaveRequest::setCollections(const QList<QContactCollection> &collections)
+{
+    Q_D(QContactCollectionSaveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    d->m_collections = collections;
+}
+
+/*!
+    Returns the collections which will be saved by this request if called prior to calling start(),
+    otherwise returns the (possibly updated) collections which have been saved.
+*/
+QList<QContactCollection> QContactCollectionSaveRequest::collections() const
+{
+    Q_D(const QContactCollectionSaveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    return d->m_collections;
+}
+
+/*!
+    Returns the map of input definition list indices to errors which occurred.
+*/
+QMap<int, QContactManager::Error> QContactCollectionSaveRequest::errorMap() const
+{
+    Q_D(const QContactCollectionSaveRequest);
+    QMutexLocker ml(&d->m_mutex);
+    return d->m_errors;
+}
+
+#include "moc_qcontactcollectionsaverequest.cpp"
+
+QT_END_NAMESPACE_CONTACTS
diff --git a/src/contacts/requests/qcontactcollectionsaverequest.h b/src/contacts/requests/qcontactcollectionsaverequest.h
new file mode 100644
index 0000000000000000000000000000000000000000..287157c5ea499294e86605f335ae2e266d95ae9c
--- /dev/null
+++ b/src/contacts/requests/qcontactcollectionsaverequest.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtOrganizer module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCONTACTCOLLECTIONSAVEREQUEST_H
+#define QCONTACTCOLLECTIONSAVEREQUEST_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+
+#include <QtContacts/qcontactabstractrequest.h>
+#include <QtContacts/qcontactcollection.h>
+
+QT_BEGIN_NAMESPACE_CONTACTS
+
+class QContactCollectionSaveRequestPrivate;
+
+/* Leaf class */
+
+class Q_CONTACTS_EXPORT QContactCollectionSaveRequest : public QContactAbstractRequest
+{
+    Q_OBJECT
+
+public:
+    QContactCollectionSaveRequest(QObject *parent = 0);
+    ~QContactCollectionSaveRequest();
+
+    void setCollection(const QContactCollection &collection);
+    void setCollections(const QList<QContactCollection> &collections);
+    QList<QContactCollection> collections() const;
+
+    QMap<int, QContactManager::Error> errorMap() const;
+
+private:
+    Q_DISABLE_COPY(QContactCollectionSaveRequest)
+    friend class QContactManagerEngine;
+    Q_DECLARE_PRIVATE_D(d_ptr, QContactCollectionSaveRequest)
+};
+
+QT_END_NAMESPACE_CONTACTS
+
+#endif // QCONTACTCOLLECTIONSAVEREQUEST_H
diff --git a/src/contacts/requests/qcontactrequests.h b/src/contacts/requests/qcontactrequests.h
index d976eecfdcfab39d29c6f0afc57e02817c5754de..778942c1ef54091537ad5e4da17caa5c24525b7e 100644
--- a/src/contacts/requests/qcontactrequests.h
+++ b/src/contacts/requests/qcontactrequests.h
@@ -55,6 +55,11 @@
 #include <QtContacts/qcontactremoverequest.h>
 #include <QtContacts/qcontactsaverequest.h>
 
+#include <QtContacts/qcontactcollectionchangeset.h>
+#include <QtContacts/qcontactcollectionfetchrequest.h>
+#include <QtContacts/qcontactcollectionremoverequest.h>
+#include <QtContacts/qcontactcollectionsaverequest.h>
+
 QT_BEGIN_NAMESPACE_CONTACTS
 QT_END_NAMESPACE_CONTACTS
 
diff --git a/src/contacts/requests/qcontactrequests_p.h b/src/contacts/requests/qcontactrequests_p.h
index f2aae7c5fa9d0d7e8c01c81c99571abcc2152c3f..6ea90fdd57f34ba1db4c87125917f994c32750b1 100644
--- a/src/contacts/requests/qcontactrequests_p.h
+++ b/src/contacts/requests/qcontactrequests_p.h
@@ -306,6 +306,92 @@ public:
     QMap<int, QContactManager::Error> m_errors;
 };
 
+class QContactCollectionFetchRequestPrivate : public QContactAbstractRequestPrivate
+{
+public:
+    QContactCollectionFetchRequestPrivate()
+        : QContactAbstractRequestPrivate(QContactAbstractRequest::CollectionFetchRequest)
+    {
+    }
+
+    ~QContactCollectionFetchRequestPrivate()
+    {
+    }
+
+#ifndef QT_NO_DEBUG_STREAM
+    QDebug& debugStreamOut(QDebug &dbg) const
+    {
+        dbg.nospace() << "QContactCollectionFetchRequestPrivate(";
+        dbg.nospace() << "collections=";
+        dbg.nospace() << m_collections;
+        dbg.nospace() << ")";
+        return dbg.maybeSpace();
+    }
+#endif
+
+    QList<QContactCollection> m_collections;
+};
+
+class QContactCollectionRemoveRequestPrivate : public QContactAbstractRequestPrivate
+{
+public:
+    QContactCollectionRemoveRequestPrivate()
+        : QContactAbstractRequestPrivate(QContactAbstractRequest::CollectionRemoveRequest)
+    {
+    }
+
+    ~QContactCollectionRemoveRequestPrivate()
+    {
+    }
+
+#ifndef QT_NO_DEBUG_STREAM
+    QDebug& debugStreamOut(QDebug &dbg) const
+    {
+        dbg.nospace() << "QContactCollectionRemoveRequestPrivate(";
+        dbg.nospace() << "collectionIds=";
+        dbg.nospace() << m_collectionIds;
+        dbg.nospace() << ",";
+        dbg.nospace() << "errorMap=";
+        dbg.nospace() << m_errors;
+        dbg.nospace() << ")";
+        return dbg.maybeSpace();
+    }
+#endif
+
+    QList<QContactCollectionId> m_collectionIds;
+    QMap<int, QContactManager::Error> m_errors;
+};
+
+class QContactCollectionSaveRequestPrivate : public QContactAbstractRequestPrivate
+{
+public:
+    QContactCollectionSaveRequestPrivate()
+        : QContactAbstractRequestPrivate(QContactAbstractRequest::CollectionSaveRequest)
+    {
+    }
+
+    ~QContactCollectionSaveRequestPrivate()
+    {
+    }
+
+#ifndef QT_NO_DEBUG_STREAM
+    QDebug& debugStreamOut(QDebug &dbg) const
+    {
+        dbg.nospace() << "QContactCollectionSaveRequestPrivate(";
+        dbg.nospace() << "collections=";
+        dbg.nospace() << m_collections;
+        dbg.nospace() << ",";
+        dbg.nospace() << "errorMap=";
+        dbg.nospace() << m_errors;
+        dbg.nospace() << ")";
+        return dbg.maybeSpace();
+    }
+#endif
+
+    QList<QContactCollection> m_collections;
+    QMap<int, QContactManager::Error> m_errors;
+};
+
 QT_END_NAMESPACE_CONTACTS
 
 #endif // QCONTACTREQUESTS_P_H
diff --git a/src/contacts/requests/requests.pri b/src/contacts/requests/requests.pri
index a8efb65b19abe1f4794ab421d55d81cc0cd3001c..d5c9dff03b7f14724a0e3e191bc05cacbbd09e58 100644
--- a/src/contacts/requests/requests.pri
+++ b/src/contacts/requests/requests.pri
@@ -1,6 +1,9 @@
 INCLUDEPATH += requests
 
 PUBLIC_HEADERS += \
+    requests/qcontactcollectionfetchrequest.h \
+    requests/qcontactcollectionremoverequest.h \
+    requests/qcontactcollectionsaverequest.h \
     requests/qcontactfetchrequest.h \
     requests/qcontactfetchbyidrequest.h \
     requests/qcontactidfetchrequest.h \
@@ -15,6 +18,9 @@ PRIVATE_HEADERS += \
     requests/qcontactrequests_p.h
 
 SOURCES += \
+    requests/qcontactcollectionfetchrequest.cpp \
+    requests/qcontactcollectionremoverequest.cpp \
+    requests/qcontactcollectionsaverequest.cpp \
     requests/qcontactfetchrequest.cpp \
     requests/qcontactfetchbyidrequest.cpp \
     requests/qcontactidfetchrequest.cpp \
diff --git a/src/imports/contacts/contacts.pro b/src/imports/contacts/contacts.pro
index b9b3c6a29fa00f0f62b5c530c2cce45c3c0d85c6..a26bb82653afc7e3dbecaa685dab4bd76dda3aa6 100644
--- a/src/imports/contacts/contacts.pro
+++ b/src/imports/contacts/contacts.pro
@@ -5,6 +5,7 @@ include(filters/filters.pri)
 
 HEADERS += qdeclarativecontactmodel_p.h \
            qdeclarativecontact_p.h \
+           qdeclarativecontactcollection_p.h \
            qdeclarativecontactdetail_p.h \
            qdeclarativecontactfilter_p.h \
            qdeclarativecontactsortorder_p.h \
@@ -15,6 +16,7 @@ HEADERS += qdeclarativecontactmodel_p.h \
 SOURCES += plugin.cpp \
     qdeclarativecontactmodel.cpp \
     qdeclarativecontact.cpp \
+    qdeclarativecontactcollection.cpp \
     qdeclarativecontactdetail.cpp \
     qdeclarativecontactfilter.cpp \
     qdeclarativecontactsortorder.cpp \
diff --git a/src/imports/contacts/filters/filters.pri b/src/imports/contacts/filters/filters.pri
index 63bf1e468b7e048784859d01ca561fb4191131ef..2bb4738e9b609b4084dcb6030ab657b7817e0715 100644
--- a/src/imports/contacts/filters/filters.pri
+++ b/src/imports/contacts/filters/filters.pri
@@ -3,6 +3,7 @@ INCLUDEPATH += filters
 HEADERS += \
     filters/qdeclarativecontactactionfilter_p.h \
     filters/qdeclarativecontactchangelogfilter_p.h \
+    filters/qdeclarativecontactcollectionfilter_p.h \
     filters/qdeclarativecontactdetailfilter_p.h \
     filters/qdeclarativecontactdetailrangefilter_p.h \
     filters/qdeclarativecontactidfilter_p.h \
diff --git a/src/imports/contacts/filters/qdeclarativecontactcollectionfilter_p.h b/src/imports/contacts/filters/qdeclarativecontactcollectionfilter_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f523db16a20d0189c238e24c856bca651b16289
--- /dev/null
+++ b/src/imports/contacts/filters/qdeclarativecontactcollectionfilter_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtContacts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVECONTACTCOLLECTIONFILTER_H
+#define QDECLARATIVECONTACTCOLLECTIONFILTER_H
+
+#include <QtContacts/qcontactcollectionfilter.h>
+
+#include "qdeclarativecontactfilter_p.h"
+
+QTCONTACTS_USE_NAMESPACE
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeContactCollectionFilter : public QDeclarativeContactFilter
+{
+    Q_OBJECT
+    Q_PROPERTY(QStringList ids READ ids WRITE setIds NOTIFY valueChanged)
+
+public:
+    QDeclarativeContactCollectionFilter(QObject *parent = 0)
+        : QDeclarativeContactFilter(parent)
+    {
+        connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged()));
+    }
+
+    QStringList ids() const
+    {
+        return m_ids;
+    }
+
+    void setIds(const QStringList &ids)
+    {
+        foreach (const QString &id, ids) {
+            if (!m_ids.contains(id)) {
+                m_ids = ids;
+                emit valueChanged();
+                return;
+            }
+        }
+
+        foreach (const QString &id, m_ids) {
+            if (!ids.contains(id)) {
+                m_ids = ids;
+                emit valueChanged();
+                return;
+            }
+        }
+    }
+
+    // used by model
+    QContactFilter filter() const
+    {
+        QContactCollectionFilter f;
+        QSet<QContactCollectionId> ids;
+
+        foreach (const QVariant &id, m_ids) {
+            QContactCollectionId cId = QContactCollectionId::fromString(id.toString());
+            if (!cId.isNull())
+                ids << cId;
+        }
+
+        f.setCollectionIds(ids);
+        return f;
+    }
+
+Q_SIGNALS:
+    void valueChanged();
+
+private:
+    QStringList m_ids;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QDeclarativeContactCollectionFilter)
+
+#endif // QDECLARATIVECONTACTCOLLECTIONFILTER_H
diff --git a/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp b/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp
index 9a83d1d03bdfb917cbe6c6f21493c9a7f3b6dbf5..c7db6f83c6e9b9411d601f4e6a8386089225ee78 100644
--- a/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp
+++ b/src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp
@@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE
 
 #include "moc_qdeclarativecontactactionfilter_p.cpp"
 #include "moc_qdeclarativecontactchangelogfilter_p.cpp"
+#include "moc_qdeclarativecontactcollectionfilter_p.cpp"
 #include "moc_qdeclarativecontactdetailfilter_p.cpp"
 #include "moc_qdeclarativecontactdetailrangefilter_p.cpp"
 #include "moc_qdeclarativecontactintersectionfilter_p.cpp"
diff --git a/src/imports/contacts/filters/qdeclarativecontactfilters_p.h b/src/imports/contacts/filters/qdeclarativecontactfilters_p.h
index c8039396504a6608385f69755084b3c065bf72eb..a073715b92a2625479e2d4b347210a4f9f7037ef 100644
--- a/src/imports/contacts/filters/qdeclarativecontactfilters_p.h
+++ b/src/imports/contacts/filters/qdeclarativecontactfilters_p.h
@@ -47,6 +47,7 @@
 
 #include "qdeclarativecontactactionfilter_p.h"
 #include "qdeclarativecontactchangelogfilter_p.h"
+#include "qdeclarativecontactcollectionfilter_p.h"
 #include "qdeclarativecontactdetailfilter_p.h"
 #include "qdeclarativecontactdetailrangefilter_p.h"
 #include "qdeclarativecontactidfilter_p.h"
diff --git a/src/imports/contacts/plugin.cpp b/src/imports/contacts/plugin.cpp
index 0b3902e8039436c68b3405f0e95cbd43b6e56e80..1060eae341b4ba75268f6817276491fd249c3db6 100644
--- a/src/imports/contacts/plugin.cpp
+++ b/src/imports/contacts/plugin.cpp
@@ -46,6 +46,7 @@
 #include <QtContacts/qcontactabstractrequest.h>
 
 #include "qdeclarativecontact_p.h"
+#include "qdeclarativecontactcollection_p.h"
 #include "qdeclarativecontactdetail_p.h"
 #include "qdeclarativecontactfetchhint_p.h"
 #include "qdeclarativecontactfilter_p.h"
@@ -73,8 +74,10 @@ public:
         qRegisterMetaType<QContactAbstractRequest::State>("QContactAbstractRequest::State");
         qRegisterMetaType<QContactId>("QContactId");
         qRegisterMetaType<QList<QContactId> >("QList<QContactId>");
+        qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>");
         qmlRegisterType<QDeclarativeContactModel>(uri, major, minor, "ContactModel");
         qmlRegisterType<QDeclarativeContact>(uri, major, minor, "Contact");
+        qmlRegisterType<QDeclarativeContactCollection>(uri, major, minor, "Collection");
         qmlRegisterType<QDeclarativeContactFetchHint>(uri, major, minor, "FetchHint");
         qmlRegisterType<QDeclarativeContactRelationshipModel>(uri, major, minor, "RelationshipModel");
         qmlRegisterType<QDeclarativeContactRelationship>(uri, major, minor, "Relationship");
@@ -115,6 +118,7 @@ public:
         qmlRegisterType<QDeclarativeContactFilter>(uri, major, minor, "Filter");
         qmlRegisterType<QDeclarativeContactActionFilter>(uri, major, minor, "ActionFilter");
         qmlRegisterType<QDeclarativeContactChangeLogFilter>(uri, major, minor, "ChangeLogFilter");
+        qmlRegisterType<QDeclarativeContactCollectionFilter>(uri, major, minor, "CollectionFilter");
         qmlRegisterType<QDeclarativeContactDetailFilter>(uri, major, minor, "DetailFilter");
         qmlRegisterType<QDeclarativeContactDetailRangeFilter>(uri, major, minor, "DetailRangeFilter");
         qmlRegisterType<QDeclarativeContactIdFilter>(uri, major, minor, "IdFilter");
diff --git a/src/imports/contacts/qdeclarativecontact.cpp b/src/imports/contacts/qdeclarativecontact.cpp
index 19b5abedd2298e1e818e1e5fb35440f8a7f03c9c..dc05ae6906a9e95c383cfa0e53392bdfbb3a0318 100644
--- a/src/imports/contacts/qdeclarativecontact.cpp
+++ b/src/imports/contacts/qdeclarativecontact.cpp
@@ -125,6 +125,7 @@ QDeclarativeContact::~QDeclarativeContact()
 void QDeclarativeContact::setContact(const QContact& contact)
 {
     m_id = contact.id();
+    m_collectionId = contact.collectionId();
     foreach (QDeclarativeContactDetail *detail, m_details)
         delete detail;
     m_details.clear();
@@ -154,6 +155,7 @@ QContact QDeclarativeContact::contact() const
 {
     QContact contact;
     contact.setId(m_id);
+    contact.setCollectionId(m_collectionId);
     foreach (QDeclarativeContactDetail *detail, m_details)
         contact.saveDetail(&detail->detail());
 
@@ -353,6 +355,29 @@ QVariantMap QDeclarativeContact::preferredDetails() const
     return result;
 }
 
+/*!
+    \qmlproperty string OContact::collectionId
+
+    This property holds the id of collection where the contact belongs to.
+*/
+QString QDeclarativeContact::collectionId() const
+{
+    return m_collectionId.toString();
+}
+
+void QDeclarativeContact::setCollectionId(const QString &collectionId)
+{
+    QContactCollectionId newCollectionId(QContactCollectionId::fromString(collectionId));
+
+    // in case invalid collectionId-string, fromString() will return default collectionId-string
+    // instead of the intended collectionId-string
+    if (newCollectionId.toString() == collectionId && m_collectionId.toString() != collectionId) {
+        m_collectionId = newCollectionId;
+        m_modified = true;
+        emit contactChanged();
+    }
+}
+
 /*!
     \qmlproperty list<ContactDetail> Contact::contactDetails
 
diff --git a/src/imports/contacts/qdeclarativecontact_p.h b/src/imports/contacts/qdeclarativecontact_p.h
index 35937fdbb7024e33e59c82696b056a37664893ee..f788e4842838f20a5aad6dbbe0187dda397080fb 100644
--- a/src/imports/contacts/qdeclarativecontact_p.h
+++ b/src/imports/contacts/qdeclarativecontact_p.h
@@ -46,6 +46,7 @@
 
 #include <QtContacts/qcontact.h>
 #include <QtContacts/qcontactid.h>
+#include <QtContacts/qcontactcollectionid.h>
 
 #include "qdeclarativecontactdetails_p.h"
 
@@ -98,6 +99,7 @@ class QDeclarativeContact : public QObject
     Q_PROPERTY (QQmlListProperty<QDeclarativeContactUrl> urls READ urls NOTIFY contactChanged)
     Q_PROPERTY (QDeclarativeContactVersion*  version READ version NOTIFY contactChanged)
     Q_PROPERTY (QVariantMap preferredDetails READ preferredDetails NOTIFY contactChanged)
+    Q_PROPERTY (QString collectionId READ collectionId WRITE setCollectionId NOTIFY contactChanged)
     Q_CLASSINFO("DefaultProperty", "contactDetails")
 
 public:
@@ -127,6 +129,9 @@ public:
     Q_INVOKABLE QDeclarativeContactDetail* preferredDetail(const QString& actionName) const;
     QVariantMap preferredDetails() const;
 
+    QString collectionId() const;
+    void setCollectionId(const QString& collectionId);
+
     QDeclarativeContactAddress* address();
     QQmlListProperty<QDeclarativeContactAddress>  addresses();
     QDeclarativeContactAnniversary* anniversary();
@@ -164,6 +169,7 @@ public:
 protected:
     bool m_modified;
     QContactId m_id;
+    QContactCollectionId m_collectionId;
 
     // always create a copy of the detail for QML
     // however, seems the garbage collection can't delete all of them (QTBUG-20377)
diff --git a/src/imports/contacts/qdeclarativecontactcollection.cpp b/src/imports/contacts/qdeclarativecontactcollection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6505f3820733c5d2374da8fcb966fd88ad286d3b
--- /dev/null
+++ b/src/imports/contacts/qdeclarativecontactcollection.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtContacts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativecontactcollection_p.h"
+
+QTCONTACTS_USE_NAMESPACE
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \qmltype Collection
+    \instantiates QDeclarativeContactCollection
+    \brief The Collection element represents a collection of items in an organizer manager.
+    \ingroup qml-contacts-main
+    \inqmlmodule QtContacts
+ */
+
+/*!
+    \internal
+ */
+QDeclarativeContactCollection::QDeclarativeContactCollection(QObject *parent)
+    : QObject(parent)
+{
+}
+
+/*!
+    \qmlproperty string Collection::collectionId
+
+    This property holds the ID of the collection.
+ */
+QString QDeclarativeContactCollection::id() const
+{
+    return d.id().toString();
+}
+
+void QDeclarativeContactCollection::setId(const QString &id)
+{
+    if (d.id().toString() != id) {
+        d.setId(QContactCollectionId::fromString(id));
+        emit valueChanged();
+    }
+}
+
+/*!
+    \qmlproperty string Collection::name
+
+    This property holds the name meta data of a collection.
+ */
+QString QDeclarativeContactCollection::name() const
+{
+    return metaData(QContactCollection::KeyName).toString();
+}
+
+void QDeclarativeContactCollection::setName(const QString &name)
+{
+    setMetaData(QContactCollection::KeyName, name);
+}
+
+/*!
+    \qmlproperty string Collection::description
+
+    This property holds the description meta data of a collection.
+ */
+QString QDeclarativeContactCollection::description() const
+{
+    return metaData(QContactCollection::KeyDescription).toString();
+}
+
+void QDeclarativeContactCollection::setDescription(const QString &description)
+{
+    setMetaData(QContactCollection::KeyDescription, description);
+}
+
+/*!
+    \qmlproperty color Collection::secondaryColor
+
+    This property holds the secondary color meta data of a collection.
+ */
+QColor QDeclarativeContactCollection::secondaryColor() const
+{
+    return metaData(QContactCollection::KeySecondaryColor).value<QColor>();
+}
+
+void QDeclarativeContactCollection::setSecondaryColor(const QColor &secondaryColor)
+{
+    setMetaData(QContactCollection::KeySecondaryColor, secondaryColor);
+}
+
+/*!
+    \qmlproperty color Collection::color
+
+    This property holds the color meta data of a collection.
+ */
+QColor QDeclarativeContactCollection::color() const
+{
+    return metaData(QContactCollection::KeyColor).value<QColor>();
+}
+
+void QDeclarativeContactCollection::setColor(const QColor &color)
+{
+    setMetaData(QContactCollection::KeyColor, color);
+}
+
+/*!
+    \qmlproperty url Collection::image
+
+    This property holds the image url meta data of a collection.
+ */
+QUrl QDeclarativeContactCollection::image() const
+{
+    return QUrl(metaData(QContactCollection::KeyImage).toString());
+}
+
+void QDeclarativeContactCollection::setImage(const QUrl &url)
+{
+    setMetaData(QContactCollection::KeyImage, url);
+}
+
+/*!
+    \qmlmethod Collection::setMetaData(key, value)
+
+    Sets the meta data of the collection for the given \a key to the given \a value. Possible keys
+    include:
+    \list
+    \li Collection.KeyName
+    \li Collection.KeyDescription
+    \li Collection.KeyColor
+    \li Collection.KeySecondaryColor
+    \li Collection.KeyImage
+    \li Collection.KeyExtended
+    \endlist
+ */
+void QDeclarativeContactCollection::setMetaData(QContactCollection::MetaDataKey key, const QVariant &value)
+{
+    if (metaData(key) != value) {
+        d.setMetaData(key, value);
+        emit valueChanged();
+    }
+}
+
+/*!
+    \qmlmethod var Collection::metaData(key)
+
+    Returns the meta data stored in this collection for the given \a key. Possible keys include:
+    \list
+    \li Collection.KeyName
+    \li Collection.KeyDescription
+    \li Collection.KeyColor
+    \li Collection.KeyImage
+    \li Collection.KeyExtended
+    \endlist
+ */
+QVariant QDeclarativeContactCollection::metaData(QContactCollection::MetaDataKey key) const
+{
+    return d.metaData(key);
+}
+
+/*!
+    \qmlmethod Collection::setExtendedMetaData(key, value)
+
+    Sets the value of the extended metadata with the given \a key to \a value.
+ */
+void QDeclarativeContactCollection::setExtendedMetaData(const QString &key, const QVariant &value)
+{
+    if (extendedMetaData(key) != value) {
+        d.setExtendedMetaData(key, value);
+        emit valueChanged();
+    }
+}
+
+/*!
+    \qmlmethod var Collection::extendedMetaData(key)
+
+    Returns the value of extended metadata with the given \a key.
+ */
+QVariant QDeclarativeContactCollection::extendedMetaData(const QString &key) const
+{
+    return d.extendedMetaData(key);
+}
+
+/*!
+    \internal
+ */
+QContactCollection QDeclarativeContactCollection::collection() const
+{
+    return d;
+}
+
+/*!
+    \internal
+ */
+void QDeclarativeContactCollection::setCollection(const QContactCollection &collection)
+{
+    d = collection;
+}
+
+#include "moc_qdeclarativecontactcollection_p.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/imports/contacts/qdeclarativecontactcollection_p.h b/src/imports/contacts/qdeclarativecontactcollection_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..1fe9a7ff71434b50ae4fedbfd135f59e0163af90
--- /dev/null
+++ b/src/imports/contacts/qdeclarativecontactcollection_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtContacts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVECONTACTCOLLECTION_H
+#define QDECLARATIVECONTACTCOLLECTION_H
+
+#include <QtCore/qurl.h>
+
+#include <QtGui/qcolor.h>
+
+#include <QtQml/qqml.h>
+
+#include <QtContacts/qcontactcollection.h>
+
+QTCONTACTS_USE_NAMESPACE
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeContactCollection : public QObject
+{
+    Q_OBJECT
+
+    Q_ENUMS(MetaDataKey)
+    Q_PROPERTY(QString collectionId READ id WRITE setId NOTIFY valueChanged)
+    Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged)
+    Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY valueChanged)
+    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY valueChanged)
+    Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY valueChanged)
+    Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY valueChanged)
+
+public:
+    enum MetaDataKey {
+        KeyName = QContactCollection::KeyName,
+        KeyDescription = QContactCollection::KeyDescription,
+        KeyColor = QContactCollection::KeyColor,
+        KeySecondaryColor = QContactCollection::KeySecondaryColor,
+        KeyImage = QContactCollection::KeyImage,
+        KeyExtended = QContactCollection::KeyExtended
+    };
+
+    QDeclarativeContactCollection(QObject *parent = 0);
+
+    QString id() const;
+    void setId(const QString &id);
+
+    QString name() const;
+    void setName(const QString &name);
+
+    QString description() const;
+    void setDescription(const QString &description);
+
+    QColor color() const;
+    void setColor(const QColor &color);
+
+    QColor secondaryColor() const;
+    void setSecondaryColor(const QColor &secondaryColor);
+
+    QUrl image() const;
+    void setImage(const QUrl &url);
+
+    Q_INVOKABLE void setMetaData(QContactCollection::MetaDataKey key, const QVariant &value);
+    Q_INVOKABLE QVariant metaData(QContactCollection::MetaDataKey key) const;
+
+    Q_INVOKABLE void setExtendedMetaData(const QString &key, const QVariant &value);
+    Q_INVOKABLE QVariant extendedMetaData(const QString &key) const;
+
+    // used by model
+    QContactCollection collection() const;
+    void setCollection(const QContactCollection & collection);
+
+Q_SIGNALS:
+    void valueChanged();
+
+private:
+    QContactCollection d;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QDeclarativeContactCollection)
+
+#endif // QDECLARATIVECONTACTCOLLECTION_H
diff --git a/src/imports/contacts/qdeclarativecontactmodel.cpp b/src/imports/contacts/qdeclarativecontactmodel.cpp
index d640116b00f09f41f9f1bcc3e6aebb23f436a5e2..9bf21367326773653711519ddb950f7c26fbc63a 100644
--- a/src/imports/contacts/qdeclarativecontactmodel.cpp
+++ b/src/imports/contacts/qdeclarativecontactmodel.cpp
@@ -179,7 +179,8 @@ public:
         m_error(QContactManager::NoError),
         m_autoUpdate(true),
         m_componentCompleted(false),
-        m_progressiveLoading(true)
+        m_progressiveLoading(true),
+        m_updatePendingFlag(QDeclarativeContactModelPrivate::NonePending)
     {
     }
     ~QDeclarativeContactModelPrivate()
@@ -188,6 +189,12 @@ public:
             delete m_manager;
     }
 
+    enum UpdateTypePending {
+        NonePending = 0x0,
+        UpdatingContactsPending = 0x1,
+        UpdatingCollectionsPending = 0x2
+    };
+
     QList<QDeclarativeContact*> m_contacts;
     QMap<QContactId, QDeclarativeContact*> m_contactMap;
     QMap<QContactId, QDeclarativeContact*> m_contactFetchedMap;
@@ -211,7 +218,9 @@ public:
     QHash<QContactAbstractRequest *, int> m_requestIdHash;
     QList<QContactFetchRequest*> m_pendingRequests;
     QList<QContact> m_pendingContacts;
+    QList<QDeclarativeContactCollection*> m_collections;
     bool m_progressiveLoading;
+    int m_updatePendingFlag;
 };
 
 QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) :
@@ -224,9 +233,9 @@ QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) :
     setRoleNames(roleNames);
 
     connect(this, SIGNAL(managerChanged()), SLOT(doUpdate()));
-    connect(this, SIGNAL(filterChanged()), SLOT(doUpdate()));
-    connect(this, SIGNAL(fetchHintChanged()), SLOT(doUpdate()));
-    connect(this, SIGNAL(sortOrdersChanged()), SLOT(doUpdate()));
+    connect(this, SIGNAL(filterChanged()), SLOT(doContactUpdate()));
+    connect(this, SIGNAL(fetchHintChanged()), SLOT(doContactUpdate()));
+    connect(this, SIGNAL(sortOrdersChanged()), SLOT(doContactUpdate()));
 
     //import vcard
     connect(&d->m_reader, SIGNAL(stateChanged(QVersitReader::State)), this, SLOT(startImport(QVersitReader::State)));
@@ -253,14 +262,21 @@ void QDeclarativeContactModel::setManager(const QString& managerName)
     if (d->m_manager && (managerName == d->m_manager->managerName() || managerName == d->m_manager->managerUri()))
         return;
 
-    if (d->m_manager)
+    if (d->m_manager) {
+        cancelUpdate();
         delete d->m_manager;
+    }
+
     d->m_manager = new QContactManager(managerName);
 
     connect(d->m_manager, SIGNAL(dataChanged()), this, SLOT(doUpdate()));
     connect(d->m_manager, SIGNAL(contactsAdded(QList<QContactId>)), this, SLOT(onContactsAdded(QList<QContactId>)));
     connect(d->m_manager, SIGNAL(contactsRemoved(QList<QContactId>)), this, SLOT(onContactsRemoved(QList<QContactId>)));
     connect(d->m_manager, SIGNAL(contactsChanged(QList<QContactId>,QList<QContactDetail::DetailType>)), this, SLOT(onContactsChanged(QList<QContactId>)));
+    connect(d->m_manager, SIGNAL(collectionsAdded(QList<QContactCollectionId>)), this, SLOT(fetchCollections()));
+    connect(d->m_manager, SIGNAL(collectionsChanged(QList<QContactCollectionId>)), this, SLOT(fetchCollections()));
+    connect(d->m_manager, SIGNAL(collectionsRemoved(QList<QContactCollectionId>)), this, SLOT(fetchCollections()));
+
 
     if (d->m_error != QContactManager::NoError) {
         d->m_error = QContactManager::NoError;
@@ -300,15 +316,71 @@ bool QDeclarativeContactModel::autoUpdate() const
 
 void QDeclarativeContactModel::update()
 {
-    if (!d->m_componentCompleted)
+    if (!d->m_componentCompleted || d->m_updatePendingFlag)
         return;
+    // Disallow possible duplicate request triggering
+    d->m_updatePendingFlag = (QDeclarativeContactModelPrivate::UpdatingContactsPending | QDeclarativeContactModelPrivate::UpdatingCollectionsPending);
+    QMetaObject::invokeMethod(this, "fetchCollections", Qt::QueuedConnection);
+}
+
+/*!
+  \qmlmethod ContactModel::updateContacts()
+
+  Manually update the contact model contacts.
+
+  \sa ContactModel::update
+  \sa ContactModel::updateCollections
+  \sa ContactModel::autoUpdate
+  */
+void QDeclarativeContactModel::updateContacts()
+{
+    if (!d->m_componentCompleted || d->m_updatePendingFlag)
+        return;
+    // Disallow possible duplicate request triggering
+    d->m_updatePendingFlag = QDeclarativeContactModelPrivate::UpdatingContactsPending;
     QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection);
 }
 
-void QDeclarativeContactModel::doUpdate()
+/*!
+  \qmlmethod ContactModel::updateCollections()
+
+  Manually update the contact model collections.
+
+  \sa ContactModel::update
+  \sa ContactModel::updateContacts
+  \sa ContactModel::autoUpdate
+  */
+void QDeclarativeContactModel::updateCollections()
+{
+    if (!d->m_componentCompleted || d->m_updatePendingFlag)
+        return;
+    // Disallow possible duplicate request triggering
+    d->m_updatePendingFlag = QDeclarativeContactModelPrivate::UpdatingCollectionsPending;
+    QMetaObject::invokeMethod(this, "fetchCollections", Qt::QueuedConnection);
+}
+
+/*!
+  \qmlmethod ContactModel::cancelUpdate()
+
+  Cancel the running contact model content update request.
+
+  \sa ContactModel::autoUpdate
+  \sa ContactModel::update
+  */
+void QDeclarativeContactModel::cancelUpdate()
+{
+    foreach (QContactFetchRequest *req, d->m_pendingRequests) {
+        req->cancel();
+        req->deleteLater();
+    }
+    d->m_pendingRequests.clear();;
+    d->m_updatePendingFlag = QDeclarativeContactModelPrivate::NonePending;
+}
+
+void QDeclarativeContactModel::doContactUpdate()
 {
     if (d->m_autoUpdate)
-        update();
+        updateContacts();
 }
 
 /*!
@@ -719,6 +791,66 @@ int QDeclarativeContactModel::fetchContacts(const QStringList &contactIds)
     }
 }
 
+/*!
+  \qmlmethod ContactModel::removeCollection(string collectionId)
+  Removes asynchronously the contact collection with the given \a collectionId from the backend.
+  */
+void QDeclarativeContactModel::removeCollection(const QString &collectionId)
+{
+    QContactCollectionRemoveRequest* req = new QContactCollectionRemoveRequest(this);
+    req->setManager(d->m_manager);
+    req->setCollectionId(QContactCollectionId::fromString(collectionId));
+
+    connect(req, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
+
+    req->start();
+}
+
+/*!
+  \qmlmethod OContactModel::saveCollection(Collection collection)
+
+  Saves asynchronously the given \a collection into the contact backend.
+ */
+void QDeclarativeContactModel::saveCollection(QDeclarativeContactCollection *declColl)
+{
+    if (declColl) {
+        QContactCollection collection = declColl->collection();
+        QContactCollectionSaveRequest* req = new QContactCollectionSaveRequest(this);
+        req->setManager(d->m_manager);
+        req->setCollection(collection);
+
+        if (declColl->collection().id().isNull()) {
+            // if the collection id is empty this means that this is a new collection
+            // we need to keep trace of this declarative collection to update with the
+            // new Id as soon as this request finish
+            QPointer<QDeclarativeContactCollection> pCollection = declColl;
+            req->setProperty("DeclarativeCollection", QVariant::fromValue(pCollection));
+        }
+
+        connect(req, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
+        req->start();
+    }
+}
+
+/*!
+  \qmlmethod OContactModel::fetchCollections()
+  Fetch asynchronously a list of contact collections from the contact backend.
+*/
+void QDeclarativeContactModel::fetchCollections()
+{
+    // fetchCollections() is used for both direct calls and
+    // signals from model. For signal from model, check also the
+    // autoupdate-flag.
+    if (sender() == d->m_manager && !d->m_autoUpdate) {
+        return;
+    }
+
+    QContactCollectionFetchRequest* req = new QContactCollectionFetchRequest(this);
+    connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(collectionsFetched()));
+    req->setManager(d->m_manager);
+    req->start();
+}
+
 /*!
     \internal
  */
@@ -764,6 +896,56 @@ void QDeclarativeContactModel::onFetchContactsRequestStateChanged(QContactAbstra
     request->deleteLater();
 }
 
+/*!
+    \internal
+ */
+void QDeclarativeContactModel::collectionsFetched()
+{
+    QContactCollectionFetchRequest* req = qobject_cast<QContactCollectionFetchRequest*>(QObject::sender());
+    Q_ASSERT(req);
+    if (req->isFinished() && QContactManager::NoError == req->error()) {
+        d->m_updatePendingFlag &= ~QDeclarativeContactModelPrivate::UpdatingCollectionsPending;
+        // prepare tables
+        QHash<QString, const QContactCollection*> collections;
+        foreach (const QContactCollection& collection, req->collections()) {
+            collections.insert(collection.id().toString(), &collection);
+        }
+        QHash<QString, QDeclarativeContactCollection*> declCollections;
+        foreach (QDeclarativeContactCollection* declCollection, d->m_collections) {
+            declCollections.insert(declCollection->collection().id().toString(), declCollection);
+        }
+        // go tables through
+        QHashIterator<QString, const QContactCollection*> collIterator(collections);
+        while (collIterator.hasNext()) {
+            collIterator.next();
+            if (declCollections.contains(collIterator.key())) {
+                // collection on both sides, update the declarative collection
+                declCollections.value(collIterator.key())->setCollection(*collections.value(collIterator.key()));
+            } else {
+                // new collection, add it to declarative collection list
+                QDeclarativeContactCollection* declCollection = new QDeclarativeContactCollection(this);
+                declCollection->setCollection(*collections.value(collIterator.key()));
+                d->m_collections.append(declCollection);
+            }
+        }
+        QHashIterator<QString, QDeclarativeContactCollection*> declCollIterator(declCollections);
+        while (declCollIterator.hasNext()) {
+            declCollIterator.next();
+            if (!collections.contains(declCollIterator.key())) {
+                // collection deleted on the backend side, delete from declarative collection list
+                QDeclarativeContactCollection* toBeDeletedColl = declCollections.value(declCollIterator.key());
+                d->m_collections.removeOne(toBeDeletedColl);
+                toBeDeletedColl->deleteLater();
+            }
+        }
+        emit collectionsChanged();
+        if (d->m_updatePendingFlag & QDeclarativeContactModelPrivate::UpdatingContactsPending)
+            QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection);
+        req->deleteLater();
+    }
+    checkError(req);
+}
+
 void QDeclarativeContactModel::clearContacts()
 {
     qDeleteAll(d->m_contacts);
@@ -860,6 +1042,7 @@ void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest:
     if (newState != QContactAbstractRequest::FinishedState)
         return;
 
+    d->m_updatePendingFlag &= ~QDeclarativeContactModelPrivate::UpdatingContactsPending;
     QContactFetchRequest* req = qobject_cast<QContactFetchRequest*>(QObject::sender());
     Q_ASSERT(req);
     if (req) {
@@ -908,6 +1091,15 @@ void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest:
     }
 }
 
+/*!
+    \internal
+ */
+void QDeclarativeContactModel::doUpdate()
+{
+    if (d->m_autoUpdate)
+        update();
+}
+
 /*!
   \qmlmethod ContactModel::saveContact(Contact contact)
 
@@ -944,18 +1136,40 @@ void QDeclarativeContactModel::onRequestStateChanged(QContactAbstractRequest::St
     QContactAbstractRequest *request = qobject_cast<QContactAbstractRequest *>(sender());
     Q_ASSERT(request);
 
-    if ((request->type() == QContactSaveRequest::ContactSaveRequest) &&
-        (request->error() == QContactManager::NoError)) {
-        QVariant vContact = request->property("DeclarativeContact");
-        if (vContact.isValid()) {
-            QPointer<QDeclarativeContact> pContact = vContact.value<QPointer<QDeclarativeContact> >();
-            // Update contact info.
-            // this is necessary to make sure that the declarative contact get the new contact ID otherwise
-            // the contact Id will be empty
-            QList<QContact> contacts = qobject_cast<QContactSaveRequest*>(request)->contacts();
-            if (pContact && contacts.length() == 1) {
-                pContact->setContact(contacts[0]);
+    if (request->error() == QContactManager::NoError) {
+        switch (request->type()) {
+        case QContactAbstractRequest::ContactSaveRequest:
+        {
+            QVariant vContact = request->property("DeclarativeContact");
+            if (vContact.isValid()) {
+                QPointer<QDeclarativeContact> pContact = vContact.value<QPointer<QDeclarativeContact> >();
+                // Update contact info.
+                // this is necessary to make sure that the declarative contact get the new contact ID otherwise
+                // the contact Id will be empty
+                QList<QContact> contacts = qobject_cast<QContactSaveRequest*>(request)->contacts();
+                if (pContact && contacts.length() == 1) {
+                    pContact->setContact(contacts[0]);
+                }
             }
+            break;
+        }
+        case QContactAbstractRequest::CollectionSaveRequest:
+        {
+            QVariant vCollection = request->property("DeclarativeCollection");
+            if (vCollection.isValid()) {
+                QPointer<QDeclarativeContactCollection> pCollection = vCollection.value<QPointer<QDeclarativeContactCollection> >();
+                // Update collection info.
+                // this is necessary to make sure that the declarative collection get the new collection ID otherwise
+                // the collection Id will be empty
+                QList<QContactCollection> collections = qobject_cast<QContactCollectionSaveRequest*>(request)->collections();
+                if (pCollection && collections.length() == 1) {
+                    pCollection->setCollection(collections[0]);
+                }
+            }
+            break;
+        }
+        default:
+            break;
         }
     }
     checkError(request);
@@ -1169,6 +1383,35 @@ void  QDeclarativeContactModel::sortOrder_clear(QQmlListProperty<QDeclarativeCon
         emit model->sortOrdersChanged();
     }
 }
+
+/*!
+  \qmlproperty list<Collection> OContactModel::collections
+
+  This property holds a list of collections in the contact model.
+
+  \sa Collection
+  */
+QQmlListProperty<QDeclarativeContactCollection> QDeclarativeContactModel::collections()
+{
+    return QQmlListProperty<QDeclarativeContactCollection>(this, 0, collection_count, collection_at);
+}
+
+int QDeclarativeContactModel::collection_count(QQmlListProperty<QDeclarativeContactCollection> *p)
+{
+    QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object);
+    return model ? model->d->m_collections.count() : 0;
+}
+
+QDeclarativeContactCollection *QDeclarativeContactModel::collection_at(QQmlListProperty<QDeclarativeContactCollection> *p, int idx)
+{
+    QDeclarativeContactModel* model = qobject_cast<QDeclarativeContactModel*>(p->object);
+    QDeclarativeContactCollection* collection = 0;
+    if (model) {
+        if (!model->d->m_collections.isEmpty() && idx >= 0 && idx < model->d->m_collections.count())
+            collection = model->d->m_collections.at(idx);
+    }
+    return collection;
+}
 /*!
     \internal
 
diff --git a/src/imports/contacts/qdeclarativecontactmodel_p.h b/src/imports/contacts/qdeclarativecontactmodel_p.h
index a3acdf88440ea15c01337d2113ed03646307b12b..bfdf27ce74bb164e3e4f9f494c31e7c3c4f61886 100644
--- a/src/imports/contacts/qdeclarativecontactmodel_p.h
+++ b/src/imports/contacts/qdeclarativecontactmodel_p.h
@@ -53,6 +53,7 @@
 #include <QtVersit/qversitwriter.h>
 
 #include "qdeclarativecontact_p.h"
+#include "qdeclarativecontactcollection_p.h"
 #include "qdeclarativecontactfetchhint_p.h"
 #include "qdeclarativecontactfilter_p.h"
 #include "qdeclarativecontactsortorder_p.h"
@@ -73,6 +74,7 @@ class QDeclarativeContactModel : public QAbstractListModel, public QQmlParserSta
     Q_PROPERTY(QDeclarativeContactFilter* filter READ filter WRITE setFilter NOTIFY filterChanged)
     Q_PROPERTY(QDeclarativeContactFetchHint* fetchHint READ fetchHint WRITE setFetchHint NOTIFY fetchHintChanged)
     Q_PROPERTY(QQmlListProperty<QDeclarativeContact> contacts READ contacts NOTIFY contactsChanged)
+    Q_PROPERTY(QQmlListProperty<QDeclarativeContactCollection> collections READ collections NOTIFY collectionsChanged)
     Q_PROPERTY(QQmlListProperty<QDeclarativeContactSortOrder> sortOrders READ sortOrders NOTIFY sortOrdersChanged)
     Q_ENUMS(ExportError)
     Q_ENUMS(ImportError)
@@ -133,17 +135,24 @@ public:
     static QDeclarativeContact* contacts_at(QQmlListProperty<QDeclarativeContact>* prop, int index);
     static void contacts_clear(QQmlListProperty<QDeclarativeContact>* prop);
 
+    QQmlListProperty<QDeclarativeContactSortOrder> sortOrders();
     static void sortOrder_append(QQmlListProperty<QDeclarativeContactSortOrder> *p, QDeclarativeContactSortOrder *sortOrder);
     static int  sortOrder_count(QQmlListProperty<QDeclarativeContactSortOrder> *p);
     static QDeclarativeContactSortOrder * sortOrder_at(QQmlListProperty<QDeclarativeContactSortOrder> *p, int idx);
     static void  sortOrder_clear(QQmlListProperty<QDeclarativeContactSortOrder> *p);
 
-    QQmlListProperty<QDeclarativeContactSortOrder> sortOrders() ;
+    QQmlListProperty<QDeclarativeContactCollection> collections();
+    static int collection_count(QQmlListProperty<QDeclarativeContactCollection> *p);
+    static QDeclarativeContactCollection* collection_at(QQmlListProperty<QDeclarativeContactCollection> *p, int idx);
 
     Q_INVOKABLE void removeContact(QString id);
     Q_INVOKABLE void removeContacts(const QStringList& ids);
     Q_INVOKABLE void saveContact(QDeclarativeContact* dc);
     Q_INVOKABLE int fetchContacts(const QStringList& contactIds);
+    Q_INVOKABLE void removeCollection(const QString& collectionId);
+    Q_INVOKABLE void saveCollection(QDeclarativeContactCollection* collection);
+    // FIXME : Naming indicates fetch from database
+    Q_INVOKABLE void fetchCollections();
     Q_INVOKABLE void importContacts(const QUrl& url, const QStringList& profiles = QStringList());
     Q_INVOKABLE void exportContacts(const QUrl& url, const QStringList& profiles = QStringList(), const QVariantList &declarativeContacts = QVariantList());
 
@@ -153,6 +162,7 @@ signals:
     void errorChanged();
     void fetchHintChanged();
     void contactsChanged();
+    void collectionsChanged();
     void sortOrdersChanged();
     void autoUpdateChanged();
     void exportCompleted(ExportError error, QUrl url);
@@ -161,6 +171,9 @@ signals:
 
 public slots:
     void update();
+    void updateContacts();
+    void updateCollections();
+    void cancelUpdate();
 
 private slots:
     void clearContacts();
@@ -168,6 +181,7 @@ private slots:
     void requestUpdated();
     void fetchRequestStateChanged(QContactAbstractRequest::State newState);
     void doUpdate();
+    void doContactUpdate();
     void onRequestStateChanged(QContactAbstractRequest::State newState);
     void onContactsAdded(const QList<QContactId>& ids);
     void onContactsRemoved(const QList<QContactId>& ids);
@@ -185,6 +199,8 @@ private slots:
     // handle fetch request from fetchContacts()
     void onFetchContactsRequestStateChanged(QContactAbstractRequest::State state);
 
+    void collectionsFetched();
+
 private:
     QContactFetchRequest *createContactFetchRequest(const QList<QContactId> &ids);
     void checkError(const QContactAbstractRequest *request);
diff --git a/src/plugins/contacts/memory/qcontactmemorybackend.cpp b/src/plugins/contacts/memory/qcontactmemorybackend.cpp
index d8e698c1dfbcc2057fc903c5e087715487ed0b6a..6476ae4850e0767041be75e82b369d6bd642aafa 100644
--- a/src/plugins/contacts/memory/qcontactmemorybackend.cpp
+++ b/src/plugins/contacts/memory/qcontactmemorybackend.cpp
@@ -134,6 +134,16 @@ QContactMemoryEngine::QContactMemoryEngine(QContactMemoryEngineData *data)
     qRegisterMetaType<QContactId>("QContactId");
     d->m_managerUri = managerUri();
     d->m_sharedEngines.append(this);
+
+    // the default collection always exists.
+    if (d->m_idToCollectionHash.isEmpty()) {
+        d->m_managerUri = managerUri();
+        const QContactCollectionId defaultId = defaultCollectionId();
+        QContactCollection defaultCollection;
+        defaultCollection.setId(defaultId);
+        defaultCollection.setMetaData(QContactCollection::KeyName, QString(QStringLiteral("Default Collection")));
+        d->m_idToCollectionHash.insert(defaultId, defaultCollection);
+    }
 }
 
 /*! Frees any memory used by this engine */
@@ -511,6 +521,102 @@ bool QContactMemoryEngine::removeRelationships(const QList<QContactRelationship>
     return (*error == QContactManager::NoError);
 }
 
+QContactCollection QContactMemoryEngine::defaultCollection(QContactManager::Error *error)
+{
+    const QContactCollectionId defaultCollectionId = this->defaultCollectionId();
+    Q_ASSERT(d->m_idToCollectionHash.contains(defaultCollectionId));
+    *error = QContactManager::NoError;
+    return d->m_idToCollectionHash.value(defaultCollectionId);
+}
+
+QContactCollection QContactMemoryEngine::collection(const QContactCollectionId &collectionId, QContactManager::Error *error)
+{
+    if (d->m_idToCollectionHash.contains(collectionId)) {
+        *error = QContactManager::NoError;
+        return d->m_idToCollectionHash.value(collectionId);
+    }
+
+    *error = QContactManager::DoesNotExistError;
+    return QContactCollection();
+}
+
+QList<QContactCollection> QContactMemoryEngine::collections(QContactManager::Error *error)
+{
+    Q_ASSERT(!d->m_idToCollectionHash.isEmpty());
+    *error = QContactManager::NoError;
+    return d->m_idToCollectionHash.values();
+}
+
+bool QContactMemoryEngine::saveCollection(QContactCollection *collection, QContactManager::Error *error)
+{
+    QContactCollectionId collectionId = collection->id();
+
+    QContactCollectionChangeSet cs;
+    if (d->m_idToCollectionHash.contains(collectionId)) {
+        // this collection already exists.  update our internal list
+        // if the collection has been modified.
+        if (d->m_idToCollectionHash.value(collectionId) == *collection) {
+            *error = QContactManager::NoError;
+            return true;
+        }
+
+        cs.insertChangedCollection(collectionId);
+    } else {
+        // this must be a new collection.  check that the id is null.
+        if (!collectionId.isNull() && collectionId.managerUri() != d->m_managerUri) {
+            // nope, this collection belongs in another manager, or has been deleted.
+            *error = QContactManager::DoesNotExistError;
+            return false;
+        }
+
+        // this is a new collection with a null id; create a new id, add it to our list.
+        QUuid id = QUuid::createUuid();
+        collectionId = this->collectionId(id.toByteArray());
+        collection->setId(collectionId);
+        cs.insertAddedCollection(collectionId);
+    }
+
+    d->m_idToCollectionHash.insert(collectionId, *collection);
+    d->emitSharedSignals(&cs);
+    *error = QContactManager::NoError;
+    return true;
+}
+
+bool QContactMemoryEngine::removeCollection(const QContactCollectionId &collectionId, QContactManager::Error *error)
+{
+    if (collectionId == defaultCollectionId()) {
+        // attempting to remove the default collection.  this is not allowed in the memory engine.
+        *error = QContactManager::PermissionsError;
+        return false;
+    }
+
+    // try to find the collection to remove it (and the items it contains)
+    if (d->m_idToCollectionHash.contains(collectionId)) {
+        // found the collection to remove.  remove the items in the collection.
+        const QList<QContactId> contactsToRemove = d->m_contactsInCollections.values(collectionId);
+        if (!contactsToRemove.isEmpty()) {
+            QMap<int, QContactManager::Error> errorMap;
+            if (!removeContacts(contactsToRemove, &errorMap, error)) {
+                // without transaction support, we can't back out.  but the operation should fail.
+                return false;
+            }
+        }
+
+        // now remove the collection from our lists.
+        d->m_idToCollectionHash.remove(collectionId);
+        d->m_contactsInCollections.remove(collectionId);
+        QContactCollectionChangeSet cs;
+        cs.insertRemovedCollection(collectionId);
+        d->emitSharedSignals(&cs);
+        *error = QContactManager::NoError;
+        return true;
+    }
+
+    // the collection doesn't exist...
+    *error = QContactManager::DoesNotExistError;
+    return false;
+}
+
 /*! \reimp */
 void QContactMemoryEngine::requestDestroyed(QContactAbstractRequest *req)
 {
@@ -728,6 +834,65 @@ void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest
         }
         break;
 
+        case QContactAbstractRequest::CollectionFetchRequest:
+        {
+            QContactCollectionFetchRequest* r = static_cast<QContactCollectionFetchRequest*>(currentRequest);
+            QContactManager::Error operationError = QContactManager::NoError;
+            QList<QContactCollection> requestedContactCollections = collections(&operationError);
+
+            // update the request with the results.
+            updateCollectionFetchRequest(r, requestedContactCollections, operationError, QContactAbstractRequest::FinishedState);
+        }
+        break;
+
+        case QContactAbstractRequest::CollectionSaveRequest:
+        {
+            QContactCollectionSaveRequest* r = static_cast<QContactCollectionSaveRequest*>(currentRequest);
+            QList<QContactCollection> collections = r->collections();
+            QList<QContactCollection> retn;
+
+            QContactManager::Error operationError = QContactManager::NoError;
+            QMap<int, QContactManager::Error> errorMap;
+            for (int i = 0; i < collections.size(); ++i) {
+                QContactManager::Error tempError = QContactManager::NoError;
+                QContactCollection curr = collections.at(i);
+                if (!saveCollection(&curr, &tempError)) {
+                    errorMap.insert(i, tempError);
+                    operationError = tempError;
+                }
+                retn.append(curr);
+            }
+
+            updateCollectionSaveRequest(r, retn, operationError, errorMap, QContactAbstractRequest::FinishedState);
+        }
+        break;
+
+        case QContactAbstractRequest::CollectionRemoveRequest:
+        {
+            // removes the collections identified in the list of ids.
+            QContactCollectionRemoveRequest* r = static_cast<QContactCollectionRemoveRequest*>(currentRequest);
+            QContactManager::Error operationError = QContactManager::NoError;
+            QList<QContactCollectionId> collectionsToRemove = r->collectionIds();
+            QMap<int, QContactManager::Error> errorMap;
+
+            for (int i = 0; i < collectionsToRemove.size(); i++) {
+                QContactManager::Error tempError = QContactManager::NoError;
+                removeCollection(collectionsToRemove.at(i), &tempError);
+
+                if (tempError != QContactManager::NoError) {
+                    errorMap.insert(i, tempError);
+                    operationError = tempError;
+                }
+            }
+
+            if (!errorMap.isEmpty() || operationError != QContactManager::NoError)
+                updateCollectionRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState);
+            else
+                updateRequestState(currentRequest, QContactAbstractRequest::FinishedState);
+        }
+        break;
+
+
         default: // unknown request type.
         break;
     }
@@ -736,6 +901,12 @@ void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest
     d->emitSharedSignals(&changeSet);
 }
 
+QContactCollectionId QContactMemoryEngine::defaultCollectionId() const
+{
+    static const QByteArray id("Personal");
+    return collectionId(id);
+}
+
 void QContactMemoryEngine::partiallySyncDetails(QContact *to, const QContact &from, const QList<QContactDetail::DetailType> &mask)
 {
     // these details in old contact
@@ -893,6 +1064,20 @@ bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &
             return false;
         }
 
+        // check the contact collection
+        QContactCollectionId collectionId = theContact->collectionId();
+        // if is null use default collection
+        if (collectionId.isNull()) {
+            collectionId = this->defaultCollectionId();
+            theContact->setCollectionId(collectionId);
+        } else {
+            // check if the collection exists
+            QContactCollection collection = this->collection(collectionId, error);
+            if (collection.id().isNull()) {
+                return false;
+            }
+        }
+
         // check if this is partial save
         if (!mask.isEmpty()) {
             QContact tempContact;
@@ -915,6 +1100,7 @@ bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &
         // finally, add the contact to our internal lists and return
         d->m_contacts.append(*theContact);                   // add contact to list
         d->m_contactIds.append(theContact->id());  // track the contact id.
+        d->m_contactsInCollections.insert(collectionId, newContactId); // link contact to collection
 
         changeSet.insertAddedContact(theContact->id());
     }
diff --git a/src/plugins/contacts/memory/qcontactmemorybackend_p.h b/src/plugins/contacts/memory/qcontactmemorybackend_p.h
index 7c44d7d3fc88bfdaf6f6b4060d0e6df4f869768d..adeff03a74f34c65e5b37a55b33bb5884bac93a1 100644
--- a/src/plugins/contacts/memory/qcontactmemorybackend_p.h
+++ b/src/plugins/contacts/memory/qcontactmemorybackend_p.h
@@ -104,7 +104,9 @@ public:
     QString m_id;                                  // the id parameter value
 
     QContactId m_selfContactId;               // the "MyCard" contact id
-    QList<QContact> m_contacts;                    // list of contacts
+    QList<QContact> m_contacts;               // list of contacts
+    QHash<QContactCollectionId, QContactId> m_contactsInCollections;   // hash of contacts for each collection
+    QHash<QContactCollectionId, QContactCollection> m_idToCollectionHash; // hash of id to the collection identified by that id
     QList<QContactId> m_contactIds;           // list of contact Id's
     QList<QContactRelationship> m_relationships;   // list of contact relationships
     QMap<QContactId, QList<QContactRelationship> > m_orderedRelationships; // map of ordered lists of contact relationships
@@ -120,6 +122,12 @@ public:
             cs->emitSignals(engine);
     }
 
+    void emitSharedSignals(QContactCollectionChangeSet *cs)
+    {
+        foreach (QContactManagerEngine *engine, m_sharedEngines)
+            cs->emitSignals(engine);
+    }
+
     QList<QContactManagerEngine*> m_sharedEngines;   // The list of engines that share this data
 };
 
@@ -157,6 +165,13 @@ public:
     virtual bool saveRelationships(QList<QContactRelationship> *relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error);
     virtual bool removeRelationships(const QList<QContactRelationship> &relationships, QMap<int, QContactManager::Error> *errorMap, QContactManager::Error *error);
 
+    // collections
+    QContactCollection defaultCollection(QContactManager::Error* error);
+    QContactCollection collection(const QContactCollectionId &collectionId, QContactManager::Error *error);
+    QList<QContactCollection> collections(QContactManager::Error* error);
+    bool saveCollection(QContactCollection* collection, QContactManager::Error* error);
+    bool removeCollection(const QContactCollectionId& collectionId, QContactManager::Error* error);
+
     /*! \reimp */
     virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const
     {
@@ -202,6 +217,8 @@ private:
 
     void performAsynchronousOperation(QContactAbstractRequest *request);
 
+    QContactCollectionId defaultCollectionId() const;
+
     QContactMemoryEngineData *d;
     static QMap<QString, QContactMemoryEngineData*> engineDatas;
 
diff --git a/tests/auto/contacts/contacts.pro b/tests/auto/contacts/contacts.pro
index fe4cb48651e2c0ebb5c02b346bf03b5cbc49f506..408b57e445e470ff4c362c431f9f043ebc74153f 100644
--- a/tests/auto/contacts/contacts.pro
+++ b/tests/auto/contacts/contacts.pro
@@ -4,6 +4,7 @@ TEMPLATE = subdirs
 SUBDIRS += \
     qcontact \
     qcontactasync \
+    qcontactcollection \
     qcontactdetail \
     qcontactdetails \
     qcontactfilter \
diff --git a/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp b/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp
index 59a584f8387ac442cf5c1d74d2944b3d65311e47..885606b27cefd13123fc46b577df848219ed56dd 100644
--- a/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp
+++ b/tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp
@@ -56,6 +56,13 @@ QTCONTACTS_USE_NAMESPACE
                                          fqcfr2.start(); \
                                          fqcfr3.start();
 
+/* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */
+#define FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(manager) QContactFetchRequest fqifr1, fqifr2, fqifr3; \
+                                                fqifr1.setManager(manager); fqifr1.start(); \
+                                                fqifr2.setManager(manager); fqifr2.start(); \
+                                                fqifr3.setManager(manager); fqifr3.start();
+
+
 //TESTED_COMPONENT=src/contacts
 
 // Unfortunately the plumbing isn't in place to allow cancelling requests at arbitrary points
@@ -230,6 +237,13 @@ private slots:
     void relationshipSave();
     void relationshipSave_data() { addManagers(); }
 
+    void collectionFetch();
+    void collectionFetch_data() { addManagers(); }
+    void collectionRemove();
+    void collectionRemove_data() { addManagers(); }
+    void collectionSave();
+    void collectionSave_data() { addManagers(); }
+
     void maliciousManager(); // uses it's own custom data (manager)
 
     void testQuickDestruction();
@@ -245,6 +259,7 @@ private:
     bool compareContacts(QContact ca, QContact cb);
     bool containsIgnoringTimestamps(const QList<QContact>& list, const QContact& c);
     bool compareIgnoringTimestamps(const QContact& ca, const QContact& cb);
+    bool containsAllCollectionIds(const QList<QContactCollectionId>& target, const QList<QContactCollectionId>& ids);
     QContactManager* prepareModel(const QString& uri);
 
     Qt::HANDLE m_mainThreadId;
@@ -376,6 +391,18 @@ bool tst_QContactAsync::compareIgnoringTimestamps(const QContact& ca, const QCon
     return false;
 }
 
+bool tst_QContactAsync::containsAllCollectionIds(const QList<QContactCollectionId> &target, const QList<QContactCollectionId> &ids)
+{
+    bool containsAllIds = true;
+    foreach (QContactCollectionId id, ids) {
+        if (!target.contains(id)) {
+            containsAllIds = false;
+            break;
+        }
+    }
+    return containsAllIds;
+}
+
 void tst_QContactAsync::testDestructor()
 {
     QFETCH(QString, uri);
@@ -1316,6 +1343,9 @@ void tst_QContactAsync::contactSave()
     //QCOMPARE(result, expected);
     // XXX: really, we should use isSuperset() from tst_QContactManager, but this will do for now:
     QVERIFY(result.first().detail<QContactName>() == nameDetail);
+
+    // check if the contact was saved on default collection
+    QCOMPARE(result.first().collectionId().toString(), cm->defaultCollection().id().toString());
     QCOMPARE(cm->contactIds().size(), originalCount + 1);
 
     // update a previously saved contact
@@ -2413,6 +2443,443 @@ void tst_QContactAsync::relationshipSave()
     }
 }
 
+void tst_QContactAsync::collectionFetch()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(prepareModel(uri));
+
+    QContactCollectionFetchRequest cfr;
+    QVERIFY(cfr.type() == QContactAbstractRequest::CollectionFetchRequest);
+
+    // initial state - not started, no manager.
+    QVERIFY(!cfr.isActive());
+    QVERIFY(!cfr.isFinished());
+    QVERIFY(!cfr.start());
+    QVERIFY(!cfr.cancel());
+    QVERIFY(!cfr.waitForFinished());
+
+    // retrieve all collections.
+    cfr.setManager(cm.data());
+    QCOMPARE(cfr.manager(), cm.data());
+    QVERIFY(!cfr.isActive());
+    QVERIFY(!cfr.isFinished());
+    QVERIFY(!cfr.cancel());
+    QVERIFY(!cfr.waitForFinished());
+    qRegisterMetaType<QContactCollectionFetchRequest*>("QContactCollectionFetchRequest*");
+    QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
+    QVERIFY(!cfr.cancel()); // not started
+
+    QVERIFY(cfr.start());
+    //QVERIFY(cfr.isFinished() || !cfr.start());  // already started. // thread scheduling means this is untestable
+    QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished());
+    QVERIFY(cfr.waitForFinished());
+    QVERIFY(cfr.isFinished());
+
+    QVERIFY(spy.count() >= 1); // active + finished progress signals
+    spy.clear();
+
+    QList<QContactCollection> syncCols = cm->collections();
+    QList<QContactCollection> cols = cfr.collections();
+    QCOMPARE(cols.size(), syncCols.size());
+    for (int i = 0; i < cols.size(); i++) {
+        QContactCollection curr = cols.at(i);
+        QVERIFY(syncCols.contains(curr));
+    }
+
+    // cancelling
+    int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
+    while (true) {
+        QVERIFY(!cfr.cancel()); // not started
+        FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data());
+        QVERIFY(cfr.start());
+        if (!cfr.cancel()) {
+            // due to thread scheduling, async cancel might be attempted
+            // after the request has already finished.. so loop and try again.
+            spy.clear();
+            cfr.waitForFinished();
+            bailoutCount -= 1;
+            if (!bailoutCount) {
+//                qWarning("Unable to test cancelling due to thread scheduling!");
+                bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
+                break;
+            }
+            continue;
+        }
+
+        // if we get here, then we are cancelling the request.
+        QVERIFY(cfr.waitForFinished());
+        QVERIFY(cfr.isCanceled());
+
+        QVERIFY(spy.count() >= 1); // active + cancelled progress signals
+        spy.clear();
+        break;
+    }
+
+    // restart, and wait for progress after cancel.
+    while (true) {
+        QVERIFY(!cfr.cancel()); // not started
+        FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data());
+        QVERIFY(cfr.start());
+        if (!cfr.cancel()) {
+            // due to thread scheduling, async cancel might be attempted
+            // after the request has already finished.. so loop and try again.
+            cfr.waitForFinished();
+            bailoutCount -= 1;
+            spy.clear();
+            if (!bailoutCount) {
+                //qWarning("Unable to test cancelling due to thread scheduling!");
+                bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
+                break;
+            }
+            continue;
+        }
+        cfr.waitForFinished();
+        QVERIFY(spy.count() >= 1); // active + cancelled progress signals
+        spy.clear();
+        QVERIFY(!cfr.isActive());
+        QVERIFY(cfr.state() == QContactAbstractRequest::CanceledState);
+        break;
+    }
+}
+
+void tst_QContactAsync::collectionRemove()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(prepareModel(uri));
+    QContactCollectionRemoveRequest crr;
+    QVERIFY(crr.type() == QContactAbstractRequest::CollectionRemoveRequest);
+
+    // initial state - not started, no manager.
+    QVERIFY(!crr.isActive());
+    QVERIFY(!crr.isFinished());
+    QVERIFY(!crr.start());
+    QVERIFY(!crr.cancel());
+    QVERIFY(!crr.waitForFinished());
+
+    // specific collection set
+    QContactCollectionId removeId = cm->collections().last().id();
+    if (cm->defaultCollection().id() == removeId)
+        removeId = cm->collections().first().id();
+    crr.setCollectionId(removeId);
+    QVERIFY(crr.collectionIds() == QList<QContactCollectionId>() << removeId);
+    int originalCount = cm->collections().size();
+    crr.setManager(cm.data());
+    QCOMPARE(crr.manager(), cm.data());
+    QVERIFY(!crr.isActive());
+    QVERIFY(!crr.isFinished());
+    QVERIFY(!crr.cancel());
+    QVERIFY(!crr.waitForFinished());
+    qRegisterMetaType<QContactCollectionRemoveRequest*>("QContactCollectionRemoveRequest*");
+    QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
+    QVERIFY(!crr.cancel()); // not started
+    QVERIFY(crr.start());
+    QVERIFY((crr.isActive() &&crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished());
+    //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable
+    QVERIFY(crr.waitForFinished());
+    QVERIFY(crr.isFinished());
+
+    QVERIFY(spy.count() >= 1); // active + finished progress signals
+    spy.clear();
+
+    QCOMPARE(cm->collections().size(), originalCount - 1); // should have removed that particular collection.
+    QVERIFY(crr.error() == QContactManager::NoError);
+    QVERIFY(crr.errorMap().isEmpty());
+
+    // remove all collections
+    QList<QContactCollectionId> allCollectionIds;
+    QList<QContactCollection> allCollections = cm->collections();
+    for (int i = 0; i < allCollections.size(); ++i)
+        allCollectionIds << allCollections.at(i).id();
+    crr.setCollectionIds(allCollectionIds);
+
+    QVERIFY(!crr.cancel()); // not started
+    QVERIFY(crr.start());
+
+    QVERIFY((crr.isActive() && crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished());
+    //QVERIFY(crr.isFinished() || !crr.start());  // already started. // thread scheduling means this is untestable
+    QVERIFY(crr.waitForFinished());
+    QVERIFY(crr.isFinished());
+
+    QVERIFY(cm->collections().size() >= 1); // at least one collection must be left, since default collection cannot be removed.
+    QVERIFY(spy.count() >= 1); // active + finished progress signals
+    spy.clear();
+
+    // remove empty list
+    QList<QContactCollectionId> collectionIdList;
+    QContactCollectionRemoveRequest crr1;
+    crr1.setManager(cm.data());
+    crr1.setCollectionIds(collectionIdList);
+    crr1.start();
+    crr1.waitForFinished();
+    QVERIFY(crr1.isFinished());
+    QVERIFY(crr1.error() == QContactManager::NoError);
+
+    // cancelling
+    QContactCollection temp;
+    temp.setMetaData(QContactCollection::KeyDescription, "Should not be removed!");
+    cm->saveCollection(&temp);
+    crr.setCollectionId(temp.id());
+
+    int collectionCount = cm->collections().size();
+    int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
+    while (true) {
+        QVERIFY(!crr.cancel()); // not started
+        FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data());
+        QVERIFY(spy.count() == 0);
+        QVERIFY(crr.start());
+        if (!crr.cancel()) {
+            // due to thread scheduling, async cancel might be attempted
+            // after the request has already finished.. so loop and try again.
+            crr.waitForFinished();
+            temp.setId(QContactCollectionId());
+            if (!cm->saveCollection(&temp)) {
+                QSKIP("Unable to save temporary item for remove request cancellation test!");
+            }
+            crr.setCollectionId(temp.id());
+            bailoutCount -= 1;
+            if (!bailoutCount) {
+//                qWarning("Unable to test cancelling due to thread scheduling!");
+                bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
+                break;
+            }
+            spy.clear();
+            continue;
+        }
+
+        // if we get here, then we are cancelling the request.
+        QVERIFY(crr.waitForFinished());
+        QVERIFY(crr.isCanceled());
+        QCOMPARE(cm->collections().size(), collectionCount); // temp collection should not have been removed
+        QList<QContactCollectionId> removeCollectionIds;
+        QList<QContactCollection> removeCollections = cm->collections();
+        for (int i = 0; i < removeCollections.size(); ++i)
+            removeCollectionIds << removeCollections.at(i).id();
+        QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds()));
+        QVERIFY(spy.count() >= 1); // active + cancelled progress signals
+        spy.clear();
+        break;
+    }
+
+    // restart, and wait for progress after cancel.
+    while (true) {
+        QVERIFY(!crr.cancel()); // not started
+        FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data());
+        QVERIFY(crr.start());
+        if (!crr.cancel()) {
+            // due to thread scheduling, async cancel might be attempted
+            // after the request has already finished.. so loop and try again.
+            crr.waitForFinished();
+            temp.setId(QContactCollectionId());
+            if (!cm->saveCollection(&temp)) {
+                QSKIP("Unable to save temporary item for remove request cancellation test!");
+            }
+            crr.setCollectionId(temp.id());
+            bailoutCount -= 1;
+            if (!bailoutCount) {
+//                qWarning("Unable to test cancelling due to thread scheduling!");
+                bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
+                break;
+            }
+            spy.clear();
+            continue;
+        }
+        crr.waitForFinished();
+        QVERIFY(crr.isCanceled());
+        QCOMPARE(cm->collections().size(), collectionCount); // temp collection should not have been removed
+        QList<QContactCollectionId> removeCollectionIds;
+        QList<QContactCollection> removeCollections = cm->collections();
+        for (int i = 0; i < removeCollections.size(); ++i)
+            removeCollectionIds << removeCollections.at(i).id();
+        QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds()));
+        QVERIFY(spy.count() >= 1); // active + cancelled progress signals
+        spy.clear();
+        break;
+    }
+
+    // now clean up our temp collection.
+    cm->removeCollection(temp.id());
+
+}
+
+void tst_QContactAsync::collectionSave()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(prepareModel(uri));
+    QContactCollectionSaveRequest csr;
+    QVERIFY(csr.type() == QContactAbstractRequest::CollectionSaveRequest);
+
+    // initial state - not started, no manager.
+    QVERIFY(!csr.isActive());
+    QVERIFY(!csr.isFinished());
+    QVERIFY(!csr.start());
+    QVERIFY(!csr.cancel());
+    QVERIFY(!csr.waitForFinished());
+
+    // save a new item
+    int originalCount = cm->collections().size();
+    QContactCollection testCollection;
+    testCollection.setMetaData(QContactCollection::KeyDescription, "test description");
+    testCollection.setMetaData(QContactCollection::KeyName, "New collection");
+    QList<QContactCollection> saveList;
+    saveList << testCollection;
+    csr.setManager(cm.data());
+    QCOMPARE(csr.manager(), cm.data());
+    QVERIFY(!csr.isActive());
+    QVERIFY(!csr.isFinished());
+    QVERIFY(!csr.cancel());
+    QVERIFY(!csr.waitForFinished());
+    qRegisterMetaType<QContactCollectionSaveRequest*>("QContactCollectionSaveRequest*");
+    QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State)));
+    csr.setCollection(testCollection);
+    QCOMPARE(csr.collections(), saveList);
+    QVERIFY(!csr.cancel()); // not started
+    QVERIFY(csr.start());
+
+    QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished());
+    //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable
+    QVERIFY(csr.waitForFinished());
+    QVERIFY(csr.isFinished());
+    QVERIFY(spy.count() >= 1); // active + finished progress signals
+    spy.clear();
+
+    QList<QContactCollection> expected = csr.collections();
+    QCOMPARE(expected.size(), 1);
+    QList<QContactCollection> result;
+    result << cm->collection(csr.collections().at(0).id());
+
+    // find the saved one, compare.
+    foreach (const QContactCollection &col, result)
+        QVERIFY(col.id() == expected.at(0).id());
+
+    // update a previously saved collection
+    QVERIFY(!result.isEmpty()); // make sure that we were able to retrieve the required collection.
+    testCollection = result.first();
+    testCollection.setMetaData(QContactCollection::KeyName, "test name");
+    saveList.clear();
+    saveList << testCollection;
+    csr.setCollections(saveList);
+    QCOMPARE(csr.collections(), saveList);
+    QVERIFY(!csr.cancel()); // not started
+    QVERIFY(csr.start());
+
+    QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished());
+    //QVERIFY(csr.isFinished() || !csr.start());  // already started. // thread scheduling means this is untestable
+    QVERIFY(csr.waitForFinished());
+
+    QVERIFY(csr.isFinished());
+    QVERIFY(spy.count() >= 1); // active + finished progress signals
+    spy.clear();
+
+    expected = csr.collections();
+    result.clear();
+    result = cm->collections();
+    // find the saved one, compare.
+    foreach (const QContactCollection& col, result) {
+        if (col.id() == expected.at(0).id()) {
+            QVERIFY(col == expected.at(0)); // XXX TODO: if we change the semantic so that save merely updates the id...?
+        }
+    }
+    QCOMPARE(cm->collections().size(), originalCount + 1); // ie shouldn't have added an extra one (would be +2)
+    QVERIFY(csr.error() == QContactManager::NoError);
+    QVERIFY(csr.errorMap().isEmpty());
+
+    // save empty list
+    QList<QContactCollection> collectionList;
+    QContactCollectionSaveRequest csr1;
+    csr1.setManager(cm.data());
+    csr1.setCollections(collectionList);
+    csr1.start();
+    csr1.waitForFinished();
+    QVERIFY(csr1.isFinished());
+    QVERIFY(csr1.error() == QContactManager::NoError);
+
+    // cancelling
+    QContactCollection temp;
+    temp.setMetaData(testCollection.metaData());
+    temp.setExtendedMetaData("test", "shouldn't be saved");
+    saveList.clear();
+    saveList << temp;
+    csr.setCollections(saveList);
+
+    int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times.  If it doesn't work due to threading, bail out.
+    while (true) {
+        QVERIFY(!csr.cancel()); // not started
+        FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data());
+        QVERIFY(csr.start());
+        if (!csr.cancel()) {
+            // due to thread scheduling, async cancel might be attempted
+            // after the request has already finished.. so loop and try again.
+            csr.waitForFinished();
+            saveList = csr.collections();
+            if (cm->collections().size() > (originalCount + 1) && !cm->removeCollection(saveList.at(0).id())) {
+                QSKIP("Unable to remove saved collection to test cancellation of collection save request");
+            }
+            saveList.clear();
+            saveList << temp;
+            csr.setCollections(saveList);
+            bailoutCount -= 1;
+            if (!bailoutCount) {
+//                qWarning("Unable to test cancelling due to thread scheduling!");
+                bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
+                break;
+            }
+            spy.clear();
+            continue;
+        }
+
+        // if we get here, then we are cancelling the request.
+        QVERIFY(csr.waitForFinished());
+        QVERIFY(csr.isCanceled());
+        QVERIFY(spy.count() >= 1); // active + cancelled progress signals
+        spy.clear();
+
+        // verify that the changes were not saved
+        expected.clear();
+        QList<QContactCollection> allCollections = cm->collections();
+        QVERIFY(!allCollections.contains(temp)); // should NOT contain it since it was cancelled.
+        QCOMPARE(allCollections.size(), originalCount + 1);
+        break;
+    }
+    // restart, and wait for progress after cancel.
+
+    while (true) {
+        QVERIFY(!csr.cancel()); // not started
+        FILL_QUEUE_WITH_FETCH_REQUESTS_WITH_MANAGER(cm.data());
+        QVERIFY(csr.start());
+        if (!csr.cancel()) {
+            // due to thread scheduling, async cancel might be attempted
+            // after the request has already finished.. so loop and try again.
+            csr.waitForFinished();
+            saveList = csr.collections();
+            if (cm->collections().size() > (originalCount + 1) && !cm->removeCollection(saveList.at(0).id())) {
+                QSKIP("Unable to remove saved item to test cancellation of item save request");
+            }
+            saveList.clear();
+            saveList << temp;
+            csr.setCollections(saveList);
+            bailoutCount -= 1;
+            if (!bailoutCount) {
+//                qWarning("Unable to test cancelling due to thread scheduling!");
+                bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT;
+                break;
+            }
+            spy.clear();
+            continue;
+        }
+        csr.waitForFinished(); // now wait until finished (if it hasn't already).
+        QVERIFY(csr.isCanceled());
+        QVERIFY(spy.count() >= 1); // active + cancelled progress signals
+        spy.clear();
+
+        // verify that the changes were not saved
+        expected.clear();
+        QList<QContactCollection> allCollections = cm->collections();
+        QVERIFY(!allCollections.contains(temp));
+        QCOMPARE(cm->collections().size(), originalCount + 1);
+        break;
+    }
+}
+
 void tst_QContactAsync::maliciousManager()
 {
     // use the invalid manager: passes all requests through to base class
@@ -2655,6 +3122,11 @@ QContactManager* tst_QContactAsync::prepareModel(const QString& managerUri)
     crb.setRelationshipType(QContactRelationship::IsSameAs());
     cm->saveRelationship(&crb);
 
+    QContactCollection testCollection;
+    testCollection.setMetaData(QContactCollection::KeyName, "Test Collection");
+    testCollection.setMetaData(QContactCollection::KeyDescription, "test collection");
+    cm->saveCollection(&testCollection);
+
     return cm;
 
     // TODO: cleanup once test is complete
diff --git a/tests/auto/contacts/qcontactcollection/qcontactcollection.pro b/tests/auto/contacts/qcontactcollection/qcontactcollection.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bb659977648fb1b848be1d17ee942a6957628b4c
--- /dev/null
+++ b/tests/auto/contacts/qcontactcollection/qcontactcollection.pro
@@ -0,0 +1,5 @@
+include(../../auto.pri)
+
+QT += contacts
+
+SOURCES  += tst_qcontactcollection.cpp
diff --git a/tests/auto/contacts/qcontactcollection/tst_qcontactcollection.cpp b/tests/auto/contacts/qcontactcollection/tst_qcontactcollection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d36f36c482e2779f28dbf741c863802cb7749577
--- /dev/null
+++ b/tests/auto/contacts/qcontactcollection/tst_qcontactcollection.cpp
@@ -0,0 +1,397 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtCore/qset.h>
+
+#include <QtContacts/qcontacts.h>
+
+
+QTCONTACTS_USE_NAMESPACE
+
+static inline QContactCollectionId makeId(const QString &managerName, uint id)
+{
+    return QContactCollectionId(QStringLiteral("qtcontacts:%1:").arg(managerName), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint)));
+}
+
+class tst_QContactCollection: public QObject
+{
+Q_OBJECT
+
+public:
+    tst_QContactCollection();
+    virtual ~tst_QContactCollection();
+
+private slots:
+    void metaData();
+    void compare();
+    void idComparison();
+    void idHash();
+    void idStringFunctions();
+    void hash();
+    void datastream();
+    void traits();
+    void idTraits();
+};
+
+tst_QContactCollection::tst_QContactCollection()
+{
+}
+
+tst_QContactCollection::~tst_QContactCollection()
+{
+}
+
+void tst_QContactCollection::metaData()
+{
+    QContactCollection c;
+    QVERIFY(c.metaData().isEmpty());
+    c.setExtendedMetaData(QString(QStringLiteral("test")), 5);
+    QCOMPARE(c.extendedMetaData(QString(QStringLiteral("test"))).toInt(), 5);
+
+    QMap<QContactCollection::MetaDataKey, QVariant> mdm;
+    mdm.insert(QContactCollection::KeyName, QString(QStringLiteral("test2")));
+    c.setMetaData(mdm);
+    QCOMPARE(c.metaData(), mdm);
+    QCOMPARE(c.metaData(QContactCollection::KeyName).toString(), QString(QStringLiteral("test2")));
+}
+
+void tst_QContactCollection::compare()
+{
+    QContactCollection c, c2;
+    QVERIFY(c == c2);
+    c.setExtendedMetaData(QStringLiteral("test"), 5);
+    QVERIFY(c != c2);
+    c2.setExtendedMetaData(QStringLiteral("test"), 5);
+    QVERIFY(c == c2);
+
+    QMap<QContactCollection::MetaDataKey, QVariant> mdm;
+    mdm.insert(QContactCollection::KeyName, QStringLiteral("test2"));
+    c.setMetaData(mdm);
+    QVERIFY(c != c2);
+    c2.setMetaData(mdm);
+    QVERIFY(c == c2);
+
+    c2 = QContactCollection();
+    QVERIFY(c != c2);
+    c2 = c;
+    QVERIFY(c == c2);
+
+    c.setId(makeId(QStringLiteral("a"), 1));
+    QVERIFY(c != c2);
+    c2.setId(makeId(QStringLiteral("a"), 1));
+    QVERIFY(c == c2);
+    c.setId(makeId(QStringLiteral("b"), 1));
+    QVERIFY(c != c2);
+    c2.setId(c.id());
+    QVERIFY(c == c2);
+    c.setId(makeId(QStringLiteral("b"), 2));
+}
+
+void tst_QContactCollection::idComparison()
+{
+    QContactCollectionId id1(makeId("a", 1));
+    QContactCollectionId id2(makeId("a", 1));
+    QVERIFY(!(id1 < id2));
+    QVERIFY(!(id2 < id1));
+    QVERIFY(id1 == id2);
+
+    QContactCollectionId id3(makeId("a", 2));
+    QContactCollectionId id4(makeId("b", 1));
+    QContactCollectionId id5(makeId("b", 2));
+    QVERIFY((((id1 < id3) && !(id3 < id1)) || ((id3 < id1) && !(id1 < id3))) && (id1 != id3));
+    QVERIFY((((id1 < id4) && !(id4 < id1)) || ((id4 < id1) && !(id1 < id4))) && (id1 != id4));
+    QVERIFY((((id3 < id4) && !(id4 < id3)) || ((id4 < id3) && !(id3 < id4))) && (id3 != id4));
+    QVERIFY((((id1 < id5) && !(id5 < id1)) || ((id5 < id1) && !(id1 < id5))) && (id3 != id4));
+
+    QContactCollectionId id6;
+    QContactCollectionId id7(QString(), "1");
+    QContactCollectionId id8(QString(), "2");
+    QContactCollectionId id9(QStringLiteral("qtcontacts:basic:"), QByteArray());
+    QVERIFY(id6.isNull());
+    QVERIFY(id7.isNull());
+    QVERIFY(id8.isNull());
+    QVERIFY(id9.isNull());
+    QVERIFY(id6 == id7);
+    QVERIFY(!(id6 < id7));
+    QVERIFY(id7 == id6);
+    QVERIFY(!(id7 < id6));
+    QVERIFY(id7 == id8);
+    QVERIFY(!(id7 < id8));
+    QVERIFY(id8 == id7);
+    QVERIFY(!(id9 < id8));
+    QVERIFY(id8 == id9);
+    QVERIFY(!(id8 < id9));
+    QVERIFY(id9 == id8);
+    QVERIFY(!(id9 < id8));
+
+    QVERIFY(!(id1 == id6));
+    QVERIFY(!(id1 < id6));
+    QVERIFY(id6 < id1);
+    QVERIFY(!(id1 == id7));
+    QVERIFY(!(id1 < id7));
+    QVERIFY(id7 < id1);
+    QVERIFY(!(id1 == id8));
+    QVERIFY(!(id1 < id8));
+    QVERIFY(id8 < id1);
+    QVERIFY(!(id1 == id9));
+    QVERIFY(!(id1 < id9));
+    QVERIFY(id9 < id1);
+}
+
+void tst_QContactCollection::idHash()
+{
+    QContactCollectionId id1(makeId("a", 1));
+    QContactCollectionId id2(makeId("a", 1));
+    QContactCollectionId id3(makeId("b", 1));
+    QContactCollectionId id4(makeId("a", 2));
+    // note that the hash function ignores the managerUri
+    QCOMPARE(qHash(id1), qHash(id2));
+    QCOMPARE(qHash(id1), qHash(id3));
+    QVERIFY(qHash(id1) != qHash(id4));
+
+    QSet<QContactCollectionId> set;
+    set.insert(id1);
+    set.insert(id2);
+    set.insert(id3);
+    set.insert(id4);
+    QCOMPARE(set.size(), 3);
+}
+
+void tst_QContactCollection::idStringFunctions()
+{
+    // TODO: review test
+    QContactCollectionId id1(makeId("a", 1));
+    QContactCollectionId id2(makeId("a", 1));
+    QContactCollectionId id3(makeId("b", 1));
+    QContactCollectionId id4(makeId("a", 2));
+    QVERIFY(qHash(id1) == qHash(id2));
+    QVERIFY(qHash(id1) != qHash(id4));
+
+    // note that the toString and fromString functions are
+    // engine and id specific.  This test merely checks that
+    // the API is hooked up correctly.
+
+    QVERIFY(id1.toString() == id2.toString());
+    QVERIFY(id1.toString() != id3.toString());
+    QVERIFY(id1.toString() != id4.toString());
+    QVERIFY(id3.toString() != id4.toString());
+
+    // this should "work" -- string of the correct format
+    const uint numericId2 = 2u;
+    const QByteArray localId2 = QByteArray(reinterpret_cast<const char *>(&numericId2), sizeof(uint));
+    QString prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString("::") + localId2.toHex();
+    QContactCollectionId rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid == id4);
+    QVERIFY(rebuiltid.localId() == id4.localId());
+
+    // this string has the right format and one parameter, but requires a working backend
+    prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + QString("key=value") + QString(":") + localId2.toHex();
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid != id4);
+    QVERIFY(rebuiltid.localId() == id4.localId());
+
+    // this string has the right format and some parameters, but requires a working backend
+    prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=value2") + QString(":") + localId2.toHex();
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid != id4);
+    QVERIFY(rebuiltid.localId() == id4.localId());
+
+    // this string has the right format but misses the value for a parameter
+    prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=") + QString(":") + localId2.toHex();
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid != id4);
+    QVERIFY(rebuiltid.localId() == id4.localId());
+
+    // this string misses a field (the parameters)
+    prebuiltidstring = QString("qtcontacts") + QString(":") + QString("a") + QString(":") + localId2.toHex();
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null.
+
+    // this string misses two fields (params plus manager uri)
+    prebuiltidstring = QString("qtcontacts") + QString(":") + QString::number(2);
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null.
+
+    // this string misses the prefix (qtorganizer)
+    prebuiltidstring = QString("notorganizer") + QString(":") + QString("a") + QString("::") + localId2.toHex();
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null.
+
+    // this string misses the manager uri
+    prebuiltidstring = QString("notorganizer") + QString(":::") + localId2.toHex();
+    rebuiltid = QContactCollectionId::fromString(prebuiltidstring);
+    QVERIFY(rebuiltid == QContactCollectionId()); // invalid so should be null.
+}
+
+void tst_QContactCollection::hash()
+{
+    // TODO: review tests
+    QContactCollectionId id(makeId("a", 1));
+    QContactCollection c1;
+    c1.setId(id);
+    c1.setExtendedMetaData("key", "value");
+    QContactCollection c2;
+    c2.setId(id);
+    c2.setExtendedMetaData("key", "value");
+    QContactCollection c3;
+    c3.setId(id);
+    c3.setExtendedMetaData("key", "another value");
+    QContactCollection c4; // no details
+    c4.setId(id);
+    QContactCollection c5;
+    c5.setId(id);
+    c5.setExtendedMetaData("key", "value");
+    QVERIFY(qHash(c1) == qHash(c2));
+    QVERIFY(qHash(c1) != qHash(c3));
+    QVERIFY(qHash(c1) != qHash(c4));
+    QVERIFY(qHash(c1) == qHash(c5));
+}
+
+void tst_QContactCollection::datastream()
+{
+    // collection datastreaming
+    QByteArray buffer;
+    QContactCollection collectionIn;
+    collectionIn.setExtendedMetaData("key", "value");
+    QContactCollection collectionOut;
+    QContactCollectionId originalId;
+
+    // first, stream an item with a complete id
+    {
+        QDataStream stream1(&buffer, QIODevice::WriteOnly);
+        QContactManager om("memory");
+        QVERIFY(om.saveCollection(&collectionIn)); // fill in its ID
+        originalId = collectionIn.id();
+        stream1 << collectionIn;
+        QVERIFY(buffer.size() > 0);
+        QDataStream stream2(buffer);
+        stream2 >> collectionOut;
+        QCOMPARE(collectionOut, collectionIn); // can use QCOMPARE for collections, since no detail keys.
+    }
+
+    // second, stream an item with an id with the mgr uri set, local id null
+    {
+        QDataStream stream1(&buffer, QIODevice::WriteOnly);
+        collectionIn.setId(QContactCollectionId());
+        stream1 << collectionIn;
+        QVERIFY(buffer.size() > 0);
+        QDataStream stream2(buffer);
+        stream2 >> collectionOut;
+        QCOMPARE(collectionOut, collectionIn); // can use QCOMPARE for collections, since no detail keys.
+    }
+
+    // third, stream an item with a null id
+    {
+        QDataStream stream1(&buffer, QIODevice::WriteOnly);
+        collectionIn.setId(QContactCollectionId());
+        stream1 << collectionIn;
+        QVERIFY(buffer.size() > 0);
+        QDataStream stream2(buffer);
+        stream2 >> collectionOut;
+        QVERIFY(collectionOut.metaData() == collectionIn.metaData());
+        QVERIFY(collectionOut.id() == collectionIn.id()); // should both be null ids.
+    }
+
+    // id datastreaming
+    buffer.clear();
+    QContactCollectionId inputId;
+    QContactCollectionId outputId;
+
+    // first, stream the whole id (mgr uri set, local id set)
+    {
+        inputId = originalId;
+        QString serializedId = inputId.toString();
+        outputId = QContactCollectionId::fromString(serializedId);
+        QCOMPARE(inputId, outputId);
+
+        inputId = originalId;
+        buffer.clear();
+        QDataStream stream1(&buffer, QIODevice::WriteOnly);
+        stream1 << inputId;
+        QVERIFY(buffer.size() > 0);
+        QDataStream stream2(buffer);
+        stream2 >> outputId;
+        QCOMPARE(inputId, outputId);
+    }
+
+    // second, stream a null id
+    {
+        inputId = QContactCollectionId();
+        QString serializedId = inputId.toString();
+        outputId = QContactCollectionId::fromString(serializedId);
+        QCOMPARE(inputId, outputId);
+
+        inputId = QContactCollectionId();
+        buffer.clear();
+        QDataStream stream1(&buffer, QIODevice::WriteOnly);
+        stream1 << inputId;
+        QVERIFY(buffer.size() > 0);
+        QDataStream stream2(buffer);
+        stream2 >> outputId;
+        QCOMPARE(inputId, outputId);
+    }
+}
+
+void tst_QContactCollection::traits()
+{
+    QCOMPARE(sizeof(QContactCollection), sizeof(void *));
+    QVERIFY(QTypeInfo<QContactCollection>::isComplex);
+    QVERIFY(!QTypeInfo<QContactCollection>::isStatic);
+    QVERIFY(!QTypeInfo<QContactCollection>::isLarge);
+    QVERIFY(!QTypeInfo<QContactCollection>::isPointer);
+    QVERIFY(!QTypeInfo<QContactCollection>::isDummy);
+}
+
+void tst_QContactCollection::idTraits()
+{
+    QCOMPARE(sizeof(QContactCollectionId), 2*sizeof(void *));
+    QVERIFY(QTypeInfo<QContactCollectionId>::isComplex);
+    QVERIFY(!QTypeInfo<QContactCollectionId>::isStatic);
+    QVERIFY(QTypeInfo<QContactCollectionId>::isLarge);
+    QVERIFY(!QTypeInfo<QContactCollectionId>::isPointer);
+    QVERIFY(!QTypeInfo<QContactCollectionId>::isDummy);
+}
+
+QTEST_MAIN(tst_QContactCollection)
+#include "tst_qcontactcollection.moc"
diff --git a/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp b/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp
index e347c3fdd5b12ce93ac38cf4fd8273da84b5dc83..4df9a25f36cf4cacf1dbf724f4cc5830b4ea1f92 100644
--- a/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp
+++ b/tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp
@@ -55,6 +55,11 @@ static inline QContactId makeId(const QString &managerName, uint id)
     return QContactId(QStringLiteral("qtcontacts:basic%1:").arg(managerName), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint)));
 }
 
+static inline QContactCollectionId makeCollectionId(uint id)
+{
+    return QContactCollectionId(QStringLiteral("qtcontacts:basic:"), QByteArray(reinterpret_cast<const char *>(&id), sizeof(uint)));
+}
+
 class tst_QContactFilter : public QObject
 {
 Q_OBJECT
@@ -81,9 +86,12 @@ private slots:
     void canonicalizedFilter_data();
     void testFilter();
     void testFilter_data();
+    void collectionFilter();
 
     void datastream();
     void datastream_data();
+    void testDebugStreamOut();
+    void testDebugStreamOut_data();
     void traits();
 };
 
@@ -1210,6 +1218,40 @@ void tst_QContactFilter::testFilter_data()
     }
 }
 
+void tst_QContactFilter::collectionFilter()
+{
+    QContactCollectionFilter icf;
+
+    QVERIFY(icf.collectionIds().isEmpty());
+
+    QContactCollectionId id1 = makeCollectionId(5);
+    QContactCollectionId id2 = makeCollectionId(6);
+    QContactCollectionId id3 = makeCollectionId(7);
+    QContactCollectionId id4 = makeCollectionId(12);
+    QSet<QContactCollectionId> ids;
+    ids << id1 << id2 << id3;
+
+    icf.setCollectionIds(ids);
+    QVERIFY(icf.collectionIds() == ids);
+
+    icf.setCollectionId(id4);
+    ids.clear();
+    ids << id4;
+    QVERIFY(icf.collectionIds() == ids);
+
+    QContactCollectionFilter icf2;
+    icf2 = icf;
+    QVERIFY(icf2.collectionIds() == ids);
+
+    QContactFilter fil;
+    fil = icf;
+    QVERIFY(fil.type() == QContactFilter::CollectionFilter);
+
+    QContactCollectionFilter icf3(fil);
+    QVERIFY(fil.type() == QContactFilter::CollectionFilter);
+    QVERIFY(icf3.collectionIds() == ids);
+}
+
 void tst_QContactFilter::datastream()
 {
     QFETCH(QContactFilter, filterIn);
@@ -1286,6 +1328,58 @@ void tst_QContactFilter::datastream_data()
     }
 }
 
+void tst_QContactFilter::testDebugStreamOut()
+{
+    QFETCH(QContactFilter, filterIn);
+    QFETCH(QString, messageExpected);
+
+    QTest::ignoreMessage(QtDebugMsg, messageExpected.toUtf8());
+    qDebug() << filterIn;
+}
+
+void tst_QContactFilter::testDebugStreamOut_data()
+{
+    QTest::addColumn<QContactFilter>("filterIn");
+    QTest::addColumn<QString>("messageExpected");
+
+    {
+        QContactCollectionFilter filter;
+        QContactCollectionId id1 = makeCollectionId(5);
+        QContactCollectionId id2 = makeCollectionId(6);
+        QContactCollectionId id3 = makeCollectionId(7);
+        QContactCollectionId id4 = makeCollectionId(12);
+        QSet<QContactCollectionId> ids;
+        ids << id1 << id2 << id3;
+        filter.setCollectionIds(ids);
+        // Testing method setCollectionIds
+        QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::05000000), QContactCollectionId(qtcontacts:basic::06000000), QContactCollectionId(qtcontacts:basic::07000000))))";
+
+        filter.setCollectionId(id2);
+        // Testing method setCollectionId (and the related clearing of the collection)
+        QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::06000000))))";
+        filter.setCollectionId(id4);
+        // Testing again method setCollectionId (and the related clearing of the collection)
+        QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))";
+        ids.clear();
+        ids << id4;
+        // Testing again method setCollectionIds
+        QTest::newRow("collection") << (QContactFilter)filter << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))";
+
+        QContactCollectionFilter filter2;
+        filter2 = filter;
+        // Testing again method setCollectionIds on the copied filter
+        QTest::newRow("collection") << (QContactFilter)filter2 << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))";
+
+        QContactFilter fil;
+        fil = filter;
+        // Testing that the assignment/conversion went fine
+        QTest::newRow("collection") << (QContactFilter)fil << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))";
+
+        QContactCollectionFilter filter3(fil);
+        QTest::newRow("collection") << (QContactFilter)filter3 << "QContactFilter(QContactCollectionFilter(collectionIds=(QContactCollectionId(qtcontacts:basic::0c000000))))";
+    }
+}
+
 void tst_QContactFilter::traits()
 {
     QCOMPARE(sizeof(QContactFilter), sizeof(void *));
diff --git a/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp b/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp
index 7e7f8d0a06d7ec735dfc0404b3cad610e019ec7d..94395c859921c745d4604fb64bd92dd2db8e248a 100644
--- a/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp
+++ b/tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp
@@ -150,6 +150,10 @@ private slots:
     void contactType();
     void lateDeletion();
     void compareVariant();
+    void createCollection();
+    void modifyCollection();
+    void removeCollection();
+    void saveContactIntoCollections();
 
 #if defined(USE_VERSIT_PLZ)
     void partialSave();
@@ -190,6 +194,10 @@ private slots:
     void lateDeletion_data() {addManagers();}
     void testInterSectionOfIdFilters_data() {addManagers();}
     void testInterSectionOfIdAndDetailFilters_data() {addManagers();}
+    void createCollection_data() {addManagers();}
+    void modifyCollection_data() {addManagers();}
+    void removeCollection_data() {addManagers();}
+    void saveContactIntoCollections_data() {addManagers();}
 };
 
 // Helper class that connects to a signal on ctor, and disconnects on dtor
@@ -1474,15 +1482,23 @@ void tst_QContactManager::memoryManager()
     nc.setLastName("Civilian");
     c.saveDetail(&nc);
     m1.saveContact(&c);
+
+    // reset ids
     c.setId(QContactId());
+    c.setCollectionId(QContactCollectionId());
+
     QContact c2;
     QContactName nc2 = c2.detail(QContactName::Type);
     c2 = c;
     nc2.setMiddleName("Public");
     c2.saveDetail(&nc2);
+
     m2.saveContact(&c2);            // save c2 first; c will be given a higher id
     m2.saveContact(&c);             // save c to m2
+
+    // reset ids
     c.setId(QContactId());
+    c.setCollectionId(QContactCollectionId());
     nc.setSuffix("MD");
     c.saveDetail(&nc);
     m3.saveContact(&c);
@@ -1871,6 +1887,7 @@ void tst_QContactManager::signalEmission()
     qRegisterMetaType<QContactId>("QContactId");
     qRegisterMetaType<QList<QContactId> >("QList<QContactId>");
     qRegisterMetaType<QList<QContactDetail::DetailType> >("QList<QContactDetail::DetailType>");
+
     QSignalSpy spyCA(m1.data(), SIGNAL(contactsAdded(QList<QContactId>)));
     QSignalSpy spyCM(m1.data(), SIGNAL(contactsChanged(QList<QContactId>, QList<QContactDetail::DetailType>)));
     QSignalSpy spyCR(m1.data(), SIGNAL(contactsRemoved(QList<QContactId>)));
@@ -1879,8 +1896,10 @@ void tst_QContactManager::signalEmission()
     QTestSignalSink cmsink(m1.data(), SIGNAL(contactsChanged(QList<QContactId>, QList<QContactDetail::DetailType>)));
     QTestSignalSink crsink(m1.data(), SIGNAL(contactsRemoved(QList<QContactId>)));
 
+
     QList<QVariant> args;
     QList<QContactId> arg;
+    QList<QContactCollectionId> collectionIdList;
     QContact c;
     QList<QContact> batchAdd;
     QList<QContactId> batchRemove;
@@ -3209,6 +3228,153 @@ void tst_QContactManager::compareVariant()
     QVERIFY((comparison + expected) == 0);
 }
 
+void tst_QContactManager::createCollection()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+    qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>");
+    QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>)));
+    QByteArray collectionName = QUuid::createUuid().toByteArray();
+
+    // create collection
+    {
+        QContactCollection col;
+        col.setMetaData(QContactCollection::KeyName, collectionName);
+        QVERIFY(cm->saveCollection(&col));
+    }
+
+    // check "collectionsAdded" signal
+    QCOMPARE(collectionsAddedSpy.count(), 1);
+    QList<QContactCollectionId> ids = collectionsAddedSpy.takeFirst().at(0).value<QList<QContactCollectionId> >();
+    QCOMPARE(ids.count(), 1);
+
+    // query for new collection
+    {
+        QContactCollection col = cm->collection(ids.at(0));
+        QVERIFY(!col.id().isNull());
+        QCOMPARE(col.id().toString(), ids.at(0).toString());
+        QCOMPARE(col.metaData(QContactCollection::KeyName).toByteArray(), collectionName);
+    }
+}
+
+void tst_QContactManager::modifyCollection()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+    qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>");
+    QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>)));
+    QSignalSpy collectionsChangedSpy(cm.data(), SIGNAL(collectionsChanged(QList<QContactCollectionId>)));
+
+    QContactCollectionId colId;
+    QByteArray collectionName = QUuid::createUuid().toByteArray();
+
+    // save a new collection
+    {
+        QContactCollection col;
+        col.setMetaData(QContactCollection::KeyName, collectionName);
+        QVERIFY(cm->saveCollection(&col));
+        QTRY_COMPARE(collectionsAddedSpy.count(), 1);
+        colId = col.id();
+        QVERIFY(!colId.isNull());
+    }
+
+    // edit collection
+    {
+        QCOMPARE(collectionsChangedSpy.count(), 0);
+        QContactCollection col = cm->collection(colId);
+        QByteArray newCollectionName = QUuid::createUuid().toByteArray();
+        col.setMetaData(QContactCollection::KeyName, newCollectionName);
+        QVERIFY(cm->saveCollection(&col));
+
+        // check signal "collectionsChanged" fired contains the collection id
+        QTRY_COMPARE(collectionsChangedSpy.count(), 1);
+        QList<QContactCollectionId> ids = collectionsChangedSpy.takeFirst().at(0).value<QList<QContactCollectionId> >();
+        QCOMPARE(ids.at(0).toString(), colId.toString());
+
+        // check if the collection name was updated
+        QContactCollection col2 = cm->collection(colId);
+        QCOMPARE(col2.metaData(QContactCollection::KeyName).toByteArray(), newCollectionName);
+    }
+}
+
+void tst_QContactManager::removeCollection()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+    qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>");
+    QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>)));
+    QSignalSpy collectionsRemovedSpy(cm.data(), SIGNAL(collectionsRemoved(QList<QContactCollectionId>)));
+
+    QContactCollectionId colId;
+
+    // save a new collection
+    {
+        QContactCollection col;
+        QByteArray collectionName = QUuid::createUuid().toByteArray();
+        col.setMetaData(QContactCollection::KeyName, collectionName);
+        QVERIFY(cm->saveCollection(&col));
+        QTRY_COMPARE(collectionsAddedSpy.count(), 1);
+        colId = col.id();
+    }
+    QList<QContactCollection> collections = cm->collections();
+
+    // remove collection
+    cm->removeCollection(colId);
+
+    // check "collectionsRemoved" signal
+    QTRY_COMPARE(collectionsRemovedSpy.count(), 1);
+    QList<QContactCollectionId> ids = collectionsRemovedSpy.takeFirst().at(0).value<QList<QContactCollectionId> >();
+    QCOMPARE(ids.at(0).toString(), colId.toString());
+
+
+    // check if the correct collection was removed
+    QList<QContactCollection> collectionsAfterRemoval = cm->collections();
+    QCOMPARE(collections.count() - 1, collectionsAfterRemoval.count());
+    Q_FOREACH (const QContactCollection &col, collectionsAfterRemoval) {
+        collections.removeAll(col);
+    }
+    QCOMPARE(collections.count(), 1);
+    QCOMPARE(collections.at(0).id().toString(), colId.toString());
+}
+
+void tst_QContactManager::saveContactIntoCollections()
+{
+    QFETCH(QString, uri);
+    QScopedPointer<QContactManager> cm(QContactManager::fromUri(uri));
+
+    qRegisterMetaType<QList<QContactCollectionId> >("QList<QContactCollectionId>");
+    QSignalSpy collectionsAddedSpy(cm.data(), SIGNAL(collectionsAdded(QList<QContactCollectionId>)));
+    QByteArray collectionName = QUuid::createUuid().toByteArray();
+    QContactCollectionId colId;
+    QContactId cId;
+
+    // create collection
+    {
+        QContactCollection col;
+        col.setMetaData(QContactCollection::KeyName, collectionName);
+        QVERIFY(cm->saveCollection(&col));
+        QTRY_COMPARE(collectionsAddedSpy.count(), 1);
+        colId = col.id();
+    }
+
+    // create contact
+    {
+        QContact c = createContact("Alice", "Last", "12345");
+        c.setCollectionId(colId);
+        cm->saveContact(&c);
+        cId = c.id();
+    }
+
+    // query new contact and check for collection
+    {
+        QContact c = cm->contact(cId);
+        QCOMPARE(c.collectionId().toString(), colId.toString());
+    }
+}
+
 void tst_QContactManager::compareVariant_data()
 {
     QTest::addColumn<QVariant>("a");
diff --git a/tests/auto/contacts/qmlcontacts/qmlcontacts.pro b/tests/auto/contacts/qmlcontacts/qmlcontacts.pro
index 540543d768f27cd09467c1a598bac0ee28851489..0a918772668826cdd79e2db051bb483b0ac842a5 100644
--- a/tests/auto/contacts/qmlcontacts/qmlcontacts.pro
+++ b/tests/auto/contacts/qmlcontacts/qmlcontacts.pro
@@ -12,8 +12,10 @@ OTHER_FILES += \
     testcases/ContactsSavingTestCase.qml \
     testcases/ContactsSignalingTestCase.qml \
     testcases/ContactsTestHelper.qml \
+    testcases/tst_collection.qml \
     testcases/tst_contact_add_detail.qml \
     testcases/tst_contact_addresses.qml \
+    testcases/tst_contact_collection_filter.qml \
     testcases/tst_contact_detail_access.qml \
     testcases/tst_contactdetail.qml \
     testcases/tst_contact_emails.qml \
diff --git a/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml b/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml
index bc2911178398e398928e6c118f5fb1aac021d027..b5e2ded90015ca6c9287a68fab8afb8cd3241ca2 100644
--- a/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml
+++ b/tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml
@@ -48,6 +48,7 @@ TestCase {
     id: contactsSavingTestCase
 
     property SignalSpy spy
+    property SignalSpy collectionSpy
     property bool debug: false
 
     ContactsTestConfiguration {
@@ -69,6 +70,15 @@ TestCase {
                     contactsSavingTestCase);
         spy.target = model;
         spy.signalName = "contactsChanged";
+
+        collectionSpy = Qt.createQmlObject(
+                    "import QtTest 1.0;" +
+                    "SignalSpy {" +
+                    "}",
+                    contactsSavingTestCase);
+        collectionSpy.target = model
+        collectionSpy.signalName = "collectionsChanged"
+
         return spy;
     }
 
@@ -83,6 +93,13 @@ TestCase {
         spy.wait();
     }
 
+    // Verify that the collectionsChanged signal is emitted
+    function waitForCollectionsChanged() {
+        logDebug("waitForCollectionsChanged");
+        collectionSpy.wait();
+    }
+
+
     // Wait until duration has elapsed, or the contactsChanged signal is emitted
     function waitUntilContactsChanged(duration) {
         logDebug("waitUntilContactsChanged");
diff --git a/tests/auto/contacts/qmlcontacts/testcases/tst_contact_collection_filter.qml b/tests/auto/contacts/qmlcontacts/testcases/tst_contact_collection_filter.qml
new file mode 100644
index 0000000000000000000000000000000000000000..074adb52affa11e071ce8712abdaba4b05c6e7e5
--- /dev/null
+++ b/tests/auto/contacts/qmlcontacts/testcases/tst_contact_collection_filter.qml
@@ -0,0 +1,161 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2015 Canonical Ltd
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtTest 1.0
+import QtContacts 5.0
+
+ContactsSavingTestCase {
+    id: testcase
+    name: "Contacts collection filter test"
+
+    property string defaultCollectionId: ""
+
+    ContactModel {
+        id: model
+        manager: getManagerUnderTest()
+        autoUpdate: true
+    }
+
+    Collection {
+        id: testCollection
+        name: 'My collection filter test'
+        description: 'collection filter test'
+    }
+
+    Contact {
+        id: contact1;
+        Name {
+            firstName: "A"
+        }
+        PhoneNumber {
+            number: "1111111111"
+        }
+    }
+
+    Contact {
+        id: contact2;
+        Name {
+            firstName: "B"
+        }
+        PhoneNumber {
+            number: "2222222222"
+        }
+    }
+
+    Contact {
+        id: contact3;
+        Name {
+            firstName: "John Joe"
+        }
+        PhoneNumber {
+            number: "3333333333"
+        }
+    }
+
+    function createCollectionFilter(ids)
+    {
+        var filter = Qt.createQmlObject(
+                    "import QtContacts 5.0;" +
+                    "CollectionFilter {}",
+                    testcase);
+
+        filter.ids = ids
+        return filter;
+    }
+
+    function initTestCase() {
+        initTestForModel(model);
+        waitUntilContactsChanged();
+        compare(model.collections.length, 1);
+        defaultCollectionId = model.collections[0].collectionId
+        // The wait is needed so the model is populated
+        // (e.g. with garbage left from previous test runs)
+        // before cleanup() is called.
+        emptyContacts(model);
+        model.saveCollection(testCollection)
+        waitForCollectionsChanged();
+        compare(model.collections.length, 2);
+        model.saveContact(contact1);
+        waitForContactsChanged();
+        compare(contact1.collectionId, defaultCollectionId);
+        contact2.collectionId = testCollection.collectionId
+        model.saveContact(contact2);
+        waitForContactsChanged();
+        contact3.collectionId = testCollection.collectionId
+        model.saveContact(contact3);
+        waitForContactsChanged();
+        compare(model.contacts.length, 3);
+    }
+
+    function test_collectionFilter(data) {
+        var filterDefaultCollection = createCollectionFilter([defaultCollectionId]);
+        model.filter = filterDefaultCollection;
+        waitForContactsChanged();
+        compare(model.contacts.length, 1);
+
+        var filterOnlyNewCollection = createCollectionFilter([testCollection.collectionId]);
+        model.filter = filterOnlyNewCollection;
+        waitForContactsChanged();
+        compare(model.contacts.length, 2);
+
+        var filterNewCollectionAndDefaultCollection = createCollectionFilter([defaultCollectionId, testCollection.collectionId])
+        model.filter = filterNewCollectionAndDefaultCollection;
+        waitForContactsChanged();
+        compare(model.contacts.length, 3);
+
+        var filterEmpty = createCollectionFilter([])
+        model.filter = filterEmpty;
+        waitForContactsChanged();
+        compare(model.contacts.length, 0);
+
+        var filterValidAndInvalidIds = createCollectionFilter([defaultCollectionId, "12345678", testCollection.collectionId])
+        model.filter = filterValidAndInvalidIds;
+        waitForContactsChanged();
+        compare(model.contacts.length, 3);
+
+        var filterWithInvalidId = createCollectionFilter(["12345678"])
+        model.filter = filterWithInvalidId;
+        waitForContactsChanged();
+        compare(model.contacts.length, 0);
+    }
+}