API с двумя SIM-картами для Android

Существует несколько вопросов о доступе к функциям с двумя SIM-картами через Android SDK, на все из которых даны краткие заявления о том, что такие функции не поддерживаются в Android.

Несмотря на это, телефоны с двумя SIM-картами существуют, и такие приложения, как MultiSim, по-видимому, могут обнаруживать это каким-то образом независимо от производителя.

Итак, начиная с этого подтверждения, позвольте мне попытаться задать несколько более острых вопросов:

  • Означает ли то, что "Android SDK не поддерживает функции нескольких SIM-карт", означает, что эти функции не существуют, или что это просто плохая идея использовать их?
  • Существует ли поставщик контента для Android или внутренний пакет (com.android...), который предоставляет информацию о SIM-карте? (Насколько я могу видеть в документации и коде, TelephonyManager не упоминает о нескольких SIM-картах)
  • Есть ли отчет какого-либо производителя, показывающий разработчикам несколько функций SIM-карты?
  • Если бы я искал недокументированную функциональность от производителя, как бы я поступил?

(Кстати, смысл всего этого просто в реализации этого алгоритма: отправить SMS с SIM-картой 1; если доставка не удалась, переключитесь на SIM-карту 2 и повторно отправьте сообщение таким образом.)

4 ответа

Решение

Android не поддерживает несколько функций SIM-карты до API 22. Но начиная с Android 5.1 (уровень API 22) и выше, Android начал поддерживать несколько SIM-карт. Подробнее о документации Android

Ссылка из этого оригинального ответа

Ты можешь использовать MultiSim библиотека для получения деталей с мультисим-устройств.

Доступная информация с каждой SIM-карты: IMEI, IMSI, серийный номер SIM-карты, состояние SIM-карты, код оператора SIM-карты, имя оператора SIM-карты, iso страны SIM-карты, код оператора сети, имя оператора сети, iso оператора сети, тип сети, статус роуминга.

Просто добавьте строки ниже в ваш скрипт Gradle уровня приложения:

dependencies {
    compile 'com.kirianov.multisim:multisim:2.0@aar'
}

Не забудьте добавить необходимые разрешения в AndroidManifest.xml:

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

Используйте похожий код в вашем коде:

MultiSimTelephonyManager multiSimTelephonyManager = new MultiSimTelephonyManager(this);
// or
MultiSimTelephonyManager multiSimTelephonyManager = new MultiSimTelephonyManager(this, new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    updateInfo();
  }
});


public void updateInfo() {

  // for update UI
  runOnUiThread(new Runnable() {
    @Override
    public void run() {
      multiSimTelephonyManager.update();
      useInfo();
    }
  }

  // for update background information
  multiSimTelephonyManager.update();
  useInfo();
}

public void useInfo() {

  // get number of slots:
  if (multiSimTelephonyManager != null) {
     multiSimTelephonyManager.sizeSlots();
  }

  // get info from each slot:
  if (multiSimTelephonyManager != null) {
    for(int i = 0; i < multiSimTelephonyManager.sizeSlots(); i++) {
      multiSimTelephonyManager.getSlot(i).getImei();
      multiSimTelephonyManager.getSlot(i).getImsi();
      multiSimTelephonyManager.getSlot(i).getSimSerialNumber();
      multiSimTelephonyManager.getSlot(i).getSimState();
      multiSimTelephonyManager.getSlot(i).getSimOperator();
      multiSimTelephonyManager.getSlot(i).getSimOperatorName();
      multiSimTelephonyManager.getSlot(i).getSimCountryIso();
      multiSimTelephonyManager.getSlot(i).getNetworkOperator();
      multiSimTelephonyManager.getSlot(i).getNetworkOperatorName();
      multiSimTelephonyManager.getSlot(i).getNetworkCountryIso();
      multiSimTelephonyManager.getSlot(i).getNetworkType();
      multiSimTelephonyManager.getSlot(i).isNetworkRoaming();
    }
  }
}

// or for devices above android 6.0
MultiSimTelephonyManager multiSimTelephonyManager = new MultiSimTelephonyManager(MyActivity.this, broadcastReceiverChange);

Usage:
// get info about slot 'i' by methods:
multiSimTelephonyManager.getSlot(i).

Force update info
// force update phone info (needed on devices above android 6.0 after confirm permissions request)
multiSimTelephonyManager.update();

Handle of permissions request (6.0+)
// in YourActivity for update info after confirm permissions request on  devices above android 6.0
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (multiSimTelephonyManager != null) {
        multiSimTelephonyManager.update();
    }
}

Есть 3 разных категории...

  1. поддерживаемые и документированные функции
  2. Возможности доступны и не документированы
  3. функции недоступны

Таким образом, функции Dual Sim доступны, но не документированы и, следовательно, официально не поддерживаются.

Сказав, что это не означает, что он не будет использоваться, это просто означает, что Android (или в этом отношении Google или даже Manufaturer) не обязан поддерживать функциональность ваших приложений.

Но это может сработать, например, контакты похожи.

Затем вы можете спросить, как все узнали бы о функциях, если в случае, если она не документирована... Эй, Android с открытым исходным кодом... иди в код и найти его для себя. Вот что, я думаю, разработчики мульти симов сделали.

Ты можешь использовать SubscriptionInfo класс, чтобы достичь этого,

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
        val mSubscriptionManager = getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
        // val mSubscriptionManager = SubscriptionManager.from(baseContext)
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
            val subscriptions = mSubscriptionManager.activeSubscriptionInfoList
        //loop through number of SIMS inserted
            for (subscriptionInfo in subscriptions) {
                //the number of this subscription if the calling app has been granted the READ_PHONE_NUMBERS permission, or an empty string otherwise
                Log.v("SIM", subscriptionInfo.number)
                //the ISO country code
                Log.v("SIM", subscriptionInfo.countryIso)
                //the name displayed to the user that identifies Subscription provider name
                Log.v("SIM", subscriptionInfo.carrierName.toString())
                //the name displayed to the user that identifies this subscription
                Log.v("SIM", subscriptionInfo.displayName.toString())
                //The MCC, as a string. This value may be null.
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    Log.v("SIM", subscriptionInfo.mccString.toString())
                } else {
                    Log.v("SIM", subscriptionInfo.mcc.toString())
                }
                //The MNC, as a string. This value may be null.
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    Log.v("SIM", subscriptionInfo.mncString.toString())
                } else {
                    Log.v("SIM", subscriptionInfo.mnc.toString())
                }
            }
        }
    }

Исходники библиотеки MultiSim теперь недоступны в VCS. Исходные коды
все еще доступны здесь https://mvnrepository.com/artifact/com.kirianov.multisim/multisim.
Я переписал его на Kotlin для личного использования.

class Slot {

    var imei: String? = null

    var imsi: String? = null

    var simState = -1

    val simStates = hashSetOf<Int>()

    var simSerialNumber: String? = null

    var simOperator: String? = null

    var simCountryIso: String? = null

    fun setSimState(state: Int?) {
        if (state == null) {
            simState = -1
            return
        }
        simState = state
    }

    private fun compare(slot: Slot?): Boolean {
        return if (slot != null) {
            imei == slot.imei && imsi == slot.imsi && simSerialNumber == slot.simSerialNumber
        } else false
    }

    fun indexIn(slots: List<Slot>?): Int {
        if (slots == null) {
            return -1
        }
        for (i in slots.indices) {
            if (compare(slots[i])) {
                return i
            }
        }
        return -1
    }

    fun containsIn(slots: List<Slot>?): Boolean {
        if (slots == null) {
            return false
        }
        for (slot in slots) {
            if (compare(slot)) {
                return true
            }
        }
        return false
    }

    override fun toString(): String {
        return "Slot(" +
            "imei=$imei, " +
            "imsi=$imsi, " +
            "simState=$simState, " +
            "simStates=$simStates, " +
            "simSerialNumber=$simSerialNumber, " +
            "simOperator=$simOperator, " +
            "simCountryIso=$simCountryIso" +
            ")"
    }
}

Также я удаляю собственные зависимости lib

import android.Manifest
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.telephony.SubscriptionManager
import android.text.TextUtils
import domain.shadowss.extension.areGranted
import domain.shadowss.extension.isLollipopMR1Plus
import domain.shadowss.extension.isMarshmallowPlus
import domain.shadowss.extension.isOreoPlus
import domain.shadowss.model.Slot
import org.jetbrains.anko.telephonyManager
import timber.log.Timber
import java.lang.ref.WeakReference
import java.lang.reflect.Modifier
import java.util.*

/**
 * https://mvnrepository.com/artifact/com.kirianov.multisim/multisim
 */
@Suppress("MemberVisibilityCanBePrivate")
class MultiSimManager(context: Context) {

    private val reference = WeakReference(context)

    val slots = arrayListOf<Slot>()
        @Synchronized get

    @Suppress("unused")
    val dualMcc: Pair<String?, String?>
        @SuppressLint("MissingPermission")
        @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
        get() = reference.get()?.run {
            if (isLollipopMR1Plus()) {
                if (areGranted(Manifest.permission.READ_PHONE_STATE)) {
                    val subscriptionManager =
                        getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
                    val list = subscriptionManager.activeSubscriptionInfoList
                    return list.getOrNull(0)?.mcc?.toString()
                        ?.padStart(3, '0') to list.getOrNull(1)?.mcc?.toString()
                        ?.padStart(3, '0')
                }
            }
            null to null
        } ?: null to null

    @Synchronized
    @SuppressLint("MissingPermission")
    fun updateData(): String? = reference.get()?.run {
        if (areGranted(Manifest.permission.READ_PHONE_STATE)) {
            val error = try {
                var slotNumber = 0
                while (true) {
                    val slot = touchSlot(slotNumber)
                    if (slot == null) {
                        for (i in slotNumber until slots.size) {
                            slots.removeAt(i)
                        }
                        break
                    }
                    if (slot.containsIn(slots) && slot.indexIn(slots) < slotNumber) {
                        // protect from Alcatel infinity bug
                        break
                    }
                    slots.apply {
                        when {
                            size > slotNumber -> {
                                removeAt(slotNumber)
                                add(slotNumber, slot)
                            }
                            size == slotNumber -> add(slot)
                        }
                    }
                    slotNumber++
                }
                null
            } catch (e: Throwable) {
                Timber.e(e)
                e.toString()
            }
            // below is my custom logic only which was found on practice
            slots.removeAll { it.imsi == null || it.simOperator?.trim()?.isEmpty() != false }
            val imsi = arrayListOf<String?>()
            slots.forEachReversedWithIndex { i, slot ->
                if (imsi.contains(slot.imsi)) {
                    slots.removeAt(i)
                } else {
                    imsi.add(slot.imsi)
                    slot.simStates.apply {
                        clear()
                        addAll(slots.filter { it.imsi == slot.imsi }.map { it.simState })
                    }
                }
            }
            error
        } else {
            slots.clear()
            null
        }
    }

    @Suppress("SpellCheckingInspection", "DEPRECATION")
    @SuppressLint("MissingPermission", "HardwareIds")
    private fun touchSlot(slotNumber: Int): Slot? = reference.get()?.run {
        val slot = Slot()
        val telephonyManager = telephonyManager
        Timber.v("telephonyManager [$telephonyManager] ${telephonyManager.deviceId}")
        val subscriberIdIntValue = ArrayList<String>()
        val subscriberIdIntIndex = ArrayList<Int>()
        for (i in 0..99) {
            val subscriber = runMethodReflect(
                telephonyManager,
                "android.telephony.TelephonyManager",
                "getSubscriberId",
                arrayOf(i),
                null
            ) as? String
            if (subscriber != null && !subscriberIdIntValue.contains(subscriber)) {
                subscriberIdIntValue.add(subscriber)
                subscriberIdIntIndex.add(i)
            }
        }
        var subIdInt =
            if (subscriberIdIntIndex.size > slotNumber) subscriberIdIntIndex[slotNumber] else null
        if (subIdInt == null) {
            try {
                subIdInt = runMethodReflect(
                    telephonyManager,
                    "android.telephony.TelephonyManager",
                    "getSubId",
                    arrayOf(slotNumber),
                    null
                ).toString().toInt()
            } catch (ignored: Throwable) {
            }
        }
        Timber.v("subIdInt $subIdInt")
        val subscriberIdLongValue = ArrayList<String>()
        val subscriberIdLongIndex = ArrayList<Long>()
        for (i in 0L until 100L) {
            val subscriber = runMethodReflect(
                telephonyManager,
                "android.telephony.TelephonyManager",
                "getSubscriberId",
                arrayOf(i),
                null
            ) as? String
            runMethodReflect(
                telephonyManager,
                "android.telephony.TelephonyManagerSprd",
                "getSubInfoForSubscriber",
                arrayOf(i),
                null
            ) ?: continue
            if (subscriber != null && !subscriberIdLongValue.contains(subscriber)) {
                subscriberIdLongValue.add(subscriber)
                subscriberIdLongIndex.add(i)
            }
        }
        if (subscriberIdLongIndex.size <= 0) {
            for (i in 0L until 100L) {
                val subscriber = runMethodReflect(
                    telephonyManager,
                    "android.telephony.TelephonyManager",
                    "getSubscriberId",
                    arrayOf(i),
                    null
                ) as? String
                if (subscriber != null && !subscriberIdLongValue.contains(subscriber)) {
                    subscriberIdLongValue.add(subscriber)
                    subscriberIdLongIndex.add(i)
                }
            }
        }
        var subIdLong =
            if (subscriberIdLongIndex.size > slotNumber) subscriberIdLongIndex[slotNumber] else null
        if (subIdLong == null) {
            subIdLong = runMethodReflect(
                telephonyManager,
                "android.telephony.TelephonyManager",
                "getSubId",
                arrayOf(slotNumber),
                null
            ) as? Long
        }
        Timber.v("subIdLong $subIdLong")
        val listParamsSubs = ArrayList<Any?>()
        if (subIdInt != null && !listParamsSubs.contains(subIdInt)) {
            listParamsSubs.add(subIdInt)
        }
        if (subIdLong != null && !listParamsSubs.contains(subIdLong)) {
            listParamsSubs.add(subIdLong)
        }
        if (!listParamsSubs.contains(slotNumber)) {
            listParamsSubs.add(slotNumber)
        }
        val objectParamsSubs = listParamsSubs.toTypedArray()
        for (i in objectParamsSubs.indices) {
            Timber.v("SPAM PARAMS_SUBS [$i]=[${objectParamsSubs[i]}]")
        }
        val listParamsSlot = ArrayList<Any?>()
        if (!listParamsSlot.contains(slotNumber)) {
            listParamsSlot.add(slotNumber)
        }
        if (subIdInt != null && !listParamsSlot.contains(subIdInt)) {
            listParamsSlot.add(subIdInt)
        }
        if (subIdLong != null && !listParamsSlot.contains(subIdLong)) {
            listParamsSlot.add(subIdLong)
        }
        val objectParamsSlot = listParamsSlot.toTypedArray()
        for (i in objectParamsSlot.indices) {
            Timber.v("SPAM PARAMS_SLOT [$i]=[${objectParamsSlot[i]}]")
        }
        // firstly all Int params, then all Long params
        Timber.v("------------------------------------------")
        Timber.v("SLOT [$slotNumber]")
        if (isMarshmallowPlus()) {
            slot.imei = telephonyManager.getDeviceId(slotNumber)
        }
        if (slot.imei == null) {
            slot.imei = iterateMethods("getDeviceId", objectParamsSlot) as? String
        }
        if (slot.imei == null) {
            slot.imei = runMethodReflect(
                null,
                "com.android.internal.telephony.Phone",
                null,
                null,
                "GEMINI_SIM_" + (slotNumber + 1)
            ) as? String
        }
        if (slot.imei == null) {
            slot.imei = runMethodReflect(
                getSystemService("phone" + (slotNumber + 1)),
                null,
                "getDeviceId",
                null,
                null
            ) as? String
        }
        Timber.v("IMEI [${slot.imei}]")
        if (slot.imei == null) {
            when (slotNumber) {
                0 -> {
                    slot.imei = if (isOreoPlus()) {
                        telephonyManager.imei
                    } else {
                        telephonyManager.deviceId
                    }
                    slot.imsi = telephonyManager.subscriberId
                    slot.simState = telephonyManager.simState
                    slot.simOperator = telephonyManager.simOperator
                    slot.simSerialNumber = telephonyManager.simSerialNumber
                    slot.simCountryIso = telephonyManager.simCountryIso
                    return slot
                }
            }
        }
        if (slot.imei == null) {
            return null
        }
        slot.setSimState(iterateMethods("getSimState", objectParamsSlot) as? Int)
        Timber.v("SIMSTATE [${slot.simState}]")
        slot.imsi = iterateMethods("getSubscriberId", objectParamsSubs) as? String
        Timber.v("IMSI [${slot.imsi}]")
        slot.simSerialNumber = iterateMethods("getSimSerialNumber", objectParamsSubs) as? String
        Timber.v("SIMSERIALNUMBER [${slot.simSerialNumber}]")
        slot.simOperator = iterateMethods("getSimOperator", objectParamsSubs) as? String
        Timber.v("SIMOPERATOR [${slot.simOperator}]")
        slot.simCountryIso = iterateMethods("getSimCountryIso", objectParamsSubs) as? String
        Timber.v("SIMCOUNTRYISO [${slot.simCountryIso}]")
        Timber.v("------------------------------------------")
        return slot
    }

    @SuppressLint("WrongConstant")
    private fun iterateMethods(methodName: String?, methodParams: Array<Any?>): Any? =
        reference.get()?.run {
            if (methodName == null || methodName.isEmpty()) {
                return null
            }
            val telephonyManager = telephonyManager
            val instanceMethods = ArrayList<Any?>()
            val multiSimTelephonyManagerExists = telephonyManager.toString()
                .startsWith("android.telephony.MultiSimTelephonyManager")
            for (methodParam in methodParams) {
                if (methodParam == null) {
                    continue
                }
                val objectMulti = if (multiSimTelephonyManagerExists) {
                    runMethodReflect(
                        null,
                        "android.telephony.MultiSimTelephonyManager",
                        "getDefault",
                        arrayOf(methodParam),
                        null
                    )
                } else {
                    telephonyManager
                }
                if (!instanceMethods.contains(objectMulti)) {
                    instanceMethods.add(objectMulti)
                }
            }
            if (!instanceMethods.contains(telephonyManager)) {
                instanceMethods.add(telephonyManager)
            }
            val telephonyManagerEx = runMethodReflect(
                null,
                "com.mediatek.telephony.TelephonyManagerEx",
                "getDefault",
                null,
                null
            )
            if (!instanceMethods.contains(telephonyManagerEx)) {
                instanceMethods.add(telephonyManagerEx)
            }
            val phoneMsim = getSystemService("phone_msim")
            if (!instanceMethods.contains(phoneMsim)) {
                instanceMethods.add(phoneMsim)
            }
            if (!instanceMethods.contains(null)) {
                instanceMethods.add(null)
            }
            var result: Any?
            for (methodSuffix in suffixes) {
                for (className in classNames) {
                    for (instanceMethod in instanceMethods) {
                        for (methodParam in methodParams) {
                            if (methodParam == null) {
                                continue
                            }
                            result = runMethodReflect(
                                instanceMethod,
                                className,
                                methodName + methodSuffix,
                                if (multiSimTelephonyManagerExists) null else arrayOf(methodParam),
                                null
                            )
                            if (result != null) {
                                return result
                            }
                        }
                    }
                }
            }
            return null
        }

    private fun runMethodReflect(
        instanceInvoke: Any?,
        classInvokeName: String?,
        methodName: String?,
        methodParams: Array<Any>?,
        field: String?
    ): Any? {
        var result: Any? = null
        try {
            val classInvoke = when {
                classInvokeName != null -> Class.forName(classInvokeName)
                instanceInvoke != null -> instanceInvoke.javaClass
                else -> return null
            }
            if (field != null) {
                val fieldReflect = classInvoke.getField(field)
                val accessible = fieldReflect.isAccessible
                fieldReflect.isAccessible = true
                result = fieldReflect.get(null).toString()
                fieldReflect.isAccessible = accessible
            } else {
                var classesParams: Array<Class<*>?>? = null
                if (methodParams != null) {
                    classesParams = arrayOfNulls(methodParams.size)
                    for (i in methodParams.indices) {
                        classesParams[i] = when {
                            methodParams[i] is Int -> Int::class.javaPrimitiveType
                            methodParams[i] is Long -> Long::class.javaPrimitiveType
                            methodParams[i] is Boolean -> Boolean::class.javaPrimitiveType
                            else -> methodParams[i].javaClass
                        }
                    }
                }
                val method = if (classesParams != null) {
                    classInvoke.getDeclaredMethod(methodName.toString(), *classesParams)
                } else {
                    classInvoke.getDeclaredMethod(methodName.toString())
                }
                val accessible = method.isAccessible
                method.isAccessible = true
                result = if (methodParams != null) {
                    method.invoke(instanceInvoke ?: classInvoke, *methodParams)
                } else {
                    method.invoke(instanceInvoke ?: classInvoke)
                }
                method.isAccessible = accessible
            }
        } catch (ignored: Throwable) {
        }
        return result
    }

    @Suppress("unused")
    val allMethodsAndFields: String
        get() = """
            Default: ${reference.get()?.telephonyManager}${'\n'}
            ${printAllMethodsAndFields("android.telephony.TelephonyManager")}
            ${printAllMethodsAndFields("android.telephony.MultiSimTelephonyManager")}
            ${printAllMethodsAndFields("android.telephony.MSimTelephonyManager")}
            ${printAllMethodsAndFields("com.mediatek.telephony.TelephonyManager")}
            ${printAllMethodsAndFields("com.mediatek.telephony.TelephonyManagerEx")}
            ${printAllMethodsAndFields("com.android.internal.telephony.ITelephony")}
        """.trimIndent()

    private fun printAllMethodsAndFields(className: String): String {
        val builder = StringBuilder()
        builder.append("========== $className\n")
        try {
            val cls = Class.forName(className)
            for (method in cls.methods) {
                val params = method.parameterTypes.map { it.name }
                builder.append(
                    "M: ${method.name} [${params.size}](${TextUtils.join(
                        ",",
                        params
                    )}) -> ${method.returnType} ${if (Modifier.isStatic(method.modifiers)) "(static)" else ""}\n"
                )
            }
            for (field in cls.fields) {
                builder.append("F: ${field.name} ${field.type}\n")
            }
        } catch (e: Throwable) {
            builder.append("E: $e\n")
        }
        return builder.toString()
    }

    companion object {

        private val classNames = arrayOf(
            null,
            "android.telephony.TelephonyManager",
            "android.telephony.MSimTelephonyManager",
            "android.telephony.MultiSimTelephonyManager",
            "com.mediatek.telephony.TelephonyManagerEx",
            "com.android.internal.telephony.Phone",
            "com.android.internal.telephony.PhoneFactory"
        )

        private val suffixes = arrayOf(
            "",
            "Gemini",
            "Ext",
            "Ds",
            "ForSubscription",
            "ForPhone"
        )
    }
}

Может быть, это будет кому-то полезно

 <receiver
     android:name=".SimChangedReceiver"
    android:enabled="true"
    android:process=":remote" >
    <intent-filter>
        <action android:name="android.intent.action.SIM_STATE_CHANGED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
 </receiver>

SimChangedReceiver class

public class SimChangedReceiver extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (action.equalsIgnoreCase("android.intent.action.SIM_STATE_CHANGED")) {
        Log.d("SimChangedReceiver", "--> SIM state changed <--");   

      //            do code  whatever u want to apply action                //

    }
   }
}

это также работает для Dual SIM, и вам не нужно вызывать этот приемник, потому что он будет работать удаленно

Другие вопросы по тегам