Android 双卡双待发送短信

来源:互联网 发布:java自学书籍推荐书目 编辑:程序博客网 时间:2024/06/05 02:16

17down voteaccepted

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 >160textSMS = "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_2ArrayList<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.

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

enter image description here

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,'.

shareimprove this answer
 
1 
Great but how can i get information about sim1 and sim2 ? I want to send text using specific operator only, lets say AT&T. do you have any idea how to find out information about each sim card? – Alireza Aug 18 '15 at 11:24
1 
Hi Alireza, see my updated answer. I included util I use for that purpose. – Maher Abuthraa Aug 19 '15 at 8:06
 
thank you very much i will definitely give it a try. – Alireza Aug 20 '15 at 8:54
 
when i tried this one.. sms is sending only sim1 only.. can u give any overall working source code pls – harikrishnan Jun 22 at 9:22
 
@harikrishnan, pass 1 as parameter for simID to be able to send message via Sim2, since 0:sim1, sim2:1 ... you have same source code I have .. there is no more code related to dual-sim sms-ing – Maher AbuthraaJun 29 at 21:34
17down voteaccepted

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 >160textSMS = "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_2ArrayList<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.

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

enter image description here

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,'.

shareimprove this answer
 
1 
Great but how can i get information about sim1 and sim2 ? I want to send text using specific operator only, lets say AT&T. do you have any idea how to find out information about each sim card? – Alireza Aug 18 '15 at 11:24
1 
Hi Alireza, see my updated answer. I included util I use for that purpose. – Maher Abuthraa Aug 19 '15 at 8:06
   
thank you very much i will definitely give it a try. – Alireza Aug 20 '15 at 8:54
   
when i tried this one.. sms is sending only sim1 only.. can u give any overall working source code pls – harikrishnan Jun 22 at 9:22
   
@harikrishnan, pass 1 as parameter for simID to be able to send message via Sim2, since 0:sim1, sim2:1 ... you have same source code I have .. there is no more code related to dual-sim sms-ing – Maher AbuthraaJun 29 at 21:34
0 0