This article is compatible with the latest version of Silverlight for Windows Phone 7.
This is part 7 from the article series on Windows Phone 7.
Since you have started for the first time your brand new Windows Phone 7, no matter the brand of your device, it immediately appeared clear that, once the device has been configured for your carrier, it is almost always connected to the network in a way you have very few control on. You for sure can switch the network on or off, and you can ask your phone to avoid calls when the roaming is active, but once the conditions for the connection are in place your phone is able to connect and disconnect to the network very easily and fastly.
From the point of view of the applications you have to develop it is a perfect condition to the misuse of the connection and the write of software that makes an heavy use of the network but unfortunately since the connection is a pay-per-service this can lead to lot of hidden costs the user will discover only when the bill comes. So, the practice of continuously poll the network to retrieve updates and give notifications to the user is something strongly discouraged due to the fact that no matter how small will be the message you exchange with the server (and since you have to use http connections it cannot be so small as it could be) on a long time it can generate unuseful costs because most of the times the message is exchanged only to know it there is nothing new.
So, given that we have an always-connected device, it is silly do not take advantage of this connection to be continuously notified about things that happen: a mail is arrived, a tweet has been posted, an earthquake has shaked a place, a road accident happened. There are plenty of opportunities from the fact your connection is always on and this is the reason the team behind Windows Phone 7 decided to implement a Push Notification protocol embedded in the operation system that is able to take advantage of the connection without flood your bill of unuseful costs.
How the magic works
Despite the title of this paragraph, and despite the Arthur C. Clarke's law that say "Any sufficiently advanced technology is indistinguishable from magic", there is not any magic behind the Push Notifications, but only a good idea. The point is that, once the phone has been connected to the network, what really generates costs are the bytes moved around by network calls and not the connection itself. So, if you connect to the network and do not make any call, the sole costs are probably generated by the bytes trasferred to establish the connection (if there are any). From this situation you have acquired an address on the network and until you will do not drop the connection, this address will remain the same. So, with few little tricks, it is possible to change the role of the device from client to server and create a service that is able to send something to the phone without the need that the phone initiates the call.
In a push notification scenario there are three actors involved. Obviously the leading actor is the phone that initially establishes the connection and then receives the notifications. The second part in this comedy is taken by a service that runs somewhere and needs to call the phone to notify that something happened. In the WP7 push notifications these two actors cannot communicate directly but there is the need of another service, called Microsoft Push Notification Service (MPNS) that is able to collect request from the service and routes them to the phone. The MPNS is a service hosted by Azure.
Let me make a real world sample: imagine you have written an Earthquake notification service. The service is able to send notifications to the phones when a shake happens. Here is the flow of the events, starting from when the phone connect to the network and register to the service. Click on the thumbnails to open the full view:
Registration Phase 1: When the user starts the applications that supports notifications, it start the registration to the MPNS sending a request to it. The service answers to this request providing an url that uniquely identifies the device on the network. The uri is not pointed directly to the ip address of the phone but it points towards the MPNS itself. It contains an unique ID used by the MPNS to discover the device. The MPNS is the sole service that is able to contact the device directly.
Registration Phase 2: Now that the MPNS known the presence of the phone on the network, it is required the phone itself provide the Uri to the Earthquake service. Until this not happen the service doesn't know there is a phone that need to be notified so it is not able to provide the notifications. The phone must call a method on the Earthquake service to provide the Uri that has been answered by the MPNS. Once the Earthquake service gets this url it can store this informations somewhere for a later use.
Notification Phase 1: Let say now there is a shake somewhere. After the seismograph registered the event and someone added it to the earthquake database, the service knows it have to notify the phones that have been registered. It is its responsibility to pack a message (there is three type of messages but we will see this in the following) and send a POST message to the uri provided by the phone. The message is send to the MPNS in the form of a short xml message that can contain few informations about the nature of the notification.
Notification Phase 2: The message sent by the Earthquake service is received by the MPNS that is able to understand the device is the recipient. So, once the message arrives, packed into a POST http method it takes the content and routes it to the phone. The MPNS does not understand the content of the message. It is only able to detect the actual network address of the phone, interpreting the url that it has received and then forward the message without any modification. If the device is actually disconnected the MPNS will answer with an exception.
Notification Phase 3: Once the phone receives the notification it is able to show it in different ways. According with the type of notification we have sent the phone will show a toast message, change a tile background or something else. The Push Notification service is deeply integrated with the operating system so the notification can be displayed also if the application has been tombstoned. There is not any need the application runs in background. The notification is displayed and, if the users wants, the application is awaked by the tombstoning.
As I've already said there is three types of notification to send to the phone. The type you choose depends on the meaning you give to the message. If you have simply to send an update to the application you can use a Raw Notification. In this case you can send a relatively free message containing the informations to update. This message is not handled by the operation system but it is simply forwarded to the application that has to be open to receive it. In the case you need to notify the phone also if the application has been tombstoned you have to send a Tile Notification. This message contains very few data; a title, a count and the url of an image. When the phone receive the notification it update the tile of the application displaying the new image, the number and the title on the start menu. Finally, in case of very urgent notifications you have to use the Toast Notification that differently from the Tile Notification display the incoming message into a small popup on top of the screen. Also in this case the application can be tombstoned.
A comprehensive example
Now that you have got to know how the Push Notification works, it is time for give you a working example. Behind the application I've attached to this article there is a very huge number of lines of code so it is really difficult to show every single step with a number of snippets. So I will try to show you the most important things but I'm pretty sure you will find easily the answers to your questions putting your nose inside the example code. The example is an extension of the Earthquake catalog I've presented when I've spoken about Pivot and Panorama control. This time I wrote a sample service that polls the USGS feed to detect new shakes. When a shake happen, it notifies the phones using a Push Notification.
The core of the application is into the Earthquake service that has a number of responsibilities. First of all it execute a separate thread that downloads every minute the feed of the shakes from the USGS. It take a reference to the latest event and when it detect a new event it start to notify the phones. The service also exposes some method trough a self-hosted WCF service. There is two methods, subscribe and unsubscribe, that are used by the phones to add its uri to the service. When this happens the service collects the uri and saves it to a collection in memory.
Once the service detect a new event it prepares a notification. The service supports raw, tile and toast notification. It always sends a raw notification side by side with a tile or a toast notification. Particularly it choose to send a toast when the magnitude of the event is greater than 5.0. The code in the following snipped shows how it prepares the notifications:
public byte[] PrepareTile()
{
XNamespace wp = "WPNotification";
return Encoding.UTF8.GetBytes(
new XDocument(
new XDeclaration("1.0", "utf-8", "true")
,new XElement(wp + "Notification"
,new XElement(wp + "Tile"
,new XElement(wp + "BackgroundImage", "http://localhost:8000/EarthquakeService/tile")
,new XElement(wp + "Count", 0)
,new XElement(wp + "Title", this.Location)))).ToString());
}
public byte[] PrepareToast()
{
XNamespace wp = "WPNotification";
return Encoding.UTF8.GetBytes(
new XDocument(
new XDeclaration("1.0", "utf-8", "false")
,new XElement(wp + "Notification"
,new XElement(wp + "Toast"
,new XElement(wp + "Text1", "Earthquake")
,new XElement(wp + "Text2", this.Location)))).ToString());
}
public byte[] PrepareRaw()
{
return Encoding.UTF8.GetBytes(
new XDocument(
new XDeclaration("1.0", "utf-8", "false")
,new XElement("earthquake"
,new XElement("location", this.Location)
,new XElement("date", this.PublishDate)
,new XElement("magnitudo", this.Magnitudo)
,new XElement("latitude", this.Latitude)
,new XElement("longitude", this.Longitude))).ToString());
}
Thanks to the syntax of Linq to XML it is easy to guess the format of the resulting notification. There are few differences between the three notifications. The smaller is the tile notification but also the toast notification anly ask for one more string that will be displayed into the popup. The raw notification instead contains information describing the event because they will be used by the application to update a map. After the xml message is ready the service crawl the collection and sends the notifications. This operation is made using the Parallel extension to spread the load across multiple threads.
private void NotifyAll()
{
IEnumerable<Notification> data = this.PrepareNotifications();
Parallel.ForEach<Uri>(this.Subscribers, (uri) => this.Notify(data, uri));
}
private void Notify(Notification notification, Uri channelUri)
{
if (notification.Data.Length > MAX_PAYLOAD_LENGTH)
throw new ArgumentOutOfRangeException("Payload is too long. Maximum payload size shouldn't exceed " + MAX_PAYLOAD_LENGTH.ToString() + " bytes");
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
request.Method = WebRequestMethods.Http.Post;
request.ContentType = "text/xml; charset=utf-8";
request.ContentLength = notification.Data.Length;
request.Headers[MESSAGE_ID_HEADER] = Guid.NewGuid().ToString();
request.Headers[NOTIFICATION_CLASS_HEADER] = ((int)notification.Type).ToString();
if (notification.Type == NotificationType.Toast)
request.Headers[WINDOWSPHONE_TARGET_HEADER] = "toast";
else if (notification.Type == NotificationType.Tile)
request.Headers[WINDOWSPHONE_TARGET_HEADER] = "token";
request.BeginGetRequestStream(
(ar) =>
{
Stream requestStream = request.EndGetRequestStream(ar);
requestStream.BeginWrite(notification.Data, 0, notification.Data.Length,
(iar) =>
{
requestStream.EndWrite(iar);
requestStream.Close();
request.BeginGetResponse((iarr) =>
{
try
{
using (WebResponse response = request.EndGetResponse(iarr))
{
string notificationStatus = response.Headers["X-NotificationStatus"];
string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
Logger.Log("notificationStatus: {0}", notificationStatus);
Logger.Log("notificationChannelStatus: {0}", notificationChannelStatus);
Logger.Log("deviceConnectionStatus: {0}", deviceConnectionStatus);
Logger.Log("SUCCESS");
}
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
NotificationManager.Current.Unsubscribe(channelUri);
}
},
null);
},
null);
},
null);
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
Logger.Log("FAILED: {0}", ex.Message);
}
throw;
}
}
The format of the notification involve the use of a set of headers used to inform the MPNS about the nature of the message. It is sent using an HttpWebRequest and a POST method. Into the response we will find some informations about the delivery of the message to the MPNS. There is not any way to ensure that the message has been delivered to the very final destination.
From the Phone side
Now that the service is up and running we need to perform the two phases of the registration. The registration happens when the user start the application. Unfortunately there is not any way to register the phone with the service without the start of the application, also if the tile is docked to the start menu. Sincerely I have expected to have a sort of offline initialization for docked applications but it is not available. So when the user start the app first we have to contact the MPNS, get the Uri and then call the Subscribe method of the Earthquake service passing it the uri. The first part is done using and instance of the class HttpNotificationChannel. When you create an instance and call the Open method it contacts the MPNS and get the Uri on behalf of us. The retrieved uri is available in the property ChannelUri. The initialization have to take in consideration that it can not be the first time the application start. Infact the second time the channel is already established and we have only to find it by name:
public void Connect()
{
try
{
httpChannel = HttpNotificationChannel.Find(channelName);
if (null != httpChannel)
{
SubscribeToChannelEvents();
SubscribeToService();
SubscribeToNotifications();
Deployment.Current.Dispatcher.BeginInvoke(() => this.UpdateStatus("Channel recovered"));
}
else
{
httpChannel = new HttpNotificationChannel(channelName, "USGSEarthquakeService");
SubscribeToChannelEvents();
httpChannel.Open();
Deployment.Current.Dispatcher.BeginInvoke(() => this.UpdateStatus("Channel open requested"));
}
}
catch (Exception ex)
{
Deployment.Current.Dispatcher.BeginInvoke(() => UpdateStatus("Channel error: " + ex.Message));
}
}
Finally I have to push my uri to the service calling the registration method. The call is made using a rest call to simplify the interaction and I will send the uri as the sole parameter. The call is a simple request made with the WebClient class so nothing really complicated. Now that the connection is established I start receiving the notifications. Since the Tile and Toast notifications are automatically handled by the phone I only have to attach to the channel events to update the map in case of Raw notifications.
Now it is up to you
I know, it is plenty of code inside the attached projects and my explanation have only scratched the surface but having a working sample I think my explanation may suffice to fully understand how the things works. I would only add that the example includes a method on the earthquake service that shows how to update the tile with some more informations. In my example I wanted to add the magnitude of the last earthquake to the tile. This can only achieved creating the background image on the fly. The GetTile method does this work writing in the middle of the image the number and filling the tile with a colour according with the severity of the quake. Please take my example as a path for creating your own notification service but please remember that to make it working in a real world you have to improve his effectiveness with a strong tuning.
Download the source code
About the Author
Andrea Boschin is 41 years old from Italy and currently lives and works in Treviso, a beautiful town near Venice. He started to work in the IT relatively late after doing some various jobs like graphic designer and school teacher. Finally he started to work into the web and learned by himself to program in VB and ASP and later in C# and ASP.NET. Since the start of his work, Andrea found he likes to learn new technologies and take them into the real world. This happened with ASP.NET, the source of his first two MVP awards, and recently with Silverlight, that he started to use from the v1.0 in some real projects.