I have a dual sim android phone. I am using a custom broadcast receiver which reads incoming messages with no problem. I wonder if there is a way to find out which sim received the message.
You can get the active sim's info by using TelephonyManager. FOr example, it's serial number.
TelephonyManager telephoneMgr = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
String simSerial = telephoneMgr.getSimSerialNumber();
You could also get the line1Number, if your network operator has put your number in there, you could compare it to the number you got on the to> field in the SMS message.
String phoneNumber = telephoneMgr.getLine1Number();
Hope it helps
I had some really hard time with this problem and finally I found a solution, although i tested it only above api level 22.
You have to take a look at the extra information in the received intent. In my case there are two keys in the extra Bundle of the intent which are useful: "slot" and "subscription".
Here is the example:
public class IncomingSms extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
// Retrieves a map of extended data from the intent.
Bundle bundle = intent.getExtras();
int slot = bundle.getInt("slot", -1);
int sub = bundle.getInt("subscription", -1);
/*
Handle the sim info
*/
}
}
I did not find documentation about this so this could be device/manufacturer dependent, i can imagine that the keys are diferent or something like that.
You can verify this by dumping the key set of the bundle:
Set<string> keyset = bundle.keySet();
Related
I am developing an android app that rapidly sends multiple SMS messages almost simultaneously to the same number (Please note this is for research purposes). I need this app to also track it's delivery intent, which I do by adding a bundle containing the information I want the intent to carry. The problem I am running into, however, is as follows:
If I use no pending intent flag (aka 0) or FLAG_IMMUTABLE, the bundle value always remains the same as the first message sent and I get a dump of all the messages delivered.
If I use FLAG_UPDATE_CURRENT, I get a bundle value that updates sporadically/randomly (multiple intents contain the same bundle value), and I get a dump of all the messages delivered.
If I use FLAG_ONE_SHOT, I get the accurate bundle value assigned (the value as it is meant to be received) BUT I only get a dump of a very small amount of the intents, and that amount stays small(~1%) even if I happen to send over 1000 messages back-to-back.
Here is the portion of the message sending method responsible for sending the SMSs in MainActivity:
int z = 0;
for(int j=1; j<=messageCnt;j++){
try {
Intent dI = new Intent("SMS_DELIVERED");
Bundle b = new Bundle();
b.putString("MSGNUM",Integer.toString(z+1));
dI.putExtras(b);
dI.putExtra("MNUMSTR",Integer.toString(z+1));
PendingIntent deliveredIntent = PendingIntent.getBroadcast(getApplicationContext(),0,dI,PendingIntent.FLAG_ONE_SHOT);
deliveredPendingIntents.add(z,deliveredIntent);
SmsManager.getDefault().sendTextMessage(phoneNum, null, message[z], null, deliveredIntent);
b.clear();
}
catch (Exception e){
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
crashTrack.add((Long.toString(System.currentTimeMillis())).concat(",").concat("'").concat(sw.toString()).concat("'\n"));
//reset all incremental value back one for proper repeat
j=j-1;
continue; //jump back to for loop and reattempt send.
}
}
Then, wherever I want (whether it be in a new app or in the current app), I put the following to register my receiver class.
//Create Broadcast receivers for sent and delivered intents
SmsDeliveredReceiver SDR = new SmsDeliveredReceiver();
//register the receivers
registerReceiver(SDR, new IntentFilter("SMS_DELIVERED"));
And finally, my broadcast reciever class for the intent, SmsDeliveredReciever:
public class SmsDeliveredReceiver extends BroadcastReceiver {
protected static int sentCount = 0;
#Override
public void onReceive(Context context, Intent dI){
/*File delivDumpName = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),MainActivity.dateTime.concat("DelivDump.txt"));
File delivfailDumpName = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),MainActivity.dateTime.concat("DelivFailDump.txt"));*/
Bundle bundle = dI.getExtras();
switch (getResultCode()){
case Activity.RESULT_OK:
if(bundle!=null){
String msgNum = bundle.getString("MSGNUM","0");
MainActivity.delvDump.add(Long.toString(System.currentTimeMillis()).concat(",'Msg ").concat(msgNum).concat("'\n"));
} else {
MainActivity.delvDump.add(Long.toString(System.currentTimeMillis()).concat(",'Msg ").concat("Number Unknown'\n"));//Integer.toString(sentCount)).concat("'\n"));
}
break;
case Activity.RESULT_CANCELED:
if(bundle!=null){
String msgNum = bundle.getString("MSGNUM","0");
MainActivity.delvDump.add(Long.toString(System.currentTimeMillis()).concat(",'Sms Failed to Deliver.',' Msg ").concat(msgNum).concat("'\n"));
} else {
MainActivity.delvDump.add(Long.toString(System.currentTimeMillis()).concat(",'Sms Failed to Deliver.',' Msg ").concat("Number Unknown'\n"));//Integer.toString(sentCount)).concat("'\n"));
}
break;
}
/*MainActivity.writeFiles(delivDumpName,String.valueOf(MainActivity.delvDump));
MainActivity.writeFiles(delivfailDumpName, String.valueOf(MainActivity.delvFailDump));*/
sentCount++;
}
}
Note that the exact same thing happens for the SendIntent. I've read all of the Google Android SDK Docs, and stackoverflow so far has provided answers that only work for alerts/alarms and not SMS intents, where the only existing tutorials and forum entries for SMS are single-send SMS. And no, I am not trying to send a multi-part message.
Desired output:
All SMS delivery intents should be captured in the broadcast receiver
Each intent captured must output its own unique message association number via the bundle.
The amount of intents captured should equal the number of messages successfully sent (yes, I do have a way to check whether the message got received or not)
Is this desired output even possible, or am I asking too much out of Android?
Whether the receiver is a separate app or not does not matter to me, though it is preferred if its a separate app.
Note: Everything in the app is functional and error-free except accurately tracking/logging the sent/delivered intents.
I have an app that checks the phone number of an incoming call against a blacklist.
I have used the below code for several versions of Android to get the phone number of an incoming call, but when I test it against Android P, it behaves unexpectedly.
For readability, I have removed all null checks from the code below.
public class IncomingCallHandler extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String state = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)) {
String phoneNumber = bundle
.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
}
}
}
On Android versions less than P, onReceive with state EXTRA_STATE_RINGING may be called several times during an incoming call, but phoneNumber always has the same value (the actual incoming phone number).
On Android P, onReceive is called twice during an incoming call. The first time phoneNumber=null, the second time it is the actual phone number.
Is this a bug? Is it supposed to be like this? Do you get the same in your apps?
I'd like to build an Android application that can contact the current caller via a pre-determined text message. Sending a text message is simple enough but determining the phone number of the current caller in a stand-alone application is the challenge. Is the there an easy way to divine the phone number so I can send them a message while still on the call?
Of course there are manual ways to do this: write down the number, key it into a new text message, enter the message. But I want to define the message up front and be able to "send it to current caller".
#Override
public void onReceive(Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneCallStateListener customPhoneListener = new PhoneCallStateListener(context);
telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
helper = new ContactDatabaseHelper(context);
list = helper.getAllContacts();
try{
incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if (list.size() != 0){
for ( int i = 0, size = list.size(); i < size; i++ ){
if (PhoneNumberUtils.compare(incomingNumber, list.get(i).getContactNumber())){
ToastMsg.showToast(context,list.get(i).getContactName()+" Calling");
}
}
}
}catch (Exception e) {
// TODO: handle exception
}
}
public class PhoneCallStateListener extends PhoneStateListener{
private Context context;
public PhoneCallStateListener(Context context){
this.context = context;
}
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
break;
case PhoneStateListener.LISTEN_CALL_STATE:
}
super.onCallStateChanged(state, incomingNumber);
}
}
For your sistuation the best I can think of is to use PhoneStateListener. It contains onCallStateChanged handler. One of the arguments is a String containing the incoming phone number.
Source:
http://developer.android.com/reference/android/telephony/PhoneStateListener.html
Ctrl + F and type in "Incoming" and you will find everything you need to know.
EDIT: To make sure you're app starts on the startup of your phone, just add a BroadcastReciever. How to start an Application on startup?
Register a BroadcastReceiver in your manifest that listens to ACTION_PHONE_STATE_CHANGED.
Broadcast intent action indicating that the call state (cellular) on
the device has changed.
The EXTRA_STATE extra indicates the new call state. If the new state
is RINGING, a second extra EXTRA_INCOMING_NUMBER provides the incoming
phone number as a String.
Requires the READ_PHONE_STATE permission.
This was a sticky broadcast in version 1.0, but it is no longer
sticky. Instead, use getCallState() to synchronously query the current
call state.
This way you don't need the user to launch your app before receiving a call.
I have class that extends BroadcastReceiver and gets all new sms.
When I get new sms I want to search the phonebook to check if phone numbers inside this message are in phone book, and if they are, send sms with result to another phone.
I use ContentResolver to check the phone numbers, but to use it I need to use it inside Activity or have pointer to activity, but my sms listener works in background.
Is there a way to search phone book without activity or to get valid activity from context or intent that I get when recieving new sms?
I tried to use something like that:
Activity act = new Activity();
DBManager dbm = new DBManager(act);
ArrayList<MyContact> res = dbm.phoneSearch(sms_ar[1]);
SmsSender sms = new SmsSender(act);
if(res.size() > 0){
String answer = res.get(0).getName()+CSStatic.SMS_SEPARATOR+res.get(0).getPhone();
for(int z = 1; z < res.size(); z++){
answer = answer+CSStatic.SMS_SEPARATOR+res.get(z).getName()+CSStatic.SMS_SEPARATOR+res.get(z).getPhone();
}
Log.d("sms", "Answer: "+answer);
sms.smsAnswer(answer, sms_ar[2]);
} else {
String phone = dbm.getPhoneToQuery(sms_txt);
sms.smsQueryNext(sms_txt, phone);
}
act.finish();
but it don't work :P
In DBManager I search for phones and in SmsSender I send new sms.
You need a valid Context object (an activity or service for example, these are subclasses of Context). These are created by android, do not use the constructor to create these objects!
To get the needed context in a BroadcastReceiver is explained here: https://stackoverflow.com/a/6074829/1127492, its the first parameter of onReceive().
Is it possible for my application to receive an SMS from a specific phone number, without letting it trig a system notification, but let all other messages pass to the default SMS application to be treated normally?
If so, how can the system know which process is first on queue to pick which messages to receive?
I didn't try this, but in theory it should work:
In Android SMS broadcast is sent as ordered broadcast, which means that receivers are handled in order and can cancel the broadcast. See SMSDispatcher.java, line 420.
In order to be called first, a receiver must have a higher priority then others.
<intent-filter android:priority="1000" >
. . .
</intent-filter>
To cancel a broadcast call broadcastReceiver.setResultCode(RESULT_CANCELED). That way a SMS broadcast will be cancelled and will not be shown by system SMS app (and SMS notifier).
Update:
Also try using broadcastReceiver.setResultCode(Intents.RESULT_SMS_HANDLED).
Update 2:
user672601 noted in another answer that this indeed works, but he used abortBroadcast() inside broadcast receiver.
I second farhan its not possible for number of reason. Anybody can do anything with such allowance. Check this out for details http://groups.google.com/group/android-developers/browse_thread/thread/78fecbc156f4a1ea
Peter Knego's answer is correct. I was trying to do this exact thing, tried his solution, and it indeed works except for I used:
this.abortBroadcast();
inside the broadcastReceiver.
public class NotifyServiceReceiver extends BroadcastReceiver{
static final String ACTION ="android.provider.Telephony.SMS_RECEIVED";
#Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
if(arg1.getAction().equalsIgnoreCase(ACTION))
{
Bundle extras = arg1.getExtras();
String strMessage = "private message";
if ( extras != null )
{
Object[] smsextras = (Object[]) extras.get( "pdus" );
for ( int i = 0; i < smsextras.length; i++ )
{
SmsMessage smsmsg = SmsMessage.createFromPdu((byte[])smsextras[i]);
String strMsgBody = smsmsg.getMessageBody().toString();
String strMsgSrc = smsmsg.getOriginatingAddress();
//Toast.makeText(GasService.this,strMessage, Toast.LENGTH_SHORT).show();
if(strMsgSrc.equals("+919XXXXXXXXX"))
{
strMessage += "SMS from " + strMsgSrc + " : " + strMsgBody;
Toast.makeText(PrivatesmsService.this,strMessage, Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
}
}
}
}
you can tell to the system by setting the priority of the activity to 100 or greater then that in manifest file so that when ever you receive sms then your application will access it and by calling abortBroadcast() it will prevent sms reaching to inbox or any other application which has set BroadcastReceiver to receive sms
I dont think its possible.... because android gives us broadcast Listener which only listen the event. so you have to read every message and check the number if its yours, do an operation else just ignore it.... the default messaging application will automatically handle it....