TURN-only option, and for unknown caller

Now, by default, we only use TURN for incoming calls from unknown
contacts. We will potentially directly connect for outgoing calls and
for incoming calls from known contacts.

Optionally, the user can disable direct connection altogether, at the
cost of some call quality.

// FREEBIE
This commit is contained in:
Michael Kirk 2017-02-22 10:06:01 -05:00
parent 7a3da3fa68
commit 4b8a5f8ccb
6 changed files with 94 additions and 20 deletions

View File

@ -231,6 +231,9 @@ protocol CallServiceObserver: class {
self.updateIsVideoEnabled()
}
/**
* Choose whether to use CallKit or a Notification backed interface for calling.
*/
public func createCallUIAdapter() {
AssertIsOnMainThread()
@ -299,7 +302,9 @@ protocol CallServiceObserver: class {
return getIceServers().then { iceServers -> Promise<HardenedRTCSessionDescription> in
Logger.debug("\(self.TAG) got ice servers:\(iceServers)")
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .outgoing)
let useTurnOnly = Environment.getCurrent().preferences.doCallsHideIPAddress()
let peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .outgoing, useTurnOnly: useTurnOnly)
assert(self.peerConnectionClient == nil, "Unexpected PeerConnectionClient instance")
Logger.debug("\(self.TAG) setting peerConnectionClient in \(#function)")
@ -478,8 +483,15 @@ protocol CallServiceObserver: class {
throw CallError.assertionError(description: "getIceServers() response for obsolete call")
}
assert(self.peerConnectionClient == nil, "Unexpected PeerConnectionClient instance")
// For contacts not stored in our system contacts, we assume they are an unknown caller, and we force
// a TURN connection, so as not to reveal any connectivity information (IP/port) to the caller.
let unknownCaller = self.contactsManager.contact(forPhoneIdentifier: thread.contactIdentifier()) == nil
let useTurnOnly = unknownCaller || Environment.getCurrent().preferences.doCallsHideIPAddress()
Logger.debug("\(self.self.TAG) setting peerConnectionClient in \(#function)")
self.peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .incoming)
self.peerConnectionClient = PeerConnectionClient(iceServers: iceServers, delegate: self, callDirection: .incoming, useTurnOnly: useTurnOnly)
let offerSessionDescription = RTCSessionDescription(type: .offer, sdp: callerSessionDescription)
let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)

View File

@ -120,7 +120,7 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
private var remoteVideoTrack: RTCVideoTrack?
private var cameraConstraints: RTCMediaConstraints
init(iceServers: [RTCIceServer], delegate: PeerConnectionClientDelegate, callDirection: CallDirection) {
init(iceServers: [RTCIceServer], delegate: PeerConnectionClientDelegate, callDirection: CallDirection, useTurnOnly: Bool) {
AssertIsOnMainThread()
self.iceServers = iceServers
@ -130,6 +130,12 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
configuration.iceServers = iceServers
configuration.bundlePolicy = .maxBundle
configuration.rtcpMuxPolicy = .require
if useTurnOnly {
Logger.debug("\(TAG) using iceTransportPolicy: relay")
configuration.iceTransportPolicy = .relay
} else {
Logger.debug("\(TAG) using iceTransportPolicy: default")
}
let connectionConstraintsDict = ["DtlsSrtpKeyAgreement": "true"]
connectionConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: connectionConstraintsDict)

View File

@ -71,6 +71,11 @@ extern NSString *const PropertyListPreferencesKeyEnableDebugLog;
- (BOOL)isCallKitEnabled;
- (void)setIsCallKitEnabled:(BOOL)flag;
#pragma mark direct call connectivity (non-TURN)
- (BOOL)doCallsHideIPAddress;
- (void)setDoCallsHideIPAddress:(BOOL)flag;
#pragma mark - Block on Identity Change
- (BOOL)shouldBlockOnIdentityChange;

View File

@ -23,6 +23,7 @@ NSString *const PropertyListPreferencesKeyHasRegisteredVoipPush = @"VOIPPushEnab
NSString *const PropertyListPreferencesKeyLastRecordedPushToken = @"LastRecordedPushToken";
NSString *const PropertyListPreferencesKeyLastRecordedVoipToken = @"LastRecordedVoipToken";
NSString *const PropertyListPreferencesKeyCallKitEnabled = @"CallKitEnabled";
NSString *const PropertyListPreferencesKeyCallsHideIPAddress = @"CallsHideIPAddress";
@implementation PropertyListPreferences
@ -138,6 +139,9 @@ NSString *const PropertyListPreferencesKeyCallKitEnabled = @"CallKitEnabled";
- (void)setLoggingEnabled:(BOOL)flag
{
// Logging preferences are stored in UserDefaults instead of the database, so that we can (optionally) start
// logging before the database is initialized. This is important because sometimes there are problems *with* the
// database initialization, and without logging it would be hard to track down.
[NSUserDefaults.standardUserDefaults setObject:@(flag) forKey:PropertyListPreferencesKeyEnableDebugLog];
[NSUserDefaults.standardUserDefaults synchronize];
}
@ -182,6 +186,21 @@ NSString *const PropertyListPreferencesKeyCallKitEnabled = @"CallKitEnabled";
[self setValueForKey:PropertyListPreferencesKeyCallKitEnabled toValue:@(flag)];
}
#pragma mark direct call connectivity (non-TURN)
// Allow callers to connect directly, when desirable, vs. enforcing TURN only proxy connectivity
- (BOOL)doCallsHideIPAddress
{
NSNumber *preference = [self tryGetValueForKey:PropertyListPreferencesKeyCallsHideIPAddress];
return preference ? [preference boolValue] : NO;
}
- (void)setDoCallsHideIPAddress:(BOOL)flag
{
[self setValueForKey:PropertyListPreferencesKeyCallsHideIPAddress toValue:@(flag)];
}
#pragma mark Notification Preferences
- (BOOL)soundInForeground

View File

@ -14,16 +14,23 @@ NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
PrivacySettingsTableViewControllerSectionIndexScreenSecurity,
PrivacySettingsTableViewControllerSectionIndexCalling,
PrivacySettingsTableViewControllerSectionIndexHistoryLog,
PrivacySettingsTableViewControllerSectionIndexBlockOnIdentityChange
PrivacySettingsTableViewControllerSectionIndexBlockOnIdentityChange,
PrivacySettingsTableViewControllerSectionIndex_Count // meta section to track how many sections
};
@interface PrivacySettingsTableViewController ()
@property (nonatomic, strong) UITableViewCell *enableScreenSecurityCell;
@property (nonatomic, strong) UISwitch *enableScreenSecuritySwitch;
@property (nonatomic) UITableViewCell *callsHideIPAddressCell;
@property (nonatomic) UISwitch *callsHideIPAddressSwitch;
@property (nonatomic, strong) UITableViewCell *blockOnIdentityChangeCell;
@property (nonatomic, strong) UISwitch *blockOnIdentityChangeSwitch;
@property (nonatomic, strong) UITableViewCell *clearHistoryLogCell;
@end
@ -59,6 +66,17 @@ typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
action:@selector(didToggleScreenSecuritySwitch:)
forControlEvents:UIControlEventTouchUpInside];
// Allow calls to connect directly vs. using TURN exclusively
self.callsHideIPAddressCell = [UITableViewCell new];
self.callsHideIPAddressCell.textLabel.text
= NSLocalizedString(@"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE", @"Table cell label");
self.callsHideIPAddressSwitch = [UISwitch new];
self.callsHideIPAddressCell.accessoryView = self.callsHideIPAddressSwitch;
[self.callsHideIPAddressSwitch setOn:[Environment.preferences doCallsHideIPAddress]];
[self.callsHideIPAddressSwitch addTarget:self
action:@selector(didToggleCallsHideIPAddressSwitch:)
forControlEvents:UIControlEventTouchUpInside];
// Clear History Log Cell
self.clearHistoryLogCell = [[UITableViewCell alloc] init];
self.clearHistoryLogCell.textLabel.text = NSLocalizedString(@"SETTINGS_CLEAR_HISTORY", @"");
@ -79,13 +97,15 @@ typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
return PrivacySettingsTableViewControllerSectionIndex_Count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case PrivacySettingsTableViewControllerSectionIndexScreenSecurity:
return 1;
case PrivacySettingsTableViewControllerSectionIndexCalling:
return 1;
case PrivacySettingsTableViewControllerSectionIndexHistoryLog:
return 1;
case PrivacySettingsTableViewControllerSectionIndexBlockOnIdentityChange:
@ -100,6 +120,9 @@ typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
switch (section) {
case PrivacySettingsTableViewControllerSectionIndexScreenSecurity:
return NSLocalizedString(@"SETTINGS_SCREEN_SECURITY_DETAIL", nil);
case PrivacySettingsTableViewControllerSectionIndexCalling:
return NSLocalizedString(@"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE_DETAIL",
@"User settings section footer, a detailed explanation");
case PrivacySettingsTableViewControllerSectionIndexBlockOnIdentityChange:
return NSLocalizedString(
@"SETTINGS_BLOCK_ON_IDENITY_CHANGE_DETAIL", @"User settings section footer, a detailed explanation");
@ -112,6 +135,8 @@ typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
switch (indexPath.section) {
case PrivacySettingsTableViewControllerSectionIndexScreenSecurity:
return self.enableScreenSecurityCell;
case PrivacySettingsTableViewControllerSectionIndexCalling:
return self.callsHideIPAddressCell;
case PrivacySettingsTableViewControllerSectionIndexHistoryLog:
return self.clearHistoryLogCell;
case PrivacySettingsTableViewControllerSectionIndexBlockOnIdentityChange:
@ -128,6 +153,8 @@ typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
switch (section) {
case PrivacySettingsTableViewControllerSectionIndexScreenSecurity:
return NSLocalizedString(@"SETTINGS_SECURITY_TITLE", @"Section header");
case PrivacySettingsTableViewControllerSectionIndexCalling:
return NSLocalizedString(@"SETTINGS_SECTION_TITLE_CALLING", @"settings topic header for table section");
case PrivacySettingsTableViewControllerSectionIndexHistoryLog:
return NSLocalizedString(@"SETTINGS_HISTORYLOG_TITLE", @"Section header");
case PrivacySettingsTableViewControllerSectionIndexBlockOnIdentityChange:
@ -182,6 +209,13 @@ typedef NS_ENUM(NSInteger, PrivacySettingsTableViewControllerSectionIndex) {
[Environment.preferences setShouldBlockOnIdentityChange:enabled];
}
- (void)didToggleCallsHideIPAddressSwitch:(UISwitch *)sender
{
BOOL enabled = sender.isOn;
DDLogInfo(@"%@ toggled callsHideIPAddress: %@", self.tag, enabled ? @"ON" : @"OFF");
[Environment.preferences setDoCallsHideIPAddress:enabled];
}
#pragma mark - Log util
+ (NSString *)tag

View File

@ -124,7 +124,7 @@
/* No comment provided by engineer. */
"COUNTRYCODE_SELECT_TITLE" = "Select Country Code";
/* Accessibility label for the create new group button. */
/* Accessibility label for the create group new group button */
"CREATE_NEW_GROUP" = "Create new group";
/* {{number of days}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 days}}'. See other *_TIME_AMOUNT strings */
@ -253,6 +253,9 @@
/* Generic notice when message failed to send. */
"ERROR_DESCRIPTION_CLIENT_SENDING_FAILURE" = "Failed to send message.";
/* Error mesage indicating that message send is disabled due to prekey update failures */
"ERROR_DESCRIPTION_MESSAGE_SEND_DISABLED_PREKEY_UPDATE_FAILURES" = "ERROR_DESCRIPTION_MESSAGE_SEND_DISABLED_PREKEY_UPDATE_FAILURES";
/* Generic error used whenver Signal can't contact the server */
"ERROR_DESCRIPTION_NO_INTERNET" = "Signal was unable to connect to the internet. Please try from another WiFi network or use mobile data.";
@ -349,9 +352,6 @@
/* No comment provided by engineer. */
"GROUP_REMOVING_FAILED" = "Failed to leave group";
/* Accessibilty label for group settings */
"GROUP_SETTINGS_LABEL" = "Group settings";
/* No comment provided by engineer. */
"GROUP_TITLE_CHANGED" = "Title is now '%@'. ";
@ -567,7 +567,8 @@
/* No comment provided by engineer. */
"OK" = "Ok";
/* Button text which opens the settings app */
/* Button text which opens the settings app
Label for button which opens the settings UI */
"OPEN_SETTINGS_BUTTON" = "Settings";
/* Info Message when {{other user}} disables or doesn't support disappearing messages */
@ -768,24 +769,21 @@
/* No comment provided by engineer. */
"SETTINGS_ADVANCED_TITLE" = "Advanced";
/* This setting is used to switch between new-style WebRTC calling and old-style RedPhone calling. */
"SETTINGS_ADVANCED_WEBRTC" = "Enable Video Calling";
/* The message of the alert shown when updates to the WebRTC property fail. */
"SETTINGS_ADVANCED_WEBRTC_FAILED_MESSAGE" = "Could not update your preferences.";
/* The title of the alert shown when updates to the WebRTC property fail. */
"SETTINGS_ADVANCED_WEBRTC_FAILED_TITLE" = "Error";
/* User settings section footer, a detailed explanation */
"SETTINGS_BLOCK_ON_IDENITY_CHANGE_DETAIL" = "Requires your approval before communicating with someone who has a new safety number, commonly from a reinstall of Signal.";
/* Table cell label */
"SETTINGS_BLOCK_ON_IDENTITY_CHANGE_TITLE" = "Require Approval on Change";
/* Settings button accessibility hint */
/* Accessibility hint for the settings button */
"SETTINGS_BUTTON_ACCESSIBILITY" = "Settings";
/* Table cell label */
"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE" = "Hide IP Address";
/* User settings section footer, a detailed explanation */
"SETTINGS_CALLING_HIDES_IP_ADDRESS_PREFERENCE_TITLE_DETAIL" = "Hiding your IP address during audio and video calls will decrease call quality.";
/* No comment provided by engineer. */
"SETTINGS_CLEAR_HISTORY" = "Clear History Logs";