For those who haven’t read Part 1 of our Network Operations Centre (NOC) Build guide, it’s a good intro to what we’re doing and why we’ve gone down the Android route. In this article, we’ll go through in detail what was required to modify the firmware and what software is running.
To drive our displays, we chose some generic Android dongles based on the Rockchip RK3188 ARM based CPU. This is a quad core 1.6GHz chip which can drive a 1080p display. We chose these over the Google Chromecast devices (which we also trialled and loved) simply for the 1080p resolution. Once Google add full high-definition support for websites (it only supports 1080p for video at present), it’d be perfect!
Gaining Root Access
Firstly, we wanted to gain “root” access to these so that we could control them remotely and install some low level software which isn’t normally allowed. This is the procedure we used, which was a very quick and easy way to do so from a Linux based OS. There’s also popular Windows based tools such as Kingo and Root Genius. Both claim to be able to root any Android based device and both are simple to use.
Then, we hit a stumbling block. Despite setting the devices at 1080p, it seems that reading up on various forums the devices simply upscaled from 720p so it wasn’t a true 1080p display. Not a good start! Thankfully, knowing that the chip was capable it was just a matter of finding the right combination of firmware and kernel to achieve this.
To make matters worse, we’d managed to buy one of the rarest variants of the generic MK908III sticks with a different WiFi / Bluetooth chipset to most. The WiFi chip is based on the ESP 8089 and the Bluetooth is based on the MTK MT6622 system-on-a-chip. Despite having a common name, these boards vary from factory to factory, so there’s quite a few different combinations which exist out there. If you’re trying to do a firmware upgrade of yours, please ensure you’ve double then triple checked that you have the matching firmware. Otherwise, you may brick the device.
Fun with Firmware
Like dealing with the Synology NAS, this only further motivated the internal hacker within me to figure this out. Step one was to do a bit of reading and figure out what what others have done and and if anyone has had success. Based on a thread on a site called freaktab.com, there looked to be two options to try.
The first option was a “cyxtech_rk31 809III 8089_140509_8G2G”, being an Android 4.2.2 based build. The 140509 is a reverse time stamp (ie it was generated on 09/05/14), so it’s a fairly recent build.
Uploading the firmware was done through the Batch Tool from Rockchip. You need to hold down the flash pin before connecting via USB, which allows the firmware to be overwritten.
While this firmware worked, I couldn’t help but notice that there was a KitKat (4.4.2) build on that same thread. Curiosity got the better of me and I had to try it. We had a successful boot, but despite the stick now outputting 1080p, it was still upscaling the UI from 720p.
Yet more scouring the web was required, I knew the chip was obviously capable but couldn’t figure out why it would be upscaling instead of displaying a native 1080p interface. The good thing was, I wasn’t the first to have had this issue. Within a few minutes, I’d found a setting in build.prop to tweak:
video.use.overlay=0
From previous tinkering with Android phones, I knew that the build.prop file contained a lot of the core system configuration. We installed Build Prop Editor to make the editing easier. Rebooting the device after this has been applied (you’ll need root access) and we now have native 1080p!
More Software
The next step was to fix the issue with the time sync. By default, Kibana based displays use the browser’s time to fetch and display information. This meant that unless the internal clock was accurate, the data displayed wouldn’t be accurate.
To fix this, we used Clock Sync. Again, this requires root access so ensure you’ve got this before installing. This little app works well, and handy for us it has the option to sync on boot as well. We’ve then configured it to manually sync each hour, as I’m wary about the accuracy of the internal clocks.
Next was working out a neat way to display web browsers automatically. While both the inbuilt browser and Google Chrome work well, getting them to automatically start in full screen and hide the address bar is tricky. We also wanted the ability to change the URL displayed without too much hassle. This started to become fiddly very quickly.
When in doubt, roll your own
We had tested a few different solutions, but none of them seemed to suit our exact needs. So, when in doubt roll your own! It’d been a while since I’d written any Java and my Android development skills certainly hadn’t progressed any further than tinkering with Hello World type applications. I was however keen to try the new Android Studio, especially since it was based off the same IDE as PyCharm (which I use on a day-to-day basis).
The basic concept was very simple, and in fact one of the project templates within Android Studio contained a full screen application template. The first step was to replace the default text widget with the WebView widget. This was simple enough to do.
The next step was to work out a method of loading the URL from a remote JSON call. I’m very used to the Python world, where some of these things are simply taken care of either by standard libraries or through the use of easy to use third party libraries.
Firstly, you can’t put blocking calls in the main activity loop. While it means more coding, this at least makes sense. There’s nothing more frustrating than having a GUI hang because the action isn’t asynchronous. I used the AsyncTask to do this, and displayed a ProcessDialog while this was retrieving. In my case, this is a blocking activity anyway as I can’t load the website without knowing the URL. I do this via an override on the onPreExecute function like this:
@Override public void onPreExecute() { mProgress = new ProgressDialog(mContext); mProgress.setMessage("Getting Config...."); mProgress.show(); }
I’ve probably done things the hard way and I’m sure there’s a few Android developers out there face palming when they see the implementation, but what I saw as a simple task quickly became convoluted. Firstly, I had to create a HTTP Client. Then, I needed to create the HTTP Get to call. Then, I had to create an InputStream. With this InputStream, I then needed to create a BufferedReader. Then with this BufferedReader I then had to create a StringBuilder. Then, I had to read the string into the StringBuilder. Finally, with the String from the StringBuilder could then pass it through a JSON parser. Phew!
In Python, this is a single line via Requests. But anyway, until there’s a decent way to run Python under Android, I’ll have to stick with Java. Back to the matter at hand. In order to address multiple devices, we get the device ID in the URL call so that it’s unique. This at least was quite simple:
String android_id = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID)
We made this call to our ElasticSearch server, since it natively talks JSON and we can re-configure on the fly via the REST API. The GET URI would therefore look like:
https://elasticsearchserver:9200/quickdash/XXXXDEVICEIDXXXXX/1
Which returns JSON data like this:
{"_index": "quickdash", "_type": "XXXXDEVICEIDXXXXX", "_id": "1", "_version": 2, "found": true, "_source": { "url": "https://<HERESMYURL>" }}
Once this async call is complete, onPostExecute is called and I load the URL and close the ProgressDialog. Here’s what it looks like:
@Override protected void onPostExecute(JSONObject data) { try { String url = data.getJSONObject("_source").get("url").toString(); Log.d("Loading URL", url); OpenURL(url); } catch (JSONException e) { e.printStackTrace(); } mProgress.dismiss(); }
From here, we leave it up to the webpage itself to refresh as required. This works well for Kibana, Grafana and PRTG based maps. For anyone who wants to copy and use the code I have, I’ve published it on GitHub here. Please read the Readme for an overview, I’m not a native Java programmer nor Android developer so I’m sure there’s plenty of mistakes and sections which don’t fit best practices! However, for anyone in the same position, at least you either have a starting point or know what not to do.
Conclusion
And that’s it! The system has been in use for a month now and switched on and off daily. We’ve changed the URL calls a few times by simply modifying the data stored on the ElasticSearch server and simply power cycling the TV to apply. Although this isn’t as neat as a system which dynamically monitors for changes, it’s simple and effective.
The sticks themselves have been perfectly stable, any small occasional glitch (like time not syncing) we just power cycle the TV to reboot it all.
Any feedback or suggestions are welcome too, please feel free to leave them in the comments below.
Update: Don’t forget to check out Part 3, which covers the data collection via Elasticsearch / Logstash / Kibana as well as other tools.