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

9

14

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?

Livre answered 13/3, 2016 at 9:45 Comment(5)
This post may help: #14051523Topping
Thanks for your comment... But your link shows how to get the first SIM active in the phone, not which SIM received a smsLivre
I mean if you know the phone number then you know which sim received the message. Right?Topping
Actually in Dual SIM phone, any one number will receive the sms... I can know the number using the TelephonyManager, but the problem persists that I cannot figure out which one out of two SIM received the smsLivre
the same issue here #28762649Solve
S
22

I had some really hard time with this problem and finally I found a solution, although i tested it only above api level 22.

You have to take a look at the extra information in the received intent. In my case there are two keys in the extra Bundle of the intent which are useful: "slot" and "subscription".

Here is the example:

public class IncomingSms extends BroadcastReceiver {

          public void onReceive(Context context, Intent intent) {

                // Retrieves a map of extended data from the intent.
                Bundle bundle = intent.getExtras();

                int slot = bundle.getInt("slot", -1);
                int sub = bundle.getInt("subscription", -1);

                /*
                  Handle the sim info
                */
            }
}

I did not find documentation about the keys in the Bundle so this could be device/manufacturer dependent, i can imagine that the keys are different or something like that. You can verify this by dumping the key set of the bundle:

Set<string> keyset = bundle.keySet();

EDIT:

The information about the phone number of the SIM card may not be available at all since if it is not stored on the SIM card it is likely cannot be queried, but all other information will be available through the SubscriptionManager:

SubscriptionManager manager = SubscriptionManager.from(appContext);
SubscriptionInfo = manager.getActiveSubscriptionInfo(sub);

or

SubscriptionInfo = manager.getActiveSubscriptionInfoForSimSlotIndex(slot);

And there are some useful info in the SubscriptionInfo such as the phone number which one is not guaranteed to be available as I explained above.

I also forgot to mention that the Dual-SIM support in stock android was added from api level 22.

Simonetta answered 11/8, 2016 at 12:57 Comment(3)
Thanks for your answer... I found documentation about it on Android Developer site developer.android.com/reference/android/telephony/…... The thing is, Dual SIM support is added from API level 22 and upwards Also, this answer shows the same information https://mcmap.net/q/88841/-android-dual-sim-card-apiLivre
Well, forgive me if i was not clear on this. It is definitely well documented that the official support for dual-sim functionality was added in API level 22, but i did not find any documentation about the keys in the Bundle that comes with the incoming sms intent. The problem here is that you want the phone number of the sim card on which the sms was received. This information is not guaranteed to be available since if it is not stored physically on the sim card (for example your carrier assigns it dynamically ) it can't be queried.Umbilical
Thanks... I also found that, that's why I changed the solution to use in only single-sim based phonesLivre
I
4

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);
 }
Inceptive answered 17/6, 2018 at 20:10 Comment(0)
T
4

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";
    }
}
Turbellarian answered 16/6, 2021 at 9:42 Comment(0)
G
1

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).

Grolier answered 28/12, 2021 at 19:29 Comment(0)
M
1

I was able to achieve this in Android 11. In my case both of my sim cards belonged to the same network. All my sms's were either sub_id 4 (for sim slot 1) or sub_id 2 (for sim slot 2).

result

package com.example.smsjavaapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.net.Uri;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String[] requiredPermissions = new String[]{, };
        int PERMISSION_READ_PHONE_STATE = 1;
        int PERMISSION_READ_SMS = 2;
        SubscriptionManager sm = SubscriptionManager.from(this.getApplicationContext());
        if (ActivityCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{"android.permission.READ_PHONE_STATE"}, PERMISSION_READ_PHONE_STATE);
        }
        if (ActivityCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{"android.permission.READ_SMS"}, PERMISSION_READ_SMS);
        }

        List<SubscriptionInfo> subscriptionListInfo = sm.getActiveSubscriptionInfoList();
        for (SubscriptionInfo info : subscriptionListInfo) {
            Log.d("MAIN ACTIVITY", String.valueOf(info.getSubscriptionId())+" belongs to slot : "+String.valueOf(info.getSimSlotIndex()));
        }

        Cursor cursor = getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, null);

        ArrayList<MySMS> arr = new ArrayList<MySMS>();
        cursor.moveToFirst();

        do{
            MySMS sms = new MySMS();
            sms.sim_id = -1;
            for(int idx=0;idx<cursor.getColumnCount();idx++)
            {

                if( cursor.getColumnName(idx).equals("sub_id")) {
                    sms.sim_id = Integer.parseInt(cursor.getString(idx));;
                }
                else if(cursor.getColumnName(idx).equals("body")) {
                    sms.message = cursor.getString(idx);
                }

            }

            if(sms.sim_id == 4) {
                arr.add(sms);
            }
        } while (cursor.moveToNext() && arr.size() < 100);

        Log.d("SMS Count", String.valueOf(arr.size()));

        for(int i=0;i<arr.size();i++) {
            Log.d(String.valueOf(arr.get(i).sim_id), arr.get(i).message);
        }
    }
}

The MySMS class is my own class with 2 variables in it sim_id and message. You can write your own sms class and store the values you need and play with them.

Reference: How do get SIM slot index of inbox SMS in android by ContentProvider?

Moke answered 16/6, 2023 at 20:11 Comment(0)
S
0

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
                );
            }

        }
Solve answered 12/5, 2021 at 2:30 Comment(0)
M
0

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;
}
Mahratta answered 18/9, 2022 at 14:18 Comment(0)
O
0

Kotlin Solution

This code doesn't show any warning

private fun detectSim(bundle: Bundle): Int {
    var slot = -1
    try {
        val keySet = bundle.keySet()
        for (key in keySet) {
            when (key) {
                "slot" -> slot = bundle.getInt("slot", -1)
                "simId" -> slot = bundle.getInt("simId", -1)
                "simSlot" -> slot = bundle.getInt("simSlot", -1)
                "slot_id" -> slot = bundle.getInt("slot_id", -1)
                "simnum" -> slot = bundle.getInt("simnum", -1)
                "slotId" -> slot = bundle.getInt("slotId", -1)
                "slotIdx" -> slot = bundle.getInt("slotIdx", -1)
                else -> if (key.lowercase(Locale.getDefault())
                        .contains("slot") or key.lowercase(
                        Locale.getDefault()
                    ).contains("sim")
                ) {
                    val value = bundle.get(key).toString()
                    if ((value == "0") or (value == "1") or (value == "2")) {
                        slot = bundle.getInt(key, -1)
                    }
                }
            }
        }
        Log.d("slot", "slot=>$slot")
    } catch (e: java.lang.Exception) {
        Log.d(TAG, "Exception=>$e")
    }
    return slot
}
Oestriol answered 1/11, 2022 at 7:6 Comment(0)
I
-1

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.

Indisposed answered 4/11, 2019 at 16:23 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.