Move all the examples from the talk directory into the webrtc examples directory.

Significant changes:

- move the libjingle_examples.gyp file into webrtc directory.
- rename talk/examples/android to webrtc/examples/androidapp to avoid name conflicts.
- update paths in talk/libjingle_tests.gyp to point to webrtc directory for Objective-C test.

BUG=
R=pthatcher@webrtc.org, tkchin@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9681}
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
new file mode 100644
index 0000000..bcc7460
--- /dev/null
+++ b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m
@@ -0,0 +1,641 @@
+/*
+ *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#import "ARDAppClient+Internal.h"
+
+#if defined(WEBRTC_IOS)
+#import "RTCAVFoundationVideoSource.h"
+#endif
+#import "RTCFileLogger.h"
+#import "RTCICEServer.h"
+#import "RTCLogging.h"
+#import "RTCMediaConstraints.h"
+#import "RTCMediaStream.h"
+#import "RTCPair.h"
+#import "RTCPeerConnectionInterface.h"
+#import "RTCVideoCapturer.h"
+
+#import "ARDAppEngineClient.h"
+#import "ARDCEODTURNClient.h"
+#import "ARDJoinResponse.h"
+#import "ARDMessageResponse.h"
+#import "ARDSDPUtils.h"
+#import "ARDSignalingMessage.h"
+#import "ARDUtilities.h"
+#import "ARDWebSocketChannel.h"
+#import "RTCICECandidate+JSON.h"
+#import "RTCSessionDescription+JSON.h"
+
+
+static NSString * const kARDDefaultSTUNServerUrl =
+    @"stun:stun.l.google.com:19302";
+// TODO(tkchin): figure out a better username for CEOD statistics.
+static NSString * const kARDTurnRequestUrl =
+    @"https://computeengineondemand.appspot.com"
+    @"/turn?username=iapprtc&key=4080218913";
+
+static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
+static NSInteger const kARDAppClientErrorUnknown = -1;
+static NSInteger const kARDAppClientErrorRoomFull = -2;
+static NSInteger const kARDAppClientErrorCreateSDP = -3;
+static NSInteger const kARDAppClientErrorSetSDP = -4;
+static NSInteger const kARDAppClientErrorInvalidClient = -5;
+static NSInteger const kARDAppClientErrorInvalidRoom = -6;
+
+@implementation ARDAppClient {
+  RTCFileLogger *_fileLogger;
+}
+
+@synthesize delegate = _delegate;
+@synthesize state = _state;
+@synthesize roomServerClient = _roomServerClient;
+@synthesize channel = _channel;
+@synthesize turnClient = _turnClient;
+@synthesize peerConnection = _peerConnection;
+@synthesize factory = _factory;
+@synthesize messageQueue = _messageQueue;
+@synthesize isTurnComplete = _isTurnComplete;
+@synthesize hasReceivedSdp  = _hasReceivedSdp;
+@synthesize roomId = _roomId;
+@synthesize clientId = _clientId;
+@synthesize isInitiator = _isInitiator;
+@synthesize iceServers = _iceServers;
+@synthesize webSocketURL = _websocketURL;
+@synthesize webSocketRestURL = _websocketRestURL;
+@synthesize defaultPeerConnectionConstraints =
+    _defaultPeerConnectionConstraints;
+
+- (instancetype)init {
+  if (self = [super init]) {
+    _roomServerClient = [[ARDAppEngineClient alloc] init];
+    NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
+    _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
+    [self configure];
+  }
+  return self;
+}
+
+- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
+  if (self = [super init]) {
+    _roomServerClient = [[ARDAppEngineClient alloc] init];
+    _delegate = delegate;
+    NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
+    _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
+    [self configure];
+  }
+  return self;
+}
+
+// TODO(tkchin): Provide signaling channel factory interface so we can recreate
+// channel if we need to on network failure. Also, make this the default public
+// constructor.
+- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
+                        signalingChannel:(id<ARDSignalingChannel>)channel
+                              turnClient:(id<ARDTURNClient>)turnClient
+                                delegate:(id<ARDAppClientDelegate>)delegate {
+  NSParameterAssert(rsClient);
+  NSParameterAssert(channel);
+  NSParameterAssert(turnClient);
+  if (self = [super init]) {
+    _roomServerClient = rsClient;
+    _channel = channel;
+    _turnClient = turnClient;
+    _delegate = delegate;
+    [self configure];
+  }
+  return self;
+}
+
+- (void)configure {
+  _factory = [[RTCPeerConnectionFactory alloc] init];
+  _messageQueue = [NSMutableArray array];
+  _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
+  _fileLogger = [[RTCFileLogger alloc] init];
+  [_fileLogger start];
+}
+
+- (void)dealloc {
+  [self disconnect];
+}
+
+- (void)setState:(ARDAppClientState)state {
+  if (_state == state) {
+    return;
+  }
+  _state = state;
+  [_delegate appClient:self didChangeState:_state];
+}
+
+- (void)connectToRoomWithId:(NSString *)roomId
+                    options:(NSDictionary *)options {
+  NSParameterAssert(roomId.length);
+  NSParameterAssert(_state == kARDAppClientStateDisconnected);
+  self.state = kARDAppClientStateConnecting;
+
+  // Request TURN.
+  __weak ARDAppClient *weakSelf = self;
+  [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
+                                                     NSError *error) {
+    if (error) {
+      RTCLogError("Error retrieving TURN servers: %@",
+                  error.localizedDescription);
+    }
+    ARDAppClient *strongSelf = weakSelf;
+    [strongSelf.iceServers addObjectsFromArray:turnServers];
+    strongSelf.isTurnComplete = YES;
+    [strongSelf startSignalingIfReady];
+  }];
+
+  // Join room on room server.
+  [_roomServerClient joinRoomWithRoomId:roomId
+      completionHandler:^(ARDJoinResponse *response, NSError *error) {
+    ARDAppClient *strongSelf = weakSelf;
+    if (error) {
+      [strongSelf.delegate appClient:strongSelf didError:error];
+      return;
+    }
+    NSError *joinError =
+        [[strongSelf class] errorForJoinResultType:response.result];
+    if (joinError) {
+      RTCLogError(@"Failed to join room:%@ on room server.", roomId);
+      [strongSelf disconnect];
+      [strongSelf.delegate appClient:strongSelf didError:joinError];
+      return;
+    }
+    RTCLog(@"Joined room:%@ on room server.", roomId);
+    strongSelf.roomId = response.roomId;
+    strongSelf.clientId = response.clientId;
+    strongSelf.isInitiator = response.isInitiator;
+    for (ARDSignalingMessage *message in response.messages) {
+      if (message.type == kARDSignalingMessageTypeOffer ||
+          message.type == kARDSignalingMessageTypeAnswer) {
+        strongSelf.hasReceivedSdp = YES;
+        [strongSelf.messageQueue insertObject:message atIndex:0];
+      } else {
+        [strongSelf.messageQueue addObject:message];
+      }
+    }
+    strongSelf.webSocketURL = response.webSocketURL;
+    strongSelf.webSocketRestURL = response.webSocketRestURL;
+    [strongSelf registerWithColliderIfReady];
+    [strongSelf startSignalingIfReady];
+  }];
+}
+
+- (void)disconnect {
+  if (_state == kARDAppClientStateDisconnected) {
+    return;
+  }
+  if (self.hasJoinedRoomServerRoom) {
+    [_roomServerClient leaveRoomWithRoomId:_roomId
+                                  clientId:_clientId
+                         completionHandler:nil];
+  }
+  if (_channel) {
+    if (_channel.state == kARDSignalingChannelStateRegistered) {
+      // Tell the other client we're hanging up.
+      ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
+      [_channel sendMessage:byeMessage];
+    }
+    // Disconnect from collider.
+    _channel = nil;
+  }
+  _clientId = nil;
+  _roomId = nil;
+  _isInitiator = NO;
+  _hasReceivedSdp = NO;
+  _messageQueue = [NSMutableArray array];
+  _peerConnection = nil;
+  self.state = kARDAppClientStateDisconnected;
+}
+
+#pragma mark - ARDSignalingChannelDelegate
+
+- (void)channel:(id<ARDSignalingChannel>)channel
+    didReceiveMessage:(ARDSignalingMessage *)message {
+  switch (message.type) {
+    case kARDSignalingMessageTypeOffer:
+    case kARDSignalingMessageTypeAnswer:
+      // Offers and answers must be processed before any other message, so we
+      // place them at the front of the queue.
+      _hasReceivedSdp = YES;
+      [_messageQueue insertObject:message atIndex:0];
+      break;
+    case kARDSignalingMessageTypeCandidate:
+      [_messageQueue addObject:message];
+      break;
+    case kARDSignalingMessageTypeBye:
+      // Disconnects can be processed immediately.
+      [self processSignalingMessage:message];
+      return;
+  }
+  [self drainMessageQueueIfReady];
+}
+
+- (void)channel:(id<ARDSignalingChannel>)channel
+    didChangeState:(ARDSignalingChannelState)state {
+  switch (state) {
+    case kARDSignalingChannelStateOpen:
+      break;
+    case kARDSignalingChannelStateRegistered:
+      break;
+    case kARDSignalingChannelStateClosed:
+    case kARDSignalingChannelStateError:
+      // TODO(tkchin): reconnection scenarios. Right now we just disconnect
+      // completely if the websocket connection fails.
+      [self disconnect];
+      break;
+  }
+}
+
+#pragma mark - RTCPeerConnectionDelegate
+// Callbacks for this delegate occur on non-main thread and need to be
+// dispatched back to main queue as needed.
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+    signalingStateChanged:(RTCSignalingState)stateChanged {
+  RTCLog(@"Signaling state changed: %d", stateChanged);
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+           addedStream:(RTCMediaStream *)stream {
+  dispatch_async(dispatch_get_main_queue(), ^{
+    RTCLog(@"Received %lu video tracks and %lu audio tracks",
+        (unsigned long)stream.videoTracks.count,
+        (unsigned long)stream.audioTracks.count);
+    if (stream.videoTracks.count) {
+      RTCVideoTrack *videoTrack = stream.videoTracks[0];
+      [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
+    }
+  });
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+        removedStream:(RTCMediaStream *)stream {
+  RTCLog(@"Stream was removed.");
+}
+
+- (void)peerConnectionOnRenegotiationNeeded:
+    (RTCPeerConnection *)peerConnection {
+  RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+    iceConnectionChanged:(RTCICEConnectionState)newState {
+  RTCLog(@"ICE state changed: %d", newState);
+  dispatch_async(dispatch_get_main_queue(), ^{
+    [_delegate appClient:self didChangeConnectionState:newState];
+  });
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+    iceGatheringChanged:(RTCICEGatheringState)newState {
+  RTCLog(@"ICE gathering state changed: %d", newState);
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+       gotICECandidate:(RTCICECandidate *)candidate {
+  dispatch_async(dispatch_get_main_queue(), ^{
+    ARDICECandidateMessage *message =
+        [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
+    [self sendSignalingMessage:message];
+  });
+}
+
+- (void)peerConnection:(RTCPeerConnection*)peerConnection
+    didOpenDataChannel:(RTCDataChannel*)dataChannel {
+}
+
+#pragma mark - RTCSessionDescriptionDelegate
+// Callbacks for this delegate occur on non-main thread and need to be
+// dispatched back to main queue as needed.
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+    didCreateSessionDescription:(RTCSessionDescription *)sdp
+                          error:(NSError *)error {
+  dispatch_async(dispatch_get_main_queue(), ^{
+    if (error) {
+      RTCLogError(@"Failed to create session description. Error: %@", error);
+      [self disconnect];
+      NSDictionary *userInfo = @{
+        NSLocalizedDescriptionKey: @"Failed to create session description.",
+      };
+      NSError *sdpError =
+          [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                     code:kARDAppClientErrorCreateSDP
+                                 userInfo:userInfo];
+      [_delegate appClient:self didError:sdpError];
+      return;
+    }
+    // Prefer H264 if available.
+    RTCSessionDescription *sdpPreferringH264 =
+        [ARDSDPUtils descriptionForDescription:sdp
+                           preferredVideoCodec:@"H264"];
+    [_peerConnection setLocalDescriptionWithDelegate:self
+                                  sessionDescription:sdpPreferringH264];
+    ARDSessionDescriptionMessage *message =
+        [[ARDSessionDescriptionMessage alloc]
+            initWithDescription:sdpPreferringH264];
+    [self sendSignalingMessage:message];
+  });
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+    didSetSessionDescriptionWithError:(NSError *)error {
+  dispatch_async(dispatch_get_main_queue(), ^{
+    if (error) {
+      RTCLogError(@"Failed to set session description. Error: %@", error);
+      [self disconnect];
+      NSDictionary *userInfo = @{
+        NSLocalizedDescriptionKey: @"Failed to set session description.",
+      };
+      NSError *sdpError =
+          [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                     code:kARDAppClientErrorSetSDP
+                                 userInfo:userInfo];
+      [_delegate appClient:self didError:sdpError];
+      return;
+    }
+    // If we're answering and we've just set the remote offer we need to create
+    // an answer and set the local description.
+    if (!_isInitiator && !_peerConnection.localDescription) {
+      RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
+      [_peerConnection createAnswerWithDelegate:self
+                                    constraints:constraints];
+
+    }
+  });
+}
+
+#pragma mark - Private
+
+- (BOOL)hasJoinedRoomServerRoom {
+  return _clientId.length;
+}
+
+// Begins the peer connection connection process if we have both joined a room
+// on the room server and tried to obtain a TURN server. Otherwise does nothing.
+// A peer connection object will be created with a stream that contains local
+// audio and video capture. If this client is the caller, an offer is created as
+// well, otherwise the client will wait for an offer to arrive.
+- (void)startSignalingIfReady {
+  if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
+    return;
+  }
+  self.state = kARDAppClientStateConnected;
+
+  // Create peer connection.
+  RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
+  RTCConfiguration *config = [[RTCConfiguration alloc] init];
+  config.iceServers = _iceServers;
+  _peerConnection = [_factory peerConnectionWithConfiguration:config
+                                                  constraints:constraints
+                                                     delegate:self];
+  // Create AV media stream and add it to the peer connection.
+  RTCMediaStream *localStream = [self createLocalMediaStream];
+  [_peerConnection addStream:localStream];
+  if (_isInitiator) {
+    // Send offer.
+    [_peerConnection createOfferWithDelegate:self
+                                 constraints:[self defaultOfferConstraints]];
+  } else {
+    // Check if we've received an offer.
+    [self drainMessageQueueIfReady];
+  }
+}
+
+// Processes the messages that we've received from the room server and the
+// signaling channel. The offer or answer message must be processed before other
+// signaling messages, however they can arrive out of order. Hence, this method
+// only processes pending messages if there is a peer connection object and
+// if we have received either an offer or answer.
+- (void)drainMessageQueueIfReady {
+  if (!_peerConnection || !_hasReceivedSdp) {
+    return;
+  }
+  for (ARDSignalingMessage *message in _messageQueue) {
+    [self processSignalingMessage:message];
+  }
+  [_messageQueue removeAllObjects];
+}
+
+// Processes the given signaling message based on its type.
+- (void)processSignalingMessage:(ARDSignalingMessage *)message {
+  NSParameterAssert(_peerConnection ||
+      message.type == kARDSignalingMessageTypeBye);
+  switch (message.type) {
+    case kARDSignalingMessageTypeOffer:
+    case kARDSignalingMessageTypeAnswer: {
+      ARDSessionDescriptionMessage *sdpMessage =
+          (ARDSessionDescriptionMessage *)message;
+      RTCSessionDescription *description = sdpMessage.sessionDescription;
+      // Prefer H264 if available.
+      RTCSessionDescription *sdpPreferringH264 =
+          [ARDSDPUtils descriptionForDescription:description
+                             preferredVideoCodec:@"H264"];
+      [_peerConnection setRemoteDescriptionWithDelegate:self
+                                     sessionDescription:sdpPreferringH264];
+      break;
+    }
+    case kARDSignalingMessageTypeCandidate: {
+      ARDICECandidateMessage *candidateMessage =
+          (ARDICECandidateMessage *)message;
+      [_peerConnection addICECandidate:candidateMessage.candidate];
+      break;
+    }
+    case kARDSignalingMessageTypeBye:
+      // Other client disconnected.
+      // TODO(tkchin): support waiting in room for next client. For now just
+      // disconnect.
+      [self disconnect];
+      break;
+  }
+}
+
+// Sends a signaling message to the other client. The caller will send messages
+// through the room server, whereas the callee will send messages over the
+// signaling channel.
+- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
+  if (_isInitiator) {
+    __weak ARDAppClient *weakSelf = self;
+    [_roomServerClient sendMessage:message
+                         forRoomId:_roomId
+                          clientId:_clientId
+                 completionHandler:^(ARDMessageResponse *response,
+                                     NSError *error) {
+      ARDAppClient *strongSelf = weakSelf;
+      if (error) {
+        [strongSelf.delegate appClient:strongSelf didError:error];
+        return;
+      }
+      NSError *messageError =
+          [[strongSelf class] errorForMessageResultType:response.result];
+      if (messageError) {
+        [strongSelf.delegate appClient:strongSelf didError:messageError];
+        return;
+      }
+    }];
+  } else {
+    [_channel sendMessage:message];
+  }
+}
+
+- (RTCMediaStream *)createLocalMediaStream {
+  RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
+  RTCVideoTrack* localVideoTrack = [self createLocalVideoTrack];
+  if (localVideoTrack) {
+    [localStream addVideoTrack:localVideoTrack];
+    [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
+  }
+  [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
+  return localStream;
+}
+
+- (RTCVideoTrack *)createLocalVideoTrack {
+  RTCVideoTrack* localVideoTrack = nil;
+  // The iOS simulator doesn't provide any sort of camera capture
+  // support or emulation (http://goo.gl/rHAnC1) so don't bother
+  // trying to open a local stream.
+  // TODO(tkchin): local video capture for OSX. See
+  // https://code.google.com/p/webrtc/issues/detail?id=3417.
+#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
+  RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
+  RTCAVFoundationVideoSource *source =
+      [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
+                                              constraints:mediaConstraints];
+  localVideoTrack =
+      [[RTCVideoTrack alloc] initWithFactory:_factory
+                                      source:source
+                                     trackId:@"ARDAMSv0"];
+#endif
+  return localVideoTrack;
+}
+
+#pragma mark - Collider methods
+
+- (void)registerWithColliderIfReady {
+  if (!self.hasJoinedRoomServerRoom) {
+    return;
+  }
+  // Open WebSocket connection.
+  if (!_channel) {
+    _channel =
+        [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
+                                         restURL:_websocketRestURL
+                                        delegate:self];
+  }
+  [_channel registerForRoomId:_roomId clientId:_clientId];
+}
+
+#pragma mark - Defaults
+
+- (RTCMediaConstraints *)defaultMediaStreamConstraints {
+  RTCMediaConstraints* constraints =
+      [[RTCMediaConstraints alloc]
+          initWithMandatoryConstraints:nil
+                   optionalConstraints:nil];
+  return constraints;
+}
+
+- (RTCMediaConstraints *)defaultAnswerConstraints {
+  return [self defaultOfferConstraints];
+}
+
+- (RTCMediaConstraints *)defaultOfferConstraints {
+  NSArray *mandatoryConstraints = @[
+      [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
+      [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
+  ];
+  RTCMediaConstraints* constraints =
+      [[RTCMediaConstraints alloc]
+          initWithMandatoryConstraints:mandatoryConstraints
+                   optionalConstraints:nil];
+  return constraints;
+}
+
+- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
+  if (_defaultPeerConnectionConstraints) {
+    return _defaultPeerConnectionConstraints;
+  }
+  NSArray *optionalConstraints = @[
+      [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
+  ];
+  RTCMediaConstraints* constraints =
+      [[RTCMediaConstraints alloc]
+          initWithMandatoryConstraints:nil
+                   optionalConstraints:optionalConstraints];
+  return constraints;
+}
+
+- (RTCICEServer *)defaultSTUNServer {
+  NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
+  return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
+                                  username:@""
+                                  password:@""];
+}
+
+#pragma mark - Errors
+
++ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
+  NSError *error = nil;
+  switch (resultType) {
+    case kARDJoinResultTypeSuccess:
+      break;
+    case kARDJoinResultTypeUnknown: {
+      error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                         code:kARDAppClientErrorUnknown
+                                     userInfo:@{
+        NSLocalizedDescriptionKey: @"Unknown error.",
+      }];
+      break;
+    }
+    case kARDJoinResultTypeFull: {
+      error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                         code:kARDAppClientErrorRoomFull
+                                     userInfo:@{
+        NSLocalizedDescriptionKey: @"Room is full.",
+      }];
+      break;
+    }
+  }
+  return error;
+}
+
++ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
+  NSError *error = nil;
+  switch (resultType) {
+    case kARDMessageResultTypeSuccess:
+      break;
+    case kARDMessageResultTypeUnknown:
+      error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                         code:kARDAppClientErrorUnknown
+                                     userInfo:@{
+        NSLocalizedDescriptionKey: @"Unknown error.",
+      }];
+      break;
+    case kARDMessageResultTypeInvalidClient:
+      error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                         code:kARDAppClientErrorInvalidClient
+                                     userInfo:@{
+        NSLocalizedDescriptionKey: @"Invalid client.",
+      }];
+      break;
+    case kARDMessageResultTypeInvalidRoom:
+      error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+                                         code:kARDAppClientErrorInvalidRoom
+                                     userInfo:@{
+        NSLocalizedDescriptionKey: @"Invalid room.",
+      }];
+      break;
+  }
+  return error;
+}
+
+@end