2014-11-08 11:35:58 -08:00
package org.thoughtcrime.securesms.jobs ;
2018-08-09 10:15:43 -04:00
import android.support.annotation.NonNull ;
2014-11-08 11:35:58 -08:00
import org.thoughtcrime.securesms.ApplicationContext ;
2018-05-22 02:13:10 -07:00
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil ;
2017-07-26 09:59:15 -07:00
import org.thoughtcrime.securesms.database.Address ;
2014-11-08 11:35:58 -08:00
import org.thoughtcrime.securesms.database.DatabaseFactory ;
2019-06-07 15:21:25 +10:00
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId ;
2014-11-08 11:35:58 -08:00
import org.thoughtcrime.securesms.database.NoSuchMessageException ;
2019-06-07 15:21:25 +10:00
import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessMode ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.database.SmsDatabase ;
2014-11-08 11:35:58 -08:00
import org.thoughtcrime.securesms.database.model.SmsMessageRecord ;
2014-11-11 19:57:53 -08:00
import org.thoughtcrime.securesms.dependencies.InjectableType ;
2019-03-28 08:56:35 -07:00
import org.thoughtcrime.securesms.jobmanager.Data ;
import org.thoughtcrime.securesms.jobmanager.Job ;
2019-10-30 09:59:11 +11:00
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities ;
2014-11-08 11:35:58 -08:00
import org.thoughtcrime.securesms.notifications.MessageNotifier ;
2017-08-01 08:56:00 -07:00
import org.thoughtcrime.securesms.recipients.Recipient ;
2016-08-15 20:23:56 -07:00
import org.thoughtcrime.securesms.service.ExpiringMessageManager ;
2014-11-08 11:35:58 -08:00
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException ;
import org.thoughtcrime.securesms.transport.RetryLaterException ;
2018-10-11 16:45:22 -07:00
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
2019-06-06 09:48:02 +10:00
import org.whispersystems.libsignal.state.PreKeyBundle ;
2017-08-14 18:11:13 -07:00
import org.whispersystems.libsignal.util.guava.Optional ;
2016-03-23 10:34:41 -07:00
import org.whispersystems.signalservice.api.SignalServiceMessageSender ;
2018-05-22 02:13:10 -07:00
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair ;
2016-03-23 10:34:41 -07:00
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException ;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage ;
2019-01-13 23:30:54 -08:00
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage ;
2016-03-23 10:34:41 -07:00
import org.whispersystems.signalservice.api.push.SignalServiceAddress ;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException ;
2019-10-30 09:59:11 +11:00
import org.whispersystems.signalservice.loki.api.LokiStorageAPI ;
2019-10-24 17:16:53 +11:00
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage ;
2019-11-01 12:15:40 +11:00
import org.whispersystems.signalservice.loki.utilities.PromiseUtil ;
2014-11-08 11:35:58 -08:00
import java.io.IOException ;
2014-11-11 19:57:53 -08:00
import javax.inject.Inject ;
public class PushTextSendJob extends PushSendJob implements InjectableType {
2014-11-08 11:35:58 -08:00
2019-03-28 08:56:35 -07:00
public static final String KEY = " PushTextSendJob " ;
2015-06-24 18:26:51 -07:00
2014-11-08 11:35:58 -08:00
private static final String TAG = PushTextSendJob . class . getSimpleName ( ) ;
2019-10-04 16:14:25 +10:00
private static final String KEY_TEMPLATE_MESSAGE_ID = " template_message_id " ;
2018-08-09 10:15:43 -04:00
private static final String KEY_MESSAGE_ID = " message_id " ;
2019-10-04 16:14:25 +10:00
private static final String KEY_DESTINATION = " destination " ;
private static final String KEY_IS_FRIEND_REQUEST = " is_friend_request " ;
private static final String KEY_CUSTOM_FR_MESSAGE = " custom_friend_request_message " ;
2019-10-24 17:16:53 +11:00
private static final String KEY_SHOULD_SEND_SYNC_MESSAGE = " should_send_sync_message " ;
2018-08-09 10:15:43 -04:00
2019-03-28 08:56:35 -07:00
@Inject SignalServiceMessageSender messageSender ;
2014-11-11 19:57:53 -08:00
2019-10-07 16:53:26 +11:00
private long messageId ; // The message ID
private long templateMessageId ; // The message ID of the message to template this send job from
2019-10-04 16:14:25 +10:00
2019-10-07 16:53:26 +11:00
// Loki - Multi device
2019-10-07 10:02:15 +11:00
private Address destination ; // Destination to check whether this is another device we're sending to
private boolean isFriendRequest ; // Whether this is a friend request message
private String customFriendRequestMessage ; // If this isn't set then we use the message body
2019-10-24 17:16:53 +11:00
private boolean shouldSendSyncMessage ;
2019-10-04 16:14:25 +10:00
2019-10-24 17:16:53 +11:00
public PushTextSendJob ( long messageId , Address destination ) { this ( messageId , messageId , destination , false ) ; }
public PushTextSendJob ( long templateMessageId , long messageId , Address destination , boolean shouldSendSyncMessage ) { this ( templateMessageId , messageId , destination , false , null , shouldSendSyncMessage ) ; }
public PushTextSendJob ( long templateMessageId , long messageId , Address destination , boolean isFriendRequest , String customFriendRequestMessage , boolean shouldSendSyncMessage ) {
this ( constructParameters ( destination ) , templateMessageId , messageId , destination , isFriendRequest , customFriendRequestMessage , shouldSendSyncMessage ) ;
2018-08-09 10:15:43 -04:00
}
2014-11-08 11:35:58 -08:00
2019-10-24 17:16:53 +11:00
private PushTextSendJob ( @NonNull Job . Parameters parameters , long templateMessageId , long messageId , Address destination , boolean isFriendRequest , String customFriendRequestMessage , boolean shouldSendSyncMessage ) {
2019-03-28 08:56:35 -07:00
super ( parameters ) ;
2019-10-04 16:14:25 +10:00
this . templateMessageId = templateMessageId ;
2014-11-08 11:35:58 -08:00
this . messageId = messageId ;
2019-10-04 16:14:25 +10:00
this . destination = destination ;
this . isFriendRequest = isFriendRequest ;
this . customFriendRequestMessage = customFriendRequestMessage ;
2019-10-24 17:16:53 +11:00
this . shouldSendSyncMessage = shouldSendSyncMessage ;
2014-11-08 11:35:58 -08:00
}
2018-08-09 10:15:43 -04:00
@Override
2019-03-28 08:56:35 -07:00
public @NonNull Data serialize ( ) {
2019-10-04 16:14:25 +10:00
Data . Builder builder = new Data . Builder ( )
2019-10-24 17:16:53 +11:00
. putLong ( KEY_TEMPLATE_MESSAGE_ID , templateMessageId )
. putLong ( KEY_MESSAGE_ID , messageId )
. putString ( KEY_DESTINATION , destination . serialize ( ) )
. putBoolean ( KEY_IS_FRIEND_REQUEST , isFriendRequest )
. putBoolean ( KEY_SHOULD_SEND_SYNC_MESSAGE , shouldSendSyncMessage ) ;
2019-10-04 16:14:25 +10:00
if ( customFriendRequestMessage ! = null ) { builder . putString ( KEY_CUSTOM_FR_MESSAGE , customFriendRequestMessage ) ; }
return builder . build ( ) ;
2018-08-09 10:15:43 -04:00
}
@Override
2019-05-22 13:51:56 -03:00
public @NonNull String getFactoryKey ( ) {
2019-03-28 08:56:35 -07:00
return KEY ;
2018-08-09 10:15:43 -04:00
}
2014-11-08 11:35:58 -08:00
@Override
2018-08-02 09:50:36 -04:00
public void onAdded ( ) {
2019-10-04 16:14:25 +10:00
if ( messageId > = 0 ) {
DatabaseFactory . getSmsDatabase ( context ) . markAsSending ( messageId ) ;
}
2018-08-02 09:50:36 -04:00
}
2014-11-08 11:35:58 -08:00
@Override
2018-02-01 19:22:48 -08:00
public void onPushSend ( ) throws NoSuchMessageException , RetryLaterException {
2016-08-15 20:23:56 -07:00
ExpiringMessageManager expirationManager = ApplicationContext . getInstance ( context ) . getExpiringMessageManager ( ) ;
2018-01-24 19:17:44 -08:00
SmsDatabase database = DatabaseFactory . getSmsDatabase ( context ) ;
2019-10-04 16:14:25 +10:00
SmsMessageRecord record = database . getMessage ( templateMessageId ) ;
Recipient recordRecipient = record . getRecipient ( ) . resolve ( ) ;
boolean hasSameDestination = destination . equals ( recordRecipient . getAddress ( ) ) ;
2014-11-08 11:35:58 -08:00
2019-10-04 16:14:25 +10:00
if ( hasSameDestination & & ! record . isPending ( ) & & ! record . isFailed ( ) ) {
warn ( TAG , " Message " + templateMessageId + " was already sent. Ignoring. " ) ;
2018-11-14 11:39:23 -08:00
return ;
}
2014-11-08 11:35:58 -08:00
try {
2019-10-04 16:14:25 +10:00
log ( TAG , " Sending message: " + templateMessageId + ( hasSameDestination ? " " : " to another device. " ) ) ;
2014-11-08 11:35:58 -08:00
2019-10-04 16:14:25 +10:00
Recipient recipient = Recipient . from ( context , destination , false ) ;
2018-10-30 16:59:00 -07:00
byte [ ] profileKey = recipient . getProfileKey ( ) ;
UnidentifiedAccessMode accessMode = recipient . getUnidentifiedAccessMode ( ) ;
2019-10-07 10:02:15 +11:00
boolean unidentified = deliver ( record ) ;
2018-10-30 16:59:00 -07:00
2019-10-04 16:14:25 +10:00
if ( messageId > = 0 ) {
database . markAsSent ( messageId , true ) ;
database . markUnidentified ( messageId , unidentified ) ;
}
2015-03-11 14:23:45 -07:00
2019-01-13 23:30:54 -08:00
if ( recipient . isLocalNumber ( ) ) {
SyncMessageId id = new SyncMessageId ( recipient . getAddress ( ) , record . getDateSent ( ) ) ;
DatabaseFactory . getMmsSmsDatabase ( context ) . incrementDeliveryReceiptCount ( id , System . currentTimeMillis ( ) ) ;
DatabaseFactory . getMmsSmsDatabase ( context ) . incrementReadReceiptCount ( id , System . currentTimeMillis ( ) ) ;
}
2018-10-11 16:45:22 -07:00
if ( TextSecurePreferences . isUnidentifiedDeliveryEnabled ( context ) ) {
2018-10-30 16:59:00 -07:00
if ( unidentified & & accessMode = = UnidentifiedAccessMode . UNKNOWN & & profileKey = = null ) {
2018-12-06 12:14:20 -08:00
log ( TAG , " Marking recipient as UD-unrestricted following a UD send. " ) ;
2018-10-30 16:59:00 -07:00
DatabaseFactory . getRecipientDatabase ( context ) . setUnidentifiedAccessMode ( recipient , UnidentifiedAccessMode . UNRESTRICTED ) ;
} else if ( unidentified & & accessMode = = UnidentifiedAccessMode . UNKNOWN ) {
2018-12-06 12:14:20 -08:00
log ( TAG , " Marking recipient as UD-enabled following a UD send. " ) ;
2018-10-11 16:45:22 -07:00
DatabaseFactory . getRecipientDatabase ( context ) . setUnidentifiedAccessMode ( recipient , UnidentifiedAccessMode . ENABLED ) ;
} else if ( ! unidentified & & accessMode ! = UnidentifiedAccessMode . DISABLED ) {
2018-12-06 12:14:20 -08:00
log ( TAG , " Marking recipient as UD-disabled following a non-UD send. " ) ;
2018-10-11 16:45:22 -07:00
DatabaseFactory . getRecipientDatabase ( context ) . setUnidentifiedAccessMode ( recipient , UnidentifiedAccessMode . DISABLED ) ;
}
}
2019-10-04 16:14:25 +10:00
if ( record . getExpiresIn ( ) > 0 & & messageId > = 0 ) {
2016-08-15 20:23:56 -07:00
database . markExpireStarted ( messageId ) ;
expirationManager . scheduleDeletion ( record . getId ( ) , record . isMms ( ) , record . getExpiresIn ( ) ) ;
}
2019-10-04 16:14:25 +10:00
log ( TAG , " Sent message: " + templateMessageId + ( hasSameDestination ? " " : " to another device. " ) ) ;
2018-08-02 09:50:36 -04:00
2014-11-08 11:35:58 -08:00
} catch ( InsecureFallbackApprovalException e ) {
2018-12-06 12:14:20 -08:00
warn ( TAG , " Failure " , e ) ;
2014-11-08 11:35:58 -08:00
database . markAsPendingInsecureSmsFallback ( record . getId ( ) ) ;
2017-08-01 08:56:00 -07:00
MessageNotifier . notifyMessageDeliveryFailed ( context , record . getRecipient ( ) , record . getThreadId ( ) ) ;
2019-03-28 08:56:35 -07:00
ApplicationContext . getInstance ( context ) . getJobManager ( ) . add ( new DirectoryRefreshJob ( false ) ) ;
2014-11-08 11:35:58 -08:00
} catch ( UntrustedIdentityException e ) {
2018-12-06 12:14:20 -08:00
warn ( TAG , " Failure " , e ) ;
2017-07-26 09:59:15 -07:00
database . addMismatchedIdentity ( record . getId ( ) , Address . fromSerialized ( e . getE164Number ( ) ) , e . getIdentityKey ( ) ) ;
2014-11-08 11:35:58 -08:00
database . markAsSentFailed ( record . getId ( ) ) ;
2015-01-15 13:35:35 -08:00
database . markAsPush ( record . getId ( ) ) ;
2014-11-08 11:35:58 -08:00
}
}
2014-11-11 19:57:53 -08:00
@Override
2019-05-22 13:51:56 -03:00
public boolean onShouldRetry ( @NonNull Exception exception ) {
2019-09-13 11:56:36 +10:00
// Loki - Disable since we have our own retrying
// if (exception instanceof RetryLaterException) return true;
2014-11-11 19:57:53 -08:00
return false ;
}
@Override
public void onCanceled ( ) {
2019-10-04 16:14:25 +10:00
if ( messageId > = 0 ) {
DatabaseFactory . getSmsDatabase ( context ) . markAsSentFailed ( messageId ) ;
2014-11-11 19:57:53 -08:00
2019-10-04 16:14:25 +10:00
long threadId = DatabaseFactory . getSmsDatabase ( context ) . getThreadIdForMessage ( messageId ) ;
Recipient recipient = DatabaseFactory . getThreadDatabase ( context ) . getRecipientForThreadId ( threadId ) ;
2014-11-11 19:57:53 -08:00
2019-10-04 16:14:25 +10:00
if ( threadId ! = - 1 & & recipient ! = null ) {
MessageNotifier . notifyMessageDeliveryFailed ( context , recipient , threadId ) ;
}
2015-04-13 08:57:20 -07:00
}
2014-11-11 19:57:53 -08:00
}
2019-10-07 10:02:15 +11:00
private boolean deliver ( SmsMessageRecord message )
2015-03-11 14:23:45 -07:00
throws UntrustedIdentityException , InsecureFallbackApprovalException , RetryLaterException
2014-11-08 11:35:58 -08:00
{
try {
2019-06-26 14:31:38 +10:00
// rotateSenderCertificateIfNecessary();
2019-10-07 10:02:15 +11:00
Recipient recipient = Recipient . from ( context , destination , false ) ;
2019-10-04 16:14:25 +10:00
SignalServiceAddress address = getPushAddress ( recipient . getAddress ( ) ) ;
Optional < byte [ ] > profileKey = getProfileKey ( recipient ) ;
Optional < UnidentifiedAccessPair > unidentifiedAccess = UnidentifiedAccessUtil . getAccessFor ( context , recipient ) ;
2018-05-22 02:13:10 -07:00
2018-12-06 12:14:20 -08:00
log ( TAG , " Have access key to use: " + unidentifiedAccess . isPresent ( ) ) ;
2018-05-22 02:13:10 -07:00
2019-07-22 14:25:59 +10:00
// Loki - Include a pre key bundle if the message is a friend request or an end session message
PreKeyBundle preKeyBundle ;
2019-10-08 14:10:16 +11:00
if ( isFriendRequest | | message . isEndSession ( ) ) {
2019-07-22 14:25:59 +10:00
preKeyBundle = DatabaseFactory . getLokiPreKeyBundleDatabase ( context ) . generatePreKeyBundle ( address . getNumber ( ) ) ;
} else {
preKeyBundle = null ;
}
2019-06-06 09:48:02 +10:00
2019-10-04 16:14:25 +10:00
String body = ( isFriendRequest & & customFriendRequestMessage ! = null ) ? customFriendRequestMessage : message . getBody ( ) ;
2018-05-22 02:13:10 -07:00
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage . newBuilder ( )
. withTimestamp ( message . getDateSent ( ) )
2019-10-04 16:14:25 +10:00
. withBody ( body )
2018-05-22 02:13:10 -07:00
. withExpiration ( ( int ) ( message . getExpiresIn ( ) / 1000 ) )
. withProfileKey ( profileKey . orNull ( ) )
. asEndSessionMessage ( message . isEndSession ( ) )
2019-10-04 16:14:25 +10:00
. asFriendRequest ( isFriendRequest )
2019-06-06 09:48:02 +10:00
. withPreKeyBundle ( preKeyBundle )
2018-05-22 02:13:10 -07:00
. build ( ) ;
2019-01-13 23:30:54 -08:00
if ( address . getNumber ( ) . equals ( TextSecurePreferences . getLocalNumber ( context ) ) ) {
Optional < UnidentifiedAccessPair > syncAccess = UnidentifiedAccessUtil . getAccessForSync ( context ) ;
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage ( context , textSecureMessage , syncAccess ) ;
2019-10-29 14:03:32 +11:00
messageSender . sendMessage ( templateMessageId , syncMessage , syncAccess ) ;
2019-01-13 23:30:54 -08:00
return syncAccess . isPresent ( ) ;
} else {
2019-10-24 17:16:53 +11:00
LokiSyncMessage syncMessage = null ;
if ( shouldSendSyncMessage ) {
2019-10-29 14:03:32 +11:00
// Set the sync message destination to the primary device, this way it will show that we sent a message to the primary device and not a secondary device
2019-11-01 12:15:40 +11:00
String primaryDevice = PromiseUtil . get ( LokiStorageAPI . shared . getPrimaryDevicePublicKey ( address . getNumber ( ) ) , null ) ;
2019-10-24 17:16:53 +11:00
SignalServiceAddress primaryAddress = primaryDevice = = null ? address : new SignalServiceAddress ( primaryDevice ) ;
// We also need to use the original message id and not -1
syncMessage = new LokiSyncMessage ( primaryAddress , templateMessageId ) ;
}
return messageSender . sendMessage ( messageId , address , unidentifiedAccess , textSecureMessage , Optional . fromNullable ( syncMessage ) ) . getSuccess ( ) . isUnidentified ( ) ;
2019-01-13 23:30:54 -08:00
}
2017-07-26 09:59:15 -07:00
} catch ( UnregisteredUserException e ) {
2018-12-06 12:14:20 -08:00
warn ( TAG , " Failure " , e ) ;
2015-03-11 14:23:45 -07:00
throw new InsecureFallbackApprovalException ( e ) ;
2014-11-08 11:35:58 -08:00
} catch ( IOException e ) {
2018-12-06 12:14:20 -08:00
warn ( TAG , " Failure " , e ) ;
2015-03-11 14:23:45 -07:00
throw new RetryLaterException ( e ) ;
2014-11-08 11:35:58 -08:00
}
}
2019-03-28 08:56:35 -07:00
public static class Factory implements Job . Factory < PushTextSendJob > {
@Override
public @NonNull PushTextSendJob create ( @NonNull Parameters parameters , @NonNull Data data ) {
2019-10-04 16:14:25 +10:00
long templateMessageID = data . getLong ( KEY_TEMPLATE_MESSAGE_ID ) ;
long messageID = data . getLong ( KEY_MESSAGE_ID ) ;
Address destination = Address . fromSerialized ( data . getString ( KEY_DESTINATION ) ) ;
boolean isFriendRequest = data . getBoolean ( KEY_IS_FRIEND_REQUEST ) ;
String frMessage = data . hasString ( KEY_CUSTOM_FR_MESSAGE ) ? data . getString ( KEY_CUSTOM_FR_MESSAGE ) : null ;
2019-10-24 17:16:53 +11:00
boolean shouldSendSyncMessage = data . getBoolean ( KEY_SHOULD_SEND_SYNC_MESSAGE ) ;
return new PushTextSendJob ( parameters , templateMessageID , messageID , destination , isFriendRequest , frMessage , shouldSendSyncMessage ) ;
2019-03-28 08:56:35 -07:00
}
}
2014-11-08 11:35:58 -08:00
}