Unit Testing Android SMS Receiver - android

i try to write an unit test on an BroadcastReceiver that gets informed when an SMS was received. Its not meant to be the default Application. Instead i just need this for an two factor authentication. For this case i created an PDU with [1].
But when i pass it down to the BroadcastReceiver the phone nr of the Sender never gets read by android it's just null. The body text is returned.
#TargetApi(Build.VERSION_CODES.KITKAT)
#Test
public void testOnReceive() throws Exception {
final byte[] decodedPDU = BaseEncoding.base16().decode(PDU);
final ReceiveSmsBroadcastReceiver receiveSmsBroadcastReceiver = spy(new ReceiveSmsBroadcastReceiver(true));
final Intent intent = new Intent();
intent.putExtra("format",SmsConstants.FORMAT_3GPP);
intent.putExtra("pdus", new Object[]{decodedPDU});
intent.setAction("android.provider.Telephony.SMS_RECEIVED");
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 1);
receiveSmsBroadcastReceiver.onReceive(InstrumentationRegistry.getTargetContext(), intent);
In the receiver i do this to get the SMSMessage Objects:
#TargetApi(Build.VERSION_CODES.KITKAT)
private void getSMSKitKat(final Context context, final Intent intent) {
final SmsMessage[] messagesFromIntent = Telephony.Sms.Intents.getMessagesFromIntent(intent);
I receive an SmsMessage array here, the body message is correct. But i need to test my check sender phone number before i can notify the UI that the SMS is received: But nr is always null here:
private boolean isCorrectSender(#Nullable final SmsMessage message) {
if (message == null) {
return false;
}
final String nr = message.getOriginatingAddress();
Can someone point me whats wrong here ?
PS: SMSConstants and PhoneConstants are all framework classes i took from AOSP to get it running because those APIs are non public
[1] http://twit88.com/home/utility/sms-pdu-encode-decode

Related

Android- read incoming messages using broadcast receiver

I have created an android application that listens for incoming sms. The issue i am encountering is that it also reads previous sms. The goal of the app was to grab sms from a specific originating address and store it in a database.
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Log.i(TAG, "Intent Received: "+intent.getAction());
if (intent.getAction()==SMS_RECEIVED){
Bundle dataBundle = intent.getExtras();
if(dataBundle != null){
//creating PDU protocol Data unit object which is a protocol for transferring message
Object[] mypdu = (Object[])dataBundle.get("pdus");
final SmsMessage[] message = new SmsMessage[mypdu.length];
for(int i =0; i< mypdu.length; i++){
//for build version >= API
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
String format = dataBundle.getString("format");
//From PDU we get all object and smsMessage using following line of code
message[i] = SmsMessage.createFromPdu((byte[])mypdu[i],format);
}else{
message[i] = SmsMessage.createFromPdu((byte[]) mypdu[i]);
}
msg += message[i].getMessageBody().toString().replace("null","");
originatingAddress = message[i].getOriginatingAddress();
}
msg = msg.replace("null","");
if(originatingAddress.trim().equals("MPESA")) {
Toast.makeText(context.getApplicationContext(), "message: " + msg, Toast.LENGTH_SHORT).show();
}
}
}
// throw new UnsupportedOperationException("Not yet implemented");
}
}
Please try with below way
(1)The Simple way you can achieve to store the unique of record on every SMS is timestamp is only unique in this case whenever you get SMS store timestamp of every Sms as a Unique or primary key as you want in database, like system.currentTimeMillisecond to get time of current SMS and store as LONG type Column in your database,
(2) you can also check with unique time on every SMS get but it is complex to check with every existing records
Hope this process will help in your way with prevent of duplicate record store in database

Android - LocalBroadcastManager for SMS interception not firing

I've got an JavascriptInterface method that registers a LocalBroadcastManager listening to received SMS's. When an SMS arrives the onReceive method doesn't get called.
public void UIregisterSMSSource(String number) {
LocalBroadcastManager.getInstance(this)
.registerReceiver(mMessageReceiver, new IntentFilter(ACTION_SMS_RECEIVE));
}
/**
* SMS Receiver
*/
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SMS_RECEIVE)) {
StringBuilder buf = new StringBuilder();
Bundle bundle = intent.getExtras();
if (bundle != null) {
Bundle extras = intent.getExtras();
Object[] pdus = (Object[]) extras.get("pdus");
for (int i = 0; i < pdus.length; i++) {
SmsMessage SMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
String sender = SMessage.getOriginatingAddress();
String body = SMessage.getMessageBody().toString();
Log.d(TAG, "[SMS] onReceive by " + sender + ". Content - " + body);
// Save preferences of the activation code
}
}
}
}
};
This is the ACTION_SMS_RECEIVE variable:
private static final String ACTION_SMS_RECEIVE = "android.provider.Telephony.SMS_RECEIVED";
I've tested this before as BroadcastaReceiver and it worked. I removed the receiver from the manifest too which I don't know if it's right.
Do I need to configure something more? On the examples I've came across there's no further configuration needed.
Thanks in advance.
Receiver mMessageReceiver can only listen the intent which sent via LocalBroadcastManager.sendBroadcast(intent).
Here, intent with android.provider.Telephony.SMS_RECEIVED action, is being raised by system (not using LocalBroadcastManager.sendBroadcast(intent)), so your app does not listen to this intent.
Your previous approach was correct, and you can continue on that logic.
You can read a detailed example of LocalBroadcastManager to make clear your doubts about its working flow.

Android: showing an received SMS

I am creating an application for android OS, which allows user to send encrypted SMS to other users. But my application has only interface for sending an SMS, not for showing it. When application receives an SMS, I wan't to decrypt it and then somehow to show the decrypted SMS through the bult-in SMS Application. Is there a way to accomplish that? For now my receiver just shows the SMS using Toast.
Here is Receiver's code (It is not full but you will get the idea):
public class SMSReceiver extends BroadcastReceiver{
private static final byte HANDSHAKE_ID = (byte) 120;
private static final byte ENCRYPTED_ID = (byte) 125;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Bundle pudsBundle = intent.getExtras();
Object[] pdus = (Object[]) pudsBundle.get("pdus");
SmsMessage messages = SmsMessage.createFromPdu((byte[]) pdus[0]);
Log.i("Message: ", messages.getMessageBody());
String msgBody = messages.getDisplayMessageBody();
byte[] msgBytes = msgBody.getBytes();
if ( msgBytes[0] == HANDSHAKE_ID ) {
//Obtain secret key from message
//TO-DO
Toast.makeText(context, "Received a secret key from: " + messages.getOriginatingAddress(), Toast.LENGTH_LONG).show();
} else if ( msgBytes[0] == ENCRYPTED_ID ) {
//Obtain encrypted message
//TO-DO
Toast.makeText(context, plainText, Toast.LENGTH_LONG).show();
}
}
Also if it is possible I want to prevent other Apps to see(receive) the message if first byte of the message is one of following constants: HANDSHAKE_ID or ENCRYPTED_ID and visible after decryption? But the main problem that I wan't to solve is how to show plaintext with Android's Built-In SMS Application. Thanks!

can i able to send broadcast to default sms application in android?

This is my code:
public class MMSReciever extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String incomingNumber = null;
if(intent.getAction().equals("android.provider.Telephony.WAP_PUSH_RECEIVED"))
{
Bundle bundle = intent.getExtras();
try
{
if (bundle != null)
{
String type = intent.getType();
if(type.trim().equalsIgnoreCase("application/vnd.wap.mms-message"))
{
byte[] buffer = bundle.getByteArray("data");
incomingNumber = new String(buffer);
int indx = incomingNumber.indexOf("/TYPE");
if(indx>0 && (indx-15)>0)
{
int newIndx = indx - 15;
incomingNumber = incomingNumber.substring(newIndx, indx);
indx = incomingNumber.indexOf("+");
if(indx>0)
{
incomingNumber = incomingNumber.substring(indx);
System.out.println("Mobile Number: " + incomingNumber);
}
}
}
I have bundle data with me when sms arrives to my device .Then can i able to send broadcast with this bundle data to default sms application
If you getting a sms broadcast event, you don't need to send from your side. As the same broadcast will be received by all applications watching for that intent.
Now unless, you want to change something. then you need to cancel the broadcast and make your own broadcast and fire it. and yes you can do that. make sure, your receiver priority is high enough to receive event before other apps do.
Hope this is what you asking.

Android - Create SMS from PDU deprecated API?

I want to create an app that gets notified when an SMS arrives and processes that SMS, but reading the reference for the createFromPdu function, it states that:
This method will soon be deprecated and all applications which handle incoming SMS messages by processing the SMS_RECEIVED_ACTION broadcast intent must now pass the new format String extra from the intent into the new method createFromPdu(byte[], String) which takes an extra format parameter. This is required in order to correctly decode the PDU on devices that require support for both 3GPP and 3GPP2 formats at the same time, such as dual-mode GSM/CDMA and CDMA/LTE phones.
However the createFromPdu(byte[], String) function is missing from both the documentation and the SDK.
What should I do? I need to support these dual-sim dual-mode phones.
In short, use this:
SmsMessage smsMessage;
if (Build.VERSION.SDK_INT >= 19) { //KITKAT
SmsMessage[] msgs = Telephony.Sms.Intents.getMessagesFromIntent(intent);
smsMessage = msgs[0];
} else {
Object pdus[] = (Object[]) bundle.get("pdus");
smsMessage = SmsMessage.createFromPdu((byte[]) pdus[0]);
}
You don't mention which Android version you're targeting but given the date of the question I'm assuming Jelly Bean 4.x.
At the time of writing this, we're at Lollipop MR1 and the deprecation note about using createFromPdu with the format parameter still stands:
https://android.googlesource.com/platform/frameworks/opt/telephony/+/android-5.1.0_r3/src/java/android/telephony/SmsMessage.java
Instead of using that API directly however, you can use the Telephony provider API getMessagesFromIntent:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.2_r1/android/provider/Telephony.java/#686
That method considers the PDU format (and the subscription ID in Lollipop for Multi SIM devices) and gives you an array of SmsMessage instances.
An example of how it's used can be seen in Google's MMS app:
https://android.googlesource.com/platform/packages/apps/Mms/+/master/src/com/android/mms/transaction/SmsReceiverService.java (see handleSmsReceived)
createFromPdu(byte[]) is deprecated from SDK_INT 23 so you have to use
createFromPdu((byte[]) , format);
here is my code to get incoming SMS
i am using SMSReceiver class as an innerclass of an Activity.
this is my working code
public class SamplaActivity extends BaseActivity {
SMSReceiver otp;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
otp=new SMSReceiver();
........
........
}
#Override
protected void onResume() {
super.onResume();
// register broadcast receiver
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(otp, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(otp);
}
private class SMSReceiver extends BroadcastReceiver {
private Bundle bundle;
private SmsMessage currentSMS;
private String message;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
bundle = intent.getExtras();
if (bundle != null) {
Object[] pdu_Objects = (Object[]) bundle.get("pdus");
if (pdu_Objects != null) {
for (Object aObject : pdu_Objects) {
currentSMS = getIncomingMessage(aObject, bundle);
String senderNo = currentSMS.getDisplayOriginatingAddress();
message = currentSMS.getDisplayMessageBody();
Toast.makeText(OtpActivity.this, "senderNum: " + senderNo + " :\n message: " + message, Toast.LENGTH_LONG).show();
}
this.abortBroadcast();
// End of loop
}
}
} // bundle null
}
}
private SmsMessage getIncomingMessage(Object aObject, Bundle bundle) {
SmsMessage currentSMS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String format = bundle.getString("format");
currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
} else {
currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
}
return currentSMS;
}
}
sorry for late response but it may help anyway.
apart from checking the android version and if it is above M and extract sing format.
I would suggest to use:
compile 'me.everything:providers-android:1.0.1'
Manifest
<uses-permission android:name="android.permission.READ_SMS" />
And in your codes
public List<Sms> mySmses(Context context)throws Exception{
TelephonyProvider telephonyProvider = new TelephonyProvider(context);
List<Sms> smses = telephonyProvider.getSms(TelephonyProvider.Filter.INBOX).getList();
if(smses.isEmpty()){
throw new Exception("No SMS found");
}
return smses;
}
Remember to use AsyncTask to fetch SMSes(consuming the above method) as this is a long running operation. It may crash your app(in certain occasion) if it is executed on the UI thread.
This library saved my time. Hope it will do the same to someone.

Categories

Resources