Skip to content

Commit

Permalink
Merge a83b5d9 into 2e90ac7
Browse files Browse the repository at this point in the history
  • Loading branch information
romtsn authored Jun 18, 2024
2 parents 2e90ac7 + a83b5d9 commit 547b3f8
Show file tree
Hide file tree
Showing 186 changed files with 10,938 additions and 419 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## Unreleased
## 7.11.0-alpha.2

- Session Replay for Android ([#3339](https://github.com/getsentry/sentry-java/pull/3339))

We released our third Alpha version of the SDK with support. To get access, it requires adding your Sentry org to our feature flag. Please let us know on the [waitlist](https://sentry.io/lp/mobile-replay-beta/) if you're interested

### Fixes

Expand Down
5 changes: 4 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ subprojects {
"sentry-android-ndk",
"sentry-android-okhttp",
"sentry-android-sqlite",
"sentry-android-replay",
"sentry-android-timber"
)
if (jacocoAndroidModules.contains(name)) {
Expand Down Expand Up @@ -296,7 +297,9 @@ private val androidLibs = setOf(
"sentry-android-navigation",
"sentry-android-okhttp",
"sentry-android-timber",
"sentry-compose-android"
"sentry-compose-android",
"sentry-android-sqlite",
"sentry-android-replay"
)

private val androidXLibs = listOf(
Expand Down
2 changes: 2 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object Config {

val minSdkVersion = 19
val minSdkVersionOkHttp = 21
val minSdkVersionReplay = 19
val minSdkVersionNdk = 19
val minSdkVersionCompose = 21
val targetSdkVersion = sdkVersion
Expand Down Expand Up @@ -194,6 +195,7 @@ object Config {
val jsonUnit = "net.javacrumbs.json-unit:json-unit:2.32.0"
val hsqldb = "org.hsqldb:hsqldb:2.6.1"
val javaFaker = "com.github.javafaker:javafaker:1.0.2"
val msgpack = "org.msgpack:msgpack-core:0.9.8"
}

object QualityPlugins {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android.useAndroidX=true
android.defaults.buildfeatures.buildconfig=true

# Release information
versionName=7.10.0
versionName=7.11.0-alpha.2

# Override the SDK name on native crashes on Android
sentryAndroidSdkName=sentry.native.android
Expand Down
2 changes: 2 additions & 0 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,11 @@ public final class io/sentry/android/core/CurrentActivityIntegration : android/a
public final class io/sentry/android/core/DeviceInfoUtil {
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;)V
public fun collectDeviceInformation (ZZ)Lio/sentry/protocol/Device;
public static fun getBatteryLevel (Landroid/content/Intent;Lio/sentry/SentryOptions;)Ljava/lang/Float;
public static fun getInstance (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;)Lio/sentry/android/core/DeviceInfoUtil;
public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem;
public fun getSideLoadedInfo ()Lio/sentry/android/core/ContextUtils$SideLoadedInfo;
public static fun isCharging (Landroid/content/Intent;Lio/sentry/SentryOptions;)Ljava/lang/Boolean;
public static fun resetInstance ()V
}

Expand Down
2 changes: 2 additions & 0 deletions sentry-android-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dependencies {
api(projects.sentry)
compileOnly(projects.sentryAndroidFragment)
compileOnly(projects.sentryAndroidTimber)
compileOnly(projects.sentryAndroidReplay)
compileOnly(projects.sentryCompose)
compileOnly(projects.sentryComposeHelper)

Expand Down Expand Up @@ -104,6 +105,7 @@ dependencies {
testImplementation(projects.sentryTestSupport)
testImplementation(projects.sentryAndroidFragment)
testImplementation(projects.sentryAndroidTimber)
testImplementation(projects.sentryAndroidReplay)
testImplementation(projects.sentryComposeHelper)
testImplementation(projects.sentryAndroidNdk)
testRuntimeOnly(Config.Libs.composeUi)
Expand Down
6 changes: 6 additions & 0 deletions sentry-android-core/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,9 @@
-keepnames class io.sentry.exception.SentryHttpClientException

##---------------End: proguard configuration for sentry-okhttp ----------

##---------------Begin: proguard configuration for sentry-android-replay ----------
-dontwarn io.sentry.android.replay.ReplayIntegration
-dontwarn io.sentry.android.replay.DefaultReplayBreadcrumbConverter
-keepnames class io.sentry.android.replay.ReplayIntegration
##---------------End: proguard configuration for sentry-android-replay ----------
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
import io.sentry.android.core.internal.util.SentryFrameMetricsCollector;
import io.sentry.android.core.performance.AppStartMetrics;
import io.sentry.android.fragment.FragmentLifecycleIntegration;
import io.sentry.android.replay.DefaultReplayBreadcrumbConverter;
import io.sentry.android.replay.ReplayIntegration;
import io.sentry.android.timber.SentryTimberIntegration;
import io.sentry.cache.PersistingOptionsObserver;
import io.sentry.cache.PersistingScopeObserver;
import io.sentry.compose.gestures.ComposeGestureTargetLocator;
import io.sentry.compose.viewhierarchy.ComposeViewHierarchyExporter;
import io.sentry.internal.gestures.GestureTargetLocator;
import io.sentry.internal.viewhierarchy.ViewHierarchyExporter;
import io.sentry.transport.CurrentDateProvider;
import io.sentry.transport.NoOpEnvelopeCache;
import io.sentry.util.LazyEvaluator;
import io.sentry.util.Objects;
Expand Down Expand Up @@ -237,7 +240,8 @@ static void installDefaultIntegrations(
final @NotNull LoadClass loadClass,
final @NotNull ActivityFramesTracker activityFramesTracker,
final boolean isFragmentAvailable,
final boolean isTimberAvailable) {
final boolean isTimberAvailable,
final boolean isReplayAvailable) {

// Integration MUST NOT cache option values in ctor, as they will be configured later by the
// user
Expand Down Expand Up @@ -302,6 +306,13 @@ static void installDefaultIntegrations(
new NetworkBreadcrumbsIntegration(context, buildInfoProvider, options.getLogger()));
options.addIntegration(new TempSensorBreadcrumbsIntegration(context));
options.addIntegration(new PhoneStateBreadcrumbsIntegration(context));
if (isReplayAvailable) {
final ReplayIntegration replay =
new ReplayIntegration(context, CurrentDateProvider.getInstance());
replay.setBreadcrumbConverter(new DefaultReplayBreadcrumbConverter());
options.addIntegration(replay);
options.setReplayController(replay);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.sentry.SentryBaseEvent;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.SentryReplayEvent;
import io.sentry.android.core.internal.util.AndroidMainThreadChecker;
import io.sentry.android.core.performance.AppStartMetrics;
import io.sentry.android.core.performance.TimeSpan;
Expand Down Expand Up @@ -303,4 +304,17 @@ private void setSideLoadedInfo(final @NotNull SentryBaseEvent event) {

return transaction;
}

@Override
public @NotNull SentryReplayEvent process(
final @NotNull SentryReplayEvent event, final @NotNull Hint hint) {
final boolean applyScopeData = shouldApplyScopeData(event, hint);
if (applyScopeData) {
processNonCachedEvent(event, hint);
}

setCommons(event, false, applyScopeData);

return event;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.util.DisplayMetrics;
import io.sentry.DateUtils;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.android.core.internal.util.CpuInfoUtils;
import io.sentry.android.core.internal.util.DeviceOrientations;
import io.sentry.android.core.internal.util.RootChecker;
Expand Down Expand Up @@ -184,8 +185,8 @@ public ContextUtils.SideLoadedInfo getSideLoadedInfo() {
private void setDeviceIO(final @NotNull Device device, final boolean includeDynamicData) {
final Intent batteryIntent = getBatteryIntent();
if (batteryIntent != null) {
device.setBatteryLevel(getBatteryLevel(batteryIntent));
device.setCharging(isCharging(batteryIntent));
device.setBatteryLevel(getBatteryLevel(batteryIntent, options));
device.setCharging(isCharging(batteryIntent, options));
device.setBatteryTemperature(getBatteryTemperature(batteryIntent));
}

Expand Down Expand Up @@ -270,7 +271,8 @@ private Intent getBatteryIntent() {
* @return the device's current battery level (as a percentage of total), or null if unknown
*/
@Nullable
private Float getBatteryLevel(final @NotNull Intent batteryIntent) {
public static Float getBatteryLevel(
final @NotNull Intent batteryIntent, final @NotNull SentryOptions options) {
try {
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Expand All @@ -294,7 +296,8 @@ private Float getBatteryLevel(final @NotNull Intent batteryIntent) {
* @return whether or not the device is currently plugged in and charging, or null if unknown
*/
@Nullable
private Boolean isCharging(final @NotNull Intent batteryIntent) {
public static Boolean isCharging(
final @NotNull Intent batteryIntent, final @NotNull SentryOptions options) {
try {
int plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return plugged == BatteryManager.BATTERY_PLUGGED_AC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.sentry.transport.ICurrentDateProvider;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -19,11 +20,12 @@
final class LifecycleWatcher implements DefaultLifecycleObserver {

private final AtomicLong lastUpdatedSession = new AtomicLong(0L);
private final AtomicBoolean isFreshSession = new AtomicBoolean(false);

private final long sessionIntervalMillis;

private @Nullable TimerTask timerTask;
private final @Nullable Timer timer;
private final @NotNull Timer timer = new Timer(true);
private final @NotNull Object timerLock = new Object();
private final @NotNull IHub hub;
private final boolean enableSessionTracking;
Expand Down Expand Up @@ -55,11 +57,6 @@ final class LifecycleWatcher implements DefaultLifecycleObserver {
this.enableAppLifecycleBreadcrumbs = enableAppLifecycleBreadcrumbs;
this.hub = hub;
this.currentDateProvider = currentDateProvider;
if (enableSessionTracking) {
timer = new Timer(true);
} else {
timer = null;
}
}

// App goes to foreground
Expand All @@ -74,41 +71,46 @@ public void onStart(final @NotNull LifecycleOwner owner) {
}

private void startSession() {
if (enableSessionTracking) {
cancelTask();
cancelTask();

final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();

hub.configureScope(
scope -> {
if (lastUpdatedSession.get() == 0L) {
final @Nullable Session currentSession = scope.getSession();
if (currentSession != null && currentSession.getStarted() != null) {
lastUpdatedSession.set(currentSession.getStarted().getTime());
}
hub.configureScope(
scope -> {
if (lastUpdatedSession.get() == 0L) {
final @Nullable Session currentSession = scope.getSession();
if (currentSession != null && currentSession.getStarted() != null) {
lastUpdatedSession.set(currentSession.getStarted().getTime());
isFreshSession.set(true);
}
});
}
});

final long lastUpdatedSession = this.lastUpdatedSession.get();
if (lastUpdatedSession == 0L
|| (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) {
final long lastUpdatedSession = this.lastUpdatedSession.get();
if (lastUpdatedSession == 0L
|| (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) {
if (enableSessionTracking) {
addSessionBreadcrumb("start");
hub.startSession();
}
this.lastUpdatedSession.set(currentTimeMillis);
hub.getOptions().getReplayController().start();
} else if (!isFreshSession.get()) {
// only resume if it's not a fresh session, which has been started in SentryAndroid.init
hub.getOptions().getReplayController().resume();
}
isFreshSession.set(false);
this.lastUpdatedSession.set(currentTimeMillis);
}

// App went to background and triggered this callback after 700ms
// as no new screen was shown
@Override
public void onStop(final @NotNull LifecycleOwner owner) {
if (enableSessionTracking) {
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
this.lastUpdatedSession.set(currentTimeMillis);
final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis();
this.lastUpdatedSession.set(currentTimeMillis);

scheduleEndSession();
}
hub.getOptions().getReplayController().pause();
scheduleEndSession();

AppState.getInstance().setInBackground(true);
addAppBreadcrumb("background");
Expand All @@ -122,8 +124,11 @@ private void scheduleEndSession() {
new TimerTask() {
@Override
public void run() {
addSessionBreadcrumb("end");
hub.endSession();
if (enableSessionTracking) {
addSessionBreadcrumb("end");
hub.endSession();
}
hub.getOptions().getReplayController().stop();
}
};

Expand Down Expand Up @@ -164,7 +169,7 @@ TimerTask getTimerTask() {
}

@TestOnly
@Nullable
@NotNull
Timer getTimer() {
return timer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ final class ManifestMetadataReader {

static final String ENABLE_METRICS = "io.sentry.enable-metrics";

static final String REPLAYS_SESSION_SAMPLE_RATE = "io.sentry.session-replay.session-sample-rate";

static final String REPLAYS_ERROR_SAMPLE_RATE = "io.sentry.session-replay.error-sample-rate";

static final String REPLAYS_REDACT_ALL_TEXT = "io.sentry.session-replay.redact-all-text";

static final String REPLAYS_REDACT_ALL_IMAGES = "io.sentry.session-replay.redact-all-images";

/** ManifestMetadataReader ctor */
private ManifestMetadataReader() {}

Expand Down Expand Up @@ -382,6 +390,41 @@ static void applyMetadata(

options.setEnableMetrics(
readBool(metadata, logger, ENABLE_METRICS, options.isEnableMetrics()));

if (options.getExperimental().getSessionReplay().getSessionSampleRate() == null) {
final Double sessionSampleRate =
readDouble(metadata, logger, REPLAYS_SESSION_SAMPLE_RATE);
if (sessionSampleRate != -1) {
options.getExperimental().getSessionReplay().setSessionSampleRate(sessionSampleRate);
}
}

if (options.getExperimental().getSessionReplay().getErrorSampleRate() == null) {
final Double errorSampleRate = readDouble(metadata, logger, REPLAYS_ERROR_SAMPLE_RATE);
if (errorSampleRate != -1) {
options.getExperimental().getSessionReplay().setErrorSampleRate(errorSampleRate);
}
}

options
.getExperimental()
.getSessionReplay()
.setRedactAllText(
readBool(
metadata,
logger,
REPLAYS_REDACT_ALL_TEXT,
options.getExperimental().getSessionReplay().getRedactAllText()));

options
.getExperimental()
.getSessionReplay()
.setRedactAllImages(
readBool(
metadata,
logger,
REPLAYS_REDACT_ALL_IMAGES,
options.getExperimental().getSessionReplay().getRedactAllImages()));
}

options
Expand Down
Loading

0 comments on commit 547b3f8

Please sign in to comment.