Skip to content

Commit a0ac761

Browse files
fix(replay): Use software canvas for the video encoder on the Android Emulator (#6282)
* fix(replay): Use software canvas for the video encoder on the Android Emulator Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io> * changelog Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io> Co-Authored-By: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Roman Zavarnitsyn <roman.zavarnitsyn@sentry.io>
1 parent b731c0b commit a0ac761

3 files changed

Lines changed: 81 additions & 18 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Android 7.1.0 (API 25) and older don't trust the Let's Encrypt root certificate (ISRG Root X1), so without this fix, the SDK on those devices can no longer send events to Sentry after the switch.
1111
- The SDK now bundles ISRG Root X1 and trusts it in addition to the device's root certificates on API 25 and lower. This only applies to the SDK's own uploads to Sentry SaaS (DSNs on `sentry.io`), not to self-hosted Sentry, not to any other connection your app makes, and not if you set your own `SentryOptions.setSslSocketFactory`.
1212
- Apps need to update to an SDK version with this fix to keep sending events from these devices.
13+
- 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))
1314

1415
### Internal
1516

‎sentry-android-replay/src/main/java/io/sentry/android/replay/video/SimpleVideoEncoder.kt‎

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -177,25 +177,8 @@ internal class SimpleVideoEncoder(
177177
}
178178

179179
fun encode(image: Bitmap) {
180-
/**
181-
* it seems that Xiaomi devices have problems with hardware canvas, so we have to use lockCanvas
182-
* instead https://stackoverflow.com/a/73520742
183-
* ---
184-
* Same for Motorola devices.
185-
* ---
186-
* As for the Spreadtrum/Unisoc chipset, it can be spread across various devices, so we have to
187-
* check the SOC_MANUFACTURER property, as the manufacturer name might have changed.
188-
* https://github.com/getsentry/sentry-android-gradle-plugin/issues/861#issuecomment-2867021256
189-
*/
190180
val canvas =
191-
if (
192-
Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) ||
193-
Build.MANUFACTURER.contains("motorola", ignoreCase = true) ||
194-
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
195-
.equals("spreadtrum", ignoreCase = true) ||
196-
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
197-
.equals("unisoc", ignoreCase = true)
198-
) {
181+
if (shouldUseSoftwareCanvas()) {
199182
surface?.lockCanvas(null)
200183
} else {
201184
surface?.lockHardwareCanvas()
@@ -338,6 +321,34 @@ internal class SimpleVideoEncoder(
338321
}
339322
}
340323

324+
/**
325+
* Whether frames should be drawn onto the encoder's input surface with a software canvas
326+
* ([Surface.lockCanvas]) instead of a hardware one ([Surface.lockHardwareCanvas]).
327+
*
328+
* It seems that Xiaomi devices have problems with hardware canvas, so we have to use lockCanvas
329+
* instead https://stackoverflow.com/a/73520742
330+
* ---
331+
* Same for Motorola devices.
332+
* ---
333+
* As for the Spreadtrum/Unisoc chipset, it can be spread across various devices, so we have to
334+
* check the SOC_MANUFACTURER property, as the manufacturer name might have changed.
335+
* https://github.com/getsentry/sentry-android-gradle-plugin/issues/861#issuecomment-2867021256
336+
* ---
337+
* On the Android Emulator (goldfish/ranchu), a hardware canvas makes hwui own an EGL window surface
338+
* on the input surface. The emulator's EGL driver keeps a dequeued buffer on it and cancels that
339+
* buffer when the surface is destroyed, which can crash natively in eglDestroySurface once the
340+
* encoder has been torn down. https://github.com/getsentry/sentry-java/issues/6279
341+
*/
342+
internal fun shouldUseSoftwareCanvas(): Boolean =
343+
Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) ||
344+
Build.MANUFACTURER.contains("motorola", ignoreCase = true) ||
345+
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
346+
.equals("spreadtrum", ignoreCase = true) ||
347+
SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER)
348+
.equals("unisoc", ignoreCase = true) ||
349+
Build.HARDWARE.contains("goldfish", ignoreCase = true) ||
350+
Build.HARDWARE.contains("ranchu", ignoreCase = true)
351+
341352
@SuppressLint("UseRequiresApi")
342353
@TargetApi(24)
343354
internal data class MuxerConfig(
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.sentry.android.replay.video
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import com.google.common.truth.Truth.assertThat
5+
import kotlin.test.Test
6+
import org.junit.runner.RunWith
7+
import org.robolectric.annotation.Config
8+
import org.robolectric.shadows.ShadowBuild
9+
10+
@RunWith(AndroidJUnit4::class)
11+
@Config(sdk = [30])
12+
class SimpleVideoEncoderCanvasTest {
13+
14+
@Test
15+
fun `uses hardware canvas on regular devices`() {
16+
ShadowBuild.setManufacturer("Google")
17+
ShadowBuild.setHardware("tensor")
18+
19+
assertThat(shouldUseSoftwareCanvas()).isFalse()
20+
}
21+
22+
@Test
23+
fun `uses software canvas on Xiaomi devices`() {
24+
ShadowBuild.setManufacturer("Xiaomi")
25+
26+
assertThat(shouldUseSoftwareCanvas()).isTrue()
27+
}
28+
29+
@Test
30+
fun `uses software canvas on Motorola devices`() {
31+
ShadowBuild.setManufacturer("motorola")
32+
33+
assertThat(shouldUseSoftwareCanvas()).isTrue()
34+
}
35+
36+
@Test
37+
fun `uses software canvas on ranchu emulators`() {
38+
ShadowBuild.setManufacturer("Google")
39+
ShadowBuild.setHardware("ranchu")
40+
41+
assertThat(shouldUseSoftwareCanvas()).isTrue()
42+
}
43+
44+
@Test
45+
fun `uses software canvas on goldfish emulators`() {
46+
ShadowBuild.setManufacturer("unknown")
47+
ShadowBuild.setHardware("goldfish")
48+
49+
assertThat(shouldUseSoftwareCanvas()).isTrue()
50+
}
51+
}

0 commit comments

Comments
 (0)