From 58bd48c1ae2c4407dea0d55279056391634965de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= <saghul@jitsi.org>
Date: Wed, 27 Nov 2019 12:08:54 +0100
Subject: [PATCH] android: disable ConnectionService if permissions are not
 granted

Some devices seem to have a bug in their Android versions and startCall fails
with SecurityError because the CALL_PHONE permissions is not granted. This is
not a requirement for self-managed connection services as per the official
documentation though:
https://developer.android.com/guide/topics/connectivity/telecom/selfManaged

Alas, connection services takes over audio device management too, so let's
handle the error and disable CS if we get SecurityError.
---
 .../jitsi/meet/sdk/RNConnectionService.java    |  6 +++++-
 .../mobile/call-integration/middleware.js      | 18 ++++++++++++++++--
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/RNConnectionService.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/RNConnectionService.java
index 464a543ff..9b0e248c2 100644
--- a/android/sdk/src/main/java/org/jitsi/meet/sdk/RNConnectionService.java
+++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/RNConnectionService.java
@@ -116,7 +116,11 @@ class RNConnectionService extends ReactContextBaseJavaModule {
                 tm.unregisterPhoneAccount(accountHandle);
             }
             ConnectionService.unregisterStartCallPromise(callUUID);
-            promise.reject(e);
+            if (e instanceof SecurityException) {
+                promise.reject("SECURITY_ERROR", "Required permissions not granted.");
+            } else {
+                promise.reject(e);
+            }
         }
     }
 
diff --git a/react/features/mobile/call-integration/middleware.js b/react/features/mobile/call-integration/middleware.js
index 101fde9fb..19dca9115 100644
--- a/react/features/mobile/call-integration/middleware.js
+++ b/react/features/mobile/call-integration/middleware.js
@@ -1,6 +1,6 @@
 // @flow
 
-import { Alert, Platform } from 'react-native';
+import { Alert, NativeModules, Platform } from 'react-native';
 import uuid from 'uuid';
 
 import { createTrackMutedEvent, sendAnalytics } from '../../analytics';
@@ -36,6 +36,7 @@ import CallKit from './CallKit';
 import ConnectionService from './ConnectionService';
 import { isCallIntegrationEnabled } from './functions';
 
+const { AudioMode } = NativeModules;
 const CallIntegration = CallKit || ConnectionService;
 
 /**
@@ -276,7 +277,8 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) {
             }
         })
         .catch(error => {
-            // Currently this error code is emitted only by Android.
+            // Currently this error codes are emitted only by Android.
+            //
             if (error.code === 'CREATE_OUTGOING_CALL_FAILED') {
                 // We're not tracking the call anymore - it doesn't exist on
                 // the native side.
@@ -290,6 +292,18 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) {
                         { text: 'OK' }
                     ],
                     { 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);
+
+                    // Set the desired audio mode, since we just reset the whole thing.
+                    AudioMode.setMode(hasVideo ? AudioMode.VIDEO_CALL : AudioMode.AUDIO_CALL);
+                }
             }
         });