Support for Signal calls.

Merge in RedPhone

// FREEBIE
This commit is contained in:
Moxie Marlinspike
2015-09-09 13:54:29 -07:00
parent 3d4ae60d81
commit d83a3d71bc
2585 changed files with 803492 additions and 45 deletions

View File

@@ -58,6 +58,8 @@ import com.afollestad.materialdialogs.AlertDialogWrapper;
import com.commonsware.cwac.camera.CameraHost.FailureReason;
import com.google.protobuf.ByteString;
import org.thoughtcrime.redphone.RedPhone;
import org.thoughtcrime.redphone.RedPhoneService;
import org.thoughtcrime.securesms.TransportOptions.OnTransportChangedListener;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.components.AnimatingToggle;
@@ -605,18 +607,27 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleDial(Recipient recipient) {
try {
if (recipient == null) return;
Intent intent = new Intent(this, RedPhoneService.class);
intent.setAction(RedPhoneService.ACTION_OUTGOING_CALL);
intent.putExtra(RedPhoneService.EXTRA_REMOTE_NUMBER, recipient.getNumber());
startService(intent);
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
Uri.parse("tel:" + recipient.getNumber()));
startActivity(dialIntent);
} catch (ActivityNotFoundException anfe) {
Log.w(TAG, anfe);
Dialogs.showAlertDialog(this,
getString(R.string.ConversationActivity_calls_not_supported),
getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions));
}
Intent activityIntent = new Intent(this, RedPhone.class);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(activityIntent);
// try {
// if (recipient == null) return;
//
// Intent dialIntent = new Intent(Intent.ACTION_DIAL,
// Uri.parse("tel:" + recipient.getNumber()));
// startActivity(dialIntent);
// } catch (ActivityNotFoundException anfe) {
// Log.w(TAG, anfe);
// Dialogs.showAlertDialog(this,
// getString(R.string.ConversationActivity_calls_not_supported),
// getString(R.string.ConversationActivity_this_device_does_not_appear_to_support_dial_actions));
// }
}
private void handleDisplayGroupRecipients() {

View File

@@ -13,7 +13,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
@@ -30,12 +29,10 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.push.TextSecureCommunicationFactory;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.RegistrationService;
import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -51,6 +48,8 @@ import static org.thoughtcrime.securesms.service.RegistrationService.Registratio
public class RegistrationProgressActivity extends BaseActionBarActivity {
private static final String TAG = RegistrationProgressActivity.class.getSimpleName();
private static final int FOCUSED_COLOR = Color.parseColor("#ff333333");
private static final int UNFOCUSED_COLOR = Color.parseColor("#ff808080");
@@ -523,17 +522,17 @@ public class RegistrationProgressActivity extends BaseActionBarActivity {
TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(context, e164number, password);
int registrationId = TextSecurePreferences.getLocalRegistrationId(context);
accountManager.verifyAccount(code, signalingKey, true, registrationId);
accountManager.verifyAccountWithCode(code, signalingKey, registrationId);
return SUCCESS;
} catch (ExpectationFailedException e) {
Log.w("RegistrationProgressActivity", e);
Log.w(TAG, e);
return MULTI_REGISTRATION_ERROR;
} catch (RateLimitException e) {
Log.w("RegistrationProgressActivity", e);
Log.w(TAG, e);
return RATE_LIMIT_ERROR;
} catch (IOException e) {
Log.w("RegistrationProgressActivity", e);
Log.w(TAG, e);
return NETWORK_ERROR;
}
}

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
@@ -26,4 +27,9 @@ public class BitmapContactPhoto implements ContactPhoto {
.setScaleType(ImageView.ScaleType.CENTER_CROP)
.setOval(true);
}
@Override
public Drawable asCallCard(Context context) {
return new BitmapDrawable(context.getResources(), bitmap);
}
}

View File

@@ -7,6 +7,6 @@ public interface ContactPhoto {
public Drawable asDrawable(Context context, int color);
public Drawable asDrawable(Context context, int color, boolean inverted);
public Drawable asCallCard(Context context);
}

View File

@@ -35,4 +35,9 @@ public class GeneratedContactPhoto implements ContactPhoto {
.endConfig()
.buildRound(String.valueOf(name.charAt(0)), inverted ? Color.WHITE : color);
}
@Override
public Drawable asCallCard(Context context) {
return context.getDrawable(R.drawable.ic_contact_picture);
}
}

View File

@@ -40,6 +40,11 @@ public class ResourceContactPhoto implements ContactPhoto {
return new ExpandingLayerDrawable(new Drawable[] {background, foreground});
}
@Override
public Drawable asCallCard(Context context) {
return context.getDrawable(resourceId);
}
private static class ExpandingLayerDrawable extends LayerDrawable {
public ExpandingLayerDrawable(Drawable[] layers) {
super(layers);

View File

@@ -5,6 +5,8 @@ import android.graphics.drawable.Drawable;
import com.makeramen.roundedimageview.RoundedDrawable;
import org.thoughtcrime.securesms.R;
public class TransparentContactPhoto implements ContactPhoto {
TransparentContactPhoto() {}
@@ -18,4 +20,9 @@ public class TransparentContactPhoto implements ContactPhoto {
public Drawable asDrawable(Context context, int color, boolean inverted) {
return RoundedDrawable.fromDrawable(context.getResources().getDrawable(android.R.color.transparent));
}
@Override
public Drawable asCallCard(Context context) {
return context.getDrawable(R.drawable.ic_contact_picture);
}
}

View File

@@ -51,30 +51,33 @@ public class TextSecureCommunicationModule {
}
@Provides TextSecureAccountManager provideTextSecureAccountManager() {
return new TextSecureAccountManager(BuildConfig.PUSH_URL,
return new TextSecureAccountManager(BuildConfig.TEXTSECURE_URL,
new TextSecurePushTrustStore(context),
TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context));
TextSecurePreferences.getPushServerPassword(context),
BuildConfig.USER_AGENT);
}
@Provides TextSecureMessageSenderFactory provideTextSecureMessageSenderFactory() {
return new TextSecureMessageSenderFactory() {
@Override
public TextSecureMessageSender create() {
return new TextSecureMessageSender(BuildConfig.PUSH_URL,
return new TextSecureMessageSender(BuildConfig.TEXTSECURE_URL,
new TextSecurePushTrustStore(context),
TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context),
new TextSecureAxolotlStore(context),
BuildConfig.USER_AGENT,
Optional.<TextSecureMessageSender.EventListener>of(new SecurityEventListener(context)));
}
};
}
@Provides TextSecureMessageReceiver provideTextSecureMessageReceiver() {
return new TextSecureMessageReceiver(BuildConfig.PUSH_URL,
return new TextSecureMessageReceiver(BuildConfig.TEXTSECURE_URL,
new TextSecurePushTrustStore(context),
new DynamicCredentialsProvider(context));
new DynamicCredentialsProvider(context),
BuildConfig.USER_AGENT);
}
public static interface TextSecureMessageSenderFactory {

View File

@@ -8,11 +8,18 @@ import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import org.thoughtcrime.redphone.RedPhoneService;
import org.thoughtcrime.redphone.crypto.EncryptedSignalMessage;
import org.thoughtcrime.redphone.crypto.InvalidEncryptedSignalException;
import org.thoughtcrime.redphone.signaling.SessionDescriptor;
import org.thoughtcrime.redphone.signaling.signals.CompressedInitiateSignalProtocol.CompressedInitiateSignal;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException;
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
private static final String TAG = GcmBroadcastReceiver.class.getSimpleName();
@@ -32,10 +39,12 @@ public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
String messageData = intent.getStringExtra("message");
String receiptData = intent.getStringExtra("receipt");
String callData = intent.getStringExtra("call");
if (!TextUtils.isEmpty(messageData)) handleReceivedMessage(context, messageData);
else if (!TextUtils.isEmpty(receiptData)) handleReceivedMessage(context, receiptData);
else if (intent.hasExtra("notification")) handleReceivedNotification(context);
else if (!TextUtils.isEmpty(callData)) handleReceivedCall(context, callData);
}
}
@@ -50,4 +59,23 @@ public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
.getJobManager()
.add(new PushNotificationReceiveJob(context));
}
private void handleReceivedCall(Context context, String data) {
try {
String signalingKey = TextSecurePreferences.getSignalingKey(context);
EncryptedSignalMessage encryptedSignalMessage = new EncryptedSignalMessage(data, signalingKey);
CompressedInitiateSignal signal = CompressedInitiateSignal.parseFrom(encryptedSignalMessage.getPlaintext());
Intent intent = new Intent(context, RedPhoneService.class);
intent.setAction(RedPhoneService.ACTION_INCOMING_CALL);
intent.putExtra(RedPhoneService.EXTRA_REMOTE_NUMBER, signal.getInitiator());
intent.putExtra(RedPhoneService.EXTRA_SESSION_DESCRIPTOR, new SessionDescriptor(signal.getServerName(),
signal.getPort(),
signal.getSessionId(),
signal.getVersion()));
context.startService(intent);
} catch (InvalidEncryptedSignalException | IOException e) {
Log.w(TAG, e);
}
}
}

View File

@@ -11,20 +11,16 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.AttachmentStreamUriLoader.AttachmentModel;
import org.thoughtcrime.securesms.push.TextSecurePushTrustStore;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.textsecure.api.crypto.AttachmentCipherInputStream;
import org.whispersystems.textsecure.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.textsecure.internal.push.PushServiceSocket;
import org.whispersystems.textsecure.internal.util.StaticCredentialsProvider;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
public class AvatarDownloadJob extends MasterSecretJob {
@@ -85,11 +81,12 @@ public class AvatarDownloadJob extends MasterSecretJob {
}
private File downloadAttachment(String relay, long contentLocation) throws IOException {
PushServiceSocket socket = new PushServiceSocket(BuildConfig.PUSH_URL,
PushServiceSocket socket = new PushServiceSocket(BuildConfig.TEXTSECURE_URL,
new TextSecurePushTrustStore(context),
new StaticCredentialsProvider(TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context),
null));
null),
BuildConfig.USER_AGENT);
File destination = File.createTempFile("avatar", "tmp");

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.push;
import android.content.Context;
import org.thoughtcrime.redphone.signaling.RedPhoneAccountManager;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@@ -20,15 +21,16 @@ import static org.whispersystems.textsecure.api.TextSecureMessageSender.EventLis
public class TextSecureCommunicationFactory {
public static TextSecureAccountManager createManager(Context context) {
return new TextSecureAccountManager(BuildConfig.PUSH_URL,
return new TextSecureAccountManager(BuildConfig.TEXTSECURE_URL,
new TextSecurePushTrustStore(context),
TextSecurePreferences.getLocalNumber(context),
TextSecurePreferences.getPushServerPassword(context));
TextSecurePreferences.getPushServerPassword(context),
BuildConfig.USER_AGENT);
}
public static TextSecureAccountManager createManager(Context context, String number, String password) {
return new TextSecureAccountManager(BuildConfig.PUSH_URL, new TextSecurePushTrustStore(context),
number, password);
return new TextSecureAccountManager(BuildConfig.TEXTSECURE_URL, new TextSecurePushTrustStore(context),
number, password, BuildConfig.USER_AGENT);
}
}

View File

@@ -12,6 +12,10 @@ import android.util.Log;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import org.thoughtcrime.redphone.signaling.RedPhoneAccountAttributes;
import org.thoughtcrime.redphone.signaling.RedPhoneAccountManager;
import org.thoughtcrime.redphone.signaling.RedPhoneTrustStore;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
@@ -159,7 +163,7 @@ public class RegistrationService extends Service {
try {
TextSecureAccountManager accountManager = TextSecureCommunicationFactory.createManager(this, number, password);
handleCommonRegistration(accountManager, number);
handleCommonRegistration(accountManager, number, password, signalingKey);
markAsVerified(number, password, signalingKey);
@@ -199,9 +203,9 @@ public class RegistrationService extends Service {
setState(new RegistrationState(RegistrationState.STATE_VERIFYING, number));
String challenge = waitForChallenge();
accountManager.verifyAccount(challenge, signalingKey, true, registrationId);
accountManager.verifyAccountWithCode(challenge, signalingKey, registrationId);
handleCommonRegistration(accountManager, number);
handleCommonRegistration(accountManager, number, password, signalingKey);
markAsVerified(number, password, signalingKey);
setState(new RegistrationState(RegistrationState.STATE_COMPLETE, number));
@@ -227,7 +231,7 @@ public class RegistrationService extends Service {
}
}
private void handleCommonRegistration(TextSecureAccountManager accountManager, String number)
private void handleCommonRegistration(TextSecureAccountManager accountManager, String number, String password, String signalingKey)
throws IOException
{
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
@@ -249,6 +253,13 @@ public class RegistrationService extends Service {
DatabaseFactory.getIdentityDatabase(this).saveIdentity(self.getRecipientId(), identityKey.getPublicKey());
DirectoryHelper.refreshDirectory(this, accountManager, number);
RedPhoneAccountManager redPhoneAccountManager = new RedPhoneAccountManager(BuildConfig.REDPHONE_MASTER_URL,
new RedPhoneTrustStore(this),
number, password);
String verificationToken = accountManager.getAccountVerificationToken();
redPhoneAccountManager.createAccount(verificationToken, new RedPhoneAccountAttributes(signalingKey, gcmRegistrationId));
DirectoryRefreshListener.schedule(this);
}