xiongzhu 11 месяцев назад
Родитель
Сommit
ee23580efc

+ 80 - 156
app/src/main/java/com/example/uicceditor/ApduChannel.kt

@@ -1,204 +1,128 @@
 package com.example.uicceditor
 
+import android.telephony.IccOpenLogicalChannelResponse
 import android.telephony.TelephonyManager
 import android.util.Log
-import java.math.BigDecimal
-import java.math.RoundingMode
-import kotlin.math.roundToInt
-
-fun String.swapNibbles(): String {
-    val sb = StringBuilder()
-    var i = 0
-    while (i < length) {
-        sb.append(this[i + 1]).append(this[i])
-        i += 2
-    }
-    return sb.toString()
-}
-
-fun String.stripF(): String {
-    return this.replace(Regex("^f+"), "").replace(Regex("f+$"), "")
-}
 
 
-class ApduChannel(private val telephonyManager: TelephonyManager) {
+class ApduResponse(rawStr: String) {
+    val data: String = rawStr.substring(0, rawStr.length - 4)
+    val sw: String = rawStr.substring(rawStr.length - 4)
+    val sw1: String = sw.substring(0, 2)
+    val sw2: String = sw.substring(2)
+}
 
-    class ApduResponse(rawStr: String) {
+class ApduChannel(private val telephonyManager: TelephonyManager, aid: String) {
+    companion object {
+        const val TAG = "APDU"
+        const val INS_SEL = 0xA4
+        const val INS_RB = 0xB0
+        const val INS_WB = 0xD6
+        const val INS_RR = 0xB2
+        const val INS_WR = 0xDC
+    }
 
-        val data: String
-        val sw: String
-        val sw1: String
-        val sw2: String
+    private val channelId: Int
+    private var closed: Boolean = false
 
-        init {
-            data = rawStr.substring(0, rawStr.length - 4)
-            sw = rawStr.substring(rawStr.length - 4)
-            sw1 = sw.substring(0, 2)
-            sw2 = sw.substring(2)
+    init {
+        val res = telephonyManager.iccOpenLogicalChannel(aid, 0x04)
+        if (res.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
+            throw Exception("Failed to open logical channel")
         }
+        channelId = res.channel
     }
 
-    private val TAG = "ApduShell"
-
-    public var authorized: Boolean = false
-
-    fun execute(cmd: String): ApduResponse {
-        val cla = cmd.substring(0, 2).toInt(16)
-        val ins = cmd.substring(2, 4).toInt(16)
-        val p1 = cmd.substring(4, 6).toInt(16)
-        val p2 = cmd.substring(6, 8).toInt(16)
-        val p3 = cmd.substring(8, 10).toInt(16)
-        val data = cmd.substring(10)
-        Log.i(TAG, "-> $cmd")
-        val res =
-            telephonyManager.iccTransmitApduBasicChannel(
-                cla,
-                ins,
-                p1,
-                p2,
-                p3,
-                data
-            )
-        Log.i(TAG, "<- $res")
-        return ApduResponse(res)
-    }
+    fun execute(cla: Int, ins: Int, p1: Int, p2: Int, lc: Int, data: String): ApduResponse {
+        if (closed) {
+            throw Exception("Channel closed")
+        }
 
-    fun asciiToHex(ascii: String): String {
-        return ascii.toByteArray().joinToString("") { "%02x".format(it) }
+        Log.d(TAG, ">>> ${String.format("%02X%02X%02X%02X%02X %s", cla, ins, p1, p2, lc, data)}")
+        val res = telephonyManager.iccTransmitApduLogicalChannel(
+            channelId, cla, ins, p1, p2, lc, data
+        )
+        Log.d(TAG, "<<< ${String.format("%S", res)}")
+        return ApduResponse(res)
     }
 
-    fun authorize(adm: String) {
-        if (adm.length != 8) {
-            Log.e(TAG, "ADM must be 8 characters long")
-            throw IllegalArgumentException("ADM must be 8 characters long")
+    fun close() {
+        if (!closed) {
+            telephonyManager.iccCloseLogicalChannel(channelId)
         }
-        val res = execute("0020000A08${asciiToHex(adm)}")
+        closed = true
+    }
 
+    fun readICCID(): String {
+        val res = execute(0x00, 0xE1, 0xA0, 0x00, 0x00, "")
         if (res.sw != "9000") {
-            Log.e(TAG, "Authorization failed")
-            throw SecurityException("Authorization failed")
-        } else {
-            authorized = true
+            throw Exception("Failed to read ICCID")
         }
-
-//        execute("00fbffff083239303833303131")
-//        execute("00fb0000095b37b1da0cea13c501")
-//        execute("00fb0001095b37b1da0cea13c500")
-//        execute("00fb0002095b37b1da0cea13c501")
-//        execute("00fb0003095b37b1da0cea13c500")
+        return decICCID(res.data)
     }
 
-    init {
-        //authorize()
+    fun writeICCID(iccid: String) {
+//        val res = execute(0x00, 0xE2, 0xA0, 0x00, 0x0A, encICCID(iccid))
+//        if (res.sw != "9000") {
+//            throw Exception("Failed to write ICCID")
+//        }
+        select(SIMView.FID_MF)
+        select(SIMView.FID_EF_ICCID)
+        writeBinary(encICCID(iccid))
     }
 
-    fun readIccid(): String {
-        var res = execute("00a40004023f00")
-        if (res.sw != "9000") {
-            throw Exception("Failed to select MF")
-        }
-        res = execute("00a40004022fe2")
+    fun readIMSI(): String {
+        val res = execute(0x00, 0xE1, 0xA2, 0x00, 0x00, "")
         if (res.sw != "9000") {
-            throw Exception("Failed to select EF.ICCID")
+            throw Exception("Failed to read IMSI")
         }
-        res = execute("00b000000a")
-        if (res.sw != "9000") {
-            throw Exception("Failed to read EF.ICCID")
-        }
-        return res.data.swapNibbles().stripF()
+        return decIMSI(res.data)!!
     }
 
-    fun readImsi(): String {
-        var res = execute("00a40004023f00")
+    fun writeIMSI(imsi: String) {
+        val res = execute(0x00, 0xE2, 0xA2, 0x00, 0x09, encIMSI(imsi))
         if (res.sw != "9000") {
-            throw Exception("Failed to select MF")
+            throw Exception("Failed to write IMSI")
         }
-        res = execute("00a4040410a0000000871002ff86ffff89ffffffff00")
-        if (res.sw != "9000") {
-            throw Exception("Failed to select ADF.USIM")
-        }
-        res = execute("00a40004026f07")
-        if (res.sw != "9000") {
-            throw Exception("Failed to select EF.IMSI")
-        }
-        res = execute("00b0000009")
-        if (res.sw != "9000") {
-            throw Exception("Failed to read EF.IMSI")
-        }
-        return decImsi(res.data)!!
-    }
-
-    fun decImsi(ef: String): String? {
-        if (ef.length < 4) {
-            return null
-        }
-        var l = ef.substring(0, 2).toInt(16) * 2
-        l -= 1
-        val swapped: String = ef.substring(2).swapNibbles().stripF()
-        if (swapped.isEmpty()) {
-            return null
-        }
-        val oe = (swapped[0].toString().toInt() shr 3) and 1
-        if (oe == 0) {
-            l -= 1
-        }
-        if (l != swapped.length - 1) {
-            return null
-        }
-        val imsi = swapped.substring(1)
-        return imsi
     }
 
-    fun encImsi(imsi: String): String {
-        val l = BigDecimal(((imsi.length + 1) / 2f).toDouble()).setScale(0, RoundingMode.HALF_UP)
-            .toInt()
-        val oe = imsi.length and 1
-        val ei =
-            String.format("%02x", l) +
-                    (String.format("%01x", (oe shl 3) or 1) + imsi.padEnd(
-                        15,
-                        'f'
-                    )).swapNibbles()
-        return ei
+    fun reset() {
+        execute(0x00, 0xE0, 0x00, 0x00, 0x00, "")
     }
 
-    fun encIccid(iccid: String): String {
-        return iccid.padEnd(20, 'f').swapNibbles()
+    fun select(fid: String) {
+        if (execute(0x80, INS_SEL, 0x00, 0x00, 0x02, fid).sw != "9000") {
+            throw Exception("Failed to select file")
+        }
     }
 
-    fun writeIccid(iccid: String) {
-        var res = execute("00a40004023f00")
+    fun readBinary(length: Int): String {
+        val res = execute(0x00, INS_RB, 0x00, 0x00, length, "")
         if (res.sw != "9000") {
-            throw Exception("Failed to select MF")
-        }
-        res = execute("00a40004022fe2")
-        if (res.sw != "9000") {
-            throw Exception("Failed to select EF.ICCID")
-        }
-        res = execute("00d600000a${encIccid(iccid)}")
-        if (res.sw != "9000") {
-            throw Exception("Failed to write EF.ICCID")
+            throw Exception("Failed to read binary")
         }
+        return res.data
     }
 
-    fun writeImsi(imsi: String) {
-
-        var res = execute("00a40004023f00")
+    fun writeBinary(data: String) {
+        val res = execute(0x00, INS_WB, 0x00, 0x00, data.length / 2, data)
         if (res.sw != "9000") {
-            throw Exception("Failed to select MF")
+            throw Exception("Failed to write binary")
         }
-        res = execute("00a40004027f2000")
-        if (res.sw != "9000") {
-            throw Exception("Failed to select DF.GSM")
-        }
-        res = execute("00a40004026f07")
+    }
+
+    fun readRecord(record: Int, length: Int): String {
+        val res = execute(0x00, INS_RR, record, 0x04, length, "")
         if (res.sw != "9000") {
-            throw Exception("Failed to select EF.IMSI")
+            throw Exception("Failed to read record")
         }
-        res = execute("00d6000009${encImsi(imsi)}")
+        return res.data
+    }
 
+    fun writeRecord(record: Int, data: String) {
+        val res = execute(0x00, INS_WR, record, 0x04, data.length / 2, data)
         if (res.sw != "9000") {
-            throw Exception("Failed to write EF.IMSI")
+            throw Exception("Failed to write record")
         }
     }
 }

+ 154 - 0
app/src/main/java/com/example/uicceditor/Encoder.kt

@@ -0,0 +1,154 @@
+package com.example.uicceditor
+
+import java.math.BigDecimal
+import java.math.RoundingMode
+
+fun String.stripF(): String {
+    return this.replace(Regex("^f+"), "").replace(Regex("f+$"), "")
+}
+
+fun String.swapNibbles(): String {
+    val sb = StringBuilder()
+    var i = 0
+    while (i < length) {
+        sb.append(this[i + 1]).append(this[i])
+        i += 2
+    }
+    return sb.toString()
+}
+
+fun asciiToHex(ascii: String): String {
+    return ascii.toByteArray().joinToString("") { "%02x".format(it) }
+}
+
+fun decIMSI(ef: String): String? {
+    if (ef.length < 4) {
+        return null
+    }
+    var l = ef.substring(0, 2).toInt(16) * 2
+    l -= 1
+    val swapped: String = ef.substring(2).swapNibbles().stripF()
+    if (swapped.isEmpty()) {
+        return null
+    }
+    val oe = (swapped[0].toString().toInt() shr 3) and 1
+    if (oe == 0) {
+        l -= 1
+    }
+    if (l != swapped.length - 1) {
+        return null
+    }
+    val imsi = swapped.substring(1)
+    return imsi
+}
+
+fun encIMSI(imsi: String): String {
+    val l = BigDecimal(((imsi.length + 1) / 2f).toDouble()).setScale(0, RoundingMode.HALF_UP)
+        .toInt()
+    val oe = imsi.length and 1
+    val ei =
+        String.format("%02x", l) +
+                (String.format("%01x", (oe shl 3) or 1) + imsi.padEnd(
+                    15,
+                    'f'
+                )).swapNibbles()
+    return ei
+}
+
+fun decICCID(ef: String): String {
+    return ef.swapNibbles().stripF()
+}
+
+fun encICCID(iccid: String): String {
+    return iccid.padEnd(20, 'f').swapNibbles()
+}
+
+/*
+* NPI (Numbering Plan Identification): This indicates the numbering plan used for the phone number. Common values include:
+    0x01: ISDN/telephony numbering plan (E.164/E.163)
+    0x02: Data numbering plan (X.121)
+    0x03: Telex numbering plan
+  ToN (Type of Number): This indicates the type of the phone number. Common values include:
+    0x00: Unknown
+    0x01: International number
+    0x02: National number
+    0x03: Network-specific number
+    0x04: Subscriber number
+    0x05: Alphanumeric (coded according to the GSM 7-bit default alphabet)
+    0x06: Abbreviated number
+* */
+fun decMSISDN(efMsisdn: String): Triple<Int, Int, String?>? {
+    println("ef_msisdn: $efMsisdn")
+    // Convert from String to ByteArray
+    val efMsisdnBytes = efMsisdn.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
+
+    // Make sure mandatory fields are present
+    if (efMsisdnBytes.size < 14) {
+        throw IllegalArgumentException("EF.MSISDN is too short")
+    }
+
+    // Skip optional Alpha Identifier
+    val xlen = efMsisdnBytes.size - 14
+    val msisdnLhv = efMsisdnBytes.copyOfRange(xlen, efMsisdnBytes.size)
+
+    // Parse the length (in bytes) of the BCD encoded number
+    var bcdLen = msisdnLhv[0].toInt()
+    // BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
+    if (bcdLen == 0xff) {
+        return null
+    } else if (bcdLen > 11 || bcdLen < 1) {
+        throw IllegalArgumentException("Length of MSISDN ($bcdLen bytes) is out of range")
+    }
+
+    // Parse ToN / NPI
+    val ton = (msisdnLhv[1].toInt() shr 4) and 0x07
+    val npi = msisdnLhv[1].toInt() and 0x0f
+    bcdLen -= 1
+
+    // No MSISDN?
+    if (bcdLen == 0) {
+        return Triple(npi, ton, null)
+    }
+
+    val msisdn =
+        msisdnLhv.copyOfRange(2, 2 + bcdLen).joinToString("") { "%02x".format(it) }
+            .swapNibbles()
+            .stripF()
+    // International number 10.5.118/3GPP TS 24.008
+    val formattedMsisdn = if (ton == 0x01) "+$msisdn" else msisdn
+
+    return Triple(npi, ton, formattedMsisdn)
+}
+
+fun encMSISDN(msisdn: String, npi: Int = 0x01, ton: Int = 0x03): String {
+    // If no MSISDN is supplied then encode the file contents as all "ff"
+    if (msisdn.isEmpty() || msisdn == "+") {
+        return "ff".repeat(14)
+    }
+
+    var msisdnVar = msisdn
+    var tonVar = ton
+
+    // Leading '+' indicates International Number
+    if (msisdnVar[0] == '+') {
+        msisdnVar = msisdnVar.substring(1)
+        tonVar = 0x01
+    }
+
+    // An MSISDN must not exceed 20 digits
+    if (msisdnVar.length > 20) {
+        throw IllegalArgumentException("msisdn must not be longer than 20 digits")
+    }
+
+    // Append 'f' padding if number of digits is odd
+    if (msisdnVar.length % 2 > 0) {
+        msisdnVar += 'f'
+    }
+
+    // BCD length also includes NPI/ToN header
+    val bcdLen = msisdnVar.length / 2 + 1
+    val npiTon = (npi and 0x0f) or ((tonVar and 0x07) shl 4) or 0x80
+    val bcd = msisdnVar.swapNibbles().padEnd(10 * 2, 'f')  // pad to 10 octets
+
+    return String.format("%02x", bcdLen) + String.format("%02x", npiTon) + bcd + "ff".repeat(2)
+}

+ 49 - 27
app/src/main/java/com/example/uicceditor/MainActivity.kt

@@ -16,8 +16,7 @@ class MainActivity : AppCompatActivity() {
     private val TAG = "APDU"
 
     private val handler = Handler(Looper.getMainLooper())
-
-    private lateinit var apduChannel: ApduChannel
+    val AID = "D07002CA44900101"
 
     private val binding: ActivityMainBinding by lazy {
         ActivityMainBinding.inflate(layoutInflater)
@@ -33,34 +32,22 @@ class MainActivity : AppCompatActivity() {
             insets
         }
 
-        val sharedPrefs = getSharedPreferences(applicationInfo.packageName, MODE_PRIVATE)
-        binding.etAdm.setText(sharedPrefs.getString("adm", ""))
-        apduChannel = ApduChannel(getSystemService(TELEPHONY_SERVICE) as TelephonyManager)
+        val telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
 
-        binding.btnAuth.setOnClickListener {
-            val admKey = binding.etAdm.text.toString()
-            if (Regex("^\\d{8}$").matches(admKey)) {
-                apduChannel.authorize(admKey)
-                Toast.makeText(this, "Authorized", Toast.LENGTH_SHORT).show()
-                sharedPrefs.edit().putString("adm", admKey).apply()
-            } else {
-                Toast.makeText(this, "Invalid ADM", Toast.LENGTH_SHORT).show()
-            }
-        }
 
         binding.btnReadIccid.setOnClickListener {
-            val iccid = apduChannel.readIccid()
+            val apduChannel = ApduChannel(telephonyManager, AID)
+            val iccid = apduChannel.readICCID()
+            apduChannel.close()
             binding.etIccid.setText(iccid)
         }
 
         binding.btnWriteIccid.setOnClickListener {
-            if (!apduChannel.authorized) {
-                Toast.makeText(this, "Not authorized", Toast.LENGTH_SHORT).show()
-                return@setOnClickListener
-            }
             val iccid = binding.etIccid.text.toString()
             if (Regex("^[0-9]{20}$").matches(iccid)) {
-                apduChannel.writeIccid(iccid)
+                val apduChannel = ApduChannel(telephonyManager, AID)
+                apduChannel.writeICCID(iccid)
+                apduChannel.close()
                 Toast.makeText(this, "ICCID written", Toast.LENGTH_SHORT).show()
             } else {
                 Toast.makeText(this, "Invalid ICCID", Toast.LENGTH_SHORT).show()
@@ -68,19 +55,19 @@ class MainActivity : AppCompatActivity() {
         }
 
         binding.btnReadImsi.setOnClickListener {
-            val imsi = apduChannel.readImsi()
+            val apduChannel = ApduChannel(telephonyManager, AID)
+            val imsi = apduChannel.readIMSI()
+            apduChannel.close()
             binding.etImsi.setText(imsi)
         }
 
         binding.btnWriteImsi.setOnClickListener {
-            if (!apduChannel.authorized) {
-                Toast.makeText(this, "Not authorized", Toast.LENGTH_SHORT).show()
-                return@setOnClickListener
-            }
             try {
                 val imsi = binding.etImsi.text.toString()
                 if (Regex("^[0-9]{15,18}$").matches(imsi)) {
-                    apduChannel.writeImsi(imsi)
+                    val apduChannel = ApduChannel(telephonyManager, AID)
+                    apduChannel.writeIMSI(imsi)
+                    apduChannel.close()
                     Toast.makeText(this, "IMSI written", Toast.LENGTH_SHORT).show()
                 } else {
                     Toast.makeText(this, "Invalid IMSI", Toast.LENGTH_SHORT).show()
@@ -91,6 +78,41 @@ class MainActivity : AppCompatActivity() {
             }
         }
 
+        binding.btnReadMsisdn.setOnClickListener {
+            val apduChannel = ApduChannel(telephonyManager, AID)
+            apduChannel.select(SIMView.FID_MF)
+            apduChannel.select(SIMView.FID_DF_TELECOM)
+            apduChannel.select(SIMView.FID_EF_MSISDN)
+            val msisdn = decMSISDN(apduChannel.readRecord(1, 28).substring(28))?.third ?: ""
+            binding.etMsisdn.setText(msisdn)
+            apduChannel.close()
+        }
+
+        binding.btnWriteMsisdn.setOnClickListener {
+            val msisdn = binding.etMsisdn.text.toString()
+            Log.i(TAG, "MSISDN: ${encMSISDN(msisdn)}")
+            val apduChannel = ApduChannel(telephonyManager, AID)
+            apduChannel.select(SIMView.FID_MF)
+            apduChannel.select(SIMView.FID_DF_TELECOM)
+            apduChannel.select(SIMView.FID_EF_MSISDN)
+            apduChannel.writeRecord(1, encMSISDN(msisdn).padStart(56, 'F'))
+            apduChannel.close()
+        }
+
+        binding.btnReset.setOnClickListener {
+            val apduChannel = ApduChannel(telephonyManager, AID)
+            apduChannel.reset()
+            apduChannel.close()
+        }
+
+        binding.btnPlmn.setOnClickListener {
+            val apduChannel = ApduChannel(telephonyManager, AID)
+            apduChannel.select(SIMView.FID_MF)
+            apduChannel.select(SIMView.FID_DF_TELECOM)
+            apduChannel.select(SIMView.FID_EF_MSISDN)
+            apduChannel.readRecord(1, 28)
+            apduChannel.close()
+        }
     }
 
     fun getRandomString(length: Int): String {

+ 341 - 0
app/src/main/java/com/example/uicceditor/SIMView.kt

@@ -0,0 +1,341 @@
+package com.example.uicceditor
+
+class SIMView {
+    companion object {
+
+        // ------------------------------- Constants ------------------------------
+        /** File identifier : MF = "3F00"                                         */
+        const val FID_MF = "3F00"
+
+        /** DF under MF                                                           */
+        /** File identifier : DF TELECOM = "7F10"                                 */
+        const val FID_DF_TELECOM = "7F10"
+
+        /** File identifier : DF GSM = "7F20"                                     */
+        const val FID_DF_GSM = "7F20"
+
+        /** File identifier : DF DCS-1800 = "7F21"                                */
+        const val FID_DF_DCS_1800 = "7F21"
+
+        /** File identifier : DF IS-41 = "7F22"                                   */
+        const val FID_DF_IS_41 = "7F22"
+
+        /** File identifier : DF FP-CTS = "7F23"                                  */
+        const val FID_DF_FP_CTS = "7F23"
+
+        /** File identifier : DF PDC = "7F80"                                     */
+        const val FID_DF_PDC = "7F80"
+
+        /** File identifier : DF TETRA = "7F90"                                   */
+        const val FID_DF_TETRA = "7F90"
+
+        /** File identifier : DF TIA-EIA-136 = "7F24"                             */
+        const val FID_DF_TIA_EIA_136 = "7F24"
+
+        /** File identifier : DF TIA-EIA-95 = "7F25"                              */
+        const val FID_DF_TIA_EIA_95 = "7F25"
+
+
+        /** DF under DF TELECOM                                                   */
+        @Deprecated("Replaced by {@link #FID_DF_GRAPHICS} in version 7.4.0    ")
+        const val FID_DF_Graphics = "5F50"
+
+        /** File identifier : DF Graphics = "5F50" (under DF TELECOM)             */
+        const val FID_DF_GRAPHICS = "5F50"
+
+
+        /** DF under DF GSM                                                       */
+        /** File identifier : DF IRIDIUM = "5F30" (under DF GSM)                  */
+        const val FID_DF_IRIDIUM = "5F30"
+
+        /** File identifier : DF Globalstar = "5F31" (under DF GSM)               */
+        const val FID_DF_GLOBALSTAR = "5F31"
+
+        /** File identifier : DF ICO = "5F32" (under DF GSM)                      */
+        const val FID_DF_ICO = "5F32"
+
+        /** File identifier : DF ACeS = "5F33" (under DF GSM)                     */
+        const val FID_DF_ACES = "5F33"
+
+        /** File identifier : DF PCS-1900 = "5F40" (under DF GSM)                 */
+        const val FID_DF_PCS_1900 = "5F40"
+
+        /** File identifier : DF CTS = "5F60" (under DF GSM)                      */
+        const val FID_DF_CTS = "5F60"
+
+        /** File identifier : DF SoLSA = "5F70" (under DF GSM)                    */
+        const val FID_DF_SOLSA = "5F70"
+
+        /** File identifier : DF TIA-EIA-553= "5F40" (under DF GSM)               */
+        const val FID_DF_TIA_EIA_553 = "5F40"
+
+        /** File identifier : DF MExE = "5F3C" (under DF GSM)                     */
+        const val FID_DF_MEXE = "5F3C"
+
+
+        /** EF under MF                                                           */
+        /** File identifier : EF ICCID = "2FE2" (under MF)                        */
+        const val FID_EF_ICCID = "2FE2"
+
+        /** File identifier : EF ELP = "2F05" (under MF)                          */
+        const val FID_EF_ELP = "2F05"
+
+
+        /** EF under DF TELECOM                                                   */
+        /** File identifier : EF ADN = "6F3A" (under DF TELECOM)                  */
+        const val FID_EF_ADN = "6F3A"
+
+        /** File identifier : EF FDN = "6F3B" (under DF TELECOM)                  */
+        const val FID_EF_FDN = "6F3B"
+
+        /** File identifier : EF SMS = "6F3C" (under DF TELECOM)                  */
+        const val FID_EF_SMS = "6F3C"
+
+        /** File identifier : EF CCP = "6F3D" (under DF TELECOM)                  */
+        const val FID_EF_CCP = "6F3D"
+
+        /** File identifier : EF MSISDN = "6F40" (under DF TELECOM)               */
+        const val FID_EF_MSISDN = "6F40"
+
+        /** File identifier : EF SMSP = "6F42" (under DF TELECOM)                 */
+        const val FID_EF_SMSP = "6F42"
+
+        /** File identifier : EF SMSS = "6F43" (under DF TELECOM)                 */
+        const val FID_EF_SMSS = "6F43"
+
+        /** File identifier : EF LND = "6F44" (under DF TELECOM)                  */
+        const val FID_EF_LND = "6F44"
+
+        /** File identifier : EF SDN = "6F49" (under DF TELECOM)                  */
+        const val FID_EF_SDN = "6F49"
+
+        /** File identifier : EF EXT1 = "6F4A" (under DF TELECOM)                 */
+        const val FID_EF_EXT1 = "6F4A"
+
+        /** File identifier : EF EXT2 = "6F4B" (under DF TELECOM)                 */
+        const val FID_EF_EXT2 = "6F4B"
+
+        /** File identifier : EF EXT3 = "6F4C" (under DF TELECOM)                 */
+        const val FID_EF_EXT3 = "6F4C"
+
+        /** File identifier : EF BDN = "6F4D" (under DF TELECOM)                  */
+        const val FID_EF_BDN = "6F4D"
+
+        /** File identifier : EF EXT4 = "6F4E" (under DF TELECOM)                 */
+        const val FID_EF_EXT4 = "6F4E"
+
+        /** File identifier : EF SMSR = "6F47" (under DF TELECOM)                 */
+        const val FID_EF_SMSR = "6F47"
+
+        /** File identifier : EF ECCP = "6F4F" (under DF TELECOM)                 */
+        const val FID_EF_ECCP = "6F4F"
+
+        /** File identifier : EF CMI = "6F58" (under DF TELECOM)                  */
+        const val FID_EF_CMI = "6F58"
+
+
+        /** EF under DF Graphics under DF TELECOM                                 */
+        /** File identifier : EF IMG = "4F20" (under DF Graphics)                 */
+        const val FID_EF_IMG = "4F20"
+
+
+        /** EF under DF GSM                                                       */
+        /** File identifier : EF LP = "6F05" (under DF GSM)                       */
+        const val FID_EF_LP = "6F05"
+
+        /** File identifier : EF IMSI = "6F07" (under DF GSM)                     */
+        const val FID_EF_IMSI = "6F07"
+
+        /** File identifier : EF Kc = "6F20" (under DF GSM)                       */
+        const val FID_EF_KC = "6F20"
+
+        /** File identifier : EF PLMNsel = "6F30" (under DF GSM)                  */
+        const val FID_EF_PLMNSEL = "6F30"
+
+        /** File identifier : EF HPLMN = "6F31" (under DF GSM)                    */
+        const val FID_EF_HPLMN = "6F31"
+
+        /** File identifier : EF ACMmax = "6F37" (under DF GSM)                   */
+        const val FID_EF_ACMMAX = "6F37"
+
+        /** File identifier : EF SST = "6F38" (under DF GSM)                      */
+        const val FID_EF_SST = "6F38"
+
+        /** File identifier : EF ACM = "6F39" (under DF GSM)                      */
+        const val FID_EF_ACM = "6F39"
+
+        /** File identifier : EF GID1 = "6F3E" (under DF GSM)                     */
+        const val FID_EF_GID1 = "6F3E"
+
+        /** File identifier : EF GID2 = "6F3F" (under DF GSM)                     */
+        const val FID_EF_GID2 = "6F3F"
+
+        /** File identifier : EF SPN = "6F46" (under DF GSM)                      */
+        const val FID_EF_SPN = "6F46"
+
+        /** File identifier : EF PUCT = "6F41" (under DF GSM)                     */
+        const val FID_EF_PUCT = "6F41"
+
+        /** File identifier : EF CBMI = "6F45" (under DF GSM)                     */
+        const val FID_EF_CBMI = "6F45"
+
+        /** File identifier : EF BCCH = "6F74" (under DF GSM)                     */
+        const val FID_EF_BCCH = "6F74"
+
+        /** File identifier : EF ACC = "6F78" (under DF GSM)                      */
+        const val FID_EF_ACC = "6F78"
+
+        /** File identifier : EF FPLMN = "6F7B" (under DF GSM)                    */
+        const val FID_EF_FPLMN = "6F7B"
+
+        /** File identifier : EF LOCI = "6F7E" (under DF GSM)                     */
+        const val FID_EF_LOCI = "6F7E"
+
+        /** File identifier : EF AD = "6FAD" (under DF GSM)                       */
+        const val FID_EF_AD = "6FAD"
+
+        /** File identifier : EF Phase = "6FAE" (under DF GSM)                    */
+        const val FID_EF_PHASE = "6FAE"
+
+        /** File identifier : EF VGCS = "6FB1" (under DF GSM)                     */
+        const val FID_EF_VGCS = "6FB1"
+
+        /** File identifier : EF VGCSS = "6FB2" (under DF GSM)                    */
+        const val FID_EF_VGCSS = "6FB2"
+
+        /** File identifier : EF VBS = "6FB3" (under DF GSM)                      */
+        const val FID_EF_VBS = "6FB3"
+
+        /** File identifier : EF VBSS = "6FB4" (under DF GSM)                     */
+        const val FID_EF_VBSS = "6FB4"
+
+        /** File identifier : EF eMLPP = "6FB5" (under DF GSM)                    */
+        const val FID_EF_EMLPP = "6FB5"
+
+        /** File identifier : EF AAeM = "6FB6" (under DF GSM)                     */
+        const val FID_EF_AAEM = "6FB6"
+
+        /** File identifier : EF CBMID = "6F48" (under DF GSM)                    */
+        const val FID_EF_CBMID = "6F48"
+
+        /** File identifier : EF ECC = "6FB7" (under DF GSM)                      */
+        const val FID_EF_ECC = "6FB7"
+
+        /** File identifier : EF CBMIR = "6F50" (under DF GSM)                    */
+        const val FID_EF_CBMIR = "6F50"
+
+        /** File identifier : EF DCK = "6F2C" (under DF GSM)                      */
+        const val FID_EF_DCK = "6F2C"
+
+        /** File identifier : EF CNL = "6F32" (under DF GSM)                      */
+        const val FID_EF_CNL = "6F32"
+
+        /** File identifier : EF NIA = "6F51" (under DF GSM)                      */
+        const val FID_EF_NIA = "6F51"
+
+        /** File identifier : EF KcGPRS = "6F52" (under DF GSM)                   */
+        const val FID_EF_KCGPRS = "6F52"
+
+        /** File identifier : EF LOCIGPRS = "6F53" (under DF GSM)                 */
+        const val FID_EF_LOCIGPRS = "6F53"
+
+        /** File identifier : EF SUME = "6F54" (under DF GSM)                     */
+        const val FID_EF_SUME = "6F54"
+
+        /** File identifier : EF PLMNwAcT= "6F60" (under DF GSM)                  */
+        const val FID_EF_PLMNWACT = "6F60"
+
+        /** File identifier : EF OPLMNwAcT= "6F61"  (under DF GSM)                */
+        const val FID_EF_OPLMNWACT = "6F61"
+
+        /** File identifier : EF HPLMNwAcT= "6F62" (under DF GSM)                  */
+        const val FID_EF_HPLMNWACT = "6F62"
+
+        /** File identifier : EF CPBCCH= "6F63"(under DF GSM)                     */
+        const val FID_EF_CPBCCH = "6F63"
+
+        /** File identifier : EF InvScan= "6F64"(under DF GSM)                    */
+        const val FID_EF_INVSCAN = "6F64"
+
+
+        /** EF under DF SoLSA (under DF GSM)                                       */
+        /** File identifier : EF SAI = "4F30" (under DF SoLSA)                    */
+        const val FID_EF_SAI = "4F30"
+
+        /** File identifier : EF SLL = "4F31" (under DF SoLSA)                    */
+        const val FID_EF_SLL = "4F31"
+
+
+        /** EF under DF TIA-EIA-553(under DF GSM)                                 */
+        /** File identifier : EF SID = "4F80" (under DF TIA-EIA-553)              */
+        const val FID_EF_SID = "4F80"
+
+        /** File identifier : EF GPI = "4F81" (under DF TIA-EIA-553)              */
+        const val FID_EF_GPI = "4F81"
+
+        /** File identifier : EF IPC = "4F82" (under DF TIA-EIA-553)              */
+        const val FID_EF_IPC = "4F82"
+
+        /** File identifier : EF COUNT = "4F83" (under DF TIA-EIA-553)            */
+        const val FID_EF_COUNT = "4F83"
+
+        /** File identifier : EF NSID = "4F84" (under DF TIA-EIA-553)             */
+        const val FID_EF_NSID = "4F84"
+
+        /** File identifier : EF PSID = "4F85" (under DF TIA-EIA-553)             */
+        const val FID_EF_PSID = "4F85"
+
+        /** File identifier : EF NETSEL = "4F86" (under DF TIA-EIA-553)           */
+        const val FID_EF_NETSEL = "4F86"
+
+        /** File identifier : EF SPL = "4F87" (under DF TIA-EIA-553)              */
+        const val FID_EF_SPL = "4F87"
+
+        /** File identifier : EF MIN = "4F88" (under DF TIA-EIA-553)              */
+        const val FID_EF_MIN = "4F88"
+
+        /** File identifier : EF ACCOLC = "4F89" (under DF TIA-EIA-553)           */
+        const val FID_EF_ACCOLC = "4F89"
+
+        /** File identifier : EF FC1= "4F8A" (under DF TIA-EIA-553)               */
+        const val FID_EF_FC1 = "4F8A"
+
+        /** File identifier : EF S_ESN = "4F8B" (under DF TIA-EIA-553)            */
+        const val FID_EF_S_ESN = "4F8B"
+
+        /** File identifier : EF CSID= "4F8C" (under DF TIA-EIA-553)              */
+        const val FID_EF_CSID = "4F8C"
+
+        /** File identifier : EF REG_THRESH = "4F8D" (under DF TIA-EIA-553)       */
+        const val FID_EF_REG_THRESH = "4F8D"
+
+        /** File identifier : EF CCCH = "4F8E" (under DF TIA-EIA-553)             */
+        const val FID_EF_CCCH = "4F8E"
+
+        /** File identifier : EF LDCC = "4F8F" (under DF TIA-EIA-553)             */
+        const val FID_EF_LDCC = "4F8F"
+
+        /** File identifier : EF GSM_RECON = "4F90" (under DF TIA-EIA-553)        */
+        const val FID_EF_GSM_RECON = "4F90"
+
+        /** File identifier : EF AMPS_2_GSM = "4F91" (under DF TIA-EIA-553)       */
+        const val FID_EF_AMPS_2_GSM = "4F91"
+
+        /** File identifier : EF AMPS_UI = "4F93" (under DF TIA-EIA-553)          */
+        const val FID_EF_AMPS_UI = "4F93"
+
+
+        /** EF under DF MExE (under DF GSM)                                       */
+        /** File identifier : EF MExE_ST= "4F40" (under DF MExE)                  */
+        const val FID_EF_MEXE_ST = "4F40"
+
+        /** File identifier : EF ORPK= "4F41" (under DF MExE)                     */
+        const val FID_EF_ORPK = "4F41"
+
+        /** File identifier : EF ARPK= "4F42" (under DF MExE)                     */
+        const val FID_EF_ARPK = "4F42"
+
+        /** File identifier : EF TPRPK = "4F43" (under DF MExE)                   */
+        const val FID_EF_TPRPK = "4F43"
+    }
+}

+ 46 - 16
app/src/main/res/layout/activity_main.xml

@@ -18,32 +18,46 @@
         <com.google.android.material.textfield.TextInputLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="ADM Key">
+            android:layout_marginTop="8dp"
+            android:hint="ICCID">
 
             <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/et_adm"
+                android:id="@+id/et_iccid"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:inputType="numberDecimal"
                 android:lines="1" />
         </com.google.android.material.textfield.TextInputLayout>
 
-        <com.google.android.material.button.MaterialButton
-            android:id="@+id/btn_auth"
-            android:layout_width="wrap_content"
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="center"
             android:layout_marginTop="8dp"
-            android:text="AUTH" />
+            android:gravity="center"
+            android:orientation="horizontal">
+
+            <com.google.android.material.button.MaterialButton
+                android:id="@+id/btn_read_iccid"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="READ" />
+
+            <com.google.android.material.button.MaterialButton
+                android:id="@+id/btn_write_iccid"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="16dp"
+                android:text="WRITE" />
+        </LinearLayout>
 
         <com.google.android.material.textfield.TextInputLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="8dp"
-            android:hint="ICCID">
+            android:layout_marginTop="16dp"
+            android:hint="IMSI">
 
             <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/et_iccid"
+                android:id="@+id/et_imsi"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:inputType="numberDecimal"
@@ -58,13 +72,13 @@
             android:orientation="horizontal">
 
             <com.google.android.material.button.MaterialButton
-                android:id="@+id/btn_read_iccid"
+                android:id="@+id/btn_read_imsi"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text="READ" />
 
             <com.google.android.material.button.MaterialButton
-                android:id="@+id/btn_write_iccid"
+                android:id="@+id/btn_write_imsi"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginLeft="16dp"
@@ -75,10 +89,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="16dp"
-            android:hint="IMSI">
+            android:hint="MSISDN">
 
             <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/et_imsi"
+                android:id="@+id/et_msisdn"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:inputType="numberDecimal"
@@ -93,17 +107,33 @@
             android:orientation="horizontal">
 
             <com.google.android.material.button.MaterialButton
-                android:id="@+id/btn_read_imsi"
+                android:id="@+id/btn_read_msisdn"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:text="READ" />
 
             <com.google.android.material.button.MaterialButton
-                android:id="@+id/btn_write_imsi"
+                android:id="@+id/btn_write_msisdn"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginLeft="16dp"
                 android:text="WRITE" />
         </LinearLayout>
+
+        <Button
+            android:id="@+id/btn_reset"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="16dp"
+            android:text="RESET" />
+
+        <Button
+            android:id="@+id/btn_plmn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginTop="16dp"
+            android:text="PLMN" />
     </LinearLayout>
 </androidx.constraintlayout.widget.ConstraintLayout>