Since I am not able to compress a 256-byte long md5-like string within 160 characters, I decided to use Multi-part message in Android to divide the SMS into several parts and the receiver will listen the SMS then combine those messages.
I use the combination algorithm from here: Android - receiving long SMS (multipart)
And after a few testing I found that the SMS I sent was totally messed, though I encoded them with Base64 before sending.
Thinking the SMS is a 7-bit Encoding method I thought it might be the encoding problem.
But I have successfully sent Base 64 encoded message, but it was short and within 1 SMS.
My question is:
If it is a encoding problem, then why I can send a totally readable Base64 encoded messages within 160 characters, but cannot get a readable result when sending messages exceeding 160 characters?
I've attached my code here:
The string I intend to send:
static final String bigNum = "C196BA05AC29E1F9C3C72D56DFFC6154A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A067CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE428782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE619ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD92D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BFFAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E5320121496DC65B3930E38047294FF877831A16D5228418DE8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A0402A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83";
The Code
private void sendMsg (String phoneNum, String msg) {
SmsManager sms = SmsManager.getDefault();
ArrayList<String> parts = sms.divideMessage(msg);
sms.sendMultipartTextMessage(phoneNum, null, parts, null, null);
}
public void onStart() {
super.onStart();
final String bigNum64 = Base64.encodeToString(bigNum.getBytes(), 0);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendMsg("5554", bigNum64);
textView.setText(bigNum64);
}
});
}
public void onResume() {
super.onResume();
Bundle receiver = getIntent().getExtras();
if (receiver != null) {
String msg = receiver.getString("SMS");
textView.setText("Received" + msg);
}
}
}
The SMS Receiver:
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdus.length];
String body = "";
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
SmsMessage sms = messages[0];
try {
if (messages.length == 1 || sms.isReplace()) {
body = sms.getDisplayMessageBody();
}
else {
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
bodyText.append(messages[i].getMessageBody());
}
body = bodyText.toString();
}
}
catch (Exception e) {
}
Intent start = new Intent(context, SendLongSMSActivity.class);
start.putExtra("SMS", body);
start.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(start);
}
Thanks for your help!
Related Post: Any way to compress a 256 bytes "MD5-like" string into 160 bytes or less?
Even if I give a rather simple long string, like
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
and no matter with or without Base64 encoding, the receiver cannot get the correct results.
UPDATE #2
When using real phone for testing, the receiver can receive correct message without base 64, it was due to the emulator garbled the text.
Question now closed.
Thanks for everyone who helps! And thanx #Dan
UPDATE:
The string "0123456789ABCDEF..." without base64, divided into 2 parts:
part [0] "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345678"
part [1] "9ABCDEF0123456789ABCDEF"
Here's the divided base64 string of the "0123456789ABCDEF...:
part[0] "MDEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4\nOUFCQ0RFRjAxMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCQ0RFRjAx"
part[1] "\nMjM0NTY3ODlBQkNERUYwMTIzNDU2Nzg5QUJDREVGMDEyMzQ1Njc4OUFCQ0RFRjAxMjM0NTY3ODlB\nQkNERUY=\n"
It appears that the sms divide is adding \n characters after every 77 characters and at the end, just strip those from your string and it will decode properly.
Related
I am trying to read an NFC tag using Android. I'm a beekeeper and this is to ID my hives when I approach them. I have searched here but I am still having issues reading the tag. I want to read the text, but when it reads, there is a square-like character and characters displayed like " Ten" before the desired text.
Here is the code I'm using. I know that the payload bytes have to be correct and I have tried changing them but to no avail.
private static NdefMessage getTestMessage() {
byte[] mimeBytes = "application/com.android.cts.verifier.nfc"
.getBytes(Charset.forName("US-ASCII"));
byte[] id = new byte[] {1, 3, 3, 7};
byte[] payload = "CTS Verifier NDEF Push Tag".getBytes(Charset.forName("US-ASCII"));
return new NdefMessage(new NdefRecord[] {
new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, id, payload)
});
}
#Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
mNfcAdapter.setNdefPushMessageCallback(this, this);
}
// sending message
#Override
public NdefMessage createNdefMessage(NfcEvent event) {
return getTestMessage();
}
private NdefMessage[] getNdefMessages(Intent intent) {
Parcelable[] rawMessages = intent
.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMessages != null) {
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < messages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
}
return messages;
} else {
return null;
}
}
static String displayByteArray(byte[] bytes) {
String res="";
StringBuilder builder = new StringBuilder().append("");
for (int i = 0; i < bytes.length; i++) {
res+=(char)bytes[i];
}
return res;
}
// displaying message
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
NdefMessage[] messages = getNdefMessages(intent);
edtUser.setText(displayByteArray(messages[0].toByteArray()));
Toast.makeText(this, "NFC tag entered", Toast.LENGTH_LONG).show();
}
You are getting odd additional characters when displaying the message since you try to display the whole raw NDEF record as text string (using some odd decoding method):
NdefMessage[] messages = getNdefMessages(intent);
edtUser.setText(displayByteArray(messages[0].toByteArray()));
There are several problems with this. First of all, you would typically want to decode the text using the same encoding that you used to write the text. For instance, if you used
String text = "...";
byte[] bytes = text.getBytes(Charset.forName("US-ASCII"));
to get a byte array in US-ASCII encoding for a given text string, you would also want to use that same US-ASCII encoding to translate the bytes into a text string again:
byte[] bytes = ...;
String text = new String(bytes, "US-ASCII");
Second, you are interpreting the whole NDEF message as a text string. However, the text that you stored on the tag is typically only contained inside the payload of an NDEF record. In your case, the prefix "Ten" suggests that you used an NFC Forum Text record (type name "T") with the language indication "en" (for English). You would, therefore, want to search the NDEF message for the Text record:
for (NdefRecord r : messages[0].getRecords()) {
if (r.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
if (Arrays.equals(r.getType(), NdefRecord.RTD_TEXT)) {
Once you found the Text record, you can decode its text payload. The payload consists of a status byte, a language field and the actual text:
byte[] payloadBytes = ndefRecord.getPayload();
boolean isUTF8 = (payloadBytes[0] & 0x080) == 0; //status byte: bit 7 indicates encoding (0 = UTF-8, 1 = UTF-16)
int languageLength = payloadBytes[0] & 0x03F; //status byte: bits 5..0 indicate length of language code
int textLength = payloadBytes.length - 1 - languageLength;
String languageCode = new String(payloadBytes, 1, languageLength, "US-ASCII");
String payloadText = new String(payloadBytes, 1 + languageLength, textLength, isUTF8 ? "UTF-8" : "UTF-16");
edtUser.setText(payloadText);
}
}
}
So I have this pretty simple app. It sends encrypted SMS messages to the specified phone number. It works swell, but I am having issues finding a way to make the recieved messages automatically show up in the message log. I currectly have a "refresh" button that updates the message log if a new message is available. I don't want to have to use a refresh button, I want the message to simply show up as it is received.
The way the app works is, it takes the message to be sent from the textbox and encrypts it. It then sends the message and when received, it decrypts and stores in a variable and presents in the message log (after I press "refresh").
I have tried many searches on google but can not find something useful because the words are too sensetive. I usually find links to people not being able to receive messages or just links to download messaging apps.
Here is some of my code. This is the receive sms part.
public class SmsReceiver extends BroadcastReceiver
{
public static String decrypted = "";
#Override
public void onReceive(Context context, Intent intent)
{
//---get the SMS message passed in---
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
String str ="";
String info = "";
if (bundle != null)
{
//---retrieve the SMS message received---
Object[] pdus = (Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
for (int i=0; i<msgs.length; i++){
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
info += "SMS from " + msgs[i].getOriginatingAddress();
info += " : ";
str += msgs[i].getMessageBody().toString();
if (i==msgs.length-1 && MainActivity.locked == true){
try {
decrypted = AESHelper.decrypt(MainActivity.seed, str);
} catch (Exception e) {
e.printStackTrace();
}
MainActivity.rand = Math.random();
MainActivity.seed = String.valueOf(MainActivity.rand);
decrypted = info + decrypted;
info = "";
MainActivity.locked = false;
}
}
}
So, in my main activity, I have the refresh button set to check the length of decrypted. If length of decrypted > 0 then I take decrypted's content and display them in the message log.
There are multiple ways to do this. Simple way would be to use TimerTask that runs every second in your MainActivity to check the length of 'decrypted' variable. Timertask does not run on ui thread. But since you are using it only to check the length of a variable you should be fine. If you need help with timertask, use my example below:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new SpecificTask(), 1000, 200);
But you have to define your SpecificTask() class...
private Specific Task extends TimerTask{
#Override
public void run() {
if(decrypted.length()>0){
//do refresh here but make sure you run this code onuithread
//just use runonuithread...
}
}
}
Or you could just use a handler object...
boolean mStopHandler = false;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (!mStopHandler) {
if(decrypted.length()>0){
//refreshlayout code
}
handler.postDelayed(this, 500);
}
}
};
// start your handler with:
handler.post(runnable);
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!
I am working on an android project, that deals with device authentication via sms.
The problem I am facing is, when the authentication key is being sent, the receiving device gets a garbled text and not the original sent content.
I am using two instances of the emulator to test the code.
Here is the relevant code :
String MyPublic = "__key("+N.toString()+")yek__";
ArrayList<String> parts = smsmgr.divideMessage(MyPublic);
smsmgr.sendMultipartTextMessage(senderNumber, null, parts, null, null);
How ever when I am sending a single sms within 160 characters, then this problem isn't disappears.
Here is the code I am using to listen for incoming messages.
public void onReceive(final Context context, Intent intent) {
msgReceived = false;
Object[] pdus=(Object[])intent.getExtras().get("pdus");
Bundle bundle = intent.getExtras();
if (bundle != null) {
pdus = (Object[])bundle.get("pdus");
final SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
}
SmsMessage sms = messages[0];
String body;
if (messages.length == 1 || sms.isReplace()) {
body = sms.getDisplayMessageBody();
} else {
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < messages.length; i++) {
bodyText.append(messages[i].getMessageBody());
}
body = bodyText.toString();
}
}
The message that is received when the 'Multi-part' thing is used is of this type :
The "HelloWorld" was sent as a single-part message(Non-Multipart) and the 3rd and second from below are parts of that multipart authentication key.
Need Help resolving this.
Regards
Priyabrata.
So im making an app that uses sms to send data between two devices and am trying to get one to automaticly respond to the other. But the string getMessageBody() is returning isnt setting of the if statment to send the automatic message even tho the log output "(test)" seems to match the condition. I have added extra text to either side of the string in the log message to check for whitespace. Thanks in advance.
The code sending the message to be received.
SendSMS(inputNum.getText().toString(),"test");
The receiver code
public void onReceive(Context context, Intent intent)
{
Bundle bundle = intent.getExtras();
SmsMessage[] msgs = null;
if(bundle != null)
{
Object[]pdus = (Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
for(int i = 0;i < msgs.length;i++)
{
msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
originNumber = msgs[i].getOriginatingAddress();
messages = msgs[i].getMessageBody();
Log.e("Received Text", messages);
}
if(messages == "test")
SendSMS(originNumber, "auto send");
else
Log.e("else Text", "("+messages+")");
}
if(messages == "test")
String comparison in java need the equals method. With the == you will compare the address of message with the address of "test". Since those are differente String object the result will be false. Change it in:
if(messages.equals("test"))