mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-17 21:47:28 +00:00
clean up deprecated message connection
This commit is contained in:
parent
5b15decdd6
commit
c5ab13aadf
@ -49,7 +49,6 @@ import android.widget.Toast;
|
|||||||
import androidx.annotation.DimenRes;
|
import androidx.annotation.DimenRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
@ -71,17 +70,12 @@ import org.thoughtcrime.securesms.components.StickerView;
|
|||||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.Quote;
|
import org.thoughtcrime.securesms.database.model.Quote;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
import org.session.libsignal.utilities.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
|
||||||
@ -1114,13 +1108,6 @@ public class ConversationItem extends LinearLayout
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v, final List<Slide> slides) {
|
public void onClick(View v, final List<Slide> slides) {
|
||||||
Log.i(TAG, "onClick() for attachment download");
|
Log.i(TAG, "onClick() for attachment download");
|
||||||
if (messageRecord.isMmsNotification()) {
|
|
||||||
Log.i(TAG, "Scheduling MMS attachment download");
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MmsDownloadJob(messageRecord.getId(),
|
|
||||||
messageRecord.getThreadId(), false));
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Scheduling push attachment downloads for " + slides.size() + " items");
|
Log.i(TAG, "Scheduling push attachment downloads for " + slides.size() + " items");
|
||||||
|
|
||||||
for (Slide slide : slides) {
|
for (Slide slide : slides) {
|
||||||
@ -1131,7 +1118,6 @@ public class ConversationItem extends LinearLayout
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private class SlideClickPassthroughListener implements SlideClickListener {
|
private class SlideClickPassthroughListener implements SlideClickListener {
|
||||||
|
|
||||||
@ -1224,58 +1210,7 @@ public class ConversationItem extends LinearLayout
|
|||||||
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush());
|
intent.putExtra(MessageDetailsActivity.IS_PUSH_GROUP_EXTRA, groupThread && messageRecord.isPush());
|
||||||
intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
|
intent.putExtra(MessageDetailsActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
} else if (!messageRecord.isOutgoing() && messageRecord.isIdentityMismatchFailure()) {
|
|
||||||
// handleApproveIdentity();
|
|
||||||
} else if (messageRecord.isPendingInsecureSmsFallback()) {
|
|
||||||
handleMessageApproval();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMessageApproval() {
|
|
||||||
final int title;
|
|
||||||
final int message;
|
|
||||||
|
|
||||||
if (messageRecord.isMms()) title = R.string.ConversationItem_click_to_approve_unencrypted_mms_dialog_title;
|
|
||||||
else title = R.string.ConversationItem_click_to_approve_unencrypted_sms_dialog_title;
|
|
||||||
|
|
||||||
message = R.string.ConversationItem_click_to_approve_unencrypted_dialog_message;
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
builder.setTitle(title);
|
|
||||||
|
|
||||||
if (message > -1) builder.setMessage(message);
|
|
||||||
|
|
||||||
builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
|
|
||||||
if (messageRecord.isMms()) {
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
database.markAsInsecure(messageRecord.getId());
|
|
||||||
database.markAsOutbox(messageRecord.getId());
|
|
||||||
database.markAsForcedSms(messageRecord.getId());
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MmsSendJob(messageRecord.getId()));
|
|
||||||
} else {
|
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
|
||||||
database.markAsInsecure(messageRecord.getId());
|
|
||||||
database.markAsOutbox(messageRecord.getId());
|
|
||||||
database.markAsForcedSms(messageRecord.getId());
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new SmsSendJob(context, messageRecord.getId(),
|
|
||||||
messageRecord.getIndividualRecipient().getAddress().serialize()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(R.string.no, (dialogInterface, i) -> {
|
|
||||||
if (messageRecord.isMms()) {
|
|
||||||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageRecord.getId());
|
|
||||||
} else {
|
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageRecord.getId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,13 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
"ClosedGroupUpdateMessageSendJob",
|
"ClosedGroupUpdateMessageSendJob",
|
||||||
"NullMessageSendJob",
|
"NullMessageSendJob",
|
||||||
"StickerDownloadJob",
|
"StickerDownloadJob",
|
||||||
"StickerPackDownloadJob");
|
"StickerPackDownloadJob",
|
||||||
|
"MmsSendJob",
|
||||||
|
"MmsReceiveJob",
|
||||||
|
"MmsDownloadJob",
|
||||||
|
"SmsSendJob",
|
||||||
|
"SmsSentJob",
|
||||||
|
"SmsReceiveJob");
|
||||||
}
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
@ -37,9 +37,6 @@ public final class JobManagerFactories {
|
|||||||
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
||||||
put(ClosedGroupUpdateMessageSendJobV2.KEY, new ClosedGroupUpdateMessageSendJobV2.Factory());
|
put(ClosedGroupUpdateMessageSendJobV2.KEY, new ClosedGroupUpdateMessageSendJobV2.Factory());
|
||||||
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
|
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
|
||||||
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
|
|
||||||
put(MmsReceiveJob.KEY, new MmsReceiveJob.Factory());
|
|
||||||
put(MmsSendJob.KEY, new MmsSendJob.Factory());
|
|
||||||
put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
|
put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
|
||||||
put(PushDecryptJob.KEY, new PushDecryptJob.Factory());
|
put(PushDecryptJob.KEY, new PushDecryptJob.Factory());
|
||||||
put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory());
|
put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory());
|
||||||
@ -50,9 +47,6 @@ public final class JobManagerFactories {
|
|||||||
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
|
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
|
||||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||||
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory());
|
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory());
|
||||||
put(SmsReceiveJob.KEY, new SmsReceiveJob.Factory());
|
|
||||||
put(SmsSendJob.KEY, new SmsSendJob.Factory());
|
|
||||||
put(SmsSentJob.KEY, new SmsSentJob.Factory());
|
|
||||||
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
|
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
|
||||||
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
||||||
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
||||||
|
@ -1,268 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.google.android.mms.pdu_alt.CharacterSets;
|
|
||||||
import com.google.android.mms.pdu_alt.EncodedStringValue;
|
|
||||||
import com.google.android.mms.pdu_alt.PduBody;
|
|
||||||
import com.google.android.mms.pdu_alt.PduPart;
|
|
||||||
import com.google.android.mms.pdu_alt.RetrieveConf;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
|
|
||||||
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
|
|
||||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
|
||||||
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
|
||||||
import org.thoughtcrime.securesms.mms.PartParser;
|
|
||||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.UriAttachment;
|
|
||||||
import org.session.libsession.messaging.threads.Address;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier;
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class MmsDownloadJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "MmsDownloadJob";
|
|
||||||
|
|
||||||
private static final String TAG = MmsDownloadJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_MESSAGE_ID = "message_id";
|
|
||||||
private static final String KEY_THREAD_ID = "thread_id";
|
|
||||||
private static final String KEY_AUTOMATIC = "automatic";
|
|
||||||
|
|
||||||
private long messageId;
|
|
||||||
private long threadId;
|
|
||||||
private boolean automatic;
|
|
||||||
private MessageNotifier messageNotifier;
|
|
||||||
|
|
||||||
public MmsDownloadJob(long messageId, long threadId, boolean automatic) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("mms-operation")
|
|
||||||
.setMaxAttempts(25)
|
|
||||||
.build(),
|
|
||||||
messageId,
|
|
||||||
threadId,
|
|
||||||
automatic);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private MmsDownloadJob(@NonNull Job.Parameters parameters, long messageId, long threadId, boolean automatic) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.messageId = messageId;
|
|
||||||
this.threadId = threadId;
|
|
||||||
this.automatic = automatic;
|
|
||||||
this.messageNotifier = ApplicationContext.getInstance(context).messageNotifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
Data serialize() {
|
|
||||||
return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId)
|
|
||||||
.putLong(KEY_THREAD_ID, threadId)
|
|
||||||
.putBoolean(KEY_AUTOMATIC, automatic)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAdded() {
|
|
||||||
if (automatic && KeyCachingService.isLocked(context)) {
|
|
||||||
DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId);
|
|
||||||
messageNotifier.updateNotification(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() {
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
Optional<MmsDatabase.MmsNotificationInfo> notification = database.getNotification(messageId);
|
|
||||||
|
|
||||||
if (!notification.isPresent()) {
|
|
||||||
Log.w(TAG, "No notification for ID: " + messageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (notification.get().getContentLocation() == null) {
|
|
||||||
throw new MmsException("Notification content location was null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
|
||||||
throw new MmsException("Not registered");
|
|
||||||
}
|
|
||||||
|
|
||||||
database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_CONNECTING);
|
|
||||||
|
|
||||||
String contentLocation = notification.get().getContentLocation();
|
|
||||||
byte[] transactionId = new byte[0];
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (notification.get().getTransactionId() != null) {
|
|
||||||
transactionId = notification.get().getTransactionId().getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "No transaction ID!");
|
|
||||||
}
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Downloading mms at " + Uri.parse(contentLocation).getHost() + ", subscription ID: " + notification.get().getSubscriptionId());
|
|
||||||
|
|
||||||
RetrieveConf retrieveConf = new CompatMmsConnection(context).retrieve(contentLocation, transactionId, notification.get().getSubscriptionId());
|
|
||||||
|
|
||||||
if (retrieveConf == null) {
|
|
||||||
throw new MmsException("RetrieveConf was null");
|
|
||||||
}
|
|
||||||
|
|
||||||
storeRetrievedMms(contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId(), notification.get().getFrom());
|
|
||||||
} catch (ApnUnavailableException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleDownloadError(messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
|
|
||||||
automatic);
|
|
||||||
} catch (MmsException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleDownloadError(messageId, threadId,
|
|
||||||
MmsDatabase.Status.DOWNLOAD_HARD_FAILURE,
|
|
||||||
automatic);
|
|
||||||
} catch (MmsRadioException | IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleDownloadError(messageId, threadId,
|
|
||||||
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
|
|
||||||
automatic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
database.markDownloadState(messageId, MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE);
|
|
||||||
|
|
||||||
if (automatic) {
|
|
||||||
database.markIncomingNotificationReceived(threadId);
|
|
||||||
messageNotifier.updateNotification(context, threadId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeRetrievedMms(String contentLocation,
|
|
||||||
long messageId, long threadId, RetrieveConf retrieved,
|
|
||||||
int subscriptionId, @Nullable Address notificationFrom)
|
|
||||||
throws MmsException
|
|
||||||
{
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
Optional<Address> group = Optional.absent();
|
|
||||||
Set<Address> members = new HashSet<>();
|
|
||||||
String body = null;
|
|
||||||
List<Attachment> attachments = new LinkedList<>();
|
|
||||||
|
|
||||||
Address from;
|
|
||||||
|
|
||||||
if (retrieved.getFrom() != null) {
|
|
||||||
from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString()));
|
|
||||||
} else if (notificationFrom != null) {
|
|
||||||
from = notificationFrom;
|
|
||||||
} else {
|
|
||||||
from = Address.Companion.getUNKNOWN();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retrieved.getTo() != null) {
|
|
||||||
for (EncodedStringValue toValue : retrieved.getTo()) {
|
|
||||||
members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retrieved.getCc() != null) {
|
|
||||||
for (EncodedStringValue ccValue : retrieved.getCc()) {
|
|
||||||
members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
members.add(from);
|
|
||||||
members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context)));
|
|
||||||
|
|
||||||
if (retrieved.getBody() != null) {
|
|
||||||
body = PartParser.getMessageText(retrieved.getBody());
|
|
||||||
PduBody media = PartParser.getSupportedMediaParts(retrieved.getBody());
|
|
||||||
|
|
||||||
for (int i=0;i<media.getPartsNum();i++) {
|
|
||||||
PduPart part = media.getPart(i);
|
|
||||||
|
|
||||||
if (part.getData() != null) {
|
|
||||||
Uri uri = BlobProvider.getInstance().forData(part.getData()).createForSingleUseInMemory();
|
|
||||||
String name = null;
|
|
||||||
|
|
||||||
if (part.getName() != null) name = Util.toIsoString(part.getName());
|
|
||||||
|
|
||||||
attachments.add(new UriAttachment(uri, Util.toIsoString(part.getContentType()),
|
|
||||||
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
|
|
||||||
part.getData().length, name, false, false, null, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (members.size() > 2) {
|
|
||||||
group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), new LinkedList<>())));
|
|
||||||
}
|
|
||||||
|
|
||||||
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false);
|
|
||||||
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
|
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
|
||||||
database.delete(messageId);
|
|
||||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDownloadError(long messageId, long threadId, int downloadStatus, boolean automatic)
|
|
||||||
{
|
|
||||||
MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
|
|
||||||
db.markDownloadState(messageId, downloadStatus);
|
|
||||||
|
|
||||||
if (automatic) {
|
|
||||||
db.markIncomingNotificationReceived(threadId);
|
|
||||||
messageNotifier.updateNotification(context, threadId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MmsDownloadJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MmsDownloadJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MmsDownloadJob(parameters,
|
|
||||||
data.getLong(KEY_MESSAGE_ID),
|
|
||||||
data.getLong(KEY_THREAD_ID),
|
|
||||||
data.getBoolean(KEY_AUTOMATIC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import com.google.android.mms.pdu_alt.GenericPdu;
|
|
||||||
import com.google.android.mms.pdu_alt.NotificationInd;
|
|
||||||
import com.google.android.mms.pdu_alt.PduHeaders;
|
|
||||||
import com.google.android.mms.pdu_alt.PduParser;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.session.libsession.messaging.threads.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
|
||||||
import org.session.libsignal.utilities.Base64;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class MmsReceiveJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "MmsReceiveJob";
|
|
||||||
|
|
||||||
private static final String TAG = MmsReceiveJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_DATA = "data";
|
|
||||||
private static final String KEY_SUBSCRIPTION_ID = "subscription_id";
|
|
||||||
|
|
||||||
private byte[] data;
|
|
||||||
private int subscriptionId;
|
|
||||||
|
|
||||||
public MmsReceiveJob(byte[] data, int subscriptionId) {
|
|
||||||
this(new Job.Parameters.Builder().setMaxAttempts(25).build(), data, subscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MmsReceiveJob(@NonNull Job.Parameters parameters, byte[] data, int subscriptionId) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.data = data;
|
|
||||||
this.subscriptionId = subscriptionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
Data serialize() {
|
|
||||||
return new Data.Builder().putString(KEY_DATA, Base64.encodeBytes(data))
|
|
||||||
.putInt(KEY_SUBSCRIPTION_ID, subscriptionId)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() {
|
|
||||||
if (data == null) {
|
|
||||||
Log.w(TAG, "Received NULL pdu, ignoring...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PduParser parser = new PduParser(data);
|
|
||||||
GenericPdu pdu = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
pdu = parser.parse();
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNotification(pdu) && !isBlocked(pdu)) {
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox((NotificationInd)pdu, subscriptionId);
|
|
||||||
|
|
||||||
Log.i(TAG, "Inserted received MMS notification...");
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MmsDownloadJob(messageAndThreadId.first,
|
|
||||||
messageAndThreadId.second,
|
|
||||||
true));
|
|
||||||
} else if (isNotification(pdu)) {
|
|
||||||
Log.w(TAG, "*** Received blocked MMS, ignoring...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBlocked(GenericPdu pdu) {
|
|
||||||
if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) {
|
|
||||||
Recipient recipients = Recipient.from(context, Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString())), false);
|
|
||||||
return recipients.isBlocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isNotification(GenericPdu pdu) {
|
|
||||||
return pdu != null && pdu.getMessageType() == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MmsReceiveJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MmsReceiveJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
try {
|
|
||||||
return new MmsReceiveJob(parameters, Base64.decode(data.getString(KEY_DATA)), data.getInt(KEY_SUBSCRIPTION_ID));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,326 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
|
|
||||||
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
|
|
||||||
import com.google.android.mms.ContentType;
|
|
||||||
import com.google.android.mms.InvalidHeaderValueException;
|
|
||||||
import com.google.android.mms.pdu_alt.CharacterSets;
|
|
||||||
import com.google.android.mms.pdu_alt.EncodedStringValue;
|
|
||||||
import com.google.android.mms.pdu_alt.PduBody;
|
|
||||||
import com.google.android.mms.pdu_alt.PduComposer;
|
|
||||||
import com.google.android.mms.pdu_alt.PduHeaders;
|
|
||||||
import com.google.android.mms.pdu_alt.PduPart;
|
|
||||||
import com.google.android.mms.pdu_alt.SendConf;
|
|
||||||
import com.google.android.mms.pdu_alt.SendReq;
|
|
||||||
import com.google.android.mms.smil.SmilHelper;
|
|
||||||
import com.klinker.android.send_message.Utils;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
|
||||||
import org.session.libsession.messaging.threads.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
|
|
||||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
|
||||||
import org.thoughtcrime.securesms.mms.MmsSendResult;
|
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
|
||||||
import org.session.libsignal.utilities.Hex;
|
|
||||||
import org.session.libsession.utilities.NumberUtil;
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class MmsSendJob extends SendJob {
|
|
||||||
|
|
||||||
public static final String KEY = "MmsSendJob";
|
|
||||||
|
|
||||||
private static final String TAG = MmsSendJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_MESSAGE_ID = "message_id";
|
|
||||||
|
|
||||||
private long messageId;
|
|
||||||
|
|
||||||
public MmsSendJob(long messageId) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("mms-operation")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxAttempts(25)
|
|
||||||
.build(),
|
|
||||||
messageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MmsSendJob(@NonNull Job.Parameters parameters, long messageId) {
|
|
||||||
super(parameters);
|
|
||||||
this.messageId = messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
Data serialize() {
|
|
||||||
return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSend() throws MmsException, NoSuchMessageException, IOException {
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
|
|
||||||
|
|
||||||
if (database.isSent(messageId)) {
|
|
||||||
Log.w(TAG, "Message " + messageId + " was already sent. Ignoring.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Log.i(TAG, "Sending message: " + messageId);
|
|
||||||
|
|
||||||
SendReq pdu = constructSendPdu(message);
|
|
||||||
|
|
||||||
validateDestinations(message, pdu);
|
|
||||||
|
|
||||||
final byte[] pduBytes = getPduBytes(pdu);
|
|
||||||
final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes, message.getSubscriptionId());
|
|
||||||
final MmsSendResult result = getSendResult(sendConf, pdu);
|
|
||||||
|
|
||||||
database.markAsSent(messageId, false);
|
|
||||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
|
||||||
|
|
||||||
Log.i(TAG, "Sent message: " + messageId);
|
|
||||||
} catch (UndeliverableMessageException | IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
database.markAsSentFailed(messageId);
|
|
||||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
|
||||||
} catch (InsecureFallbackApprovalException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
database.markAsPendingInsecureSmsFallback(messageId);
|
|
||||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
Log.i(TAG, "onCanceled() messageId: " + messageId);
|
|
||||||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
|
|
||||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] getPduBytes(SendReq message)
|
|
||||||
throws IOException, UndeliverableMessageException, InsecureFallbackApprovalException
|
|
||||||
{
|
|
||||||
byte[] pduBytes = new PduComposer(context, message).make();
|
|
||||||
|
|
||||||
if (pduBytes == null) {
|
|
||||||
throw new UndeliverableMessageException("PDU composition failed, null payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
return pduBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MmsSendResult getSendResult(SendConf conf, SendReq message)
|
|
||||||
throws UndeliverableMessageException
|
|
||||||
{
|
|
||||||
if (conf == null) {
|
|
||||||
throw new UndeliverableMessageException("No M-Send.conf received in response to send.");
|
|
||||||
} else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
|
|
||||||
throw new UndeliverableMessageException("Got bad response: " + conf.getResponseStatus());
|
|
||||||
} else if (isInconsistentResponse(message, conf)) {
|
|
||||||
throw new UndeliverableMessageException("Mismatched response!");
|
|
||||||
} else {
|
|
||||||
return new MmsSendResult(conf.getMessageId(), conf.getResponseStatus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInconsistentResponse(SendReq message, SendConf response) {
|
|
||||||
Log.i(TAG, "Comparing: " + Hex.toString(message.getTransactionId()));
|
|
||||||
Log.i(TAG, "With: " + Hex.toString(response.getTransactionId()));
|
|
||||||
return !Arrays.equals(message.getTransactionId(), response.getTransactionId());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateDestinations(EncodedStringValue[] destinations) throws UndeliverableMessageException {
|
|
||||||
if (destinations == null) return;
|
|
||||||
|
|
||||||
for (EncodedStringValue destination : destinations) {
|
|
||||||
if (destination == null || !NumberUtil.isValidSmsOrEmail(destination.getString())) {
|
|
||||||
throw new UndeliverableMessageException("Invalid destination: " +
|
|
||||||
(destination == null ? null : destination.getString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateDestinations(OutgoingMediaMessage media, SendReq message) throws UndeliverableMessageException {
|
|
||||||
validateDestinations(message.getTo());
|
|
||||||
validateDestinations(message.getCc());
|
|
||||||
validateDestinations(message.getBcc());
|
|
||||||
|
|
||||||
if (message.getTo() == null && message.getCc() == null && message.getBcc() == null) {
|
|
||||||
throw new UndeliverableMessageException("No to, cc, or bcc specified!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (media.isSecure()) {
|
|
||||||
throw new UndeliverableMessageException("Attempt to send encrypted MMS?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SendReq constructSendPdu(OutgoingMediaMessage message)
|
|
||||||
throws UndeliverableMessageException
|
|
||||||
{
|
|
||||||
SendReq req = new SendReq();
|
|
||||||
String lineNumber = getMyNumber(context);
|
|
||||||
Address destination = message.getRecipient().getAddress();
|
|
||||||
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
|
||||||
List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(lineNumber)) {
|
|
||||||
req.setFrom(new EncodedStringValue(lineNumber));
|
|
||||||
} else {
|
|
||||||
req.setFrom(new EncodedStringValue(TextSecurePreferences.getLocalNumber(context)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destination.isMmsGroup()) {
|
|
||||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(destination.toGroupString(), false);
|
|
||||||
|
|
||||||
for (Recipient member : members) {
|
|
||||||
if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
|
|
||||||
req.addBcc(new EncodedStringValue(member.getAddress().serialize()));
|
|
||||||
} else {
|
|
||||||
req.addTo(new EncodedStringValue(member.getAddress().serialize()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
req.addTo(new EncodedStringValue(destination.serialize()));
|
|
||||||
}
|
|
||||||
|
|
||||||
req.setDate(System.currentTimeMillis() / 1000);
|
|
||||||
|
|
||||||
PduBody body = new PduBody();
|
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(message.getBody())) {
|
|
||||||
PduPart part = new PduPart();
|
|
||||||
String name = String.valueOf(System.currentTimeMillis());
|
|
||||||
part.setData(Util.toUtf8Bytes(message.getBody()));
|
|
||||||
part.setCharset(CharacterSets.UTF_8);
|
|
||||||
part.setContentType(ContentType.TEXT_PLAIN.getBytes());
|
|
||||||
part.setContentId(name.getBytes());
|
|
||||||
part.setContentLocation((name + ".txt").getBytes());
|
|
||||||
part.setName((name + ".txt").getBytes());
|
|
||||||
|
|
||||||
body.addPart(part);
|
|
||||||
size += getPartSize(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Attachment attachment : scaledAttachments) {
|
|
||||||
try {
|
|
||||||
if (attachment.getDataUri() == null) throw new IOException("Assertion failed, attachment for outgoing MMS has no data!");
|
|
||||||
|
|
||||||
String fileName = attachment.getFileName();
|
|
||||||
PduPart part = new PduPart();
|
|
||||||
|
|
||||||
if (fileName == null) {
|
|
||||||
fileName = String.valueOf(Math.abs(Util.getSecureRandom().nextLong()));
|
|
||||||
String fileExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(attachment.getContentType());
|
|
||||||
|
|
||||||
if (fileExtension != null) fileName = fileName + "." + fileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attachment.getContentType().startsWith("text")) {
|
|
||||||
part.setCharset(CharacterSets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
part.setContentType(attachment.getContentType().getBytes());
|
|
||||||
part.setContentLocation(fileName.getBytes());
|
|
||||||
part.setName(fileName.getBytes());
|
|
||||||
|
|
||||||
int index = fileName.lastIndexOf(".");
|
|
||||||
String contentId = (index == -1) ? fileName : fileName.substring(0, index);
|
|
||||||
part.setContentId(contentId.getBytes());
|
|
||||||
part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, attachment.getDataUri())));
|
|
||||||
|
|
||||||
body.addPart(part);
|
|
||||||
size += getPartSize(part);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(body), out);
|
|
||||||
PduPart smilPart = new PduPart();
|
|
||||||
smilPart.setContentId("smil".getBytes());
|
|
||||||
smilPart.setContentLocation("smil.xml".getBytes());
|
|
||||||
smilPart.setContentType(ContentType.APP_SMIL.getBytes());
|
|
||||||
smilPart.setData(out.toByteArray());
|
|
||||||
body.addPart(0, smilPart);
|
|
||||||
|
|
||||||
req.setBody(body);
|
|
||||||
req.setMessageSize(size);
|
|
||||||
req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
|
|
||||||
req.setExpiry(7 * 24 * 60 * 60);
|
|
||||||
|
|
||||||
try {
|
|
||||||
req.setPriority(PduHeaders.PRIORITY_NORMAL);
|
|
||||||
req.setDeliveryReport(PduHeaders.VALUE_NO);
|
|
||||||
req.setReadReport(PduHeaders.VALUE_NO);
|
|
||||||
} catch (InvalidHeaderValueException e) {}
|
|
||||||
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getPartSize(PduPart part) {
|
|
||||||
return part.getName().length + part.getContentLocation().length +
|
|
||||||
part.getContentType().length + part.getData().length +
|
|
||||||
part.getContentId().length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyMediaMessageDeliveryFailed(Context context, long messageId) {
|
|
||||||
long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId);
|
|
||||||
Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
|
||||||
|
|
||||||
if (recipient != null) {
|
|
||||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getMyNumber(Context context) throws UndeliverableMessageException {
|
|
||||||
try {
|
|
||||||
return Utils.getMyPhoneNumber(context);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
throw new UndeliverableMessageException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<MmsSendJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MmsSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MmsSendJob(parameters, data.getLong(KEY_MESSAGE_ID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import android.telephony.SmsMessage;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
|
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Base64;
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SmsReceiveJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "SmsReceiveJob";
|
|
||||||
|
|
||||||
private static final String TAG = SmsReceiveJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_PDUS = "pdus";
|
|
||||||
private static final String KEY_SUBSCRIPTION_ID = "subscription_id";
|
|
||||||
|
|
||||||
private @Nullable Object[] pdus;
|
|
||||||
|
|
||||||
private int subscriptionId;
|
|
||||||
|
|
||||||
public SmsReceiveJob(@Nullable Object[] pdus, int subscriptionId) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(SqlCipherMigrationConstraint.KEY)
|
|
||||||
.setMaxAttempts(25)
|
|
||||||
.build(),
|
|
||||||
pdus,
|
|
||||||
subscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SmsReceiveJob(@NonNull Job.Parameters parameters, @Nullable Object[] pdus, int subscriptionId) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.pdus = pdus;
|
|
||||||
this.subscriptionId = subscriptionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
Data serialize() {
|
|
||||||
String[] encoded = new String[pdus.length];
|
|
||||||
for (int i = 0; i < pdus.length; i++) {
|
|
||||||
encoded[i] = Base64.encodeBytes((byte[]) pdus[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Data.Builder().putStringArray(KEY_PDUS, encoded)
|
|
||||||
.putInt(KEY_SUBSCRIPTION_ID, subscriptionId)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws MigrationPendingException {
|
|
||||||
Log.i(TAG, "onRun()");
|
|
||||||
|
|
||||||
Optional<IncomingTextMessage> message = assembleMessageFragments(pdus, subscriptionId);
|
|
||||||
|
|
||||||
if (message.isPresent() && !isBlocked(message.get())) {
|
|
||||||
Optional<InsertResult> insertResult = storeMessage(message.get());
|
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
|
||||||
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
|
||||||
}
|
|
||||||
} else if (message.isPresent()) {
|
|
||||||
Log.w(TAG, "*** Received blocked SMS, ignoring...");
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "*** Failed to assemble message fragments!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return exception instanceof MigrationPendingException;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBlocked(IncomingTextMessage message) {
|
|
||||||
if (message.getSender() != null) {
|
|
||||||
Recipient recipient = Recipient.from(context, message.getSender(), false);
|
|
||||||
return recipient.isBlocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<InsertResult> storeMessage(IncomingTextMessage message) throws MigrationPendingException {
|
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
|
||||||
database.ensureMigration();
|
|
||||||
|
|
||||||
if (TextSecurePreferences.getNeedsSqlCipherMigration(context)) {
|
|
||||||
throw new MigrationPendingException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.isSecureMessage()) {
|
|
||||||
IncomingTextMessage placeholder = new IncomingTextMessage(message, "");
|
|
||||||
Optional<InsertResult> insertResult = database.insertMessageInbox(placeholder);
|
|
||||||
database.markAsLegacyVersion(insertResult.get().getMessageId());
|
|
||||||
|
|
||||||
return insertResult;
|
|
||||||
} else {
|
|
||||||
return database.insertMessageInbox(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<IncomingTextMessage> assembleMessageFragments(@Nullable Object[] pdus, int subscriptionId) {
|
|
||||||
if (pdus == null) {
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<IncomingTextMessage> messages = new LinkedList<>();
|
|
||||||
|
|
||||||
for (Object pdu : pdus) {
|
|
||||||
messages.add(new IncomingTextMessage(context, SmsMessage.createFromPdu((byte[])pdu), subscriptionId));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages.isEmpty()) {
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.of(new IncomingTextMessage(messages));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MigrationPendingException extends Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<SmsReceiveJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull SmsReceiveJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
try {
|
|
||||||
int subscriptionId = data.getInt(KEY_SUBSCRIPTION_ID);
|
|
||||||
String[] encoded = data.getStringArray(KEY_PDUS);
|
|
||||||
Object[] pdus = new Object[encoded.length];
|
|
||||||
|
|
||||||
for (int i = 0; i < encoded.length; i++) {
|
|
||||||
pdus[i] = Base64.decode(encoded[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SmsReceiveJob(parameters, pdus, subscriptionId);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,249 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.telephony.PhoneNumberUtils;
|
|
||||||
import android.telephony.SmsManager;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.CellServiceConstraint;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
|
||||||
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
|
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.threads.recipients.Recipient;
|
|
||||||
import org.session.libsession.utilities.NumberUtil;
|
|
||||||
import org.session.libsession.utilities.TextSecurePreferences;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class SmsSendJob extends SendJob {
|
|
||||||
|
|
||||||
public static final String KEY = "SmsSendJob";
|
|
||||||
|
|
||||||
private static final String TAG = SmsSendJob.class.getSimpleName();
|
|
||||||
private static final int MAX_ATTEMPTS = 25;
|
|
||||||
private static final String KEY_MESSAGE_ID = "message_id";
|
|
||||||
private static final String KEY_RUN_ATTEMPT = "run_attempt";
|
|
||||||
|
|
||||||
private long messageId;
|
|
||||||
private int runAttempt;
|
|
||||||
|
|
||||||
public SmsSendJob(Context context, long messageId, String name) {
|
|
||||||
this(context, messageId, name, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SmsSendJob(Context context, long messageId, String name, int runAttempt) {
|
|
||||||
this(constructParameters(context, name), messageId, runAttempt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SmsSendJob(@NonNull Job.Parameters parameters, long messageId, int runAttempt) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.messageId = messageId;
|
|
||||||
this.runAttempt = runAttempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
Data serialize() {
|
|
||||||
return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId)
|
|
||||||
.putInt(KEY_RUN_ATTEMPT, runAttempt)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSend() throws NoSuchMessageException, TooManyRetriesException {
|
|
||||||
if (runAttempt >= MAX_ATTEMPTS) {
|
|
||||||
warn(TAG, "Hit the retry limit. Failing.");
|
|
||||||
throw new TooManyRetriesException();
|
|
||||||
}
|
|
||||||
|
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
|
||||||
SmsMessageRecord record = database.getMessage(messageId);
|
|
||||||
|
|
||||||
if (!record.isPending() && !record.isFailed()) {
|
|
||||||
warn(TAG, "Message " + messageId + " was already sent. Ignoring.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
log(TAG, "Sending message: " + messageId + " (attempt " + runAttempt + ")");
|
|
||||||
deliver(record);
|
|
||||||
log(TAG, "Sent message: " + messageId);
|
|
||||||
} catch (UndeliverableMessageException ude) {
|
|
||||||
warn(TAG, ude);
|
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(record.getId());
|
|
||||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception throwable) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
warn(TAG, "onCanceled() messageId: " + messageId);
|
|
||||||
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
|
|
||||||
Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
|
||||||
|
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
|
||||||
|
|
||||||
if (threadId != -1 && recipient != null) {
|
|
||||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deliver(SmsMessageRecord message)
|
|
||||||
throws UndeliverableMessageException
|
|
||||||
{
|
|
||||||
if (message.isSecure() || message.isKeyExchange() || message.isEndSession()) {
|
|
||||||
throw new UndeliverableMessageException("Trying to send a secure SMS?");
|
|
||||||
}
|
|
||||||
|
|
||||||
String recipient = message.getIndividualRecipient().getAddress().serialize();
|
|
||||||
|
|
||||||
// See issue #1516 for bug report, and discussion on commits related to #4833 for problems
|
|
||||||
// related to the original fix to #1516. This still may not be a correct fix if networks allow
|
|
||||||
// SMS/MMS sending to alphanumeric recipients other than email addresses, but should also
|
|
||||||
// help to fix issue #3099.
|
|
||||||
if (!NumberUtil.isValidEmail(recipient)) {
|
|
||||||
recipient = PhoneNumberUtils.stripSeparators(PhoneNumberUtils.convertKeypadLettersToDigits(recipient));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NumberUtil.isValidSmsOrEmail(recipient)) {
|
|
||||||
throw new UndeliverableMessageException("Not a valid SMS destination! " + recipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<String> messages = SmsManager.getDefault().divideMessage(message.getBody());
|
|
||||||
ArrayList<PendingIntent> sentIntents = constructSentIntents(message.getId(), message.getType(), messages, false);
|
|
||||||
ArrayList<PendingIntent> deliveredIntents = constructDeliveredIntents(message.getId(), message.getType(), messages);
|
|
||||||
|
|
||||||
// NOTE 11/04/14 -- There's apparently a bug where for some unknown recipients
|
|
||||||
// and messages, this will throw an NPE. We have no idea why, so we're just
|
|
||||||
// catching it and marking the message as a failure. That way at least it doesn't
|
|
||||||
// repeatedly crash every time you start the app.
|
|
||||||
try {
|
|
||||||
getSmsManagerFor(message.getSubscriptionId()).sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
|
|
||||||
} catch (NullPointerException | IllegalArgumentException npe) {
|
|
||||||
warn(TAG, npe);
|
|
||||||
log(TAG, "Recipient: " + recipient);
|
|
||||||
log(TAG, "Message Parts: " + messages.size());
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (int i=0;i<messages.size();i++) {
|
|
||||||
getSmsManagerFor(message.getSubscriptionId()).sendTextMessage(recipient, null, messages.get(i),
|
|
||||||
sentIntents.get(i),
|
|
||||||
deliveredIntents == null ? null : deliveredIntents.get(i));
|
|
||||||
}
|
|
||||||
} catch (NullPointerException | IllegalArgumentException npe2) {
|
|
||||||
warn(TAG, npe);
|
|
||||||
throw new UndeliverableMessageException(npe2);
|
|
||||||
}
|
|
||||||
} catch (SecurityException se) {
|
|
||||||
warn(TAG, se);
|
|
||||||
throw new UndeliverableMessageException(se);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<PendingIntent> constructSentIntents(long messageId, long type,
|
|
||||||
ArrayList<String> messages, boolean secure)
|
|
||||||
{
|
|
||||||
ArrayList<PendingIntent> sentIntents = new ArrayList<>(messages.size());
|
|
||||||
|
|
||||||
for (String ignored : messages) {
|
|
||||||
sentIntents.add(PendingIntent.getBroadcast(context, 0,
|
|
||||||
constructSentIntent(context, messageId, type, secure, false),
|
|
||||||
0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sentIntents;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<PendingIntent> constructDeliveredIntents(long messageId, long type, ArrayList<String> messages) {
|
|
||||||
if (!TextSecurePreferences.isSmsDeliveryReportsEnabled(context)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<PendingIntent> deliveredIntents = new ArrayList<>(messages.size());
|
|
||||||
|
|
||||||
for (String ignored : messages) {
|
|
||||||
deliveredIntents.add(PendingIntent.getBroadcast(context, 0,
|
|
||||||
constructDeliveredIntent(context, messageId, type),
|
|
||||||
0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return deliveredIntents;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Intent constructSentIntent(Context context, long messageId, long type,
|
|
||||||
boolean upgraded, boolean push)
|
|
||||||
{
|
|
||||||
Intent pending = new Intent(SmsDeliveryListener.SENT_SMS_ACTION,
|
|
||||||
Uri.parse("custom://" + messageId + System.currentTimeMillis()),
|
|
||||||
context, SmsDeliveryListener.class);
|
|
||||||
|
|
||||||
pending.putExtra("type", type);
|
|
||||||
pending.putExtra("message_id", messageId);
|
|
||||||
pending.putExtra("run_attempt", Math.max(runAttempt, getRunAttempt()));
|
|
||||||
pending.putExtra("upgraded", upgraded);
|
|
||||||
pending.putExtra("push", push);
|
|
||||||
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Intent constructDeliveredIntent(Context context, long messageId, long type) {
|
|
||||||
Intent pending = new Intent(SmsDeliveryListener.DELIVERED_SMS_ACTION,
|
|
||||||
Uri.parse("custom://" + messageId + System.currentTimeMillis()),
|
|
||||||
context, SmsDeliveryListener.class);
|
|
||||||
pending.putExtra("type", type);
|
|
||||||
pending.putExtra("message_id", messageId);
|
|
||||||
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SmsManager getSmsManagerFor(int subscriptionId) {
|
|
||||||
if (Build.VERSION.SDK_INT >= 22 && subscriptionId != -1) {
|
|
||||||
return SmsManager.getSmsManagerForSubscriptionId(subscriptionId);
|
|
||||||
} else {
|
|
||||||
return SmsManager.getDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Job.Parameters constructParameters(@NonNull Context context, String name) {
|
|
||||||
String constraint = TextSecurePreferences.isWifiSmsEnabled(context) ? NetworkOrCellServiceConstraint.KEY
|
|
||||||
: CellServiceConstraint.KEY;
|
|
||||||
return new Job.Parameters.Builder()
|
|
||||||
.setMaxAttempts(MAX_ATTEMPTS)
|
|
||||||
.setQueue(name)
|
|
||||||
.addConstraint(constraint)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TooManyRetriesException extends Exception { }
|
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<SmsSendJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull SmsSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new SmsSendJob(parameters, data.getLong(KEY_MESSAGE_ID), data.getInt(KEY_RUN_ATTEMPT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.telephony.SmsManager;
|
|
||||||
|
|
||||||
import org.session.libsession.messaging.jobs.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
|
||||||
import org.thoughtcrime.securesms.service.SmsDeliveryListener;
|
|
||||||
|
|
||||||
public class SmsSentJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "SmsSentJob";
|
|
||||||
|
|
||||||
private static final String TAG = SmsSentJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_MESSAGE_ID = "message_id";
|
|
||||||
private static final String KEY_ACTION = "action";
|
|
||||||
private static final String KEY_RESULT = "result";
|
|
||||||
private static final String KEY_RUN_ATTEMPT = "run_attempt";
|
|
||||||
|
|
||||||
private long messageId;
|
|
||||||
private String action;
|
|
||||||
private int result;
|
|
||||||
private int runAttempt;
|
|
||||||
|
|
||||||
public SmsSentJob(long messageId, String action, int result, int runAttempt) {
|
|
||||||
this(new Job.Parameters.Builder().build(),
|
|
||||||
messageId,
|
|
||||||
action,
|
|
||||||
result,
|
|
||||||
runAttempt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SmsSentJob(@NonNull Job.Parameters parameters, long messageId, String action, int result, int runAttempt) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.messageId = messageId;
|
|
||||||
this.action = action;
|
|
||||||
this.result = result;
|
|
||||||
this.runAttempt = runAttempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
Data serialize() {
|
|
||||||
return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId)
|
|
||||||
.putString(KEY_ACTION, action)
|
|
||||||
.putInt(KEY_RESULT, result)
|
|
||||||
.putInt(KEY_RUN_ATTEMPT, runAttempt)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() {
|
|
||||||
Log.i(TAG, "Got SMS callback: " + action + " , " + result);
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case SmsDeliveryListener.SENT_SMS_ACTION:
|
|
||||||
handleSentResult(messageId, result);
|
|
||||||
break;
|
|
||||||
case SmsDeliveryListener.DELIVERED_SMS_ACTION:
|
|
||||||
handleDeliveredResult(messageId, result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception throwable) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDeliveredResult(long messageId, int result) {
|
|
||||||
DatabaseFactory.getSmsDatabase(context).markStatus(messageId, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSentResult(long messageId, int result) {
|
|
||||||
try {
|
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
|
||||||
SmsMessageRecord record = database.getMessage(messageId);
|
|
||||||
|
|
||||||
switch (result) {
|
|
||||||
case Activity.RESULT_OK:
|
|
||||||
database.markAsSent(messageId, false);
|
|
||||||
break;
|
|
||||||
case SmsManager.RESULT_ERROR_NO_SERVICE:
|
|
||||||
case SmsManager.RESULT_ERROR_RADIO_OFF:
|
|
||||||
Log.w(TAG, "Service connectivity problem, requeuing...");
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new SmsSendJob(context, messageId, record.getIndividualRecipient().getAddress().serialize(), runAttempt + 1));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
database.markAsSentFailed(messageId);
|
|
||||||
ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
|
|
||||||
}
|
|
||||||
} catch (NoSuchMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<SmsSentJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull SmsSentJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new SmsSentJob(parameters,
|
|
||||||
data.getLong(KEY_MESSAGE_ID),
|
|
||||||
data.getString(KEY_ACTION),
|
|
||||||
data.getInt(KEY_RESULT),
|
|
||||||
data.getInt(KEY_RUN_ATTEMPT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.mms;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import com.google.android.mms.pdu_alt.PduHeaders;
|
|
||||||
import com.google.android.mms.pdu_alt.RetrieveConf;
|
|
||||||
import com.google.android.mms.pdu_alt.SendConf;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class CompatMmsConnection implements OutgoingMmsConnection, IncomingMmsConnection {
|
|
||||||
private static final String TAG = CompatMmsConnection.class.getSimpleName();
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public CompatMmsConnection(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public SendConf send(@NonNull byte[] pduBytes, int subscriptionId)
|
|
||||||
throws UndeliverableMessageException
|
|
||||||
{
|
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP_MR1) {
|
|
||||||
try {
|
|
||||||
Log.i(TAG, "Sending via Lollipop API");
|
|
||||||
return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId);
|
|
||||||
} catch (UndeliverableMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Falling back to legacy connection...");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subscriptionId == -1) {
|
|
||||||
Log.i(TAG, "Sending via legacy connection");
|
|
||||||
try {
|
|
||||||
SendConf result = new OutgoingLegacyMmsConnection(context).send(pduBytes, subscriptionId);
|
|
||||||
|
|
||||||
if (result != null && result.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) {
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Got bad legacy response: " + (result != null ? result.getResponseStatus() : null));
|
|
||||||
}
|
|
||||||
} catch (UndeliverableMessageException | ApnUnavailableException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP_MR1) {
|
|
||||||
Log.i(TAG, "Falling back to sending via Lollipop API");
|
|
||||||
return new OutgoingLollipopMmsConnection(context).send(pduBytes, subscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UndeliverableMessageException("Both lollipop and legacy connections failed...");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public RetrieveConf retrieve(@NonNull String contentLocation,
|
|
||||||
byte[] transactionId,
|
|
||||||
int subscriptionId)
|
|
||||||
throws MmsException, MmsRadioException, ApnUnavailableException, IOException
|
|
||||||
{
|
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP_MR1) {
|
|
||||||
Log.i(TAG, "Receiving via Lollipop API");
|
|
||||||
try {
|
|
||||||
return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId);
|
|
||||||
} catch (MmsException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Falling back to receiving via legacy connection");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VERSION.SDK_INT < 22 || subscriptionId == -1) {
|
|
||||||
Log.i(TAG, "Receiving via legacy API");
|
|
||||||
try {
|
|
||||||
return new IncomingLegacyMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId);
|
|
||||||
} catch (MmsRadioException | ApnUnavailableException | IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && VERSION.SDK_INT < VERSION_CODES.LOLLIPOP_MR1) {
|
|
||||||
Log.i(TAG, "Falling back to receiving via Lollipop API");
|
|
||||||
return new IncomingLollipopMmsConnection(context).retrieve(contentLocation, transactionId, subscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IOException("Both lollipop and fallback APIs failed...");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,156 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2015 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.thoughtcrime.securesms.mms;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import com.google.android.mms.InvalidHeaderValueException;
|
|
||||||
import com.google.android.mms.pdu_alt.NotifyRespInd;
|
|
||||||
import com.google.android.mms.pdu_alt.PduComposer;
|
|
||||||
import com.google.android.mms.pdu_alt.PduHeaders;
|
|
||||||
import com.google.android.mms.pdu_alt.PduParser;
|
|
||||||
import com.google.android.mms.pdu_alt.RetrieveConf;
|
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.client.config.RequestConfig;
|
|
||||||
import org.apache.http.client.methods.HttpGetHC4;
|
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements IncomingMmsConnection {
|
|
||||||
private static final String TAG = IncomingLegacyMmsConnection.class.getSimpleName();
|
|
||||||
|
|
||||||
public IncomingLegacyMmsConnection(Context context) throws ApnUnavailableException {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpUriRequest constructRequest(Apn contentApn, boolean useProxy) throws IOException {
|
|
||||||
HttpGetHC4 request;
|
|
||||||
|
|
||||||
try {
|
|
||||||
request = new HttpGetHC4(contentApn.getMmsc());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
// #7339
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Header header : getBaseHeaders()) {
|
|
||||||
request.addHeader(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useProxy) {
|
|
||||||
HttpHost proxy = new HttpHost(contentApn.getProxy(), contentApn.getPort());
|
|
||||||
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable RetrieveConf retrieve(@NonNull String contentLocation,
|
|
||||||
byte[] transactionId, int subscriptionId)
|
|
||||||
throws MmsRadioException, ApnUnavailableException, IOException
|
|
||||||
{
|
|
||||||
MmsRadio radio = MmsRadio.getInstance(context);
|
|
||||||
Apn contentApn = new Apn(contentLocation, apn.getProxy(), Integer.toString(apn.getPort()), apn.getUsername(), apn.getPassword());
|
|
||||||
|
|
||||||
if (isDirectConnect()) {
|
|
||||||
Log.i(TAG, "Connecting directly...");
|
|
||||||
try {
|
|
||||||
return retrieve(contentApn, transactionId, false, false);
|
|
||||||
} catch (IOException | ApnUnavailableException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Changing radio to MMS mode..");
|
|
||||||
radio.connect();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Log.i(TAG, "Downloading in MMS mode with proxy...");
|
|
||||||
|
|
||||||
try {
|
|
||||||
return retrieve(contentApn, transactionId, true, true);
|
|
||||||
} catch (IOException | ApnUnavailableException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Downloading in MMS mode without proxy...");
|
|
||||||
|
|
||||||
return retrieve(contentApn, transactionId, true, false);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
radio.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public RetrieveConf retrieve(Apn contentApn, byte[] transactionId, boolean usingMmsRadio, boolean useProxyIfAvailable)
|
|
||||||
throws IOException, ApnUnavailableException
|
|
||||||
{
|
|
||||||
byte[] pdu = null;
|
|
||||||
|
|
||||||
final boolean useProxy = useProxyIfAvailable && contentApn.hasProxy();
|
|
||||||
final String targetHost = useProxy
|
|
||||||
? contentApn.getProxy()
|
|
||||||
: Uri.parse(contentApn.getMmsc()).getHost();
|
|
||||||
if (checkRouteToHost(context, targetHost, usingMmsRadio)) {
|
|
||||||
Log.i(TAG, "got successful route to host " + targetHost);
|
|
||||||
pdu = execute(constructRequest(contentApn, useProxy));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pdu == null) {
|
|
||||||
throw new IOException("Connection manager could not obtain route to host.");
|
|
||||||
}
|
|
||||||
|
|
||||||
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
|
|
||||||
|
|
||||||
if (retrieved == null) {
|
|
||||||
Log.w(TAG, "Couldn't parse PDU, byte response: " + Arrays.toString(pdu));
|
|
||||||
Log.w(TAG, "Couldn't parse PDU, ASCII: " + new String(pdu));
|
|
||||||
throw new IOException("Bad retrieved PDU");
|
|
||||||
}
|
|
||||||
|
|
||||||
sendRetrievedAcknowledgement(transactionId, usingMmsRadio, useProxy);
|
|
||||||
return retrieved;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendRetrievedAcknowledgement(byte[] transactionId,
|
|
||||||
boolean usingRadio,
|
|
||||||
boolean useProxy)
|
|
||||||
throws ApnUnavailableException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION,
|
|
||||||
transactionId,
|
|
||||||
PduHeaders.STATUS_RETRIEVED);
|
|
||||||
|
|
||||||
OutgoingLegacyMmsConnection connection = new OutgoingLegacyMmsConnection(context);
|
|
||||||
connection.sendNotificationReceived(new PduComposer(context, notifyResponse).make(), usingRadio, useProxy);
|
|
||||||
} catch (InvalidHeaderValueException | IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2015 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.thoughtcrime.securesms.mms;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.ConnectivityManager;
|
|
||||||
import android.net.NetworkInfo;
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import com.google.android.mms.pdu_alt.PduParser;
|
|
||||||
import com.google.android.mms.pdu_alt.SendConf;
|
|
||||||
|
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.client.config.RequestConfig;
|
|
||||||
import org.apache.http.client.methods.HttpPostHC4;
|
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
|
||||||
import org.apache.http.entity.ByteArrayEntityHC4;
|
|
||||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public class OutgoingLegacyMmsConnection extends LegacyMmsConnection implements OutgoingMmsConnection {
|
|
||||||
private final static String TAG = OutgoingLegacyMmsConnection.class.getSimpleName();
|
|
||||||
|
|
||||||
public OutgoingLegacyMmsConnection(Context context) throws ApnUnavailableException {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpUriRequest constructRequest(byte[] pduBytes, boolean useProxy)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
HttpPostHC4 request = new HttpPostHC4(apn.getMmsc());
|
|
||||||
for (Header header : getBaseHeaders()) {
|
|
||||||
request.addHeader(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setEntity(new ByteArrayEntityHC4(pduBytes));
|
|
||||||
if (useProxy) {
|
|
||||||
HttpHost proxy = new HttpHost(apn.getProxy(), apn.getPort());
|
|
||||||
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
|
|
||||||
}
|
|
||||||
return request;
|
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
throw new IOException(iae);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendNotificationReceived(byte[] pduBytes, boolean usingMmsRadio, boolean useProxyIfAvailable)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
sendBytes(pduBytes, usingMmsRadio, useProxyIfAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable SendConf send(@NonNull byte[] pduBytes, int subscriptionId) throws UndeliverableMessageException {
|
|
||||||
try {
|
|
||||||
MmsRadio radio = MmsRadio.getInstance(context);
|
|
||||||
|
|
||||||
if (isDirectConnect()) {
|
|
||||||
Log.i(TAG, "Sending MMS directly without radio change...");
|
|
||||||
try {
|
|
||||||
return send(pduBytes, false, false);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Sending MMS with radio change and proxy...");
|
|
||||||
radio.connect();
|
|
||||||
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
return send(pduBytes, true, true);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Sending MMS with radio change and without proxy...");
|
|
||||||
|
|
||||||
try {
|
|
||||||
return send(pduBytes, true, false);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.w(TAG, ioe);
|
|
||||||
throw new UndeliverableMessageException(ioe);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
radio.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (MmsRadioException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
throw new UndeliverableMessageException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private SendConf send(byte[] pduBytes, boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException {
|
|
||||||
byte[] response = sendBytes(pduBytes, useMmsRadio, useProxyIfAvailable);
|
|
||||||
return (SendConf) new PduParser(response).parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] sendBytes(byte[] pduBytes, boolean useMmsRadio, boolean useProxyIfAvailable) throws IOException {
|
|
||||||
final boolean useProxy = useProxyIfAvailable && apn.hasProxy();
|
|
||||||
final String targetHost = useProxy
|
|
||||||
? apn.getProxy()
|
|
||||||
: Uri.parse(apn.getMmsc()).getHost();
|
|
||||||
|
|
||||||
Log.i(TAG, "Sending MMS of length: " + pduBytes.length
|
|
||||||
+ (useMmsRadio ? ", using mms radio" : "")
|
|
||||||
+ (useProxy ? ", using proxy" : ""));
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (checkRouteToHost(context, targetHost, useMmsRadio)) {
|
|
||||||
Log.i(TAG, "got successful route to host " + targetHost);
|
|
||||||
byte[] response = execute(constructRequest(pduBytes, useProxy));
|
|
||||||
if (response != null) return response;
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.w(TAG, ioe);
|
|
||||||
}
|
|
||||||
throw new IOException("Connection manager could not obtain route to host.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean isConnectionPossible(Context context) {
|
|
||||||
try {
|
|
||||||
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(MmsRadio.TYPE_MOBILE_MMS);
|
|
||||||
if (networkInfo == null) {
|
|
||||||
Log.w(TAG, "MMS network info was null, unsupported by this device");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getApn(context);
|
|
||||||
return true;
|
|
||||||
} catch (ApnUnavailableException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2011 Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.thoughtcrime.securesms.service;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.provider.Telephony;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MmsReceiveJob;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
|
|
||||||
public class MmsListener extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private static final String TAG = MmsListener.class.getSimpleName();
|
|
||||||
|
|
||||||
private boolean isRelevant(Context context, Intent intent) {
|
|
||||||
if (!ApplicationMigrationService.isDatabaseImported(context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction()) && Util.isDefaultSmsProvider(context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Log.i(TAG, "Got MMS broadcast..." + intent.getAction());
|
|
||||||
|
|
||||||
if ((Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION.equals(intent.getAction()) &&
|
|
||||||
Util.isDefaultSmsProvider(context)) ||
|
|
||||||
(Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction()) &&
|
|
||||||
isRelevant(context, intent)))
|
|
||||||
{
|
|
||||||
Log.i(TAG, "Relevant!");
|
|
||||||
int subscriptionId = intent.getExtras().getInt("subscription", -1);
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MmsReceiveJob(intent.getByteArrayExtra("data"), subscriptionId));
|
|
||||||
|
|
||||||
abortBroadcast();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.service;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.telephony.SmsMessage;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SmsSentJob;
|
|
||||||
|
|
||||||
public class SmsDeliveryListener extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private static final String TAG = SmsDeliveryListener.class.getSimpleName();
|
|
||||||
|
|
||||||
public static final String SENT_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SENT_SMS_ACTION";
|
|
||||||
public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
|
||||||
long messageId = intent.getLongExtra("message_id", -1);
|
|
||||||
int runAttempt = intent.getIntExtra("run_attempt", 0);
|
|
||||||
|
|
||||||
switch (intent.getAction()) {
|
|
||||||
case SENT_SMS_ACTION:
|
|
||||||
int result = getResultCode();
|
|
||||||
|
|
||||||
jobManager.add(new SmsSentJob(messageId, SENT_SMS_ACTION, result, runAttempt));
|
|
||||||
break;
|
|
||||||
case DELIVERED_SMS_ACTION:
|
|
||||||
byte[] pdu = intent.getByteArrayExtra("pdu");
|
|
||||||
|
|
||||||
if (pdu == null) {
|
|
||||||
Log.w(TAG, "No PDU in delivery receipt!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SmsMessage message = SmsMessage.createFromPdu(pdu);
|
|
||||||
|
|
||||||
if (message == null) {
|
|
||||||
Log.w(TAG, "Delivery receipt failed to parse!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int status = message.getStatus();
|
|
||||||
|
|
||||||
Log.i(TAG, "Original status: " + status);
|
|
||||||
|
|
||||||
// Note: https://developer.android.com/reference/android/telephony/SmsMessage.html#getStatus()
|
|
||||||
// " CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16"
|
|
||||||
// Note: https://stackoverflow.com/a/33240109
|
|
||||||
if ("3gpp2".equals(intent.getStringExtra("format"))) {
|
|
||||||
Log.w(TAG, "Correcting for CDMA delivery receipt...");
|
|
||||||
if (status >> 24 <= 0) status = SmsDatabase.Status.STATUS_COMPLETE;
|
|
||||||
else if (status >> 24 == 2) status = SmsDatabase.Status.STATUS_PENDING;
|
|
||||||
else if (status >> 24 == 3) status = SmsDatabase.Status.STATUS_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobManager.add(new SmsSentJob(messageId, DELIVERED_SMS_ACTION, status, runAttempt));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.w(TAG, "Unknown action: " + intent.getAction());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,113 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2011 Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.thoughtcrime.securesms.service;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.Telephony;
|
|
||||||
import android.telephony.SmsMessage;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
|
|
||||||
import org.session.libsession.utilities.Util;
|
|
||||||
|
|
||||||
public class SmsListener extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private static final String SMS_RECEIVED_ACTION = Telephony.Sms.Intents.SMS_RECEIVED_ACTION;
|
|
||||||
private static final String SMS_DELIVERED_ACTION = Telephony.Sms.Intents.SMS_DELIVER_ACTION;
|
|
||||||
|
|
||||||
private boolean isExemption(SmsMessage message, String messageBody) {
|
|
||||||
|
|
||||||
// ignore CLASS0 ("flash") messages
|
|
||||||
if (message.getMessageClass() == SmsMessage.MessageClass.CLASS_0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// ignore OTP messages from Sparebank1 (Norwegian bank)
|
|
||||||
if (messageBody.startsWith("Sparebank1://otp?")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
message.getOriginatingAddress().length() < 7 &&
|
|
||||||
(messageBody.toUpperCase().startsWith("//ANDROID:") || // Sprint Visual Voicemail
|
|
||||||
messageBody.startsWith("//BREW:")); //BREW stands for “Binary Runtime Environment for Wireless"
|
|
||||||
}
|
|
||||||
|
|
||||||
private SmsMessage getSmsMessageFromIntent(Intent intent) {
|
|
||||||
Bundle bundle = intent.getExtras();
|
|
||||||
Object[] pdus = (Object[])bundle.get("pdus");
|
|
||||||
|
|
||||||
if (pdus == null || pdus.length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return SmsMessage.createFromPdu((byte[])pdus[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSmsMessageBodyFromIntent(Intent intent) {
|
|
||||||
Bundle bundle = intent.getExtras();
|
|
||||||
Object[] pdus = (Object[])bundle.get("pdus");
|
|
||||||
StringBuilder bodyBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
if (pdus == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
for (Object pdu : pdus)
|
|
||||||
bodyBuilder.append(SmsMessage.createFromPdu((byte[])pdu).getDisplayMessageBody());
|
|
||||||
|
|
||||||
return bodyBuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isRelevant(Context context, Intent intent) {
|
|
||||||
SmsMessage message = getSmsMessageFromIntent(intent);
|
|
||||||
String messageBody = getSmsMessageBodyFromIntent(intent);
|
|
||||||
|
|
||||||
if (message == null && messageBody == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (isExemption(message, messageBody))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!ApplicationMigrationService.isDatabaseImported(context))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (SMS_RECEIVED_ACTION.equals(intent.getAction()) && Util.isDefaultSmsProvider(context)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Log.i("SMSListener", "Got SMS broadcast...");
|
|
||||||
|
|
||||||
if ((intent.getAction().equals(SMS_DELIVERED_ACTION)) ||
|
|
||||||
(intent.getAction().equals(SMS_RECEIVED_ACTION)) && isRelevant(context, intent))
|
|
||||||
{
|
|
||||||
Log.i("SmsListener", "Constructing SmsReceiveJob...");
|
|
||||||
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
|
|
||||||
int subscriptionId = intent.getExtras().getInt("subscription", -1);
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new SmsReceiveJob(pdus, subscriptionId));
|
|
||||||
|
|
||||||
abortBroadcast();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,11 +33,9 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
import org.session.libsignal.utilities.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
@ -142,13 +140,11 @@ public class MessageSender {
|
|||||||
private static void sendTextPush(Context context, Recipient recipient, long messageId) {
|
private static void sendTextPush(Context context, Recipient recipient, long messageId) {
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||||
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
jobManager.add(new PushTextSendJob(messageId, recipient.getAddress()));
|
||||||
// MultiDeviceProtocol.sendTextPush(context, recipient, messageId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
|
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||||
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
|
PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress());
|
||||||
// MultiDeviceProtocol.sendMediaPush(context, recipient, messageId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) {
|
private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) {
|
||||||
@ -156,48 +152,6 @@ public class MessageSender {
|
|||||||
PushGroupSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), filterAddress);
|
PushGroupSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), filterAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendSms(Context context, Recipient recipient, long messageId) {
|
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
|
||||||
jobManager.add(new SmsSendJob(context, messageId, recipient.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendMms(Context context, long messageId) {
|
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
|
||||||
jobManager.add(new MmsSendJob(messageId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) {
|
|
||||||
return true;
|
|
||||||
// Loki - Original code
|
|
||||||
// ========
|
|
||||||
// if (!TextSecurePreferences.isPushRegistered(context)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (keyExchange) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return isPushDestination(context, recipient);
|
|
||||||
// ========
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isPushMediaSend(Context context, Recipient recipient) {
|
|
||||||
return true;
|
|
||||||
// Loki - Original code
|
|
||||||
// ========
|
|
||||||
// if (!TextSecurePreferences.isPushRegistered(context)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (recipient.isGroupRecipient()) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return isPushDestination(context, recipient);
|
|
||||||
// ========
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isGroupPushSend(Recipient recipient) {
|
private static boolean isGroupPushSend(Recipient recipient) {
|
||||||
return recipient.getAddress().isGroup() &&
|
return recipient.getAddress().isGroup() &&
|
||||||
!recipient.getAddress().isMmsGroup();
|
!recipient.getAddress().isMmsGroup();
|
||||||
|
@ -25,7 +25,6 @@ import android.os.Build.VERSION_CODES;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.ComposeText;
|
import org.thoughtcrime.securesms.components.ComposeText;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection;
|
|
||||||
|
|
||||||
import network.loki.messenger.BuildConfig;
|
import network.loki.messenger.BuildConfig;
|
||||||
|
|
||||||
@ -70,6 +69,6 @@ public class Util {
|
|||||||
|
|
||||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||||
public static boolean isMmsCapable(Context context) {
|
public static boolean isMmsCapable(Context context) {
|
||||||
return (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) || OutgoingLegacyMmsConnection.isConnectionPossible(context);
|
return VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user