šŸ•µļøAccessing device location  using SIM Card šŸ—ŗļøšŸ“

šŸ•µļøAccessing device location using SIM Card šŸ—ŗļøšŸ“

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, etc
  • mccā€”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 Code
  • cidā€” 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!


Resources

Did you find this article valuable?

Support Shreyas Patil by becoming a sponsor. Any amount is appreciated!

Ā