androidazureazure-communication-services

How do I specify who to call using an Azure Communication Service?


I'm working on building an Android app that uses an Azure Communication Service for video. I used this tutorial as a starting point, and made a number of changes. But in the tutorial, it has the user copy a User Access Token generated from the CLI or Portal. This token is input into the "Call ID" to start things off. However, if I have two people, each of which has a token that was generated via the Portal, how do I use that token for calling each other?

The code in the tutorial for starting a call is:

private void startCall() {
Context context = this.getApplicationContext();
EditText callIdView = findViewById(R.id.call_id);
String callId = callIdView.getText().toString();
ArrayList<CommunicationIdentifier> participants = new ArrayList<CommunicationIdentifier>();
List<VideoDeviceInfo> cameras = deviceManager.getCameras();


StartCallOptions options = new StartCallOptions();
if(!cameras.isEmpty()) {
    currentCamera = getNextAvailableCamera(null);
    currentVideoStream = new LocalVideoStream(currentCamera, context);
    LocalVideoStream[] videoStreams = new LocalVideoStream[1];
    videoStreams[0] = currentVideoStream;
    VideoOptions videoOptions = new VideoOptions(videoStreams);
    options.setVideoOptions(videoOptions);
    showPreview(currentVideoStream);
}
participants.add(new CommunicationUserIdentifier(callId));

call = callAgent.startCall(
        context,
        participants,
        options);

//Subscribe to events on updates of call state and remote participants
remoteParticipantUpdatedListener = this::handleRemoteParticipantsUpdate;
onStateChangedListener = this::handleCallOnStateChanged;
call.addOnRemoteParticipantsUpdatedListener(remoteParticipantUpdatedListener);
call.addOnStateChangedListener(onStateChangedListener);
}

I have modified this code as follows:

private void startCall(String whoCall, String whoCalled) {
    Context context = this.getApplicationContext();

    ArrayList<CommunicationIdentifier> participants = new ArrayList<CommunicationIdentifier>();
    List<VideoDeviceInfo> cameras = deviceManager.getCameras();

    StartCallOptions options = new StartCallOptions();
    if(!cameras.isEmpty()) {
        currentCamera = getNextAvailableCamera(null);
        currentVideoStream = new LocalVideoStream(currentCamera, context);
        LocalVideoStream[] videoStreams = new LocalVideoStream[1];
        videoStreams[0] = currentVideoStream;
        VideoOptions videoOptions = new VideoOptions(videoStreams);
        options.setVideoOptions(videoOptions);
        showPreview(currentVideoStream);
    }

    participants.add(new CommunicationUserIdentifier(whoCall));
    participants.add(new CommunicationUserIdentifier(whoCalled));

    call = callAgent.startCall(
            context,
            participants,
            options);

    //Subscribe to events on updates of call state and remote participants
    remoteParticipantUpdatedListener = this::handleRemoteParticipantsUpdate;
    onStateChangedListener = this::handleCallOnStateChanged;
    call.addOnRemoteParticipantsUpdatedListener(remoteParticipantUpdatedListener);
    call.addOnStateChangedListener(onStateChangedListener);

    callButton.setVisibility(View.INVISIBLE);
    funStuffButton.setVisibility((View.VISIBLE));
    hangupButton.setVisibility(View.VISIBLE);
}

Where 'whoCall' and 'whoCalled' are strings that are the two tokens from the Portal, and are set using radio buttons for who to call, the idea of which is to select one of the two radio buttons for who you want to call, which will set 'whoCall' to one token and 'whoCalled' to the other.

When I try using things this way, though, via the emulator on my computer and the APK on my phone, nothing actually connects. I think my problem is not understanding how the token from the Portal is used in the Android app to connect one user to another user.

Some thoughts I have are:

Any other thoughts anyone has are appreciated.


Solution

  • The issue is that you are directly passing the tokens (whoCall and whoCalled) as CommunicationUserIdentifier.

    Tokens cannot be used as identifiers for calling. Instead, you must use the user ID (which is associated with token) to set up the participants list.

    Firstly Tokens and User IDs Are Managed Correctly. Have a unique user identity It should be issued an access token associated with that identity.

    Update the startCall method to use the user IDs (id in the above example) as CommunicationUserIdentifier, not the raw tokens.

    private void startCall(String whoCallId, String whoCalledId) {
        Context context = this.getApplicationContext();
    
        ArrayList<CommunicationIdentifier> participants = new ArrayList<CommunicationIdentifier>();
        List<VideoDeviceInfo> cameras = deviceManager.getCameras();
    
        StartCallOptions options = new StartCallOptions();
        if (!cameras.isEmpty()) {
            currentCamera = getNextAvailableCamera(null);
            currentVideoStream = new LocalVideoStream(currentCamera, context);
            LocalVideoStream[] videoStreams = new LocalVideoStream[1];
            videoStreams[0] = currentVideoStream;
            VideoOptions videoOptions = new VideoOptions(videoStreams);
            options.setVideoOptions(videoOptions);
            showPreview(currentVideoStream);
        }
    
        // Use user IDs as CommunicationUserIdentifiers
        participants.add(new CommunicationUserIdentifier(whoCallId));
        participants.add(new CommunicationUserIdentifier(whoCalledId));
    
        call = callAgent.startCall(
                context,
                participants,
                options);
    
        // Subscribe to events on updates of call state and remote participants
        remoteParticipantUpdatedListener = this::handleRemoteParticipantsUpdate;
        onStateChangedListener = this::handleCallOnStateChanged;
        call.addOnRemoteParticipantsUpdatedListener(remoteParticipantUpdatedListener);
        call.addOnStateChangedListener(onStateChangedListener);
    
        callButton.setVisibility(View.INVISIBLE);
        funStuffButton.setVisibility((View.VISIBLE));
        hangupButton.setVisibility(View.VISIBLE);
    }
    

    Both users (caller and callee) need to authenticate with their access tokens to join the call.

    CommunicationTokenCredential credential = new CommunicationTokenCredential(accessToken);
    callAgent = new CallClient().createCallAgent(context, credential).get();
    

    Console output:

    [INFO] Initializing CallAgent with token.
    [DEBUG] CommunicationTokenCredential initialized successfully.
    [INFO] CallAgent created successfully for user: 8:acs:12345678-90ab-cdef-ghij-klmnopqrstuv
    [DEBUG] Device Manager initialized. Cameras detected: [Front Camera, Back Camera].
    [INFO] Starting call to user: 8:acs:abcdefgh-ijkl-mnop-qrst-uvwxyzabcdef
    [DEBUG] Adding participant: 8:acs:abcdefgh-ijkl-mnop-qrst-uvwxyzabcdef
    [INFO] Call state changed: CONNECTING
    [DEBUG] Local video stream enabled using Front Camera.
    [INFO] Call is active. Total participants: 2
    [DEBUG] Remote participant is speaking: true
    [INFO] HangUp initiated by the local user.
    [INFO] Call state changed: DISCONNECTED
    [ERROR] Failed to initialize CallAgent: Invalid access token.
    [ERROR] No internet connection detected. Cannot start call.
    [ERROR] Call state changed: DISCONNECTED due to Network Error.
    

    Update:

    Create a backend service to issue tokens programmatically using the Azure Communication Services SDK or REST API.

    // Create a Communication Identity Client
    CommunicationIdentityClient client = new CommunicationIdentityClientBuilder()
        .connectionString("<YOUR_CONNECTION_STRING>")
        .buildClient();
    
    // Generate a user and issue a token
    CommunicationUserIdentifier user = client.createUser();
    AccessToken token = client.getToken(user, Arrays.asList("voip"));
    
    // Save `user.getId()` and `token.getToken()` for the app