amazon-chime-sdk-ios icon indicating copy to clipboard operation
amazon-chime-sdk-ios copied to clipboard

Callkit integration Issue after ending normal phone call by caller.

Open ashirkhan94 opened this issue 1 year ago • 6 comments

Describe the bug We integrate the callkit in the react-native-demo app with the help of below link https://aws.amazon.com/blogs/business-productivity/how-to-integrate-apples-callkit-into-ios-applications-using-the-amazon-chime-sdk/ and everything is working fine except for one case that is when a meeting is going on with outgoing callkit call and we got an incoming phone call, we put the chime meeting in the hold and if we end the phone call from our side there is no issue rejoined on meeting, But if the phone call ended by the caller then the reconnection is not happening. we are still out of the meeting that is the issue

To Reproduce Steps to reproduce the behavior:

  1. setup the demo app for react native
  2. integrate call kit with above link like step 1: start the callkitcall step2: configure audiosession step3: start the meeting session step4: make normal phone call to the device wich running demo app with callkit and take the phone call and make call kit call on hold step5: end the phone call at caller side
  3. got the issue

code sample:

in NativeMobileSDKBridge.m

CXHandle *handle;
CXCallController *callController;
CXProvider *provider;
NSUUID *uuid;
BOOL isOnHold;

RCT_EXPORT_METHOD(startCallKitConfiguration)
{
  CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:@"Demo"];
  configuration.maximumCallGroups=1;
  configuration.maximumCallsPerCallGroup=1;
  configuration.supportsVideo = YES;
  configuration.supportedHandleTypes = [NSSet setWithObject:@(CXHandleTypeGeneric)];
  configuration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:@"callkit-icon"]);
  _provider = [[CXProvider alloc] initWithConfiguration:configuration];
    callObserver = [[CXCallObserver alloc] init]; 
  [callObserver setDelegate:self queue:dispatch_get_main_queue()];
  [_provider setDelegate:self queue:dispatch_get_main_queue()];
  [self startCall];
  
}

RCT_EXPORT_METHOD(startCall){
  handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:@"Demo"];
  _uuid = [NSUUID UUID];
  _isOnHold=false;
  CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:_uuid handle:handle];
  callController = [[CXCallController alloc] init];
  CXTransaction *transaction = [[CXTransaction alloc] initWithActions:@[startCallAction]];
  [callController requestTransaction:transaction completion:^(NSError * _Nullable error) {
    if (error) {
      NSLog(@"Call_KIT Error starting call: %@", error);
    } else {
      NSLog(@"Call_KIT started successfully");
    }
  }];
}


- (void)configureAudioSession {
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    NSError *error = nil;

    @try {
        if (audioSession.category != AVAudioSessionCategoryPlayAndRecord) {
            [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
                           withOptions:AVAudioSessionCategoryOptionAllowBluetooth
                                 error:&error];
        }
        if (audioSession.mode != AVAudioSessionModeVoiceChat) {
            [audioSession setMode:AVAudioSessionModeVoiceChat error:&error];
        }
    } @catch (NSException *exception) {
        NSLog(@"Error configuring AVAudioSession: %@", [error localizedDescription]);
    }
}

- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
  [self configureAudioSession];
  [action fulfill];
}

- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
  [meetingSession.audioVideo stop];
  [action fulfill];
}

- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action {
  if ([action isOnHold])
  {
    _isOnHold=true;
    [meetingSession.audioVideo stop];
  }
  [action fulfill];
}

- (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action {
  BOOL success = true;
  if ([action isMuted])
  {
    success=[meetingSession.audioVideo realtimeLocalMute];
  }
  else
  {
    success=[meetingSession.audioVideo realtimeLocalUnmute];
  }
  [action fulfill];
}

-(void)startAudioVideo
{
   NSError* error = nil;
   BOOL started = [meetingSession.audioVideo startWithCallKitEnabled:true error:&error];
   if (started && error == nil)
   {
     [logger infoWithMsg:@"RN meeting session was started successfully"];
     [meetingSession.audioVideo startRemoteVideo];
   }
   else
   {
     NSString *errorMsg = [NSString stringWithFormat:@"Failed to start meeting, error: %@", error.description];
     [logger errorWithMsg:errorMsg];
     // Handle missing permission error
     if ([error.domain isEqual:@"AmazonChimeSDK.PermissionError"])
     {
       AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
       if (permissionStatus == AVAudioSessionRecordPermissionUndetermined)
       {
         [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted)
         {
           if (granted)
           {
             [logger infoWithMsg:@"Audio permission granted"];
             // Retry after permission is granted
             [self startAudioVideo];
           }
           else
           {
             [logger infoWithMsg:@"Audio permission not granted"];
             [self sendEventWithName:kEventOnMeetingEnd body:nil];
           }
         }];
       }
       else if (permissionStatus == AVAudioSessionRecordPermissionDenied)
       {
         [logger errorWithMsg:@"User did not grant permission, should redirect to Settings"];
         [self sendEventWithName:kEventOnMeetingEnd body:nil];
       }
     }
     else
     {
       // Uncaught error
       [self sendEventWithName:kEventOnError body: errorMsg];
       [self sendEventWithName:kEventOnMeetingEnd body:nil];
     }
   }
}


- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession{
  NSLog(@"Call_KIT didActivateAudioSession successfully");
  @try {
    DefaultActiveSpeakerPolicy* defaultPolicy = [[DefaultActiveSpeakerPolicy alloc] init];
    observer = [[MeetingObservers alloc] initWithBridge:self logger:logger];
    [meetingSession.audioVideo addRealtimeObserverWithObserver:observer];
    [meetingSession.audioVideo addVideoTileObserverWithObserver:observer];
    [meetingSession.audioVideo addAudioVideoObserverWithObserver:observer];
    [meetingSession.audioVideo addDeviceChangeObserverWithObserver:observer];
    [meetingSession.audioVideo addRealtimeDataMessageObserverWithTopic:@"chat" observer:observer];
    [self startAudioVideo]; 
    [self configureActiveAudioDevice:[meetingSession.audioVideo listAudioDevices]];
  } @catch (NSException *exception) {
      NSLog(@"Exception: %@", exception.reason);
  }
};

In MeetingObservers.m file added

- (void)audioSessionDidStartConnectingWithReconnecting:(BOOL)reconnecting
{
  if(!reconnecting){
   [_bridge.provider reportOutgoingCallWithUUID:[_bridge uuid] startedConnectingAtDate:[NSDate date]];
  }
}

- (void)audioSessionDidStartWithReconnecting:(BOOL)reconnecting
{
  if (!reconnecting)
  {
    [_bridge.provider reportOutgoingCallWithUUID:[_bridge uuid] connectedAtDate:[NSDate date]];

    [_logger infoWithMsg:@"Meeting Started!"];
    [_bridge sendEventWithName:kEventOnMeetingStart body:nil];
  }
}

- (void)audioSessionDidStopWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
{
  NSLog(@"Call_KIT audioSessionTest----DidStop SessionStatus: %u" ,[sessionStatus statusCode]);
  
  switch ([sessionStatus statusCode]) {
        case 0:
            // If the call is on hold, do not exit the meeting
            if (_bridge.isOnHold) {
              NSLog(@"Call_KIT audioSessionTest 0");
                return;
            }
            break;
    case 75:NSLog(@"Call_KIT audioSessionTest 75");
        case 60:
      NSLog(@"Call_KIT audioSessionTest 60");
      [_bridge.provider reportCallWithUUID:[_bridge uuid] endedAtDate:[NSDate date] reason:CXCallEndedReasonRemoteEnded];
      
            break;
        case 61:
      NSLog(@"Call_KIT audioSessionTest 61");
      [_bridge.provider reportCallWithUUID:[_bridge uuid] endedAtDate:[NSDate date] reason:CXCallEndedReasonAnsweredElsewhere];
      
            break;
        case 69:
      NSLog(@"Call_KIT audioSessionTest 69");
      [_bridge.provider reportCallWithUUID:[_bridge uuid] endedAtDate:[NSDate date] reason:CXCallEndedReasonDeclinedElsewhere];
     
            break;
        default:
      NSLog(@"Call_KIT audioSessionTest default");
      [_bridge.provider reportCallWithUUID:[_bridge uuid] endedAtDate:[NSDate date]         reason:CXCallEndedReasonFailed];
     
            break;
    }
}

 **// Here we only get `case 0`**

Expected behavior if the phone call ended by the caller then the reconnection should happen

Test environment Info (please complete the following information):

  • Device: iPhone 11
  • OS: iOS 16.6
  • Version AmazonChimeSDK: 0.22.3
  • Version AmazonChimeSDKMedia: 0.17.7

Pls take a look Thanks

ashirkhan94 avatar Jan 02 '24 11:01 ashirkhan94