Currently I am trying to modify an incoming SMS before it is saved to the Inbox.
As the system is Android 4.4.4, a simple interception with a high priority broadcast receiver is not possible. That is also the reason why I'm modifying the Android Source (AOSP) and not building an App.
So far I have managed to identify a promising class: InboundSmsHandler. Within the inner class SmsBroadcastReceiver the method onReceive is triggered when a SMS has arrived and later on sends an "SMS_RECEIVED" intend. So basically this method seems to be at a good spot.
The problem is that I can not modify the SMS that is delivered with the intend of onReceive.
I have already tried to modify it with PDU:
byte[] pdu = createFakePDU("15555215556", "modified body");
intent.putExtra("pdus", new Object[] { pdu });
intent.putExtra("format", "3gpp");
(This approach did not work, the SMS App has shown the original message)
Tried to modify the body of a SmsMessage directly:
(I have added a method to SmsMessage to be able to modify the body)
SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
int pduCount = msgs.length;
for(int i=0; i<pduCount; i++)
{
msgs[i].modifyBody("test");
}
(This approach did not work, the SMS App has shown the original message)
And finally added a new SMS to the database:
....
contentResolver.insert( Uri.parse( SMS_URI ), values );
....
(The problem with that approach is that the original SMS still arrives and therefore not only one modified SMS but one original SMS and one modified arrive. The original SMS must be deleted, but I don't know how.)
Does anyone know how I can modify a SMS before it arrives at the Inbox?
Best regards
mint
AFAIK, on 4.4.4 there is nothing that can prevent your app to receive SMS by registering your BroadcastReceiver, setting the right permissions and the right intent filter. That is:
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
and
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
(if I correctly remember them)
Then inside your BroadcastReceiver you call abortBroadcast(), modify the SMS as needed, and finally store it manually with
getContentResolver().insert(Uri.parse("content://sms/sent"), values);
Thanks for all the answers, I have found a spot in InboundSmsHandler where it is possible to modify the PDU before the broadcast is sent: the method proccessMessagePart. Before the command "intent.putExtra("pdus", pdus);" is executed, the pdus array and therefore the message body can be modified.
Related
I'm writing a small android app which has a broadcast receiver for incoming sms messages. Normal text works just fine, but emoji show up as weird characters ().
The relevant code:
class SmsReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val pdus = intent.extras?.get("pdus") as Array<ByteArray>
pdus.map { SmsMessage.createFromPdu(it) }
.forEach { Log.i("Receiver", "Got message ${it.displayMessageBody}") }
}
The message I'm sending to the emulator:
💩💩💩
What I get in my log file:
I/Receiver: Got message
Does anyone know how to get the proper message text?
UPDATE
So after going down a rabbit hole of encodings and the EmojiCompat library, I discovered it's an issue with the emulator itself. When I got hold of a second phone and tried it on an actual device, it just worked. I created a bug with the android developers so hopefully they have a fix: https://issuetracker.google.com/issues/149141727
The message you are sending the emulator is ascii, and the message you are displaying to the log is UTF8, you need to read it as ascii
I've got an app that sends a text message as a response after receiving a text message. (Auto Respond) When SMS is enabled in hangouts, my app wasn't sending its messages. I fixed that by doing this:
<intent-filter android:priority="500">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
After sending the message, my app also writes that sent message to the user's SMS log (inbox/outbox displayed by messaging apps.)
But now that my SMS receiver is higher priority than Hangouts, the sent message is written to the user's SMS log AFTER the received message when it should be the other way around.
So it shows like this:
Response Message
Received Message - this is what triggered the response
But it should be:
Received Message - triggers response
Response Message
Is there a way for me to wait for the received message to be written before writing the response message? It works fine when SMS is disabled in Hangouts. But since Hangouts is now writing that message instead of the default SMS receiver, it messes things up like crazy.
EDIT: Thanks to Keith's response, this is the code that worked for me:
context.getContentResolver().registerContentObserver(
Uri.parse("content://sms"),
true,
smsObserver);
And this class:
private class SMSObserver extends ContentObserver
{
public SMSObserver()
{
super(null);
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
if(!selfChange)
//sendResponse
context.getContentResolver().unregisterContentObserver(this);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if(!selfChange)
//sendResponse
context.getContentResolver().unregisterContentObserver(this);
}
}
I'm not sure if the self change part is necessary, but it works so I'm not changing it.
Try a ContentObserver on the SMS database to listen for when Hangouts writes to the SMS content provider. This approach should be compatible with 4.4/Hangouts as well as earlier versions; you'd just wait until something is written to write your sent message.
Tested two different versions on Android 4.3, Galaxy Nexus:
context.getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, myContentObserver);
or
cursor = context.getContentResolver().query(Uri.parse("content://sms/inbox"),
new String[] { SMS_ID, SMS_ADDRESS, SMS_READ },
"read = 0",
null,
null);
cursor.registerContentObserver(myContentObserver);
But I couldn't use the non-Cursor version with sms/inbox for some reason. The downside of the Cursor-based version is that it seems to need to stay open so then you have to be sure to close it later.
Also, neither version is being called when the read status changes.
I have tried to get a message id which is saved in phone.
But I failed to get it.
My existing code is here
#Override
public void onReceive(Context context, Intent intent) {
String address = "";
String message = "";
String msg_id="";
Bundle extras = intent.getExtras();
if (extras != null) {
Object[] smsExtra = (Object[]) extras.get("pdus");
for (int i = 0; i < smsExtra.length; i++) {
SmsMessage sms = SmsMessage.createFromPdu((byte[]) smsExtra[i]);
message += sms.getMessageBody();
address = sms.getOriginatingAddress();
}
sms = new Sms(msg_id,message, address);
UploadSms up = new UploadSms();
up.start();
Toast.makeText(context,
"SMS Received>>" + message + "From >>" + address,
Toast.LENGTH_LONG).show();
}
}
By this I get sms body and sender number.
But I know there exist a unique number for every message in android phone, I retrieved it.
But unable to retrieved in onReceive methods.
Thanks
You will have to parse the SMS PDU (in the raw PDU format) since at the android application layer there exists no API to retrieve the user data header. I believe that the short message identifier resides within the User Data Header (Octet 1). Please refer to the post Parsing SMS PDU.
In that post one of the users mentions a freeware by the name 'SMSLib' (under APACHE License) in order to parse and extract the message parameters. The message ID could be extracted using the method getMessageId() - Method in class org.smslib.Message.
Hope this helps.
I am not sure if this suffices for your purposes, but the SMSMessage.getIndexOnICC() method returns an int, indexed from 1 (up to presumably Integer.MAX_VALUE) which is the unique identifier for the SMSMessage assigned by the smart card/ICC. It can also return -1 if the SMSMessage was synthesized opposed to processed through the ICC.
Instead of synthesizing using the PDU, you should probably be using Telephony.Sms.Intents#getMessagesFromIntent(Intent) to grab a list of the SMSMessages via the PDUs using an API call instead of rolling your own. However, that was added in API 19 and so it may not be supported depending on your API version.
If you want to roll your own, the current version (API 30 at the time of writing, though the link is to the master so it may change) of the getMessageFromIntent(Intent) source code can be viewed here for inspiration of how to do things without using the API (if you want to do that for some reason). It seems that the proper way to synthesize an SMSMessage is actually the way you are doing it though, at least for the most part. Though you really should be using SMSMessage.createFromPdu(byte[], String) and specifying the format you get from Intent.getStringExtra("format"), as otherwise you may run into problems depdending on how the message is formatted (for example you might find both formats on multi-sim devices).
If you want to get really low-level and parse things out yourself, you should look to the CDMA and GSM specific implementations of SMSMessage (though this is not a subclass to be clear), which are of course android internal and not meant to be used as exposed APIs. The CDMA implementation for parsing the PDU is at ...cdma.SMSMessage.parsePdu(byte[]) whereas the GSM implementation is at ...gsm.SMSMessage.parsePdu(byte[]). For reference to the underlying base class you should see SMSMessageBase.
I wish to achieve a SMS loopback, i.e. to send and receive SMS from the same application. In order to do so, I have created a class that extends BroadcastReciever, implemented the onReceive() method, and declared the relevant permissions.
I verified the implementation by sending a SMS using telnet.
I want to automate the telnet process, i.e. having the application test itself by sending the SMS. In order to do so, I invoke the following method in the main activity, but the BroadcastReceiver is never called:
private final void sendSMS() {
final TelephonyManager telMgr = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
final int len = telMgr.getLine1Number().length();
final String phoneNum = telMgr.getLine1Number().substring(len - 4, len);
final String msg = "msg";
SmsManager.getDefault().sendTextMessage(phoneNum, null, msg, null, null);
}
Any clue what is wrong...?
UPDATE: Note that the code above is intended for the emulator.
Not sure if I understand you question right, but are you trying send an SMS from the emulator to itself? As far as I know, that is not possible. Just load up another emulator, and send messages between them.
Since telnet commands work, your BroadcastReceiver is probably correctly implemented, but you should probably attach the code for it anyways... Its hard to troubleshoot code you can't see :)
I am working on a simple app for the HTC EVO that blinks the alternate notification LED when a new text message is received. I have this part working great via a Broadcast Receiver but I need some way to turn the LED off when the user has read the message(s) using their default SMS app. I'm not sure if it is best to do this in the receiver or in a background service. I found this, which might be what I am looking for, but I have no idea on how to use it as I could not find any instructions or tutorials.
Alright, I have worked out the following code which I think will meet my needs.
private int getUnreadSMSCount()
{
int count = 0;
Uri smsURI = Uri.parse("content://sms");
ContentResolver contentResolver = this.getContentResolver();
Cursor cursor = contentResolver.query(smsURI, null, "read=0", null, null);
if (cursor != null)
{
try
{
count = cursor.getCount();
}
finally
{
cursor.close();
}
}
return count;
}
Unfortunately I do not believe there is a way to do this.
When your BroadcastReceiver receives the Intent it is a copy of the Intent, same with the default SMS app. So you each have copies of the message independent of eachother.
You can set your own copy of the message to read, but you will be unable to see its status in the default SMS app. Also, the default app does not send out a broadcast that the message has been read, all that data is kept locally.
The only way you would be able to implement this would be to write a full replacement of the Messaging app.
Sorry, I hope this helps, let me know if you have any other questions.