These days, almost many apps use the device locationš. There might be some use case in your app which is strongly dependent on the userās location. For example, if you have developed a social app š¬ which shows posts of users and imagine youāve feature which shows trending posts in userās nearby area. For such use case, youāll get a userās device location and after some processing, youāll be able to manage this feature for your cool app.
But waitā¦ š¤
What if your app userās enabled Mock location? š®
If your cool app users have enabled mock locations then your feature will not work as expected. If the user is currently at Mumbai, India and he enabled mock location and showing his location at Beijing, Chinaš© then that Indian user will see useless Chinese content in the app. Your cool appās cool feature wonāt work as expected š¢
Then is there any solution? š¤·āāļø
Yesš, for such use case you wonāt need userās perfect location. Just area/city level location is enough for such use case. You can get the userās cell tower location by extracting the SIM cardš” details from the userās device.
The famous app TikTok app extracts SIM Cardš¶ details and fetches the userās location for the perfect updates and avoiding spoofing of location.
How did we come up with this Idea? š”
We at ScaleReal were developing a product which was heavily dependent on the userās location and we thought āWhat if GPS location isnāt available due to conditions like device failure or bad weather or if the user provides fake locationāš¤. We thoroughly studied these scenarios with all aspects š§ and came with the solution of using SIM card details to extract at least the cell tower location of the user. This solution helped us to pinpoint the user's location using cell tower triangulation and the results were even better when we wrote a custom algorithm on top of these two. In turn a better product! š
In this article, weāll learn to access the deviceās cell tower location by extracting SIM Card details from the device. So letās start implementation.
Note: GPS is not involved in this process. So doesnāt matter if itās enabled tr disabled!
Letās Start š
First of all, weāll need to get API for accessing Geolocation information. Iāve seen two APIs for such use caseš¤.
- Googleās Geolocation APIā This API is good to go with but requires a billing account to be enabled for your projectš¤.
- Unwiredlabs OpenCellIDā This API is amazing and easy to use and the worldās largest open database of cell towersš.(Weāre using this API for geolocation)
Just go to above link and Sign Up your account and youāll get API key. See API documentation here for more details about accessing API.
How API Works?š¤
From your app, you just have to send this data payload to API:
Letās understand these parameters:
radio
ā Network type. For e.g. GSM, LTE, etcmcc
āMobile Country Code used to identify the country which a mobile subscriber belongs to.mnc
ā Used to uniquely identify a mobile subscribers network the MCC is combined with a Mobile Network Code.lac
ā Location Area Codecid
ā Cell ID
When you request API with above details, youāll get a response from API as belowš.
Thatās the main thing! I hope now you have API key with you so letās proceed to implementationš.
Getting Started š»
- Open Android Studio
- Create a new project or you can simply clone or import this project.
Make Request model
Request model will be used to request API with data payload. This will include network details of the device. Hereās you can see how our request model will look like š
data class CellInfo(
val token: String = BuildConfig.OPENCELLID_API_KEY,
var radio: String? = null,
var mcc: Int? = null,
var mnc: Int? = null,
var cells: List<Cell> = emptyList(),
val address: Int = 1
)
data class Cell(
val lac: Int,
val cid: Int,
val psc: Int? = null
)
object RadioType {
const val GSM = "gsm"
const val CDMA = "cdma"
const val UMTS = "umts"
const val LTE = "lte"
}
Make Response model
Weāll get a response from API and for that, weāll have to make response model as below.
data class CellLocation(
val status: String,
val message: String?,
val accuracy: Int? = null,
val address: String? = null,
@Json(name = "lat")
val latitude: Double? = null,
@Json(name = "lon")
val longitude: Double? = null
) {
fun isSuccess() = status == "ok"
}
So now we have to make a service which will communicate with API which will look like as below š
interface UnwiredLabsService {
@POST("v2/process.php")
suspend fun getLocationByCellInfo(@Body cellInfo: CellInfo): Response<CellLocation>
companion object {
const val BASE_URL = "https://ap1.unwiredlabs.com/"
}
}
We have used Retrofit in this app. Iāll not show the whole implementation of it here. For that, you can refer app source code for implementation details. You can that here weāve implemented ViewModel and Activity which is now able to communicate with API.
Remember one thing! š¤Ø
For accessing the deviceās network information youāll need to include below permission in your appās Manifest.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Getting network Details š¶
Here the main thing is how to get Network details from the device. Here we have created a method getCurrentCellInfo()
as below š which will help us to extract proper network details.
fun getCurrentCellInfo(context: Context): List<CellInfo> {
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val allCellInfo = telephonyManager.allCellInfo
return allCellInfo.mapNotNull {
when (it) {
is CellInfoGsm -> getCellInfo(it)
is CellInfoWcdma -> getCellInfo(it)
is CellInfoLte -> getCellInfo(it)
else -> null
}
}
}
As you can see in the above code, for extracting network details, we need to check whether network type is GSM, CDMA or LTE. Weāll need to create a method getCellInfo()
for every network type. For example, the below method is created for GSM type š
fun getCellInfo(info: CellInfoGsm): CellInfo {
val cellInfo = CellInfo()
cellInfo.radio = RadioType.GSM
info.cellIdentity.let {
val (mcc, mnc) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Pair(it.mccString?.toInt() ?: 0, it.mncString?.toInt() ?: 0)
} else {
Pair(it.mcc, it.mnc)
}
cellInfo.mcc = mcc
cellInfo.mnc = mnc
cellInfo.cells = listOf(Cell(it.lac, it.cid, it.psc))
}
return cellInfo
}
The same method can be repeated for other network types.
Okay! By this, weāve completed the main part of the applicationāØ. As you can see, we havenāt used GPS or location service in this app.
Now just send this information along as data payload to the API and youāll get a response which will include location details š„.
Okayš. Now letās run this appš. Youāll see like thisā¦ š
Yeah!š Our app is working as expected š£. Thatās all. I hope you liked this article š.
Thank you! š
Sharing is caring!