Make sure we have SEND_SMS permission before sending an SMS

Fixes #7246
pull/1/head
Moxie Marlinspike 7 years ago
parent 8686691a5a
commit 27e11e9627

@ -1313,6 +1313,7 @@
<string name="CallNotificationBuilder_connecting">Connecting...</string>
<string name="Permissions_permission_required">Permission required</string>
<string name="Permissions_continue">Continue</string>
<string name="ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms">Signal needs SMS permission in order to send an SMS, but it has been permanently denied. Please continue to app settings, select \"Permissions\" and enable \"SMS\".</string>
<!-- EOF -->

@ -112,7 +112,6 @@ import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
@ -1663,48 +1662,49 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
sendMediaMessage(forceSms, getMessage(), attachmentManager.buildSlideDeck(), expiresIn, subscriptionId, initiating);
}
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck, final long expiresIn, final int subscriptionId, final boolean initiating)
throws InvalidMessageException
{
final SettableFuture<Void> future = new SettableFuture<>();
final Context context = getApplicationContext();
OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(recipient,
slideDeck,
body,
System.currentTimeMillis(),
subscriptionId,
expiresIn,
distributionType);
private ListenableFuture<Void> sendMediaMessage(final boolean forceSms, String body, SlideDeck slideDeck, final long expiresIn, final int subscriptionId, final boolean initiating) {
if (isSecureText && !forceSms) {
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessage);
}
OutgoingMediaMessage outgoingMessageCandidate = new OutgoingMediaMessage(recipient, slideDeck, body, System.currentTimeMillis(), subscriptionId, expiresIn, distributionType);
attachmentManager.clear(glideRequests, false);
composeText.setText("");
final long id = fragment.stageOutgoingMessage(outgoingMessage);
final SettableFuture<Void> future = new SettableFuture<>();
final Context context = getApplicationContext();
new AsyncTask<OutgoingMediaMessage, Void, Long>() {
@Override
protected Long doInBackground(OutgoingMediaMessage... messages) {
if (initiating) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
}
final OutgoingMediaMessage outgoingMessage;
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() {
@Override
public void onComplete() {
fragment.releaseOutgoingMessage(id);
}
});
}
if (isSecureText && !forceSms) {
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessageCandidate);
} else {
outgoingMessage = outgoingMessageCandidate;
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result);
future.set(null);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, outgoingMessage);
Permissions.with(this)
.request(Manifest.permission.SEND_SMS)
.ifNecessary(isSecureText || forceSms)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms))
.onAllGranted(() -> {
attachmentManager.clear(glideRequests, false);
composeText.setText("");
final long id = fragment.stageOutgoingMessage(outgoingMessage);
new AsyncTask<Void, Void, Long>() {
@Override
protected Long doInBackground(Void... param) {
if (initiating) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
}
return MessageSender.send(context, masterSecret, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result);
future.set(null);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
})
.onAnyDenied(() -> future.set(null))
.execute();
return future;
}
@ -1712,38 +1712,43 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void sendTextMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, final boolean initiatingConversation)
throws InvalidMessageException
{
final Context context = getApplicationContext();
final Context context = getApplicationContext();
final String messageBody = getMessage();
OutgoingTextMessage message;
if (isSecureText && !forceSms) {
message = new OutgoingEncryptedMessage(recipient, getMessage(), expiresIn);
message = new OutgoingEncryptedMessage(recipient, messageBody, expiresIn);
} else {
message = new OutgoingTextMessage(recipient, getMessage(), expiresIn, subscriptionId);
message = new OutgoingTextMessage(recipient, messageBody, expiresIn, subscriptionId);
}
this.composeText.setText("");
final long id = fragment.stageOutgoingMessage(message);
new AsyncTask<OutgoingTextMessage, Void, Long>() {
@Override
protected Long doInBackground(OutgoingTextMessage... messages) {
if (initiatingConversation) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
}
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, new SmsDatabase.InsertListener() {
@Override
public void onComplete() {
fragment.releaseOutgoingMessage(id);
}
});
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
Permissions.with(this)
.request(Manifest.permission.SEND_SMS)
.ifNecessary(forceSms || !isSecureText)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_sms_permission_in_order_to_send_an_sms))
.onAllGranted(() -> {
this.composeText.setText("");
final long id = fragment.stageOutgoingMessage(message);
new AsyncTask<OutgoingTextMessage, Void, Long>() {
@Override
protected Long doInBackground(OutgoingTextMessage... messages) {
if (initiatingConversation) {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
}
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
}
@Override
protected void onPostExecute(Long result) {
sendComplete(result);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
})
.execute();
}
private void updateToggleButtonState() {
@ -1830,31 +1835,26 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
future.addListener(new ListenableFuture.Listener<Pair<Uri, Long>>() {
@Override
public void onSuccess(final @NonNull Pair<Uri, Long> result) {
try {
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipient.getExpireMessages() * 1000;
boolean initiating = threadId == -1;
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true);
SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide);
sendMediaMessage(forceSms, "", slideDeck, expiresIn, subscriptionId, initiating).addListener(new AssertedSuccessListener<Void>() {
@Override
public void onSuccess(Void nothing) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(result.first);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
} catch (InvalidMessageException e) {
Log.w(TAG, e);
Toast.makeText(ConversationActivity.this, R.string.ConversationActivity_error_sending_voice_message, Toast.LENGTH_LONG).show();
}
boolean forceSms = sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
int subscriptionId = sendButton.getSelectedTransport().getSimSubscriptionId().or(-1);
long expiresIn = recipient.getExpireMessages() * 1000;
boolean initiating = threadId == -1;
AudioSlide audioSlide = new AudioSlide(ConversationActivity.this, result.first, result.second, MediaUtil.AUDIO_AAC, true);
SlideDeck slideDeck = new SlideDeck();
slideDeck.addSlide(audioSlide);
sendMediaMessage(forceSms, "", slideDeck, expiresIn, subscriptionId, initiating).addListener(new AssertedSuccessListener<Void>() {
@Override
public void onSuccess(Void nothing) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(result.first);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
}
@Override

@ -118,6 +118,9 @@ public class SmsSendJob extends SendJob {
Log.w(TAG, npe);
throw new UndeliverableMessageException(npe2);
}
} catch (SecurityException se) {
Log.w(TAG, se);
throw new UndeliverableMessageException(se);
}
}

@ -64,6 +64,8 @@ public class Permissions {
private boolean ifNecesary;
private boolean condition = true;
PermissionsBuilder(PermissionObject permissionObject) {
this.permissionObject = permissionObject;
}
@ -78,6 +80,12 @@ public class Permissions {
return this;
}
public PermissionsBuilder ifNecessary(boolean condition) {
this.ifNecesary = true;
this.condition = condition;
return this;
}
public PermissionsBuilder withRationaleDialog(@NonNull String message, @NonNull @DrawableRes int... headers) {
this.rationalDialogHeader = headers;
this.rationaleDialogMessage = message;
@ -128,7 +136,7 @@ public class Permissions {
PermissionsRequest request = new PermissionsRequest(allGrantedListener, anyDeniedListener, anyPermanentlyDeniedListener, anyResultListener,
someGrantedListener, someDeniedListener, somePermanentlyDeniedListener);
if (ifNecesary && permissionObject.hasAll(requestedPermissions)) {
if (ifNecesary && (permissionObject.hasAll(requestedPermissions) || !condition)) {
executePreGrantedPermissionsRequest(request);
} else if (rationaleDialogMessage != null && rationalDialogHeader != null) {
executePermissionsRequestWithRationale(request);

Loading…
Cancel
Save