Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Fixes

- Measure Session Replay's 1h recording cap and its touch-move debounce on a monotonic clock, so a device time change no longer stops a healthy recording early, keeps a finished one alive, or suppresses gesture events ([#6090](https://github.com/getsentry/sentry-java/pull/6090))
- Session Replay: Use a software canvas for video encoding on the Android Emulator to avoid a native crash in `eglDestroySurface` ([#6282](https://github.com/getsentry/sentry-java/pull/6282))

## 8.61.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,8 @@ internal class SimpleVideoEncoder(
}

fun encode(image: Bitmap) {
/**
* it seems that Xiaomi devices have problems with hardware canvas, so we have to use lockCanvas
* instead https://stackoverflow.com/a/73520742
* ---
* Same for Motorola devices.
* ---
* As for the Spreadtrum/Unisoc chipset, it can be spread across various devices, so we have to
* check the SOC_MANUFACTURER property, as the manufacturer name might have changed.
* https://github.com/getsentry/sentry-android-gradle-plugin/issues/861#issuecomment-2867021256
*/
val canvas =
if (
Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) ||
Build.MANUFACTURER.contains("motorola", ignoreCase = true) ||
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
.equals("spreadtrum", ignoreCase = true) ||
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
.equals("unisoc", ignoreCase = true)
) {
if (shouldUseSoftwareCanvas()) {
surface?.lockCanvas(null)
} else {
surface?.lockHardwareCanvas()
Expand Down Expand Up @@ -338,6 +321,34 @@ internal class SimpleVideoEncoder(
}
}

/**
* Whether frames should be drawn onto the encoder's input surface with a software canvas
* ([Surface.lockCanvas]) instead of a hardware one ([Surface.lockHardwareCanvas]).
*
* It seems that Xiaomi devices have problems with hardware canvas, so we have to use lockCanvas
* instead https://stackoverflow.com/a/73520742
* ---
* Same for Motorola devices.
* ---
* As for the Spreadtrum/Unisoc chipset, it can be spread across various devices, so we have to
* check the SOC_MANUFACTURER property, as the manufacturer name might have changed.
* https://github.com/getsentry/sentry-android-gradle-plugin/issues/861#issuecomment-2867021256
* ---
* On the Android Emulator (goldfish/ranchu), a hardware canvas makes hwui own an EGL window surface
* on the input surface. The emulator's EGL driver keeps a dequeued buffer on it and cancels that
* buffer when the surface is destroyed, which can crash natively in eglDestroySurface once the
* encoder has been torn down. https://github.com/getsentry/sentry-java/issues/6279
*/
internal fun shouldUseSoftwareCanvas(): Boolean =
Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) ||
Build.MANUFACTURER.contains("motorola", ignoreCase = true) ||
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
.equals("spreadtrum", ignoreCase = true) ||
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
.equals("unisoc", ignoreCase = true) ||
Build.HARDWARE.contains("goldfish", ignoreCase = true) ||
Build.HARDWARE.contains("ranchu", ignoreCase = true)

@SuppressLint("UseRequiresApi")
@TargetApi(24)
internal data class MuxerConfig(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.sentry.android.replay.video

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.shadows.ShadowBuild

@RunWith(AndroidJUnit4::class)
@Config(sdk = [30])
class SimpleVideoEncoderCanvasTest {

@Test
fun `uses hardware canvas on regular devices`() {
ShadowBuild.setManufacturer("Google")
ShadowBuild.setHardware("tensor")

assertThat(shouldUseSoftwareCanvas()).isFalse()
}

@Test
fun `uses software canvas on Xiaomi devices`() {
ShadowBuild.setManufacturer("Xiaomi")

assertThat(shouldUseSoftwareCanvas()).isTrue()
}

@Test
fun `uses software canvas on Motorola devices`() {
ShadowBuild.setManufacturer("motorola")

assertThat(shouldUseSoftwareCanvas()).isTrue()
}

@Test
fun `uses software canvas on ranchu emulators`() {
ShadowBuild.setManufacturer("Google")
ShadowBuild.setHardware("ranchu")

assertThat(shouldUseSoftwareCanvas()).isTrue()
}

@Test
fun `uses software canvas on goldfish emulators`() {
ShadowBuild.setManufacturer("unknown")
ShadowBuild.setHardware("goldfish")

assertThat(shouldUseSoftwareCanvas()).isTrue()
}
}
Loading