How to send a SMS using SMSmanager in Dual SIM mobile?
with regard to @Vibhav solution, here is how I could successfully implement it
method = Class.forName("android.telephony.SubscriptionManager").getDeclaredMethod("getSubId", int.class);
method.setAccessible(true);
int simID = 1; //while simID is the slot number of your second simCard
param = (int[]) method.invoke(null, new Integer(simID));
int inst = param[0];
smsMan = SmsManager.getSmsManagerForSubscriptionId(inst);
smsMan.sendTextMessage(toNum, null, smsText, null, null);
This solution worked fine with me, but I would highly recommend this much cleaner solution (no reflection needed, works for API level 22+) found here https://stackoverflow.com/a/51380282/3427883
Maher's solution is almost right.
I tried it on Motorola motog 5.1 android (single sim), but his solution with reading table content://telephony/siminfo
had one slight bug:
on my motorola there was no field slot
but sim_id
rest of this was fine and identical.
I use this way to manage which sim to use for sending SMS even long message .. Its working on my dual sim phone Lenovo A319 (4.4.3), no need for root. its built on reflection.
import android.app.PendingIntent;
import android.content.Context;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Apipas on 6/4/15.
*/
public class SimUtil {
public static boolean sendSMS(Context ctx, int simID, String toNum, String centerNum, String smsText, PendingIntent sentIntent, PendingIntent deliveryIntent) {
String name;
try {
if (simID == 0) {
name = "isms";
// for model : "Philips T939" name = "isms0"
} else if (simID == 1) {
name = "isms2";
} else {
throw new Exception("can not get service which for sim '" + simID + "', only 0,1 accepted as values");
}
Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class);
method.setAccessible(true);
Object param = method.invoke(null, name);
method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", IBinder.class);
method.setAccessible(true);
Object stubObj = method.invoke(null, param);
if (Build.VERSION.SDK_INT < 18) {
method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, PendingIntent.class, PendingIntent.class);
method.invoke(stubObj, toNum, centerNum, smsText, sentIntent, deliveryIntent);
} else {
method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, String.class, PendingIntent.class, PendingIntent.class);
method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsText, sentIntent, deliveryIntent);
}
return true;
} catch (ClassNotFoundException e) {
Log.e("apipas", "ClassNotFoundException:" + e.getMessage());
} catch (NoSuchMethodException e) {
Log.e("apipas", "NoSuchMethodException:" + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("apipas", "InvocationTargetException:" + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("apipas", "IllegalAccessException:" + e.getMessage());
} catch (Exception e) {
Log.e("apipas", "Exception:" + e.getMessage());
}
return false;
}
public static boolean sendMultipartTextSMS(Context ctx, int simID, String toNum, String centerNum, ArrayList<String> smsTextlist, ArrayList<PendingIntent> sentIntentList, ArrayList<PendingIntent> deliveryIntentList) {
String name;
try {
if (simID == 0) {
name = "isms";
// for model : "Philips T939" name = "isms0"
} else if (simID == 1) {
name = "isms2";
} else {
throw new Exception("can not get service which for sim '" + simID + "', only 0,1 accepted as values");
}
Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class);
method.setAccessible(true);
Object param = method.invoke(null, name);
method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", IBinder.class);
method.setAccessible(true);
Object stubObj = method.invoke(null, param);
if (Build.VERSION.SDK_INT < 18) {
method = stubObj.getClass().getMethod("sendMultipartText", String.class, String.class, List.class, List.class, List.class);
method.invoke(stubObj, toNum, centerNum, smsTextlist, sentIntentList, deliveryIntentList);
} else {
method = stubObj.getClass().getMethod("sendMultipartText", String.class, String.class, String.class, List.class, List.class, List.class);
method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsTextlist, sentIntentList, deliveryIntentList);
}
return true;
} catch (ClassNotFoundException e) {
Log.e("apipas", "ClassNotFoundException:" + e.getMessage());
} catch (NoSuchMethodException e) {
Log.e("apipas", "NoSuchMethodException:" + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("apipas", "InvocationTargetException:" + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("apipas", "IllegalAccessException:" + e.getMessage());
} catch (Exception e) {
Log.e("apipas", "Exception:" + e.getMessage());
}
return false;
}
}
Add permission:
<uses-permission android:name="android.permission.SEND_SMS"/>
then just call that (bloody) static method like this :)
To use SIM1:
SimUtil.sendSMS(this,0,"00970XXXXXXXXX",null,"Hi Stackoverflow! its me Maher. Sent by sim1",null,null);
To use SIM2:
SimUtil.sendSMS(this,1,"00970XXXXXXXXX",null,"Hi Stackoverflow! its me Maher. Sent by sim2",null,null);
But wait...that won't work if message is longer than 160 characters.. so better way:
String textSMS;
//short <160
// textSMS = "Hi Stackoverflow! its me Maher.";
//long >160
textSMS = "Hi Jerusalem, hi Cairo, Hi Prague, hi Baghdad, hi Riyadh, hi Jeddah, hi Dammam, hi Aleppo, hi Casablanca, hi Damascus, hi Alexandria, hi Algiers, hi Mosul, hi Basra, hi Arabia, hi Tripoli, hi Amman, hi Kuwait, hi Beirut, hi Abu Dhabi";
int simID = 0;//0:sim_1, 1:sim_2
ArrayList<String> messageList = SmsManager.getDefault().divideMessage(textSMS);
if (messageList.size() > 1) {
SimUtil.sendMultipartTextSMS(this, simID, "00972XXXXXXXXX", null, messageList, null, null);
} else {
SimUtil.sendSMS(this, simID, "00972XXXXXXXXX", null, textSMS, null, null);
}
so you can safely pass message body without worrying about length.
------------UPDATE 09.10.2016----------
To use PendingIntent/DeliveryIntent in MultipartMessage.. just create ArrayList with same content and pass it. Here is an implementation of creating List of PendingIntent:
final static String sSMSManagerIntentSENT = "package.DeliveryReport.SMS_SENT";
int numParts = parts.size();
ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
for (int i = 0; i < numParts; i++) {
Intent pendingIntent = new Intent(sSMSManagerIntentSENT);
//optional if you want to keep info about what action has been done for feedback or analysis later when message is sent
pendingIntent.putExtra("package.DeliveryReport.phoneNumber", phoneNo); // receiver phoneNo
pendingIntent.putExtra("package.DeliveryReport.textSMS", msg);// msg body
pendingIntent.putExtra("SIM", simID); // which sim is sending this message
pendingIntents.add(PendingIntent.getBroadcast(getActivity(), 0, pendingIntent,PendingIntent.FLAG_ONE_SHOT));
}
For deliver,just use the same approach.
------------------ Extra ------------------
I have seen that Android 22 supports multi sim cards from Android 5.1 and here is how to use it .. unfortunately I don't have device with that version for testing, so please for feedback:
SmsManager.getSmsManagerForSubscriptionId(int subscriptionId).sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent);
How to get subscriptionId? to review all available subscriptionID that belong to sim card:
SubscriptionManager subscriptionManager = SubscriptionManager.from(getApplicationContext());
List<SubscriptionInfo> subscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
int subscriptionId = subscriptionInfo.getSubscriptionId();
Log.d("apipas","subscriptionId:"+subscriptionId);
}
** Please note that this code is working on 5.1. if you try to run it on older version you'd get an exception that method doesn't exist.
------------UPDATE 19.8.2015----------
Information on SIMs, are located in DB: telephony.db (by default: /data/data/com.android.providers.telephony/databases/telephony.db ) in table siminfo. See screenshot on table siminfo in DB on real device.
Fortunately there is a content provider for that:
"content://telephony/siminfo/"
So basically just query data from that table.Its important to mention that slot 0 represents SIM1, and 1 represents SIM2, and slot -1 from old/removed/replaced SIMs.
This applies on Lenovo A319 . I guess that may work on other devices. Here is the util method I use:
public static List<SimInfo> getSIMInfo(Context context) {
List<SimInfo> simInfoList = new ArrayList<>();
Uri URI_TELEPHONY = Uri.parse("content://telephony/siminfo/");
Cursor c = context.getContentResolver().query(URI_TELEPHONY, null, null, null, null);
if (c.moveToFirst()) {
do {
int id = c.getInt(c.getColumnIndex("_id"));
int slot = c.getInt(c.getColumnIndex("slot"));
String display_name = c.getString(c.getColumnIndex("display_name"));
String icc_id = c.getString(c.getColumnIndex("icc_id"));
SimInfo simInfo = new SimInfo(id, display_name, icc_id, slot);
Log.d("apipas_sim_info", simInfo.toString());
simInfoList.add(simInfo);
} while (c.moveToNext());
}
c.close();
return simInfoList;
}
and here is the entity class SimInfo:
public class SimInfo {
private int id_;
private String display_name;
private String icc_id;
private int slot;
public SimInfo(int id_, String display_name, String icc_id, int slot) {
this.id_ = id_;
this.display_name = display_name;
this.icc_id = icc_id;
this.slot = slot;
}
public int getId_() {
return id_;
}
public String getDisplay_name() {
return display_name;
}
public String getIcc_id() {
return icc_id;
}
public int getSlot() {
return slot;
}
@Override
public String toString() {
return "SimInfo{" +
"id_=" + id_ +
", display_name='" + display_name + '\'' +
", icc_id='" + icc_id + '\'' +
", slot=" + slot +
'}';
}
}
Good luck,'.
I tried Mahers Refletion method for sending sms in dual sim android phone(API 19 and below). The chipset in the smartphone was from Spreadtrum. I encountered exceptions with Maher's code, first it was Null Pointer exception, in the name = isms2. For me, sim1 was isms0 and sim2 was isms1, I retrieved this information in the dumpsys. With much debugging and some more searching following code worked for me:
public class SimUtil {
public static boolean sendSMS(Context ctx, int simID, String toNum, String centerNum, String smsText, PendingIntent sentIntent, PendingIntent deliveryIntent) {
String name;
try {
if (simID == 0) {
name = "isms0";
} else if (simID == 1) {
name = "isms1";
} else {
throw new Exception("can not get service which for sim '" + simID + "', only 0,1 accepted as values");
}
try
{
Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", new Class[]{String.class});
method.setAccessible(true);
Object param = method.invoke(null, new Object[]{name});
if (param == null)
{
throw new RuntimeException("can not get service which is named '" + name + "'");
}
method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", new Class[]{IBinder.class});
method.setAccessible(true);
Object stubObj = method.invoke(null, new Object[]{param});
method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, String.class, PendingIntent.class, PendingIntent.class);
method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsText, sentIntent, deliveryIntent);
} catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
} catch (NoSuchMethodException e)
{
throw new RuntimeException(e);
} catch (InvocationTargetException e)
{
throw new RuntimeException(e);
} catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
return true;
} catch (ClassNotFoundException e) {
Log.e("Exception", "ClassNotFoundException:" + e.getMessage());
} catch (NoSuchMethodException e) {
Log.e("Exception", "NoSuchMethodException:" + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("Exception", "InvocationTargetException:" + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("Exception", "IllegalAccessException:" + e.getMessage());
} catch (Exception e) {
Log.e("Exception", "Exception:" + e);
}
return false;
}
}
Following links maybe helpfull:
- https://github.com/imflyn/FlynAndroidCommon/blob/master/app/src/main/java/com/flyn/telephone/SmsUtils.java
- https://github.com/pinodex/loadcentral-android/blob/master/app/src/main/java/com/pinodex/loadcentral/Sender/Sms.java