Time Tortoise: A Companion App

Pipes

This is one in a series of articles about Time Tortoise, a Universal Windows Platform app for planning and tracking your work schedule. For more on the development of this app and the ideas behind it, see my Time Tortoise category page.

Last week, I brought up some limitations of UWP apps, including one that will affect Time Tortoise. I also suggested the idea of a companion app that could address these limitations. This week, I’ll start exploring the companion app in more detail.

Inter-App Communication

The companion app approach works as follows: Because UWP apps have technical limitations, we create a non-UWP app to handle functionality that the UWP app doesn’t support. The UWP app communicates with the non-UWP app to get information that it can’t get on its own. For users who are unwilling (or unable due to their device capabilities) to install the companion app, the UWP app simply works without the additional functionality provided by the companion app.

A key part of the companion app approach is having a way for the UWP app and the companion app to communicate. Traditional Windows apps can take advantage of a number of ways to communicate with each other, including Windows-specific technologies such as COM and DDE, as well as UNIX favorites like pipes and sockets. For UWP apps, the choices are more limited:

  • The title of the app-to-app communication topic in the UWP developer documentation sounds promising. But it only covers user-initiated data sharing such as copy/paste, drag and drop, and the Share contract.
  • App Services is described as a way to “expose some aspect of your application in such a way that it can be called by other applications.” But it seems to apply only to communication between UWP apps, so that doesn’t help us.
  • Sockets are available for UWP apps, but communication between two apps on the same machine is restricted by default. However, there are ways for the user to enable it. So this seems like the best choice.

Sockets

A network socket is used for sending or receiving data over a network, so it’s a good choice for interprocess communication. UWP supports sockets, but in the default configuration the two processes need to be on different machines:

Network communications using an IP loopback address cannot normally be used for interprocess communication between a UWP app and a different process (a different UWP app or a desktop app) because this is restricted by network isolation.

This restriction is due to the philosophy that UWP apps should be secure by default. If a UWP app could communicate with a desktop app on the same machine, then presumably a user could be convinced to install a desktop app which, having bypassed the app store privacy vetting process, could steal data from the UWP app.

Fortunately, there is a way for the user to consent, using appropriate dialog boxes, to allo their UWP app to communicate with a desktop app. So that’s what we’re going to provide to interested users, in the form of the Time Tortoise companion app.

Socket communication between Time Tortoise and Time Tortoise Companion works as follows:

Time Tortoise Companion (server, non-UWP)

  • Create a System.Net.Sockets.TcpListener to send and receive messages on an agreed-upon port.
  • Start the listener.
  • Create a System.Net.Sockets.Socket to accept connections on the listener.
  • Read a message from Time Tortoise, the client, which is asking for data that it can’t generate itself due to the limitations of UWP.
  • Send the requested data back to Time Tortoise.

Time Tortoise (client, UWP)

  • Create a Windows.Networking.Sockets.StreamSocket to send and receive messages.
  • Create a Windows.Networking.HostName on localhost.
  • Connect the socket to the hostname using the same port as the server uses.
  • Write a request to the socket.
  • Read the result from the socket.

As expected with the client–server pattern, the server is always waiting for a request from the client. When a request arrives, the server sends a response. For example, here’s the idle time detection example from last week, implemented using this pattern:

  • The server continually monitors mouse and keyboard activity for the system as a whole, since a desktop app is allowed to do that outside of its own window.
  • The client periodically sends a message to the server requesting the current idle time.
  • The server sends the result back to the client.
  • If the idle time exceeds a threshold (generally a few minutes), the client assumes that the user has walked away from the machine, and it stops the time tracker.
  • When the idle time resets to (near) zero, the client assumes that the user has returned, and asks them what they want to do with their away time (add it to the current activity, or discard it).

The server can later be extended to inform the client of other information that the client can’t determine for itself. For example, it might read information from a file that the client (as a UWP app) doesn’t have access to.

(Image credit: Petras Gagilas)