android: handle ConnectionService failures more resiliently

Some Samsung devices will fail to fully engage ConnectionService if no SIM card
was ever installed on the device. We could check for it, but it would require
the CALL_PHONE permission, which is not something we want to do, so fallback to
not using ConnectionService.
This commit is contained in:
Saúl Ibarra Corretgé 2019-12-03 10:02:56 +01:00 committed by Saúl Ibarra Corretgé
parent 1aaaae24ee
commit a622a4c713
3 changed files with 59 additions and 20 deletions

View File

@ -123,14 +123,17 @@ public class ConnectionService extends android.telecom.ConnectionService {
* {@link android.telecom.Connection#STATE_ACTIVE}.
*
* @param callUUID the call UUID which identifies the connection.
* @return Whether the connection was set as active or not.
*/
static void setConnectionActive(String callUUID) {
static boolean setConnectionActive(String callUUID) {
ConnectionImpl connection = connections.get(callUUID);
if (connection != null) {
connection.setActive();
return true;
} else {
JitsiMeetLogger.e("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
JitsiMeetLogger.w("%s setConnectionActive - no connection for UUID: %s", TAG, callUUID);
return false;
}
}

View File

@ -158,8 +158,11 @@ class RNConnectionService extends ReactContextBaseJavaModule {
@ReactMethod
public void reportConnectedOutgoingCall(String callUUID, Promise promise) {
JitsiMeetLogger.d(TAG + " reportConnectedOutgoingCall " + callUUID);
ConnectionService.setConnectionActive(callUUID);
promise.resolve(null);
if (ConnectionService.setConnectionActive(callUUID)) {
promise.resolve(null);
} else {
promise.reject("CONNECTION_NOT_FOUND_ERROR", "Connection wasn't found.");
}
}
@Override

View File

@ -186,13 +186,25 @@ function _conferenceJoined({ getState }, next, action) {
const { callUUID } = action.conference;
if (callUUID) {
CallIntegration.reportConnectedOutgoingCall(callUUID).then(() => {
// iOS 13 doesn't like the mute state to be false before the call is started
// so we update it here in case the user selected startWithAudioMuted.
if (Platform.OS === 'ios') {
_updateCallIntegrationMuted(action.conference, getState());
}
});
CallIntegration.reportConnectedOutgoingCall(callUUID)
.then(() => {
// iOS 13 doesn't like the mute state to be false before the call is started
// so we update it here in case the user selected startWithAudioMuted.
if (Platform.OS === 'ios') {
_updateCallIntegrationMuted(action.conference, getState());
}
})
.catch(error => {
// Currently this error code is emitted only by Android.
//
if (error.code === 'CONNECTION_NOT_FOUND_ERROR') {
// Some Samsung devices will fail to fully engage ConnectionService if no SIM card
// was ever installed on the device. We could check for it, but it would require
// the CALL_PHONE permission, which is not something we want to do, so fallback to
// not using ConnectionService.
_handleConnectionServiceFailure(getState());
}
});
}
return result;
@ -294,22 +306,43 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) {
{ cancelable: false });
} else if (error.code === 'SECURITY_ERROR') {
// Some devices fail because the CALL_PHONE permission is not granted, which is
// nonsense, because it's not needed for self-managed connections. Alas, this also
// means audio device management would be broken, so fallback to not using ConnectionService.
// NOTE: We are not storing this in Settings, in case it's a transient issue, as far fetched as
// that may be.
if (AudioMode.setUseConnectionService) {
AudioMode.setUseConnectionService(false);
// nonsense, because it's not needed for self-managed connections.
// Set the desired audio mode, since we just reset the whole thing.
AudioMode.setMode(hasVideo ? AudioMode.VIDEO_CALL : AudioMode.AUDIO_CALL);
}
_handleConnectionServiceFailure(state);
}
});
return result;
}
/**
* Handles a ConnectionService fatal error by falling back to non-ConnectionService device management.
*
* @param {Object} state - Redux store.
* @returns {void}
*/
function _handleConnectionServiceFailure(state: Object) {
const conference = getCurrentConference(state);
if (conference) {
// We're not tracking the call anymore.
delete conference.callUUID;
// ConnectionService has fatally failed. Alas, this also means audio device management would be broken, so
// fallback to not using ConnectionService.
// NOTE: We are not storing this in Settings, in case it's a transient issue, as far fetched as
// that may be.
if (AudioMode.setUseConnectionService) {
AudioMode.setUseConnectionService(false);
const hasVideo = !isVideoMutedByAudioOnly(state);
// Set the desired audio mode, since we just reset the whole thing.
AudioMode.setMode(hasVideo ? AudioMode.VIDEO_CALL : AudioMode.AUDIO_CALL);
}
}
}
/**
* Handles CallKit's event {@code performEndCallAction}.
*