【问题标题】:How to send a SMS using SMSmanager in Dual SIM mobile?如何在双卡手机中使用 SMSmanager 发送短信?
【发布时间】:2015-02-05 18:01:21
【问题描述】:

我正在使用短信管理器发送短信。对于单 SIM 卡,它可以完美地发送短信。但在双 SIM 卡中,短信不会发送。是否可以从双 SIM 卡发送短信,如果可能意味着我该如何选择发送短信的 SIM 卡。谁能知道帮我解决这个问题。

单 SIM 卡工作代码

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

【问题讨论】:

  • 这可能就是你要找的东西stackoverflow.com/questions/24007885/…
  • 也许您会在这里找到解决方案。 stackoverflow.com/questions/14276328/…
  • @MurtazaHussain 我想在自己的编码中做所有事情。有可能吗?
  • 是的。找到数字 scAddress 并将其传递给您的代码。
  • @MurtazaHussain 但在那个答案中他们说。 显然似乎没有办法以编程方式获取数字

标签: android sms smsmanager


【解决方案1】:

我使用这种方式来管理用于发送 SMS 甚至长消息的 sim 卡。它在我的双卡手机联想 A319 (4.4.3) 上工作,无需 root。它建立在反射之上。

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


}

添加权限:

<uses-permission android:name="android.permission.SEND_SMS"/>

然后像这样调用那个(血腥的)静态方法:)

使用 SIM1:

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

使用 SIM2:

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

但是等等……如果消息长度超过 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);
}

这样您就可以安全地传递消息正文而不必担心长度。

------------更新 09.10.2016---------

要在 MultipartMessage 中使用 PendingIntent/DeliveryIntent.. 只需创建具有相同内容的 ArrayList 并传递它。下面是创建 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));
}

对于交付,只需使用相同的方法。

------------------ 额外 ------------------

我已经看到 Android 22 支持 Android 5.1 的多 sim 卡,这里是如何使用它.. 不幸的是我没有该版本的设备进行测试,所以请反馈:

SmsManager.getSmsManagerForSubscriptionId(int subscriptionId).sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent);

如何获取订阅 ID? 查看属于 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);
        }

** 请注意,此代码适用于 5.1。如果你尝试在旧版本上运行它,你会得到一个方法不存在的异常。

------------更新 19.8.2015------------

SIM 卡信息位于数据库:telephony.db(默认为:/data/data/com.android.providers.telephony/databases/telephony.db)中的表 siminfo。 请参阅真实设备上 DB 中表 siminfo 的屏幕截图。

幸运的是,有一个内容提供者:

"content://telephony/siminfo/"

所以基本上只是从该表中查询数据。重要的是要提到插槽 0 代表 SIM1,1 代表 SIM2,插槽 -1 来自旧/移除/替换的 SIM。

这适用于联想 A319。我想这可能适用于其他设备。 这是我使用的 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;
    }

这里是实体类 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 +
                '}';
    }
}

祝你好运,'。

【讨论】:

  • 很好,但我如何获得有关 sim1 和 sim2 的信息?我只想使用特定的运营商发送文本,比如说 AT&T。你知道如何找出每张 sim 卡的信息吗?
  • 嗨 Alireza,请参阅我的更新答案。我包括了我为此目的使用的 util。
  • 非常感谢,我一定会试一试的。
  • @e.hadid 我已经更新了我的答案.. 见更新 09.10.2016
  • @maherAbuthraa。在 android 6 marshmallow 中,它给了我 "NoSuchMethodException:sendText" 。你能帮我做什么吗?
【解决方案2】:

我尝试了 Mahers Refletion 方法在双 sim android 手机(API 19 及以下)中发送短信。智能手机中的芯片组来自 Spreadtrum。我遇到了 Maher 的代码异常,首先是 Null Pointer 异常,名称为 =isms2。对我来说,sim1 是isms0,sim2 是isms1,我在dumpsys 中检索到了这些信息。经过大量调试和更多搜索,以下代码对我有用:

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

}

以下链接可能会有所帮助:

【讨论】:

    【解决方案3】:

    Maher 的解决方案几乎是正确的。

    我在摩托罗拉motog 5.1 android(单sim)上试过,但他的阅读表content://telephony/siminfo的解决方案有一个小错误:

    在我的摩托罗拉上没有字段slotsim_id

    其余的都很好并且相同。

    【讨论】:

      【解决方案4】:
          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);
      

      【讨论】:

        【解决方案5】:

        关于@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);
        

        这个解决方案对我来说很好,但我强烈推荐这个更清洁的解决方案(不需要反射,适用于 API 级别 22+)在这里找到https://stackoverflow.com/a/51380282/3427883

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多