Android and Speech Recognition (2)

In part 2 about speech recognition we do the reverse, instead od openly calling Google Search or integrating the speech recognition intent (prominently showing the Google logo/splash screen) we call the same recognition method in a headless way, making the experience more seamless. To make it menaingful in the aviation context, we will request BA flight information (through IAG Webservices) through audio.

(Please note, the below code snippets are incomplete and only highlighting the key methods, the complete sourcecode you find at Github, see link at the end of the post.)

Calling the speech recognizer

sr = SpeechRecognizer.createSpeechRecognizer(this);
sr.setRecognitionListener(new listener());

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);

intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,10);
sr.startListening(intent);

Implementation of the speech recognition class

class listener implements RecognitionListener
{
	public void onResults(Bundle results)
	{
		ArrayList data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < data.size(); i++)
		{
			Log.d(TAG, "result " + data.get(i));
			result.append(i + ":" + data.get(i) + "\n");
		}
		textViewResult.setText(result);
	}
}

With this approach we get the usual 2 acoustic sounds signalling the begin of the speech recognition phase and the end (after a short time out).

If we need to create a hands-free user experience, avoiding the user to touch the screen, we will make use of the play or call button that you usually find on headsets. We can capture the event that gets fired when pressing these hardware buttons too.

Capture hardware button events

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
	Log.v(TAG, event.toString());
	if(keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode==KeyEvent.KEYCODE_MEDIA_PLAY){
		Log.i(TAG, event.toString());
		listenHeadless();
		return true;
	}
	return super.onKeyDown(keyCode, event);
}

Text-To-Speech

The missing link to the hands-in-the-pocket experience is the audio response by the app through our headset. We will add the standard Android Text-To-Speech (TTS) implementation.

textToSpeech = new TextToSpeech(getApplicationContext(),null);
...
textToSpeech.setPitch(1.0f);
textToSpeech.setSpeechRate(0.80f);
int speechStatus = textToSpeech.speak(textToSpeak, TextToSpeech.QUEUE_FLUSH, null, null);

if (speechStatus == TextToSpeech.ERROR) {
	Log.e("TTS", "Error in converting Text to Speech!");
}

A remark about text to speech engines, the default speech engines are not very pleasant to listen to, for an application speaking to users repeatedly or over a long time I recommend to look for commercial TTS engines (eg. Nuance, Cereproc,..). Check out the sound samples at the end of the post. TTS engines are usually produced per language. For a multi-lingual application you need to cater for every language.

Adding Webservice

To make this sample app more business relevant (airport related), we use the spoken text to request for flight data. For this purpose I will reuse the BA webservice call that was used in an earlier post. Do not forget to add permission for internet access to our app.

We straight away hit the first challenge here, we receive a text version of the spoken request. As we wont implement a semantic or contextual speech recognition we have to apply regular expression in order to attempt to find the key elements such as fligh number and date. The human requests the information in dozens of possible variations plus the varying interpretations by the speech-to-text engine, some listed below in the regex test screen (link).
To keep it simple we allow the user to request flight info for one particular British Airways flight number between yesterday and tomorrow. We will look for a 2 character 1..4 number combination in the text, plus using the keyword yesterday/tomorrow/today (or no statement representing today).

regular expression to identify flight number

To push the scenario further we can let the TTS engine read the response to the user, after selecting relevant information from the JSON formatted WS response.

Soundsamples

Sourcecode

References

Android and Speech Recognition (1)

Speech recognition, the translation of spoken language to text, as an interdisciplinary subfield of computational linguistics finds its roots and first steps as early as 1952 with Audrey, a device that could recognize numerical digits, created in the Bell Labs, and the 1960’s when IBM developed the Shoebox, a machine that could recognize and arithmetic and sent the result to an attache printer (I highly recommend the video in the IBM archive). A lot of research happened in the field by IBM, DARPA, CMU but it would take more than 2 decades before products hit the shelf to be used by a wider audience. In 1981 it took up to 100 minutes to decode 30 seconds of spoken text (see the Sarasota Business Journal article).

The first time I started working with commerical products in this field was in the mid-end 1990’s with Dragon Dictate and IBM Via Voice, the engine had to be trained for a specific speaker in a 30min+ training session. Once you had passed the training you could achieve decent results when talking to Word using a plugin, the experience was not quite real-time, as you saw the text magically being typed few seconds after you said it. The product also allowed saying commands to control Windows to open or close windows, starting applications and similar simple tasks.

Fast forward to today, and you find speech recognition in quite some consumer applications, most prominently in the assistance area with products like Amazon Alexa, Apple Siri, Google Assistant dominating the market. If we look at the Gartner Hype Cycle 2019, reaching the Plateau of Productivity “Speech Recognition is less than two years to mainstream adoption and is predicted to deliver the most significant transformational benefits of all technologies on the Hype Cycle.” [Quote].

For many simple use-cases or applications a conversational model nor a semantic interpretation is required, we can focus on recognizing keywords. I will discuss pro and cons later in this series.

In this first part I am demonstrating how simple it is to integrate speech recognition in Android Apps. To put this into an aviation context, lets pretend we search for a flight by calling the flightnumber.

We can start with an empty activity skeleton application in Android Studio.
Required is the permission to record audio.

String requiredPermission = Manifest.permission.RECORD_AUDIO;

We need to call the Android Speech by calling the respective intent.

import android.speech.RecognizerIntent;
..
Intent i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
i.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
i.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
i.putExtra(RecognizerIntent.EXTRA_PROMPT, "Say something");
..
startActivityForResult(i, 100);

Handle the result of the activity response

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	if (requestCode == 100) {
		if (resultCode == RESULT_OK && null != data) {
			ArrayList res = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
			Log.d("TTS", res.toString());
		}
	}
}
The default Google style audio input.
Result for “Flight LH 778”

The longer the statement we speak the more results we get back in the text array.

Result for “Departure Information for flight LH 778”

We could defer the search directly into the Google Search by changing the code to another action. Here I have to ask a complete question and cannot just say the flight number. The request is diverted to the web search and wont return to the application. So this is more completeness but not helpful for our use-case.

Intent i = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
i.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
i.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());

In the next part we will omit the Google standard screen and read the audio input directly. We will also look at the further processing challenges, as well add speech synthesis.

Stay tuned !

Blockchain – Big Topic broken down to pieces

bccloud2

Blockchain is certainly not the latest buzzword any longer, it moved well forward on the Gartner Hype Cycle, passed the peak of inflated expectations and I am sure we will find it in the trough of disillusionment in the soon to be updated 2018 version. It is picked up by various industries looking for use-cases and applications. Unfortunately we are looking at Blockchain fatigue already, as there is much hype but little visible implementations outside the cryptocurrency space. I prefer projects that implement blockchain as the right tool for a particular problem over the “let’s see which business case we can throw blockchain at” approach.

In the aviation (airport) space I believe Blockchain has its appliance, but as previously stated I wont attempt to build an AODB with Blockchain as “database” for milestones just for the sake of integrating this technology. For some scenarios you certainly need immutability of data, but we can implement this with the means of other immutable data storage. It is also no point implementing a blockchain into a corporate network infrastructure with few nodes under the control of one entity, this does not fulfill the promise of distributed ledger and trust. A few use-cases that I see, usually involving multiple business parties:

  • Baggage tracking from end-to-end (goes well with IATA 753 effective since June 1st)
  • Service and contract management, billing (eg. groundhandler-airline)
  • Aircraft spare parts management (here the track of provenance have a huge impact)

Trying to understand blockchain can be overwhelming, ranging from Satoshi Nakamoto’s original whitepaper to a endless number of books, talks, websites.
One approach to understand the technology is to break it down into smaller pieces that are implementing proven existing technology or algorithms and understand how they come together eventually forming the much more complex blockchain.

I wont attempt to explain blockchain here, this is redundant, plenty of knowledgeable people have written books and articles you can refer to, but split it into some basic easy digestible portions, some coding included. Before attempting to code against real blockchain implementations, like Ethereum or Hyperledger, I will implement the most basic and simple blockchain first.

1. Hash

The most essential element of blockchain is a hash, a digital signature. A hash is a one way encryption,  once something is hashed there is no way to reverse the process and reveal the original text (decrypt it). Using the SHA (Secure Hash Algorithm), the most popular algorithm with its variants of 256, 384 and 512 bit, defined by NIST, we can convert a text (data) of any length to a 256 bit representation (for SHA256) which is represented by a 64 byte hex string. There is a number of libraries implementing the algorithm, below is the Apache DigestUtils version.

	private void testSHA(){

		String sha256hex = DigestUtils.sha256Hex("Jim Smith");
		System.out.println("\n1 SHA256: " + sha256hex);

		String sha256hex2 = DigestUtils.sha256Hex("jim smith");
		System.out.println("\n2 SHA256: " + sha256hex2);

		String sha256hex3 = DigestUtils.sha256Hex("jim smith and the lazy brown fox");
		System.out.println("\n3 SHA256: " + sha256hex3);

		String sha384hex = DigestUtils.sha384Hex("Jim Smith");
		System.out.println("\n4 SHA384: " + sha384hex);

		String sha512hex = DigestUtils.sha512Hex("Jim Smith");
		System.out.println("\n5 SHA512: " + sha512hex);
	}

resulting in

1 SHA256: 65742910cc03889474f1ee2c8f321a105603d0ae2f91070ffd95b35f8da88261
2 SHA256: bfae13266154ec3c4de5c09cf14358305e44f48d2156953723ebbb184a724499
3 SHA256: e5a4a1b8bd88eb7cf8bff9ee5dd235f87ef996262d4d0213c1387f6141ab9574
4 SHA384: c6e76ad773905c1eedb6a0bd9c0b1602a56928d1ce95d70190cd908797466b948dd342aa69dd0343251afece2e48bfc2
5 SHA512: f813c3d9deb66d4999f6839acc60eb6e2fff6a84266c02e0d4b183f5e56d9674c70b0b136f9e1388673cefbc9278f583e3a4c9803ef0c49f9af28aca60dae5ac

Important to notice:
– Change of one character in the original text produces a complete new hash.
– Independent from the length of the original text the hash has the same length.

2. Chained blocks

As the wording implies, there are blocks of information that are linked together. Sounds like a linked list, where every list entry is pointing to the next information. The chained blocks are linked differently, every block points to the hashed previous block.

For illustration I choose a typical baggage journey (simplified).

blockchain

A bag passes different key touchpoints and changes its custody a few times between the various parties during the handling. Every time there is a new milestone event we record it, eg. bag scanned by groundhandler at the chute at what time, and include the hash of the previous milestone. This way the lifecycle from bag drop at departure to bag delivery at destination is recorded in an immutable way and cannot be changed afterwards.

Pitfall: The bag journey is recorded in an immutable way, but the blockchain cannot verify or confirm the milestone actually happened. This falls into the responsibility of the overall design and service orchestration.

Let’s build a very simple application implementing the above blockchain for baggage handling.

A java class BagTransaction representing the bag attributes inclusive timestamp and the custody transfer.

package blockchaindemo;

import java.time.Instant;

import org.apache.commons.codec.digest.DigestUtils;

public class BagTransaction {

	private String bagTag;
	private String timeStamp;
	private String pnr;
	private String transferFrom;
	private String transferTo;

	private long blockID;
	private String blockHash;
	private String previousBlockHash;

	public BagTransaction(String bagTag, String pnr, String transferFrom, String transferTo, long blockID,
			String previousBlockHash) {
		super();
		this.bagTag = bagTag;
		this.timeStamp = Instant.now().toString();
		this.pnr = pnr;
		this.transferFrom = transferFrom;
		this.transferTo = transferTo;
		this.blockID = blockID;
		this.previousBlockHash = previousBlockHash;

		this.blockHash = createCurrentHash();
	}

	public String getHash() {
		return this.blockHash;
	}

	@Override
	public String toString() {
		return "BagTransaction [bagTag=" + bagTag + ", timeStamp=" + timeStamp + ", pnr=" + pnr + ", transferFrom="
				+ transferFrom + ", transferTo=" + transferTo + ", blockID=" + blockID + ", blockHash=" + blockHash
				+ ", previousBlockHash=" + previousBlockHash + "]";
	}

	private String createCurrentHash() {
		String returnHash = "";

		returnHash = DigestUtils.sha256Hex(
				this.bagTag + this.timeStamp + this.pnr + this.transferFrom + this.transferTo + this.previousBlockHash);

		return returnHash;
	}

}

Take note of the hashing method that includes all fields inclusive of the previous hash.

A java class BagDemoApp using the transaction class.

package blockchaindemo;

import java.util.Random;

public class BagDemoApp {

	public static void main(String[] args) {

		BagDemoApp demoApp = new BagDemoApp();
		demoApp.demo1();

	}

	public void demo1() {

		String myBagTag = randomBagTagID();
		String myPNR = randomPNR();

		String currentBagBlockHash = "";

		// Print Bag Tag (Genesis Block)
		BagTransaction bagTransaction1 = new BagTransaction(myBagTag, myPNR, Entity.NIL.name(), Entity.PAX.name(), 1,
				"0");
		currentBagBlockHash = bagTransaction1.getHash();

		// Bag Drop
		BagTransaction bagTransaction2 = new BagTransaction(myBagTag, myPNR, Entity.PAX.name(), Entity.AIRP.name(), 2,
				currentBagBlockHash);
		currentBagBlockHash = bagTransaction2.getHash();

		// Bag SEC Scan
		BagTransaction bagTransaction3 = new BagTransaction(myBagTag, myPNR, Entity.AIRP.name(), Entity.SEC.name(), 3,
				currentBagBlockHash);
		currentBagBlockHash = bagTransaction3.getHash();

		// Display Transactions
		System.out.println(bagTransaction1);
		System.out.println(bagTransaction2);
		System.out.println(bagTransaction3);

	}

	// HELPER METHODS --------------------------------------------

	public String randomPNR() {
		final String alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		final int N = alphabet.length();

		Random r = new Random();
		StringBuffer tempPNR = new StringBuffer();

		for (int i = 0; i < 6; i++) {
			char nxtChar = alphabet.charAt(r.nextInt(N));
			while ((i == 0) && (Character.isDigit(nxtChar)))
				nxtChar = alphabet.charAt(r.nextInt(N));
			tempPNR.append(nxtChar);
		}
		return tempPNR.toString();
	}

	public String randomBagTagID() {
		String tempBagTag = "";

		long range = 9999999999L;
		Random r = new Random();
		long number = (long) (r.nextDouble() * range);

		tempBagTag = String.format("%010d", number);
		return tempBagTag;
	}

	public enum Entity {

		PAX {
			@Override
			public String toString() {
				return "Passenger";
			}
		},
		GH {
			@Override
			public String toString() {
				return "Groundhandler";
			}
		},
		AIRL {
			@Override
			public String toString() {
				return "Airline";
			}
		},
		AIRP {
			@Override
			public String toString() {
				return "Airport";
			}
		},
		SEC {
			@Override
			public String toString() {
				return "Security";
			}
		},
		NIL {
			@Override
			public String toString() {
				return "nil";
			}
		}

	}

}

Executing the application

BagTransaction [bagTag=1691462171, timeStamp=2018-08-12T08:02:25.745Z, pnr=ICSEAH, transferFrom=NIL, transferTo=PAX, blockID=1, blockHash=3ff736f7158d224db6e2e8ba25f3d50321903cd911646576f442a60f8c5872ed, previousBlockHash=0]
BagTransaction [bagTag=1691462171, timeStamp=2018-08-12T08:02:25.808Z, pnr=ICSEAH, transferFrom=PAX, transferTo=AIRP, blockID=2, blockHash=88dd4a2be3bc90ebce71635bedd6bcb63b326044e4bd49634a859a86458de243, previousBlockHash=3ff736f7158d224db6e2e8ba25f3d50321903cd911646576f442a60f8c5872ed]
BagTransaction [bagTag=1691462171, timeStamp=2018-08-12T08:02:25.808Z, pnr=ICSEAH, transferFrom=AIRP, transferTo=SEC, blockID=3, blockHash=ea7767ddb2dd7c2bfed4d3a038b9249e43df15a0396ff71da717783db9fee3c4, previousBlockHash=88dd4a2be3bc90ebce71635bedd6bcb63b326044e4bd49634a859a86458de243]

Please note, this is the most simple implementation of a blockchain for illustration purpose, it still misses a lot of features to pass to production, eg. mining, proof-of-work, etc.

In a second part might spin this a bit further. Stay tuned.

 

Disclaimer: This discussion, datamodel and sourcecode or application is for study purpose solely. It does not reflect or replicate any existing commercial product.

Aviation API – Airline and Airport Webservices

The term API (Application programming interface) is not new, basically a defined access to a set of methods, subroutines and data-types made available by one component/service to be used/consumed by another component or application. You can think of API as the car manual and the library being the engine under the hood.

Since the early days of tinkering with the Windows DLL hell more than 20 years have passed, we have better tools and standards by now. In the space of native apps for Windows, Android and iOS we still work with libraries and SDK’s (still can be challenging when resolving dependencies and deployment).
In the world of web applications today we look mostly at RESTful Webservices responding in JSON or XML, a rather straight forward implementation, the only complexity depends on the authorization mode or the mapping of attributes. A lot of websites, portals, services or products expose their WS for usage by third parties, from Salesforce, Ebay, Amazon to Twitter and infinite more. While these mentioned samples operate independently or work as standalone services there is not much need for standardization of the payload, aka the structure of attributes, naming conventions etc. For others business domain there is a need of standardization of such, despite the availability of AIDX, AIDM and a few more data exchange models, there is no standard widely used in the (public) WS space in aviation.
I have to highlight in ACI ACRIS a Semantic Model is being actively developed and the Open API Shop exists as project as well.

I researched what webservices are currently offered to the public by airports and airlines, excluding the API’s of system vendors and travel platforms.
So far I found these webservices (last update 2018-07-06):

Airline

International Airline Group
British Airways, Iberia, Avios
https://developer.iairgroup.com/
Lufthansa https://developer.lufthansa.com/
Airfrance, KLM https://developer.airfranceklm.com/
Alaska Air
https://developer.alaskaair.com/
Transavia https://developer.transavia.com/
FlyDubai https://developer.flydubai.com/
Virgin Australia
https://developer.virginaustralia.com/
Ryanair https://developer.ryanair.com/
Turkish Airlines
https://developer.turkishairlines.com

Airports

Schiphol Airport
https://developer.schiphol.nl/
San Francisco Airport
https://www.flysfo.com/api
Frankfurt Airport
https://developer.fraport.de/
Svedavia Airports
https://apideveloper.swedavia.se/

Others

FAA US
https://app.swaggerhub.com/apis/FAA/ASWS/1.1.0
Flightaware
http://flightaware.com/commercial/flightxml

The usage terms and price models vary but basically all give some kind of developer access to evaluate the services and data at no cost.

To compare a few of the services using a flight status related call, omitting the authentication. The lack of standard, putting aside JSON response format and date format, is quite obvious. It might not be relevant in the space of individual apps to have the same response format, but if you want to combine data from various sources you have to handle the formats separately. Even the request format with the query parameter differs.

British Airways

https://api.ba.com/rest-v1/v1/flights;departureLocation=FRA;startTime=06:00;endTime=11:00

ba

Swedavia Airports

https://api.swedavia.se/flightinfo/v2/query?filter=airport eq ‘ARN’ and scheduled eq ‘180713’ and flightType eq ‘A’ and flightId eq ‘DY4572’

IMG122

Lufthansa

https://api.lufthansa.com/v1/operations/flightstatus/LH778/2018-07-13

IMG123

I also tried with RyanAir (still waiting for approval to get api key), Turkish Airlines (no flight status API), Schiphol (no webservice test available on the website).

Image: Creative Commons, Robert Yarnall Richie Photograph Collection, “Models with Oldsmobile Automobile, Lockheed 10B Electra, Delta Air Lines, 1940”

MOOC – E-Learning on Steroids

Hardly any industry is moving as fast as the IT industry. While your operational experience and knowledge of the vertical domain you are working in, is growing naturally along your career, it is not the same for IT. For example the airport environment, the underlying basics and physics of handling aircrafts, planning flights, etc. are exposed to changes, innovations and challenges, look at the A-CDM program, it took quite some years to take off and become main-stream, that is a much slower than any new general IT technology or platform soaring. Though this industry is picking up speed too and the boundaries between the digital and physical world start to blur more and more, airports are running digital transformation programs, though passengers still flying in the physical world.

But on the IT side of things, the speed is way beyond breath-taking and it is hard to keep a minimal overview over many areas of IT concerns as well dive into specific topics. How to stay up-to-date and tune into relevant topics ? Books (ink and electronic versions) and forums are certainly the traditional approach, on top of that you join conferences and in-persons seminars and training (which comes at a cost and time spent).

Since the 2000’s online courses came into the picture, as the successor for e-learning, and allow a much bigger audience to learn new technologies, skills and more. The very positive part, there are lots and lots of free courses, most platforms offer free and commercial courses, sometimes free to participate and only charge a fee if you want to get an official certificate (one can argue about the value of such certs) but most important, you can learn and move forward and update your knowledge with the click of a button.

The big challenge though is to identify what you need or interested in, find the right courses and, most important, manage your time. Using your spare time you have to choose wisely, you can’t run for every course out there, even they are so many you are interested in and you are temped to sign up for a dozen of courses, only not to finish any of them.

Todays key-/buzzword for this is MOOC or Massive Open Online Courses. This is like e-learning on steroids, in the past you had to look at dull corporate slides pretty much by yourself, now we look at videos, reading material online and offline, interactions with the organizer, mentor, trainer or your virtual peers at various levels.

Not only the organizations that started online learning, like schools and universities, are into the game, as well companies operating specific online course platforms and now book publishing companies offering courses and finally professional social platforms like LinkedIn.

I attended online courses at Coursera and Udacity, which offer a broad range of topics, and now started with some specific courses on HCI and UX at Interaction Design Foundation which solely offers courses on UX, HCI, Visualization and related topics. Though the courses are unattended (except the rating of your text answers or comments) but repeating, you still have a motivation to participate and go though the lessons because you pay money and they help you pacing the whole course by releasing the lesson packages over time.

Stay tuned for the results.

Build a RESTful Webservice in less than 5 minutes

There are quite some tutorials around about building and exposing a RESTful Webservice, but some of them are outdated and make you wade through complex dependencies and tinkering with deployment descriptors and web.xml files. But using RESTeasy, the JBoss implementation that is fully compliant with the JAX-RS 2.0 JCP specification, and Eclipse you can build a simple webservice (“hello world”) with less than 10 lines of sourceode with annotations and no web.xml used in a few minutes and run it on Wildfly.

Lets build a webservice that creates random numbers.

Continue reading

Geolocation of mobile phones in the GSM network

As user of mobile phones we are used to have an almost 100% coverage for phone calls (not for data though) and the user-experience is absolutely seamless in most urban and sub-urban areas in Europe and most other countries. Moving around in trains and cars we cant sense the hand-over in the background of the GSM (2G, GPRS, EDGE), UMTS (3G), LTE (4G) network passing on our connection between the BTS ( base transceiver station). As regular user we dont have an idea about the number and location of cell towers around us, some towers or antennas are mounted on very obvious structures (antennas on towers), some are almost hidden. Though the GSM antennas can have a range of up to 35km (flat plane vs less than 5km in hilly areas), we have much higher density of cells in the urban area with antennas almost every few 100 metres or less. There are a lot of parameters influencing the infrastructure and its layout at a certain place, I wont dive into the details of if, you can get some info on the reference sites listed at the end of the article.

Rather approaching this topic hands-on, I was curious about the information that I can retrieve with Android about the active cells, its location and ultimately about the information the network operator (or other interested parties) collects about one. We might disable the GPS function of a phone to stop apps to collect our whereabouts, giving apps access to the phone state still gives a coarse location profile.

Usually phones dont reveal any network information other than the network operator but with the help of some regular Android methods of the TelephonyManager and GsmCellLocation class we get the crucial information.

The key info we are looking for is

  • MCC – Mobile Country Code
  • MNC – Mobile Network Code
  • LAC – Location Area Code
  • CELLID – The ID of the cell

Only the combination of the above 4 values is unique and can identify the location. You can look at a directory of MCC codes at Wikipedia, but there is no list of of LAC and CELLID codes published by the provider. But in the era of the “crowd” there is a collaborative community collecting the measurements of cellphones and putting them into a DB (CC-BY-SA 3.0). At  opencellid.org you can both retrieve information about cell towers as well download the complete DB.

OpenCellID.org - cell info

OpenCellID.org – cell info

I was curious how often we change the cell and what geo information I could retrieve from this, respectively build a geo profile of myself moving around. I build an app the listens to the currently active cell, lists it up on the screen and logs it into a csv files, and optionally gives an acoustic notification (beep) every time cell has changed.

MyCellDroid Beta App

MyCellDroid Beta App

Quite surprising, during one of the first tests, driving a 100km distance along the highway, both rural and suburban area, I passed through more than 80 cells !

Relevant Android methods

TelephonyManager telephonyManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
mcc = telephonyManager.getNetworkOperator().substring(0, 3);
mnc = telephonyManager.getNetworkOperator().substring(3);
operator = telephonyManager.getNetworkOperatorName();

GsmCellLocation location = (GsmCellLocation) telephonyManager.getCellLocation();
lac = location.getLac();
cellid = location.getCid();

Be aware of the permissions required

 <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

A extra note about Android 6.x+ development, Google changed massively the permission concept and apps require an additional confirmation of the required permissions (for so-called dangerous permissions) during runtime, this has to be implemented specifically, otherwise your application which runs on earlier Android versions will crash. I will share the relevant implementation in an upcoming entry.

Note for Samsung Phones: The function to get information about the nearby cells is not supported by Samsung phones

List<NeighboringCellInfo> neighborCells = telephonyManager.getNeighboringCellInfo();

This list will all be null on Samsung phones. You only can retrieve information about the currently connected cell.

I will try to make some more sense out of the collected data and see how fine-grain the collected data reveals my location.

Btw, there are dozens of similar apps in the Playstore and some even report back collected data to improve and build up the opencellid project database.

References:

  1. https://en.wikipedia.org/wiki/Cell_site
  2. https://en.wikipedia.org/wiki/Base_transceiver_station
  3. https://en.wikipedia.org/wiki/Sector_antenna
  4. https://en.wikipedia.org/wiki/Cellular_network
  5. https://en.wikipedia.org/wiki/Mobile_country_code
  6. https://developer.android.com/reference/android/telephony/NeighboringCellInfo.html
  7. https://developer.android.com/reference/android/telephony/TelephonyManager.html
  8. https://developer.android.com/reference/android/telephony/gsm/GsmCellLocation.html
  9. https://developer.android.com/guide/topics/security/permissions.html