I'm trying to create a call app using Android's ConnectionService. I can successfully receive calls, but I'm trying to pass application specific data through the "extras" parameter in TelecomManager.addNewIncomingCall. When I'm actually creating the Connection object in my ConnectionService class, however, I can't find where to access the Bundle that I set and passed in.
When I check the incoming parameters in onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request), neither connectionManagerPhoneAccount.getExtras() or request.getExtras() are the extras that I passed into TelecomManager.addNewIncomingCall.
Does anyone have experience setting and retrieving this extra object after passing it into TelecomManager.addNewIncomingCall?
I'm trying to pass the phone number that's calling into the Connection object so I can properly resolve the caller ID from the device's contact and display it.
Code:
package com.twilio.voice.quickstart;
...
public class VoiceActivity extends AppCompatActivity {
@RequiresApi(api = Build.VERSION_CODES.M)
private void handleIncomingCall() {
Log.d(TAG, "handleIncomingCall() triggered");
Bundle extras = new Bundle();
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, activeCallInvite.getFrom(), null);
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri);
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle);
extras.putParcelable(Constants.INCOMING_CALL_INVITE, activeCallInvite);
telecomManager.addNewIncomingCall(handle, extras);
}
package com.twilio.voice.quickstart;
@RequiresApi(api = Build.VERSION_CODES.M)
public class VoiceConnectionService extends ConnectionService {
private static final String TAG = "VoiceConnectionService";
private static Connection connection;
public static Connection getConnection() {
return connection;
}
public static void deinitConnection() {
connection = null;
}
@Override
public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
//Not sure how to get the extra information to rebuild the twilio call invitation back up..
Log.i(TAG, "Incoming Connection Request, came with extras: " + request.getExtras());
// CallInvite activeCallInvite = intent.getParcelableExtra(Constants.INCOMING_CALL_INVITE);
Connection incomingCallConnection = createConnection(request);
incomingCallConnection.setRinging();
return incomingCallConnection;
}
@Override
public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
Connection outgoingCallConnection = createConnection(request);
outgoingCallConnection.setDialing();
return outgoingCallConnection;
}
private Connection createConnection(ConnectionRequest request) {
connection = new Connection() {
@Override
public void onStateChanged(int state) { //Is this where we can get the phone number & set the caller id?
Log.i(TAG, "Connection Request: " + request.toString());
if (state == Connection.STATE_DIALING) {
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
// sendCallRequestToActivity(ACTION_OUTGOING_CALL); //I think this is getting triggered as an extra thing....
}
});
}
}
@Override
public void onCallAudioStateChanged(CallAudioState state) {
Log.d(TAG, "onCallAudioStateChanged called, current state is " + state);
}
@Override
public void onPlayDtmfTone(char c) {
Log.d(TAG, "onPlayDtmfTone called with DTMF " + c);
Bundle extras = new Bundle();
extras.putString(DTMF, Character.toString(c));
connection.setExtras(extras);
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
sendCallRequestToActivity(ACTION_DTMF_SEND);
}
});
}
@Override
public void onDisconnect() {
super.onDisconnect();
connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
connection.destroy();
connection = null;
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
sendCallRequestToActivity(ACTION_DISCONNECT_CALL);
}
});
}
@Override
public void onSeparate() {
super.onSeparate();
}
@Override
public void onAbort() {
super.onAbort();
connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
connection.destroy();
}
@Override
public void onAnswer() {
super.onAnswer();
Log.i(TAG, "Connection was answered!");
connection.setActive();
final Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
sendCallRequestToActivity(ACTION_ANSWER_CALL);
}
});
}
@Override
public void onReject() {
super.onReject();
connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
connection.destroy();
}
@Override
public void onPostDialContinue(boolean proceed) {
super.onPostDialContinue(true);
}
};
//How to get the phone number, and resolve it to a contact/address?
//https://developer.android.com/reference/android/telecom/Connection
Log.i(TAG, "Creating the connection object for the incoming call, here are the extras: " + request.getExtras() + "; " + connection.getExtras());
connection.setConnectionCapabilities(Connection.CAPABILITY_MUTE);
if (request.getExtras().getString(CALLEE) == null) {
connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
} else {
connection.setAddress(Uri.parse(request.getExtras().getString(CALLEE)), TelecomManager.PRESENTATION_ALLOWED);
}
//both returning null
// Log.i(TAG, "CALLEE: " + request.getExtras().getString(CALLEE));
// Log.i(TAG, "CALLER: " + request.getExtras().getString(CALLER));
// connection.setDialing();
connection.setExtras(request.getExtras()); //This is where we want to connect..?
//How can we get the caller Id...?
connection.setCallerDisplayName("Example Contact.", TelecomManager.PRESENTATION_ALLOWED);
return connection;
}
/*
* Send call request to the VoiceConnectionServiceActivity
*/
private void sendCallRequestToActivity(String action) {
Log.i(TAG, "Transmitting broadcast from VoiceConnectionService to Voice Activity with action: " + action);
Intent intent = new Intent(action);
Bundle extras = new Bundle();
switch (action) {
case ACTION_OUTGOING_CALL:
Uri address = connection.getAddress();
extras.putString(OUTGOING_CALL_ADDRESS, address.toString());
intent.putExtras(extras);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
break;
case ACTION_DISCONNECT_CALL:
extras.putInt("Reason", DisconnectCause.LOCAL);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(extras);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
break;
case ACTION_DTMF_SEND:
String d = connection.getExtras().getString(DTMF);
extras.putString(DTMF, connection.getExtras().getString(DTMF));
intent.putExtras(extras);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
break;
case ACTION_ANSWER_CALL:
// Uri address2 = connection.getAddress();
// extras.putString(OUTGOING_CALL_ADDRESS, address2.toString());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtras(extras);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
break;
default:
break;
}
}
}
I once had the same issue, and it took quite a while to discover the solution.
The key was to store my own bundle within the key
EXTRA_INCOMING_CALL_EXTRAS
, similar to the recommendation to useEXTRA_OUTGOING_CALL_EXTRAS
for the outgoing call case.