android: add a uncaught exception handler

Its main task is to cleanup conferences (specially the connection services
stuff) to make sure the system is left in a working state even when the
unexpected happens.
This commit is contained in:
Saúl Ibarra Corretgé 2019-06-19 16:51:52 +02:00 committed by Saúl Ibarra Corretgé
parent bf67a4a675
commit 803870ef8f
4 changed files with 81 additions and 0 deletions

View File

@ -29,6 +29,7 @@ import com.facebook.react.bridge.ReadableMap;
import com.rnimmersive.RNImmersiveModule; import com.rnimmersive.RNImmersiveModule;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -77,6 +78,15 @@ public abstract class BaseReactView<ListenerT>
return null; return null;
} }
/**
* Gets all registered React views.
*
* @return An {@link ArrayList} containing all views currently held by React.
*/
static ArrayList<BaseReactView> getViews() {
return new ArrayList<>(views);
}
/** /**
* The unique identifier of this {@code BaseReactView} within the process * The unique identifier of this {@code BaseReactView} within the process
* for the purposes of {@link ExternalAPIModule}. The name scope was * for the purposes of {@link ExternalAPIModule}. The name scope was

View File

@ -63,6 +63,16 @@ public class ConnectionService extends android.telecom.ConnectionService {
static private final HashMap<String, Promise> startCallPromises static private final HashMap<String, Promise> startCallPromises
= new HashMap<>(); = 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. * Adds {@link ConnectionImpl} to the list.
* *

View File

@ -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);
}
}
}

View File

@ -182,5 +182,8 @@ class ReactInstanceManagerHolder {
if (devSettings != null) { if (devSettings != null) {
devSettings.setBundleDeltasEnabled(false); devSettings.setBundleDeltasEnabled(false);
} }
// Register our uncaught exception handler.
JitsiMeetUncaughtExceptionHandler.register();
} }
} }