Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable the configuration of the uncaught exception handler #73

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.support.annotation.Nullable;
import android.util.Log;

import com.ibm.mobilefirstplatform.clientsdk.android.analytics.internal.BMSAnalytics;
Expand All @@ -39,8 +40,6 @@
import java.io.UnsupportedEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.WeakHashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -194,8 +193,6 @@ public final class LogPersister {
private static Logger.LEVEL level = null;
// we keep a global java.util.logging.Handler to capture third-party stuff:
private static JULHandler julHandler = new JULHandler();
// don't set up the static UncaughtExceptionHandler until after we have a context
private static UncaughtExceptionHandler uncaughtExceptionHandler = null;
// Track instances so we give back the same one for the same logger name passed to getInstance method.
// We use a WeakHashMap because some instances in this map may go out of scope
// very soon after instantiation, thus no reason to keep a strong reference, and let
Expand Down Expand Up @@ -240,25 +237,36 @@ public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
static private FileLoggerInterface fileLoggerInstance;

// log application crash stack traces. This handler is registered after we have the context object in Logger.setContext(Context)
private static class UncaughtExceptionHandler implements Thread.UncaughtExceptionHandler
{
private final Thread.UncaughtExceptionHandler defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
private static class UncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

@Nullable
private Thread.UncaughtExceptionHandler original;

public UncaughtExceptionHandler(@Nullable Thread.UncaughtExceptionHandler original) {
this.original = original;
}

@Nullable
public Thread.UncaughtExceptionHandler getOriginal() {
return original;
}

@Override
public final void uncaughtException(final Thread t, final Throwable e)
{
public final void uncaughtException(final Thread t, final Throwable e) {
// place a marker that indicates for next run that a crash was caught:
if (null != context) {
context.getSharedPreferences (SHARED_PREF_KEY, Context.MODE_PRIVATE).edit ().putBoolean (SHARED_PREF_KEY_CRASH_DETECTED, true).commit();
context.getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE).edit().putBoolean(SHARED_PREF_KEY_CRASH_DETECTED, true).commit();
}
// log it to file:
Logger logger = Logger.getLogger(this.getClass().getName());
logger.fatal ("Uncaught Exception", e);
logger.fatal("Uncaught Exception", e);

MFPAnalyticsActivityLifecycleListener.getInstance().logAppCrash();

// allow it to pass through:
defaultUEH.uncaughtException(t, e);
if (original != null) {
original.uncaughtException(t, e);
}
}
}

Expand All @@ -273,8 +281,8 @@ protected static void unsetContext() {
analyticsCapture = null;
logFileMaxSize = null;
level = null;
uncaughtExceptionHandler = null;
fileLoggerInstance = null;
installUncaughtExceptionHandler(false);
LogManager.getLogManager().getLogger("").removeHandler(julHandler);
}

Expand Down Expand Up @@ -323,8 +331,7 @@ static public void setContext(final Context context) {
setCaptureSync(prefs.getBoolean (SHARED_PREF_KEY_logPersistence, DEFAULT_capture));
}

uncaughtExceptionHandler = new UncaughtExceptionHandler ();
Thread.setDefaultUncaughtExceptionHandler (uncaughtExceptionHandler);
installUncaughtExceptionHandler(true);
}
}

Expand Down Expand Up @@ -412,6 +419,29 @@ static public void storeLogs(final boolean shouldStoreLogs) {
});
}

/**
* Disables the internal uncaught exception handler, so uncaught exceptions are not reported
* to the Analytics backend.
*/
static public void disableUncaughtExceptionHandler() {
installUncaughtExceptionHandler(false);
}

static synchronized private void installUncaughtExceptionHandler(final boolean install) {
// The uncaught exception handler needs the context to operate properly, so
// we postpone its configuration until this is set
if (null == context) return;

Thread.UncaughtExceptionHandler current = Thread.getDefaultUncaughtExceptionHandler();
if (install && !(current instanceof UncaughtExceptionHandler)) {
UncaughtExceptionHandler wrapped = new UncaughtExceptionHandler(current);
Thread.setDefaultUncaughtExceptionHandler(wrapped);
} else if (!install && (current instanceof UncaughtExceptionHandler)) {
Thread.UncaughtExceptionHandler original = ((UncaughtExceptionHandler) current).getOriginal();
Thread.setDefaultUncaughtExceptionHandler(original);
}
}

static synchronized private void setCaptureSync(final boolean capture) {
// to avoid thread deadlocks, we have this method that can be called within a thread that is already on the work queue
LogPersister.capture = capture;
Expand Down