[英]How to send a SMS using SMSmanager in Dual SIM mobile?
正在使用短信管理器发送短信。对于单卡,它可以完美地发送短信。但在双卡中,短信不会发送。是否可以从双卡发送短信,如果可能意味着我如何选择哪张 SIM 卡发送短信。有谁知道可以帮我解决这个问题。
单卡工作码
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(ph_number, null, body, null,null);
我使用这种方式来管理使用哪个 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 的多卡,这里是如何使用它.. 不幸的是,我没有该版本的设备进行测试,所以请提供反馈:
SmsManager.getSmsManagerForSubscriptionId(int subscriptionId).sendTextMessage(String destinationAddress, String scAddress, String text,PendingIntent sentIntent, PendingIntent deliveryIntent);
如何获取订阅号? 查看属于 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 的信息位于表 siminfo 中的 DB:telephony.db(默认情况下:/data/data/com.android.providers.telephony/databases/telephony.db)。 在真实设备上查看 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 +
'}';
}
}
祝你好运,'。
Maher基于反射的解决方案适用于模拟器Android SDK 10、17、18、19、20、21 ,并且如上所述,基于SubscriptionId的解决方案适用于SDK 22,它实际上也适用于Micromax Canvas 4(Android 4.2)。
但是对于某些手机品牌,例如Lenevo,华硕(Android)5.0的品牌来说,
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);
这意味着它无法获取正确的stubObj
。
我尝试了 Mahers Refletion 方法在双卡安卓手机(API 19 及以下)中发送短信。 智能手机中的芯片组来自展讯。 我遇到了 Maher 代码的异常,首先是空指针异常,名称为 =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;
}
}
以下链接可能有帮助:
Maher 的解决方案几乎是正确的。
我在 Motorola motog 5.1 android (single sim) 上尝试过,但他的解决方案阅读 table content://telephony/siminfo
有一个小错误:
在我的摩托罗拉上,没有字段slot
只有sim_id
其余部分很好且相同。
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);
关于@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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.