Implementing an Exchange ActiveSync client: folder synchronization

Summary:   This article examines the implementation of folder synchronization in an Exchange ActiveSync client.

Applies to: Exchange Server 2010 | Exchange Server 2010 Service Pack 1 (SP1) | Visual Studio

In this article
Prerequisites
Planning the implementation
Implementing the FolderSync command
Implementing the Sync command
Sample application
Digging deeper
Seeing it in action
Additional resources

By Jason Johnston

Exchange ActiveSync is a collection of protocols that enables mobile devices to synchronize and exchange messaging objects such as email, contacts, or calendar items with a server. The Microsoft Exchange Server protocol documentation includes a number of Exchange ActiveSync protocol specifications that provide the information you need to implement a fully-functional Exchange ActiveSync client or server. This article is the third in a series of articles that will provide detailed guidance for implementing an Exchange ActiveSync client by using the Exchange ActiveSync protocols. In this article, we will examine the implementation of folder synchronization in an Exchange ActiveSync client. Our discussion will leverage information in the following Exchange Server protocol specifications:

Note

The complete set of Exchange ActiveSync protocol specifications is available in the MSDN Library. If you’re new to using the Exchange Server Open Specifications, I’d highly recommend that you review Exploring the Microsoft Exchange Server Open Specifications for information about how the specifications are named and structured, and for tips that’ll help you derive maximum benefit from using the specifications.

Prerequisites

While the Exchange Server protocols can be implemented with any development tools and on any platform, this sample relies on the following items.

Item

Tool/Platform

Development environment

Visual Studio 2010 Professional (using C#)

Platform

.NET Framework version 4

Exchange ActiveSync server

Exchange Server 2010 Service Pack 1 (SP1)

Because we are using Exchange Server 2010 SP1 as the server, we will focus on Exchange ActiveSync version 14.1. This sample will not include any special handling for previous Exchange ActiveSync versions.

Planning the implementation

Synchronization in Exchange ActiveSync consists of downloading changes from the server and uploading client-side changes to the server. This is broken down into two major categories: folders and items. Within each category, this is broken down further into adds, deletes, and updates. This sample will focus on a basic sync scenario: downloading changes from the server.

In order to synchronize, we must first download the folder hierarchy from the server. This will give us the available folders (such as Inbox, Calendar, and so on), as well as how they are arranged in the hierarchy. This will require our implementation to be able to save state in the form of a synchronization key for the hierarchy.

Once the hierarchy is synchronized, we can synchronize the items inside those folders. Since each folder is synchronized independently of other folders in the hierarchy, we need to be able to save state on a per-folder basis. Again, we will need to save a synchronization key for the folder. Additionally, there are a number of options that can be specified at the folder level for synchronization, including body format, rights management support, and how far back to synchronize (days, weeks, and so on). For convenience, it makes sense to save these options as part of the folder state so we don't have to decide what to do for every synchronization.

Note

The implementation discussed in this article builds on the sample implementation in Implementing an Exchange ActiveSync client: provisioning. That sample will provide the transport and provisioning support for our FolderSync and Sync commands.

Implementing the FolderSync command

In order to download the hierarchy from the server, clients need to implement the FolderSync command. This command retrieves any adds, deletes, or updates to the hierarchy since the last synchronization. In order for a client to implement the FolderSync command, it must be able to do the following:

  • Generate and send FolderSync requests to the server and receive responses.

  • Parse hierarchy data contained in responses and maintain state in the form of the current hierarchy layout and the synchronization key for the hierarchy.

Implementing the Sync command

In order to download items contained in folders from the server, clients need to implement the Sync command. This command retrieves any adds, deletes, or updates to the contents of a folder since the last synchronization. It also allows clients to upload any adds, deletes, or updates made on the client to the server. However, for this implementation, we will limit the scope of the Sync command to downloading adds only. In order for a client to implement the Sync command, it must be able to do the following:

  • Generate and send Sync requests to the server and receive responses.

  • Parse responses from the server and maintain state in the form of a list of the current items contained within a given folder and the synchronization key for the folder.

Additionally, this implementation will save synchronization options along with the state for the folder, though this isn't strictly required.

Sample application

The sample application discussed in this article can be found in the MSDN Sample Gallery, Exchange 2010: Implementing Exchange ActiveSync Folder Sync. Let's take a look at the classes implemented in the sample.

Class Name

Description

ASFolderSyncRequest

This class extends the ASCommandRequest class discussed in Implementing an Exchange ActiveSync client: the transport mechanism to add functionality specific to the FolderSync command.

ASFolderSyncResponse

This class extends the ASCommandResponse class discussed in Implementing an Exchange ActiveSync client: the transport mechanism to add functionality specific to the FolderSync command. It also uses the Folder class to maintain the state of the folder hierarchy.

Folder

This class represents a folder in the user's mailbox. It maintains state for the folder including the current synchronization key and a folder in the local file system to store downloaded items. It implements functionality to add, delete, or update folders within a hierarchy.

ASSyncRequest

This class extends the ASCommandRequest class to add functionality specific to the Sync command.

ASSyncResponse

This class extends the ASCommandResponse class to add functionality specific to the Sync command. It uses the ServerSyncCommand class to extract any Adds from the server's response.

ServerSyncCommand

This class represents a single command in a Sync response from the server. The types of commands that are possible are add, delete, update, and soft delete.

Digging deeper

In this sample, we build on the functionality implemented in the examples in Implementing an Exchange ActiveSync client: the transport mechanism and Implementing an Exchange ActiveSync client: provisioning. By extending the ASCommandRequest and ASCommandResponse classes and re-using the ASProvisionRequest and ASProvisionResponse classes, we can focus on implementing the synchronization-specific functionality.

Folder class

The Folder class is designed to be used in a hierarchical manner. Each Folder object has a SubFolders property, which is a .NET List of Folder objects that represent the subfolders for that folder. The Folder class is also designed to represent the "root folder" of a mailbox, which serves as a container for the highest-level folders that Exchange ActiveSync sends to the client, such as the inbox and the default calendar.

The Folder class implements functionality to persist the hierarchy as a hierarchy of file system folders. It also saves metadata about the folders in the hierarchy in XML format in the FolderInfo.xml file. It can also initialize a hierarchy from the FolderInfo.xml file, allowing the sample client to re-use state when being run repeatedly.

ServerSyncCommand Class

The ServerSyncCommand represents a single child element of the Commands element in a Sync response. A Sync response typically contains a single Collection element for each folder that was synchronized in the request. Any changes to that folder are represented by an Add, Delete, Change, or SoftDelete element underneath the Commands element. The ServerSyncCommand class was designed to be able to represent any of the four types of changes. However, this sample only implements the Add functionality.

The ServerSyncCommand class exposes the type of change, the server id of the item, the class of the item, and the XML fragment from the response that represents the change.

Requests

The ASFolderSyncRequest class implements FolderSync command requests. The FolderSync command request format is fairly simple, with the only parameter being the synchronization key.

The ASSyncRequest class implements Sync command requests. Unlike the FolderSync command request format, the Sync request format is fairly complex. The Sync command request has a few parameters to specify options that apply to the entire Sync command, but also supports parameters that are per-folder. Additionally, the Sync command request can include adds, deletes, and updates that the client needs to upload to the server. This implementation does not include support for uploading changes, so this class provides no functionality in that regard.

The ASSyncRequest class takes a List of Folder objects and will build a Sync command request that includes those folders. It uses the current synchronization key and options set on each Folder to build the request.

Responses

The ASFolderSyncResponse class implements FolderSync responses. The UpdateFolderTree method takes the Folder object that represents the root folder and updates the entire hierarchy based on the information in the FolderSync response.

The ASSyncResponse class implements Sync responses. The GetSyncKeyForFolder method allows the client to extract the updated synchronization key for a given folder. The GetServerAddsForFolder method allows the client to retrieve a List of ServerSyncCommand objects that represent the Add element nodes for a given folder.

Seeing it in action

The sample application in the MSDN Sample Gallery shows how to use the ASFolderSync, ASFolderResponse, ASSyncRequest, and ASSyncResponse classes to synchronize the folder hierarchy and download the items in the inbox. The following output is generated by the sample.

Note

Output pertaining to the OPTIONS request and provisioning that is generated by the sample have been removed for clarity. Example output pertaining to OPTIONS requests and provisioning can be found in the previous articles in the series.

Sending FolderSync request...
FolderSync Request:
<?xml version="1.0" encoding="utf-8"?>
<folderhierarchy:FolderSync xmlns:folderhierarchy="FolderHierarchy">
  <folderhierarchy:SyncKey>0</folderhierarchy:SyncKey>
</folderhierarchy:FolderSync>
+++ HIT ENTER TO CONTINUE +++

FolderSync Response:
<?xml version="1.0" encoding="utf-8"?>
<FolderSync xmlns="FolderHierarchy">
  <Status>1</Status>
  <SyncKey>1</SyncKey>
  <Changes>
    <Count>19</Count>
    <Add>
      <ServerId>1</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Calendar</DisplayName>
      <Type>8</Type>
    </Add>
    <Add>
      <ServerId>2</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Contacts</DisplayName>
      <Type>9</Type>
    </Add>
    <Add>
      <ServerId>3</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Deleted Items</DisplayName>
      <Type>4</Type>
    </Add>
    <Add>
      <ServerId>6</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Drafts</DisplayName>
      <Type>3</Type>
    </Add>
    <Add>
      <ServerId>7</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Inbox</DisplayName>
      <Type>2</Type>
    </Add>
    <Add>
      <ServerId>11</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Journal</DisplayName>
      <Type>11</Type>
    </Add>
    <Add>
      <ServerId>12</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Junk E-Mail</DisplayName>
      <Type>12</Type>
    </Add>
    <Add>
      <ServerId>13</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Mingle</DisplayName>
      <Type>14</Type>
    </Add>
    <Add>
      <ServerId>14</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Notes</DisplayName>
      <Type>10</Type>
    </Add>
    <Add>
      <ServerId>15</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Outbox</DisplayName>
      <Type>6</Type>
    </Add>
    <Add>
      <ServerId>16</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>RSS Feeds</DisplayName>
      <Type>12</Type>
    </Add>
    <Add>
      <ServerId>17</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Sent Items</DisplayName>
      <Type>5</Type>
    </Add>
    <Add>
      <ServerId>18</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Suggested Contacts</DisplayName>
      <Type>14</Type>
    </Add>
    <Add>
      <ServerId>19</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Sync Issues</DisplayName>
      <Type>12</Type>
    </Add>
    <Add>
      <ServerId>20</ServerId>
      <ParentId>19</ParentId>
      <DisplayName>Conflicts</DisplayName>
      <Type>12</Type>
    </Add>
    <Add>
      <ServerId>21</ServerId>
      <ParentId>19</ParentId>
      <DisplayName>Local Failures</DisplayName>
      <Type>12</Type>
    </Add>
    <Add>
      <ServerId>22</ServerId>
      <ParentId>19</ParentId>
      <DisplayName>Server Failures</DisplayName>
      <Type>12</Type>
    </Add>
    <Add>
      <ServerId>23</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>Tasks</DisplayName>
      <Type>7</Type>
    </Add>
    <Add>
      <ServerId>RI</ServerId>
      <ParentId>0</ParentId>
      <DisplayName>RecipientInfo</DisplayName>
      <Type>19</Type>
    </Add>
  </Changes>
</FolderSync>
+++ HIT ENTER TO CONTINUE +++

Response Status: 1
Inbox found, Id: 7, Sync Key: 0, Last sync: 1/1/0001 12:00:00 AM

Sending initial Sync request to prime the sync state for Inbox...
Response status: 1
Initial Sync Request:
<?xml version="1.0" encoding="utf-8"?>
<airsync:Sync xmlns:airsync="AirSync">
  <airsync:Collections>
    <airsync:Collection>
      <airsync:SyncKey>0</airsync:SyncKey>
      <airsync:CollectionId>7</airsync:CollectionId>
    </airsync:Collection>
  </airsync:Collections>
</airsync:Sync>
+++ HIT ENTER TO CONTINUE +++

Initial Sync Response:
<?xml version="1.0" encoding="utf-8"?>
<Sync xmlns="AirSync">
  <Collections>
    <Collection>
      <SyncKey>866776076</SyncKey>
      <CollectionId>7</CollectionId>
      <Status>1</Status>
    </Collection>
  </Collections>
</Sync>
+++ HIT ENTER TO CONTINUE +++

Sending Inbox Sync request...
Response status: 1
Inbox Sync Request:
<?xml version="1.0" encoding="utf-8"?>
<airsync:Sync xmlns:airsync="AirSync">
  <airsync:Collections>
    <airsync:Collection>
      <airsync:SyncKey>866776076</airsync:SyncKey>
      <airsync:CollectionId>7</airsync:CollectionId>
      <airsync:ConversationMode>1</airsync:ConversationMode>
      <airsync:Options>
        <airsyncbase:BodyPreference xmlns:airsyncbase="AirSyncBase">
          <airsyncbase:Type>2</airsyncbase:Type>
          <airsyncbase:TruncationSize>500</airsyncbase:TruncationSize>
          <airsyncbase:Preview>100</airsyncbase:Preview>
        </airsyncbase:BodyPreference>
      </airsync:Options>
    </airsync:Collection>
  </airsync:Collections>
  <airsync:HeartbeatInterval>60</airsync:HeartbeatInterval>
</airsync:Sync>
+++ HIT ENTER TO CONTINUE +++

Inbox Sync Response:
<?xml version="1.0" encoding="utf-8"?>
<Sync xmlns="AirSync" xmlns:email="Email" xmlns:airsyncbase="AirSyncBase" xmlns:
email2="Email2">
  <Collections>
    <Collection>
      <SyncKey>1992475662</SyncKey>
      <CollectionId>7</CollectionId>
      <Status>1</Status>
      <Commands>
        <Add>
          <ServerId>7:1</ServerId>
          <ApplicationData>
            <email:To>"Jason Carlson" &lt;jason@contoso.com&gt;</email:To>
            <email:From>"Sanjay Shah" &lt;sanjay@contoso.com&gt;</email:From
>
            <email:Subject>Test Mail</email:Subject>
            <email:DateReceived>2012-06-20T19:13:48.827Z</email:DateReceived>
            <email:DisplayTo>Jason Carlson</email:DisplayTo>
            <email:ThreadTopic>Test Mail</email:ThreadTopic>
            <email:Importance>1</email:Importance>
            <email:Read>0</email:Read>
            <airsyncbase:Body>
              <airsyncbase:Type>2</airsyncbase:Type>
              <airsyncbase:EstimatedDataSize>319</airsyncbase:EstimatedDataSize>

              <airsyncbase:Data>&lt;html dir="ltr"&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;
&lt;style id="owaParaStyle"&gt;
&lt;!--
p
        {margin-top:0px;
        margin-bottom:0px}
--&gt;
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div style="direction:ltr; font-family:Tahoma; color:#000000; font-size:10pt"
&gt;Hello World!&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</airsyncbase:Data>
              <airsyncbase:Preview>Hello World!</airsyncbase:Preview>
            </airsyncbase:Body>
            <email:MessageClass>IPM.Note</email:MessageClass>
            <email:InternetCPID>28591</email:InternetCPID>
            <email:Flag />
            <email:ContentClass>urn:content-classes:message</email:ContentClass>

            <airsyncbase:NativeBodyType>2</airsyncbase:NativeBodyType>
            <email2:ConversationId><![CDATA[96198F80F06044EDA67815EB92B45573]]><
/email2:ConversationId>
            <email2:ConversationIndex><![CDATA[CD4F18CF13]]></email2:Conversatio
nIndex>
            <email:Categories />
          </ApplicationData>
        </Add>
      </Commands>
    </Collection>
  </Collections>
</Sync>
+++ HIT ENTER TO CONTINUE +++

Adding 1 items...
+++ HIT ENTER TO EXIT +++

Additional resources

Now that we understand how to do basic synchronization, we can move on to more advanced synchronization. The next article in this series will look at implementing support for updates, deletes, and soft deletes from the server, as well as uploading changes from the client to the server.

See Also

Other Resources

[MS-ASCMD]: ActiveSync Command Reference Protocol Specification

[MS-ASAIRS]: ActiveSync AirSyncBase Namespace Protocol Specification

Implementing an Exchange ActiveSync client: provisioning

Implementing an Exchange ActiveSync client: the transport mechanism

Code Sample - Exchange 2010: Implementing Exchange ActiveSync Folder Sync