diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java index 2704e92e3..b71a364b9 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/BaseReactView.java @@ -29,6 +29,7 @@ import com.facebook.react.bridge.ReadableMap; import com.rnimmersive.RNImmersiveModule; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -77,6 +78,15 @@ public abstract class BaseReactView return null; } + /** + * Gets all registered React views. + * + * @return An {@link ArrayList} containing all views currently held by React. + */ + static ArrayList getViews() { + return new ArrayList<>(views); + } + /** * The unique identifier of this {@code BaseReactView} within the process * for the purposes of {@link ExternalAPIModule}. The name scope was diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/ConnectionService.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/ConnectionService.java index 48e27d95c..ccda3429a 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/ConnectionService.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/ConnectionService.java @@ -63,6 +63,16 @@ public class ConnectionService extends android.telecom.ConnectionService { static private final HashMap startCallPromises = new HashMap<>(); + /** + * Aborts all ongoing connections. This is a last resort mechanism which forces all resources to + * be freed on the system in case of fatal error. + */ + static void abortConnections() { + for (ConnectionImpl connection: getConnections()) { + connection.onAbort(); + } + } + /** * Adds {@link ConnectionImpl} to the list. * diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUncaughtExceptionHandler.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUncaughtExceptionHandler.java new file mode 100644 index 000000000..d1298cd1c --- /dev/null +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUncaughtExceptionHandler.java @@ -0,0 +1,58 @@ +/* + * Copyright @ 2018-present 8x8, Inc. + * Copyright @ 2017-2018 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jitsi.meet.sdk; + +import android.util.Log; + +class JitsiMeetUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { + private final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler; + + public static void register() { + Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); + + JitsiMeetUncaughtExceptionHandler uncaughtExceptionHandler + = new JitsiMeetUncaughtExceptionHandler(defaultUncaughtExceptionHandler); + + Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler); + } + + private JitsiMeetUncaughtExceptionHandler(Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler) { + this.defaultUncaughtExceptionHandler = defaultUncaughtExceptionHandler; + } + + @Override + public void uncaughtException(Thread t, Throwable e) { + Log.e(this.getClass().getSimpleName(), "FATAL ERROR", e); + + // Terminate all conferences + for (BaseReactView view: BaseReactView.getViews()) { + if (view instanceof JitsiMeetView) { + ((JitsiMeetView) view).leave(); + } + } + + // Abort all ConnectionService ongoing calls + if (AudioModeModule.useConnectionService()) { + ConnectionService.abortConnections(); + } + + if (defaultUncaughtExceptionHandler != null) { + defaultUncaughtExceptionHandler.uncaughtException(t, e); + } + } +} diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java index 8c3c1ce45..0bc2175d4 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java @@ -182,5 +182,8 @@ class ReactInstanceManagerHolder { if (devSettings != null) { devSettings.setBundleDeltasEnabled(false); } + + // Register our uncaught exception handler. + JitsiMeetUncaughtExceptionHandler.register(); } }