Pass around the candidate removals events in IOS clients

When local candidates are removed, signal to RTCPeerConnection
and eventually send to the remote client.
When a candidate-removal message is received, notify the native PeerConnection.

BUG=
R=tkchin@webrtc.org

Review URL: https://codereview.webrtc.org/1972483002 .

Cr-Commit-Position: refs/heads/master@{#12852}
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
index 86d18d1..8fb1841 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
@@ -330,6 +330,7 @@
       [_messageQueue insertObject:message atIndex:0];
       break;
     case kARDSignalingMessageTypeCandidate:
+    case kARDSignalingMessageTypeCandidateRemoval:
       [_messageQueue addObject:message];
       break;
     case kARDSignalingMessageTypeBye:
@@ -410,6 +411,16 @@
 }
 
 - (void)peerConnection:(RTCPeerConnection *)peerConnection
+    didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
+  dispatch_async(dispatch_get_main_queue(), ^{
+    ARDICECandidateRemovalMessage *message =
+        [[ARDICECandidateRemovalMessage alloc]
+            initWithRemovedCandidates:candidates];
+    [self sendSignalingMessage:message];
+  });
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
     didOpenDataChannel:(RTCDataChannel *)dataChannel {
 }
 
@@ -573,6 +584,12 @@
       [_peerConnection addIceCandidate:candidateMessage.candidate];
       break;
     }
+    case kARDSignalingMessageTypeCandidateRemoval: {
+      ARDICECandidateRemovalMessage *candidateMessage =
+          (ARDICECandidateRemovalMessage *)message;
+      [_peerConnection removeIceCandidates:candidateMessage.candidates];
+      break;
+    }
     case kARDSignalingMessageTypeBye:
       // Other client disconnected.
       // TODO(tkchin): support waiting in room for next client. For now just
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.h b/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.h
index 18eafe2..e605172 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.h
+++ b/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.h
@@ -15,6 +15,7 @@
 
 typedef enum {
   kARDSignalingMessageTypeCandidate,
+  kARDSignalingMessageTypeCandidateRemoval,
   kARDSignalingMessageTypeOffer,
   kARDSignalingMessageTypeAnswer,
   kARDSignalingMessageTypeBye,
@@ -37,6 +38,15 @@
 
 @end
 
+@interface ARDICECandidateRemovalMessage : ARDSignalingMessage
+
+@property(nonatomic, readonly) NSArray<RTCIceCandidate *> *candidates;
+
+- (instancetype)initWithRemovedCandidates:
+    (NSArray<RTCIceCandidate *> *)candidates;
+
+@end
+
 @interface ARDSessionDescriptionMessage : ARDSignalingMessage
 
 @property(nonatomic, readonly) RTCSessionDescription *sessionDescription;
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.m b/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.m
index a5ca295..3fab185 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDSignalingMessage.m
@@ -16,7 +16,8 @@
 #import "RTCIceCandidate+JSON.h"
 #import "RTCSessionDescription+JSON.h"
 
-static NSString const *kARDSignalingMessageTypeKey = @"type";
+static NSString * const kARDSignalingMessageTypeKey = @"type";
+static NSString * const kARDTypeValueRemoveCandidates = @"remove-candidates";
 
 @implementation ARDSignalingMessage
 
@@ -47,6 +48,12 @@
     RTCIceCandidate *candidate =
         [RTCIceCandidate candidateFromJSONDictionary:values];
     message = [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
+  } else if ([typeString isEqualToString:kARDTypeValueRemoveCandidates]) {
+    RTCLogInfo(@"Received remove-candidates message");
+    NSArray<RTCIceCandidate *> *candidates =
+        [RTCIceCandidate candidatesFromJSONDictionary:values];
+    message = [[ARDICECandidateRemovalMessage alloc]
+                  initWithRemovedCandidates:candidates];
   } else if ([typeString isEqualToString:@"offer"] ||
              [typeString isEqualToString:@"answer"]) {
     RTCSessionDescription *description =
@@ -84,6 +91,27 @@
 
 @end
 
+@implementation ARDICECandidateRemovalMessage
+
+@synthesize candidates = _candidates;
+
+- (instancetype)initWithRemovedCandidates:(
+    NSArray<RTCIceCandidate *> *)candidates {
+  NSParameterAssert(candidates.count);
+  if (self = [super initWithType:kARDSignalingMessageTypeCandidateRemoval]) {
+    _candidates = candidates;
+  }
+  return self;
+}
+
+- (NSData *)JSONData {
+  return
+      [RTCIceCandidate JSONDataForIceCandidates:_candidates
+                                       withType:kARDTypeValueRemoveCandidates];
+}
+
+@end
+
 @implementation ARDSessionDescriptionMessage
 
 @synthesize sessionDescription = _sessionDescription;
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m b/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
index 87a690e..6f60380 100644
--- a/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
+++ b/webrtc/examples/objc/AppRTCDemo/ARDWebSocketChannel.m
@@ -233,6 +233,7 @@
       // Should not receive answer in loopback scenario.
       break;
     case kARDSignalingMessageTypeCandidate:
+    case kARDSignalingMessageTypeCandidateRemoval:
       // Send back to server.
       [self sendMessage:message];
       break;
diff --git a/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h b/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h
index 1051f8e..d2e5e33 100644
--- a/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h
+++ b/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.h
@@ -13,6 +13,10 @@
 @interface RTCIceCandidate (JSON)
 
 + (RTCIceCandidate *)candidateFromJSONDictionary:(NSDictionary *)dictionary;
++ (NSArray<RTCIceCandidate *> *)candidatesFromJSONDictionary:
+    (NSDictionary *)dictionary;
++ (NSData *)JSONDataForIceCandidates:(NSArray<RTCIceCandidate *> *)candidates
+                            withType:(NSString *)typeValue;
 - (NSData *)JSONData;
 
 @end
diff --git a/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m b/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m
index f20c490..b1be7fb 100644
--- a/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m
+++ b/webrtc/examples/objc/AppRTCDemo/RTCICECandidate+JSON.m
@@ -17,6 +17,8 @@
 static NSString const *kRTCICECandidateMidKey = @"id";
 static NSString const *kRTCICECandidateMLineIndexKey = @"label";
 static NSString const *kRTCICECandidateSdpKey = @"candidate";
+static NSString const *kRTCICECandidatesTypeKey = @"candidates";
+
 
 @implementation RTCIceCandidate (JSON)
 
@@ -30,6 +32,43 @@
                                        sdpMid:mid];
 }
 
++ (NSData *)JSONDataForIceCandidates:(NSArray<RTCIceCandidate *> *)candidates
+                            withType:(NSString *)typeValue {
+  NSMutableArray *jsonCandidates =
+      [NSMutableArray arrayWithCapacity:candidates.count];
+  for (RTCIceCandidate *candidate in candidates) {
+    NSDictionary *jsonCandidate = [candidate JSONDictionary];
+    [jsonCandidates addObject:jsonCandidate];
+  }
+  NSDictionary *json = @{
+    kRTCICECandidateTypeKey : typeValue,
+    kRTCICECandidatesTypeKey : jsonCandidates
+  };
+  NSError *error = nil;
+  NSData *data =
+      [NSJSONSerialization dataWithJSONObject:json
+                                      options:NSJSONWritingPrettyPrinted
+                                        error:&error];
+  if (error) {
+    RTCLogError(@"Error serializing JSON: %@", error);
+    return nil;
+  }
+  return data;
+}
+
++ (NSArray<RTCIceCandidate *> *)candidatesFromJSONDictionary:
+    (NSDictionary *)dictionary {
+  NSArray *jsonCandidates = dictionary[kRTCICECandidatesTypeKey];
+  NSMutableArray<RTCIceCandidate *> *candidates =
+      [NSMutableArray arrayWithCapacity:jsonCandidates.count];
+  for (NSDictionary *jsonCandidate in jsonCandidates) {
+    RTCIceCandidate *candidate =
+        [RTCIceCandidate candidateFromJSONDictionary:jsonCandidate];
+    [candidates addObject:candidate];
+  }
+  return candidates;
+}
+
 - (NSData *)JSONData {
   NSDictionary *json = @{
     kRTCICECandidateTypeKey : kRTCICECandidateTypeValue,
@@ -49,4 +88,13 @@
   return data;
 }
 
+- (NSDictionary *)JSONDictionary{
+  NSDictionary *json = @{
+    kRTCICECandidateMLineIndexKey : @(self.sdpMLineIndex),
+    kRTCICECandidateMidKey : self.sdpMid,
+    kRTCICECandidateSdpKey : self.sdp
+  };
+  return json;
+}
+
 @end