The recent assassination of Iranian Major General Soleimani has been dubbed the first black Swan event of 2020. People are shocked by the ability of the US to hit targets with precision when no other innocent lives have been taken. After all, the last person recorded to have achieved the feat of “taking the head of a general out of a million troops is our big brother Zhang Fei.”

I also have nothing to do, open the thousands of years not open micro blog, saw a micro blog content:

Suddenly feel tiger body shock, in today’s age of science and technology, to master the commanding heights of science and technology and core research and development capabilities is how important. However, if for this figure really through IMEI can be simple positioning, maybe they need a Xposed Android phone. See a business opportunity?

Shocked, I pulled out my notes of android device ID information and Sim card information that I had just reviewed this week and went over them carefully.

Here are the results:

IMEI and MEID

Both IMEI and MEID are mobile device identifiers. An ID card similar to the device. Their differences are as follows:

  • IMEI: International Mobile Equipment Identity (IMEI), commonly known as Mobile phone “string number”, is used to identify each independent Mobile phone and other Mobile communication devices in the Mobile phone network. The serial number consists of 15 digits.
  • MEID: Mobile Equipment Identifier (MEID) is the Identifier of a CDMA Mobile phone. The serial number has 14 digits.

Under android 8.0 mobile phones, we through APITelephonyManager getDeviceId () get inserted with telecom operators SIM card slot, the default return MEID number; The IMEI number is returned to the SIM card slots of China Mobile and China Unicom operators.

Obtain the IMEI or MEID

On Android 8.0 and above, TelephonyManager discards the getDeviceId() method and provides two separate apis to accurately obtain IMEI and MEID: getImei() and getMeid(). Both apis can support passing in slotIndex to get the IMEI and MEID of the corresponding location.

On systems below Android8.0, TelephonyManager uses getDeviceId() to get the IMEI or MEID; In Android 6.0 or later, getDeviceId(slotIndex) can obtain the IMEI and MEID of the corresponding position by passing slotIndex to adapt to the situation of one computer with two cards. The getDeviceId() method returns the MEID preferentially when a telecom SIM card is present, and the IMEI when mobile and Unicom SIM cards are present.

Card 1 The card 2 getDeviceId(0) getDeviceId(1)
No/Non-telecom card No/Non-telecom card IMEI IMEI
DianXinKa No/Non-telecom card MEID IMEI
No/Non-telecom card DianXinKa IMEI MEID
    /** * Obtain the IMEI or MEID */
    fun getIMEIORMEID(a){
        // Check if you have the READ_PHONE_STATE permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
            == PackageManager.PERMISSION_GRANTED){
            // Obtain the IMEI and MEID
            val tm = getSystemService<TelephonyManager>()
            GetDeviceId () already exists from API1 and returns the IMEI or MEID of slot 1 by default
            varimei: String? = tm? .getDeviceId()//getDeviceId(solotIndex) Adds the IMEI or MEID to API23
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //solotIndex:0 -> Slot 1 IMEI or MEID
                tvIMEI1.text = "Card slot 1 IMEI or MEID:${tm? .getDeviceId(0)}"
                //solotIndex:1 -> Slot 2 IMEI or MEID
                tvIMEI2.text = "Slot 2 IMEI or MEID:${tm? .getDeviceId(1)}"}}}/** * get the MEID */
    fun getMEID(a){
        // Check if you have the READ_PHONE_STATE permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
            == PackageManager.PERMISSION_GRANTED){
            // Obtain the IMEI and MEID
            val tm = getSystemService<TelephonyManager>()
            // Get the MEID added in API26
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // Returns the MEID of the default available slot
                varmeid:String? = tm? .meid// The MEID can be obtained from the specified card slot
                tvMEID.text = "MEID:${tm? .getMeid(0)}"}}}Copy the code

The MAC address

Media Access Control Address (MAC) is an Address used to confirm the location of network devices. A MAC address uniquely identifies a NIC on a network. If a device has one or more nics, each NIC has a unique MAC address.

Generally, the IMEI or MEID combined with the MAC address is used as the unique identifier of the device.

MAC addresses are obtained in different versions of Android in different ways. You can Google it yourself. I will not repeat it here. The code will also be shown in a subsequent Demo.

IMSI and ICCID

IMSI

International Mobile Subscriber Identity (IUSM) is a unique id used to distinguish different users ona cellular network.

Get IMSI

We can use TelephonyManager. GetSubscriberId () to obtain a SIM card IMSI, the API in the SDK API 1 already exists, but if the double card double stay phones have more than one card slot, we want to obtain the corresponding card slot IMSI, . You need to call TelephonyManager getSubscriberId (int subId) method, was introduced into the corresponding card slot subId, 0 on behalf of the card slot 2, 1, 1 on behalf of the card slot of the method in the SDK API 21 to join, but in the SDK API than 29 and will not call again, But we can call it by reflection right now.

Here is a tool for amway: for an API, the source code for each Android system can be viewed at http://androidxref.com/.

We search through the site to view, also found in the SDK API 21, TelephonyManager. GetSubscriberId (long subId) it into the parameter is long, in the later version is type int. So in version-compatible times, we may need to pay attention to parameter types if we are making reflection calls.

SDK API getSubscriberId()
1 ~ 20 getSubscriberId()
21 GetSubscriberId () or getSubscriberId (long subId)
22 ~ 28 GetSubscriberId () or getSubscriberId (int subId)
29 and above The getSubscriberId() method is exposed, but the getSubscriberId(int subId) method is not
    /** ** calls */ via reflection
    fun getIMSI(a){
        // Check if you have the READ_PHONE_STATE permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
            == PackageManager.PERMISSION_GRANTED){
            / / get IMSI
            val tm = getSystemService<TelephonyManager>()
            // There is a getSubscriberId () method in SDKAPI 1 that returns the IMSI of the default card slot
            varimsi = tm? .subscriberId// Add getSubscriberId(subId) to SDKAPI 21 to specify the IMSI of the return card slot location
            // Above SDKAPI 29, the method to specify the slot position is no longer exposed, but can still be obtained by reflection
            tvIMSI1.text = "Card slot 1 IMSI:${getReflectMethod(this,"getSubscriberId",0) as CharSequence? }"
            tvIMSI2.text = "Card slot 2 IMSI:${getReflectMethod(this,"getSubscriberId",1) as CharSequence? }"}}fun getReflectMethod(context: Context, method: String, param: Int): Any? {
        val telephony = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        try {
            val telephonyClass = Class.forName(telephony.javaClass.name)
            val parameter = arrayOfNulls<Class<*>>(1)
            parameter[0] = Int: :class.javaPrimitiveType
            val getSimState = telephonyClass.getMethod(method, *parameter)
            val ob_phone = getSimState.invoke(telephony, param)

            if(ob_phone ! =null) {
                return ob_phone
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return null
    }
Copy the code

The above code shows how to get the IMSI through reflection. After obtaining the IMSI, we can distinguish the country and operator of the mobile user by the first five digits of the IMSI.

The first three digits in China IMSI are 460, and the next two represent the operator information in China:

Operator, Newsun focus number
mobile 00, 02,, 07 preceding
unicom 01, 06,09
telecom ’03,’ 05
Telecom 4 g 11
tietong 20

ICCID

SIM card number, which is not used for network access authentication, can be queried after the SIM card. Format: Most numbers are 19 or 20 digits from 0 to 9, but also 6 /12 digits.

Get ICCID

There are two ways to obtain ICCID:

  • Through the TelephonyManagergetSimSerialNumber()Method to get ICCID, added in SDK API 21getSubscriberId(subId)To specify the ICCID that gets the corresponding card slot location; But like IMSI, in SDK API 29 and above,getSubscriberId(subId)Method no longer exposes external calls, but we can call them through reflection.
  • Through the SubscriptionManagergetActiveSubscriptionInfoList()Method to obtainSubscriptionInfoList, this list contains information about valid SIM cards, if there are more than one SIM card, will be returnedSubscriptionInfoObject. eachSubscriptionInfoThe object represents a valid SIM card. Iterate through the loopSubscriptionInfoList, callSubscriptionInfothegetIccId()Can get the value of ICCID, but SubscriptionManager was added to the SDk in SDk API 22.
		/** ** calls */ via reflection
    fun getReflectMethod(context: Context, method: String, param: Int): Any? {
        val telephony = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        try {
            val telephonyClass = Class.forName(telephony.javaClass.name)
            val parameter = arrayOfNulls<Class<*>>(1)
            parameter[0] = Int: :class.javaPrimitiveType
            val getSimState = telephonyClass.getMethod(method, *parameter)
            val ob_phone = getSimState.invoke(telephony, param)

            if(ob_phone ! =null) {
                return ob_phone
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return null
    }

    /** * Get CCID from TelephonyManager */
    fun getICCIDByTM(a){
        // Check if you have the READ_PHONE_STATE permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
            == PackageManager.PERMISSION_GRANTED){
            / / get IMSI
            val tm = getSystemService<TelephonyManager>()
            // There is a getSubscriberId () method in SDKAPI 1 that returns the IMSI of the default card slot
            variccid = tm? .simSerialNumber// Add getSubscriberId(subId) to SDKAPI 21 to specify the IMSI of the return card slot location
            // Above SDKAPI 29, the method to specify the slot position is no longer exposed, but can still be obtained by reflection
            tvICCID1.text = "Card slot 1 ICCID:${getReflectMethod(this,"getSimSerialNumber",0) as CharSequence? }"
            tvICCID2.text = "Card slot 2 ICCID:${getReflectMethod(this,"getSimSerialNumber",1) as CharSequence? }"}}/** * Get the CCID SDK API from SubscriptionManager
    fun getICCIDBySM(a){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            val mSubscriptionManager = getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
            // Check if you have the READ_PHONE_STATE permission
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
                == PackageManager.PERMISSION_GRANTED){
                // Get the list of valid SIM cards
                var subInfos = mSubscriptionManager.activeSubscriptionInfoList
                for ((index,item) in subInfos.withIndex()){
                    when(index){
                        0->tvICCID1.text = "Card slot 1 ICCID:${item.iccId}"
                        1->tvICCID2.text = "Card slot 2 ICCID:${item.iccId}"
                    }
                }
            }
        }
    }
Copy the code

SN

SN code is the abbreviation of Serial Number, which is the Serial Number of a product. It is mainly used to verify the “legitimacy of the product” and protect the rights and interests of users. It’s generally immutable. We can check this out with the ADB Devices command.

Get SN

Below SDK API 26, we can directly obtain build. SERIAL, above SDK API 26, we need READ_PHONE_STATE permission, call build.getserial () method to obtain.

		/** * Obtain SN */
    fun getSN(a){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
                == PackageManager.PERMISSION_GRANTED){
                // Requires READ_PHONE_STATE permission
                tvSN.text = "SN:${Build.getSerial()}"}}else{
            tvSN.text = "SN:${Build.SERIAL}"}}Copy the code

ANDROID ID

The ID is a string of 16 characters that is generated randomly after the device is started for the first time and restored to factory Settings or upgraded. However, some devices may generate the same ID or return null due to a Bug in the customized system of the manufacturer

Obtain the ANDROID ID

Through Settings. Secure. Get string (enclosing getContentResolver (), Settings. Secure. ANDROID_ID can get ANDROID ID

		/** * get AndroidId */
    fun getAndroidId(a){
        tvAndroidId.text = "Android ID:${Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID)}"
    }

Copy the code

Android10 AndroidId

Android10 has failed to obtain device hardware related Id, such as IMEI, MAC address, etc. If this information is retrieved from your code, you will be thrown a SecurityException in uppercase.

The affected apis are:

  • Build

    getSerial()

  • TelephonyManager

    getImei()

    getDeviceId()

    getMeid()

    getSimSerialNumber()

    getSubscriberId()

On Android10, the above API is not accessible unless a system application or a special application vendor applies the READ_PRIVILEGED_PHONE_STATE. This means that many of the ways to get Android IDS are basic in Android10.

Google has come up with a way for Android10 to get the ID of the software category. See article: Best Practices for Unique Identifiers

However, this solution does not solve many of our scenarios where we need to obtain a unique immutable ID for the device, such as risk control, push, and user portraits. But don’t worry, our great home country vendor system gives us a unified OAID (Anonymous Device Identifier) to replace the role of IMEI. Specific reference document: OAID document

SIM card related expansion

Finally, a few more things related to SIM cards, but not Android device information. It is mainly about how to detect how many SIM cards are currently inserted into a device, how many SIM cards are currently supported by the mobile phone and how to obtain the SIM card that is currently using the cellular network.

Check how many SIM cards are inserted into the current mobile phone

There are also two ways to obtain the number of SIM cards inserted into the current phone, one is obtained by TelephonyManager’s getSimState() method, and the other is obtained by SubscriptionManager

  • TelephonyManager added getSimState() in SDK API 1Method to obtain the default Sim card status. Added in SDK API 21getSimState(int slotId)Method to get the Sim card status of the corresponding card slot, but this method is annotated@hide ‘decorates, so there are no exposed methods, which cannot be called externally, but can be called via reflection; It was only exposed in SDK API 26.
SDK API getSubscriberId()
1 ~ 20 getSimState()
21 ~ 26 GetSimState () or reflection call getSimState(int slotId)
More than 26 GetSimState () or getSimState (int slotId)

Sim card status table:

state Status code explain
SIM_STATE_UNKNOWN 0 SIM card status: Unknown
SIM_STATE_ABSENT 1 SIM card status: The device has no SIM card
SIM_STATE_PIN_REQUIRED 2 SIM card status: Locked: Unlock the SIM card PIN
SIM_STATE_PUK_REQUIRED 3 SIM card status: Locked: The SIM CARD needs to be unlocked
SIM_STATE_NETWORK_LOCKED 4 SIM card status: Locked: You need a network PIN to unlock the SIM card
SIM_STATE_READY 5 SIM card status: Ready
SIM_STATE_NOT_READY 6 SIM card status: The SIM card is not ready
SIM_STATE_PERM_DISABLED 7 SIM card status: The SIM card is incorrect and permanently disabled
SIM_STATE_CARD_IO_ERROR 8 SIM card status: The SIM card exists but is faulty
SIM_STATE_CARD_RESTRICTED 9 SIM card status: The SIM card is limited and exists, but cannot be used due to carrier restrictions
  • SubscriptionManager was added to the SDK API in SDK API 22. We can pass SDK API 22 and aboveSubscriptionManager.getActiveSubscriptionInfoCount()Method Obtain the number of valid Sim cards. But this method requiresREAD_PHONE_STATEUser rights.

So combining TelephonyManager. GetSimState () and SubscriptionManager getActiveSubscriptionInfoCount () method, personal finishing a method for current mobile phone SIM card.

		/** * Number of Sim cards *@param context
     * @return* /
    fun checkSimCount(context: Context): Int {
        var simCount: Int
        try {
            // If SDK Version >=22 && has manifest.permission.READ_PHONE_STATE permission
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1 && ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.READ_PHONE_STATE
                ) == PackageManager.PERMISSION_GRANTED
            ) {
                simCount = getSimCountBySubscriptionManager(context)
                if (simCount == - 1) {
                    // If SubscriptionManager fails to get, TelephonyManager tries to get it
                    simCount = getSimCountByTelephonyManager(context)
                }
            } else {
                simCount = getSimCountByTelephonyManager(context)
            }

        } catch (e: Exception) {
            simCount = getSimCountByTelephonyManager(context)
        }

        return simCount
    }


    /** * Get the number of Sim cards through SubscriptionManager. If the number fails to get, -1 * is returned@param context
     * @return* /
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
    private fun getSimCountBySubscriptionManager(context: Context): Int {
        try {
            val subscriptionManager =
                context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
            returnsubscriptionManager? .activeSubscriptionInfoCount ? :- 1
        } catch (e: Throwable) {
            return - 1}}/** * Get the number of Sim cards * by TelephonyManager reflection@param context
     * @return* /
    private fun getSimCountByTelephonyManager(context: Context): Int {
        val slotOne = getSimStateBySlotIdx(context, 0)
        val slotTwo = getSimStateBySlotIdx(context, 1)
        if (slotOne && slotTwo) {
            return 2
        } else if (slotOne || slotTwo) {
            return 1
        } else {
            val telephony = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
            val simState = telephony.simState
            return if(simState ! = TelephonyManager.SIM_STATE_ABSENT && simState ! = TelephonyManager.SIM_STATE_UNKNOWN) {1
            } else 0}}/ * * * through the reflection to invoke the TelephonyManager getSimState (int slotIdx) method, get a sim card status * SIM_STATE_UNKNOWN 0 sim card status: Unknown * SIM_STATE_ABSENT 1 SIM card status: No SIM card is present in the device * SIM_STATE_PIN_REQUIRED 2 SIM card status: Locked: SIM card PIN must be unlocked * SIM_STATE_PUK_REQUIRED 3 SIM card status: Locked: SIM PUK must be unlocked * SIM_STATE_NETWORK_LOCKED 4 SIM card status: locked: Network PIN is required * SIM_STATE_READY 5 SIM card status: Ready * SIM_STATE_NOT_READY 6 SIM card status: SIM card is not ready * SIM_STATE_PERM_DISABLED 7 SIM card status: SIM card error, permanently disabled * SIM_STATE_CARD_IO_ERROR 8 SIM card status: SIM card error exists but is faulty * SIM_STATE_CARD_RESTRICTED 9 SIM card status: SIM card limited, exists, but cannot be used due to carrier restrictions. *@param context
     * @param slotIdx:0(sim1),1(sim2)
     * @return* /
    fun getSimStateBySlotIdx(context: Context, slotIdx: Int): Boolean {
        var isReady = false
        try {
            val getSimState = getSimByMethod(context, "getSimState", slotIdx)
            if(getSimState ! =null) {
                val simState = Integer.parseInt(getSimState.toString())
                if(simState ! = TelephonyManager.SIM_STATE_ABSENT && simState ! = TelephonyManager.SIM_STATE_UNKNOWN) { isReady =true}}}catch (e: Throwable) {
        }

        return isReady
    }


    /** * Obtain THE Sim card status by reflection *@param context
     * @param method
     * @param param
     * @return* /
    private fun getSimByMethod(context: Context, method: String, param: Int): Any? {
        val telephony = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        try {
            val telephonyClass = Class.forName(telephony.javaClass.name)
            val parameter = arrayOfNulls<Class<*>>(1)
            parameter[0] = Int: :class.javaPrimitiveType
            val getSimState = telephonyClass.getMethod(method, *parameter)
            val obParameter = arrayOfNulls<Any>(1)
            obParameter[0] = param
            val result = getSimState.invoke(telephony, *obParameter)

            if(result ! =null) {
                return result
            }
        } catch (e: Throwable) {
            e.printStackTrace()
        }

        return null
    }

Copy the code

The maximum number of Sim cards supported

The SubscriptionManager we can getActiveSubscriptionInfoCountMax () method to get the current mobile phone can support the maximum number of Sim card.

		/** * Obtain the maximum number of SIM cards supported */
    fun getMaxSimCount(context: Context){
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                val subscriptionManager = context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
                tvSimMaxCount.text =  "Maximum number of Sim cards currently supported:${subscriptionManager? .activeSubscriptionInfoCountMax}"}}catch (e: Throwable) {
        }
    }
Copy the code

Current cellular Sim card information

We can obtain the MCC+MNC (Mobile Country code + Mobile Network Code) of the SIM card of the current network through the getSimOperator() method of TelephonyManager, which is the top five of IMSI. This API was added in SDK API 1. Based on the returned information, determine the corresponding carrier information based on the carrier MNC.

		/** * Current cellular network operator information through TelephonyManager */
    fun getNetType(a){
        // Check if you have the READ_PHONE_STATE permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
            == PackageManager.PERMISSION_GRANTED){
            / / get TelephonyManager
            val tm = getSystemService<TelephonyManager>()
            // In SDK API 1 getSimOperator () method
            when(tm? .simOperator){"46000"."46002"."46004"."46007"."46008"->tvSimNetType.text = "SIM card operator: Mobile"
                "46001"."46006"."46009"->tvSimNetType.text = "Current SIM card operator: China Unicom"
                "46003"."46005"."46011"->tvSimNetType.text = "Current SIM card operator: Telecom"
                "46020"->tvSimNetType.text = "Current SIM card operator: Tietong"}}}Copy the code

Source: github.com/DaMaiGit/An…