简体   繁体   English

如何在双卡手机中使用 SMSmanager 发送短信?

[英]How to send a SMS using SMSmanager in Dual SIM mobile?

Am using SMS manager to send SMS.For single SIM its works perfectly to send the SMS.but in dual SIM the SMS will not send.Is it possible to send the SMS from dual SIM,if possible means how can i select the Which SIM to send SMS.Can any one know help me to solve this issue.正在使用短信管理器发送短信。对于单卡,它可以完美地发送短信。但在双卡中,短信不会发送。是否可以从双卡发送短信,如果可能意味着我如何选择哪张 SIM 卡发送短信。有谁知道可以帮我解决这个问题。

Single SIM working Code单卡工作码

SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(ph_number, null, body, null,null);

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.我使用这种方式来管理使用哪个 sim 发送短信甚至是长消息..它在我的双卡手机联想 A319 (4.4.3) 上工作,不需要 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:要使用 SIM1:

SimUtil.sendSMS(this,0,"00970XXXXXXXXX",null,"Hi Stackoverflow! its me Maher. Sent by sim1",null,null);

To use SIM2:要使用 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:但是等等......如果消息超过 160 个字符,那将不起作用......所以更好的方法:

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---------- ------------更新 09.10.2016 -----------

To use PendingIntent/DeliveryIntent in MultipartMessage.. just create ArrayList with same content and pass it.要在 MultipartMessage 中使用 PendingIntent/DeliveryIntent .. 只需创建具有相同内容的 ArrayList 并传递它。 Here is an implementation of creating List of PendingIntent:这是创建 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:我已经看到 Android 22 支持来自 Android 5.1 的多卡,这里是如何使用它.. 不幸的是,我没有该版本的设备进行测试,所以请提供反馈:

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:查看属于 sim 卡的所有可用订阅 ID:

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. ** 请注意,此代码适用于 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---------- ------------更新 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. SIM 的信息位于表 siminfo 中的 DB:telephony.db(默认情况下:/data/data/com.android.providers.telephony/databases/telephony.db)。 See screenshot on table siminfo in DB on real device.在真实设备上查看 DB 中表 siminfo 的屏幕截图。

在此处输入图片说明

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.所以基本上只是从那个表中查询数据。重要的是要提到插槽 0 代表 SIM1,1 代表 SIM2,插槽 -1 来自旧/移除/更换的 SIM。

This applies on Lenovo A319 .这适用于联想 A319。 I guess that may work on other devices.我想这可能适用于其他设备。 Here is the util method I use:这是我使用的 util 方法:

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:这是实体类 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,'.祝你好运,'。

Maher's reflection based solution works for emulator Android SDK 10, 17, 18, 19,20,21 and as mentioned SubscriptionId based solution works for SDK 22 it actually works for Micromax Canvas 4 (Android 4.2) as well. Maher基于反射的解决方案适用于模拟器Android SDK 10、17、18、19、20、21 ,并且如上所述,基于SubscriptionId的解决方案适用于SDK 22,它实际上也适用于Micromax Canvas 4(Android 4.2)。

But for some phones brands like Lenevo, Asus brands with Android 5.0 it errors out with 但是对于某些手机品牌,例如Lenevo,华硕(Android)5.0的品牌来说,

java.lang.NullPointerException: Attempt to invoke Virtual Method 'Java.lang.Class Java.Lang.Object.GetClass() ' on a null objects reference" at code java.lang.NullPointerException:尝试在代码处调用虚拟方法' Java.lang.Class Java.Lang.Object.GetClass() '在空对象引用上“

" method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, String.class, PendingIntent.class, PendingIntent.class); method = stubObj.getClass()。getMethod(” sendText“,String.class,String.class,String.class,String.class,PendingIntent.class,PendingIntent.class);

which means its not able to get the proper stubObj . 这意味着它无法获取正确的stubObj

I tried Mahers Refletion method for sending sms in dual sim android phone(API 19 and below).我尝试了 Mahers Refletion 方法在双卡安卓手机(API 19 及以下)中发送短信。 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.我遇到了 Maher 代码的异常,首先是空指针异常,名称为 =isms2。 For me, sim1 was isms0 and sim2 was isms1, I retrieved this information in the dumpsys.对我来说,sim1 是isms0,sim2 是isms1,我在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:以下链接可能有帮助:

Maher's solution is almost right. Maher 的解决方案几乎是正确的。

I tried it on Motorola motog 5.1 android (single sim), but his solution with reading table content://telephony/siminfo had one slight bug:我在 Motorola motog 5.1 android (single sim) 上尝试过,但他的解决方案阅读 table content://telephony/siminfo有一个小错误:

on my motorola there was no field slot but sim_id在我的摩托罗拉上,没有字段slot只有sim_id

rest of this was fine and identical.其余部分很好且相同。

    private void sendSMS(String phoneNumber, String message ,int simSlot)
    {
        String SENT = "SMS_SENT";
        String DELIVERED = "SMS_DELIVERED";

        PendingIntent sentPI = PendingIntent.getBroadcast(getActivity(), 0,
                new Intent(SENT), 0);

        PendingIntent deliveredPI = PendingIntent.getBroadcast(getActivity(), 0,
                new Intent(DELIVERED), 0);


        //---when the SMS has been sent---
        getActivity().registerReceiver(new BroadcastReceiver(){
            @Override
            public void onReceive(Context arg0, Intent arg1) {
                switch (getResultCode())
                {
                    case Activity.RESULT_OK:
                        Toast.makeText(getContext(), "SMS sent",
                                Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                        Toast.makeText(getContext(), "Generic failure",
                                Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_NO_SERVICE:
                        Toast.makeText(getContext(), "No service",
                                Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_NULL_PDU:
                        Toast.makeText(getContext(), "Null PDU",
                                Toast.LENGTH_SHORT).show();
                        break;
                    case SmsManager.RESULT_ERROR_RADIO_OFF:
                        Toast.makeText(getContext(), "Radio off",
                                Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }, new IntentFilter(SENT));

        //---when the SMS has been delivered---
        getActivity().registerReceiver(new BroadcastReceiver(){
            @Override
            public void onReceive(Context arg0, Intent arg1) {
                switch (getResultCode())
                {
                    case Activity.RESULT_OK:
                        Toast.makeText(getContext(), "SMS delivered",
                                Toast.LENGTH_SHORT).show();
                        break;
                    case Activity.RESULT_CANCELED:
                        Toast.makeText(getContext(), "SMS not delivered",
                                Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }, new IntentFilter(DELIVERED));

        SmsManager.getSmsManagerForSubscriptionId(simSlot).sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);
    }

 SubscriptionManager subscriptionManager = SubscriptionManager.from(getActivity());
        @SuppressLint("MissingPermission") List<SubscriptionInfo> subscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
        for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
            int subscriptionId = subscriptionInfo.getSubscriptionId();
            Log.d("apipas","subscriptionId:"+subscriptionId);
        }

//subscriptionId for dual sim

        sendSMS("xxxxxxxxxxx", "message" ,subscriptionId);

with regard to @Vibhav solution, here is how I could successfully implement it关于@Vibhav 解决方案,这是我如何成功实施它

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这个解决方案对我来说很好用,但我强烈推荐这个更干净的解决方案(不需要反射,适用于 API 级别 22+) https://stackoverflow.com/a/51380282/3427883

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM