How to figure out which SIM received SMS in Dual SIM Android Device

Viewed 14597

I'm working on a project to sync sms received in a Android phone to a online database. I can get sender's number by calling getOriginatingAddress() method. But I can't find any solution to figure out which SIM in my device received the sms.

I searched the web about it, but couldn't find a way to figure this out. Is there any way to get SMS receiver's number?

7 Answers

I used Levente Püsök 's answer with a little change. But I did not test on all devices.

try {
Bundle bundle = intent.getExtras();
int slot = -1;
if (bundle != null) {
Set<String> keySet = bundle.keySet();
for(String key:keySet){
  switch (key){
    case "slot":slot = bundle.getInt("slot", -1);
    break;
    case "simId":slot = bundle.getInt("simId", -1);
    break;
    case "simSlot":slot = bundle.getInt("simSlot", -1);
    break;
    case "slot_id":slot = bundle.getInt("slot_id", -1);
    break;
    case "simnum":slot = bundle.getInt("simnum", -1);
    break;
    case "slotId":slot = bundle.getInt("slotId", -1);
    break;
    case "slotIdx":slot = bundle.getInt("slotIdx", -1);
    break;
    default:
      if(key.toLowerCase().contains("slot")|key.toLowerCase().contains("sim")){
       String value = bundle.getString(key, "-1");
       if(value.equals("0")|value.equals("1")|value.equals("2")){
         slot = bundle.getInt(key, -1);
       }
    }


  }
}

 Log.d("slot", "slot=>"+slot);

 }

}catch (Exception e){
Log.d(TAG, "Exception=>"+e);
 }

I achieved a very good result by combining the answers and updating

    private String detectSim(Bundle bundle) {
    int slot = -1;
    Set<String> keySet = bundle.keySet();
    for (String key : keySet)
    {
        switch (key) {
            case "phone":
                slot = bundle.getInt("phone", -1);
                break;
            case "slot":
                slot = bundle.getInt("slot", -1);
                break;
            case "simId":
                slot = bundle.getInt("simId", -1);
                break;
            case "simSlot":
                slot = bundle.getInt("simSlot", -1);
                break;
            case "slot_id":
                slot = bundle.getInt("slot_id", -1);
                break;
            case "simnum":
                slot = bundle.getInt("simnum", -1);
                break;
            case "slotId":
                slot = bundle.getInt("slotId", -1);
                break;
            case "slotIdx":
                slot = bundle.getInt("slotIdx", -1);
                break;
            default:
                if (key.toLowerCase().contains("slot") | key.toLowerCase().contains("sim")) {
                    String value = bundle.getString(key, "-1");
                    if (value.equals("0") | value.equals("1") | value.equals("2")) {
                        slot = bundle.getInt(key, -1);
                    }
                }
        }
    }

    Log.d("KKK",String.valueOf(slot));

    if (slot==0)
    {
        return "sim1";
    }
    else if (slot==1)
    {
        return "sim2";
    }
    else
    {
        return "undetected";
    }
}

this works fine with me try may help you

        if (bundle != null) {
            int slot = -1;
            Set<String> keySet = bundle.keySet();
            for(String key:keySet){
                if(key.equals("phone")){slot = bundle.getInt("phone", -1);}//in api 29
                else if(key.equals("slot")){slot = bundle.getInt("slot", -1);}
                else if(key.equals("slotId")){slot = bundle.getInt("slotId", -1);}
                else if(key.equals("slot_id")){slot = bundle.getInt("slot_id", -1);}
                else if(key.equals("slotIdx")){slot = bundle.getInt("slotIdx", -1);}
                else if(key.equals("simId")){slot = bundle.getInt("simId", -1);}
                else if(key.equals("simSlot")){slot = bundle.getInt("simSlot", -1);}
                else if(key.equals("simnum")){slot = bundle.getInt("simnum", -1);}
            }
            System.out.println("  The sim no is >>>  "+slot);

            //or get subscription and do next


            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                int sub = bundle.getInt("subscription", -1);
                SubscriptionManager manager = SubscriptionManager.from(context);
                SubscriptionInfo subnfo = manager.getActiveSubscriptionInfo(sub);//this requires READ_PHONE_STATE permission
                System.out.println(
                        "\n The sim no is >>>  "+subnfo.getSimSlotIndex()
                        //or subnfo.getCardId()
                        +"\n The sim no is >>>  "+subnfo.getCardId()//this requires api 29 and +++
                        //some infos may you need it
                        +"\n Phone No is >>>  "+subnfo.getNumber()//not guaranteed to be available
                        +"\n carrier Name is >>>  "+subnfo.getCarrierName()//the operator name
                        +"\n carrier display Name is >>>  "+subnfo.getDisplayName()//that you typed in dual sim settings
                );
            }

        }

In Kotlin and with the SubscriptionManager EXTRA Strings:

private var simSlotIndexLastSms = -100

private val subscriptionManager by lazy { getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager }

private val smsReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        intent ?: return

        try {
            val slotIndex: Int = when {
                isAndroid11Plus() -> {
                    intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, -100)
                }
                else -> {
                    val subscriptionId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, -100)
                    if (!hasPhonePermission()) {
                        -100
                    } else {
                        subscriptionManager
                            .activeSubscriptionInfoList
                            ?.find { it.subscriptionId == subscriptionId }
                            ?.simSlotIndex ?: -100
                    }
                }
            }

            simSlotIndexLastSms = if (slotIndex != -100) slotIndex.plus(1) else slotIndex // make it 1-index instead of 0-indexed so corresponds with physical slots 1 and 2

        } catch (e: Exception) {
            Timber.e(e, "Error while getting SIM slot index for incoming SMS")
        }
    }
}

private fun registerSmsReceiver() {
    val intentFilter = IntentFilter().apply {
        addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
        priority = 500
    }

    registerReceiver(smsReceiver, intentFilter)
}

For the smsReceiver to be called you need to request the runtime "android.permission.RECEIVE_SMS" permission from the user. To access the subscription list the READ_PHONE_STATE and READ_PHONE_NUMBERS (Only for Android 12 and up) are also required for this to work.

Tested on Android 10, 11 and 12 (Samsung, Pixel and Xiaomi devices).

mahmood-karimizade solution was excellent and it helped me. However I found some additional keys that could also contain the sim slot value, and this was on my Google Pixel 4a (Android 13) so I'd assume it's going to be mainstream. The key android.telephony.extra.SLOT_INDEX also contained sim slot index. And another thing was that sim slot index always seems to be an int value so I also updated the default condition in the switch expression to use bundle.getInt function instead of bundle.getString

private int detectSim(Bundle bundle) {
    int slot = -1;
    Set<String> keySet = bundle.keySet();
    for (String key : keySet)
    {
        switch (key) {
            case "phone":
                slot = bundle.getInt("phone", -1);
                break;
            case "slot":
                slot = bundle.getInt("slot", -1);
                break;
            case "simId":
                slot = bundle.getInt("simId", -1);
                break;
            case "simSlot":
                slot = bundle.getInt("simSlot", -1);
                break;
            case "slot_id":
                slot = bundle.getInt("slot_id", -1);
                break;
            case "simnum":
                slot = bundle.getInt("simnum", -1);
                break;
            case "slotId":
                slot = bundle.getInt("slotId", -1);
                break;
            case "slotIdx":
                slot = bundle.getInt("slotIdx", -1);
                break;
            case "android.telephony.extra.SLOT_INDEX": 
                // Present on Pixel 4a with physical sim + eSIM configuration
                slot = bundle.getInt("android.telephony.extra.SLOT_INDEX", -1);
                break;
            default:
                try {
                    if (key.toLowerCase().contains("slot") || key.toLowerCase().contains("sim")) {
                        int value = bundle.getInt(key, -1);
                        if (value == 0 || value == 1) {
                            slot = value;
                        }
                    }
                } catch(Error e) {}
        }
    }

    return slot;
}

SMSs have thread_id field, maybe unique for participants set. Maybe it is different for the same sender and the two SIMs and helps at least to differentiate.

Related