package com.getsentry.raven.android;
import android.Manifest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
import com.getsentry.raven.DefaultRavenFactory;
import com.getsentry.raven.RavenFactory;
import com.getsentry.raven.dsn.Dsn;
import com.getsentry.raven.event.Event;
import com.getsentry.raven.event.EventBuilder;
/**
* Android specific class to interface with Raven. Supplements the default Java classes
* with Android specific state and features.
*/
public final class Raven {
/**
* Logger tag.
*/
public static final String TAG = Raven.class.getName();
private static volatile com.getsentry.raven.Raven raven;
/**
* Hide constructor.
*/
private Raven() {
}
/**
* Initialize Raven using a DSN set in the AndroidManifest.
*
* @param ctx Android application ctx
*/
public static void init(Context ctx) {
init(ctx, getDefaultRavenFactory(ctx));
}
/**
* Initialize Raven using a DSN set in the AndroidManifest.
*
* @param ctx Android application ctx
* @param ravenFactory the RavenFactory to be used to generate the Raven instance
*/
public static void init(Context ctx, AndroidRavenFactory ravenFactory) {
ctx = ctx.getApplicationContext();
String dsn = "";
// attempt to get DSN from AndroidManifest
ApplicationInfo appInfo = null;
try {
PackageManager packageManager = ctx.getPackageManager();
appInfo = packageManager.getApplicationInfo(ctx.getPackageName(), PackageManager.GET_META_DATA);
dsn = appInfo.metaData.getString("com.getsentry.raven.android.DSN");
} catch (PackageManager.NameNotFoundException e) {
// skip
}
if (TextUtils.isEmpty(dsn)) {
throw new NullPointerException("Raven DSN is not set, you must provide it via"
+ "the constructor or AndroidManifest.");
}
init(ctx, dsn, ravenFactory);
}
/**
* Initialize Raven using a string DSN.
*
* @param ctx Android application ctx
* @param dsn Sentry DSN string
*/
public static void init(Context ctx, String dsn) {
init(ctx, new Dsn(dsn), getDefaultRavenFactory(ctx));
}
/**
* Initialize Raven using a string DSN.
*
* @param ctx Android application ctx
* @param dsn Sentry DSN string
* @param ravenFactory the RavenFactory to be used to generate the Raven instance
*/
public static void init(Context ctx, String dsn, AndroidRavenFactory ravenFactory) {
init(ctx, new Dsn(dsn), ravenFactory);
}
/**
* Initialize Raven using a DSN object.
*
* @param ctx Android application ctx
* @param dsn Sentry DSN object
*/
public static void init(Context ctx, Dsn dsn) {
init(ctx, dsn, getDefaultRavenFactory(ctx));
}
/**
* Initialize Raven using a DSN object. This is the 'main' initializer that other methods
* eventually call.
*
* @param ctx Android application ctx
* @param dsn Sentry DSN object
* @param ravenFactory the RavenFactory to be used to generate the Raven instance
*/
public static void init(Context ctx, Dsn dsn, AndroidRavenFactory ravenFactory) {
if (raven != null) {
Log.e(TAG, "Initializing Raven multiple times.");
// cleanup existing connections
raven.closeConnection();
}
// Ensure we have the application context
Context context = ctx.getApplicationContext();
if (!Util.checkPermission(context, Manifest.permission.INTERNET)) {
Log.e(TAG, Manifest.permission.INTERNET + " is required to connect to the Sentry server,"
+ " please add it to your AndroidManifest.xml");
}
Log.d(TAG, "Raven init with ctx='" + ctx.toString() + "' and dsn='" + dsn + "'");
String protocol = dsn.getProtocol();
if (!(protocol.equalsIgnoreCase("http") || protocol.equalsIgnoreCase("https"))) {
throw new IllegalArgumentException("Only 'http' or 'https' connections are supported in"
+ " Raven Android, but received: " + protocol);
}
if ("false".equalsIgnoreCase(dsn.getOptions().get(DefaultRavenFactory.ASYNC_OPTION))) {
throw new IllegalArgumentException("Raven Android cannot use synchronous connections, remove '"
+ DefaultRavenFactory.ASYNC_OPTION + "=false' from your DSN.");
}
RavenFactory.registerFactory(ravenFactory);
raven = RavenFactory.ravenInstance(dsn);
setupUncaughtExceptionHandler();
}
private static AndroidRavenFactory getDefaultRavenFactory(Context ctx) {
return new AndroidRavenFactory(ctx);
}
/**
* Configures an Android uncaught exception handler which sends events to
* Sentry, then calls the preexisting uncaught exception handler.
*/
private static void setupUncaughtExceptionHandler() {
Thread.UncaughtExceptionHandler currentHandler = Thread.getDefaultUncaughtExceptionHandler();
if (currentHandler != null) {
Log.d(TAG, "default UncaughtExceptionHandler class='" + currentHandler.getClass().getName() + "'");
}
// don't double register
if (!(currentHandler instanceof RavenUncaughtExceptionHandler)) {
// register as default exception handler
Thread.setDefaultUncaughtExceptionHandler(
new RavenUncaughtExceptionHandler(currentHandler));
}
}
/**
* Send an Event using the statically stored Raven instance.
*
* @param event Event to send to the Sentry server
*/
public static void capture(Event event) {
raven.sendEvent(event);
}
/**
* Sends an exception (or throwable) to the Sentry server using the statically stored Raven instance.
* <p>
* The exception will be logged at the {@link Event.Level#ERROR} level.
*
* @param throwable exception to send to Sentry.
*/
public static void capture(Throwable throwable) {
raven.sendException(throwable);
}
/**
* Sends a message to the Sentry server using the statically stored Raven instance.
* <p>
* The message will be logged at the {@link Event.Level#INFO} level.
*
* @param message message to send to Sentry.
*/
public static void capture(String message) {
raven.sendMessage(message);
}
/**
* Builds and sends an {@link Event} to the Sentry server using the statically stored Raven instance.
*
* @param eventBuilder {@link EventBuilder} to send to Sentry.
*/
public static void capture(EventBuilder eventBuilder) {
raven.sendEvent(eventBuilder);
}
/**
* Clear statically stored Raven instance. Useful for tests.
*/
public static void clearStoredRaven() {
raven = null;
}
}