kpToggle is a small Android application and related cloud-server, both fully developed by CaSensitive, a one-man company. This project has been developed so as to provide prospective customers an idea of what a person with a very diverse skillset can achieve, as well as provide some insights into the complexities associated with developing reliable software, which are really only mastered with experience. It should also be noted that this was not a fulltime project, rather something that was developed in freetime, so it should not be compared to something developed with any significant budget.
The game itself it is very simple, having been originally developed for the desktop and browser using Java Swing several years ago on a rainy weekend. The nature of it though lends itself very well to touchscreens, so it was decided to redevelop it for Android. If you're none the wiser, you might think this is just a case of migrating a Java source base to Android, but Android application architecture is totally different.
If you have an Android device, you can install kpToggle from the Google Play Store, otherwise you can still look at the screenshots there. For anyone with Java applets enabled in their web-browser, you can even still play the original version, but the Android app is more enjoyable and contains more features, including tutorials.
The original version defined the basic game, but if going to the effort of redeveloping it for a device that permitted local persistence, it was logical that the concept of top scores could easily be supported.
Android also permits applications to collect a lot of information about users, generally persisted on servers, but this game had no requirement for persisting user identifying data. In fact, it was to be avoided, as it just creates more security concerns, such as those reported so often in the media nowadays. It was desired though to be able to monitor how often the game is installed and how frequently it is played, so this minimal information was required to be submitted to a server.
The requirements decribed so far are not complex, but still lack a fair amount of detail, including:
With the exception of the last point, these points can be roughly abstracted into two issues:
Expanding on the first point, although kpToggle is not managing any critical data, it was still preferred to know if submitted data could be trusted. Generating a unique identifier for an application instance is trivial, but determining if messages submitted with such an id are indeed submitted by the application is a bit more complex. Of course the communications would be encrypted, but this does not make it secure. It is relatively easy for a user to compromise the security of an Android device and then focus on security of applications installed. Given the lack of critical data, the approach taken was to simply record if a device has been compromised (i.e. rooted), so that results submitted could be excluded in the future e.g. in competitions. The source for determining if devices were compromised had to be trusted though, which obviously excludes the device from being that source. The solution was to be implemented primarily in the server, obtained information from a Google-supplied service.
The following system overview diagram depicts simply the interactions between the the client app, server and Google services.
The app is fully implemented in Java, with 4 Android activities:
The tutorials activity was added after having a few people try the game. Some people figured out how the game functions after just a few clicks, but others didn't. Adding some tutorials helped address this. To reduce the effort required though, rather than implementing a fully guided tutorial, a text-based solution was implemented with pre-configured games. The main benefit of this approach was that the game code used in the tutorials is actually the same code used in the normal games - it just appears a little different due to the dimensions. This approach also does not restict users to single solutions while exploring the tutorials.
There are also some background services that manage persistence and network-related code, namely:
As mentioned above, a little complexity arises when recovering from errors. By viewing instance registration also as server state synchronisation, it was possible to abstract the state management, as a single reusable implementation using a combination of network state monitoring and timers, that are only activated when required so as not to waste resources on the mobile device. This resulted in the same code being used for recovering from all of the failure scenarios and can also reused in future applications. This resiliance in the client means that the server can be taken down temporarily e.g. for upgrading, without any implications.
The server is implemented in C++ and runs on Linux, currently within a Docker container. C++ was chosen simply so it could be run on extremely cheap hardware, originally a Raspberry Pi, but in the end, it and the database were deployed to a cloud server with an annual running cost of around 60 Swiss Francs per year.
What is perhaps a bit unusual is that all the infrastructure libraries were also provided by CaSensitive, with the exception of using OpenSSL for encryption implementation, a PostgreSQL client library and of course the standard C/C++ libraries. Although it is uncommon for small companies to develop low-level infrastructure, this was done just to show how good design can lead to the development of reusable code, as over 90% of the server code is reusable, and again to demonstrate just how much an experienced developer with a diverse skillset can achieve. Of course it could have been quicker to develop using other frameworks too, but they generally also suffer from the following issues:
It would be too lengthy to discuss the infrastructure libraries in any detail, but the following image highlights the reusable functionality provided by them.
The server implementation is fairly straight forward, once you consider how much is actually handled by the reusable infrastructure libraries. One thing perhaps worth mentioning though is that it incorporates not only a HTTPS server, but also a HTTPS client. This provides the solution of verifying that an application instance identifier is valid by forwarding a request to a service provided by Google, which also indicates in the reply, whether the security of the device has been compromised (i.e. rooted). Further queries to the service can be used to determine if an application instance is still installed.
When it came to deploying the server, rather than being tied into a proprietary cloud software service, cloud deployment of the server & database was managed using Docker and related utilites, so the choice of Linux distribution, version and database were a lot less restricted. The Docker images were built locally before deploying to Docker Hub, but they excluded any sensitive information such as Google access codes, DB passwords or certificates. This is where the modular configuration services in the infrastructure libraries really pays off, as these can then be accessed from a separate mounted volume, but the server application code is not aware of the split deployment.
That pretty much wraps up the insights into the development of kpToggle, a simple game, but required the application of many software development skills including: