osu-framework icon indicating copy to clipboard operation
osu-framework copied to clipboard

Bring Android framework to .NET 6

Open huoyaoyuan opened this issue 2 years ago • 36 comments

  • [x] Requires https://github.com/ppy/osuTK/pull/75

Development requirement

Run dotnet workload install android to install the necessary SDK components (may require sudo). Can be changed to dotnet workload restore once there's no old projects (Xamarin) remaining. The default Android SDK version changed from 29 to 31. Xamarin toolchain in Visual Studio should be still required. I haven't tested if the managed components can be compiled without Xamarin installed.

The framework visual test works fine on my phone.

huoyaoyuan avatar Mar 26 '22 14:03 huoyaoyuan

image The constants are all correct on Windows. The video test failure shouldn't be my fault since I don't touch anything else.

huoyaoyuan avatar Mar 26 '22 15:03 huoyaoyuan

This will require 2 rounds of deployment: the first one to deploy updated NativeLibs, the second one to update android framework. The CI build accepting updated NativeLibs should not show warning XA4301. I tested it with local folder as nuget source.

Or, we can separate the NativeLibs change to another PR to validate it.

huoyaoyuan avatar Mar 26 '22 17:03 huoyaoyuan

Also, re: a267cf6 - it looks a bit weird to create an empty dir in a correct location just so RID fallback to linux doesn't happen. Is it viable to perhaps move binaries out of osu.Framework.Android to the nativelibs project? Seems like that'd be a cleaner resolution.

If that happens I'd much agree to splitting to a separate PR.

bdach avatar Mar 26 '22 18:03 bdach

Is it viable to perhaps move binaries out of osu.Framework.Android to the nativelibs project? Seems like that'd be a cleaner resolution.

Yes I've considered this option. The downside is to increase nuget restore size for desktop only development. Leaving for decision here.

huoyaoyuan avatar Mar 26 '22 18:03 huoyaoyuan

Crashes when returning from activity pause (eg. press home and open the game again) with InvalidFramebufferOperationExt. Only happens on debug build, release works fine.

Can repro on two devices, API 28 and 30. Are you able to reproduce?

2022-03-28 21:31:43.319 10374-10374/osu.Framework.Tests.Android E/AndroidRuntime: FATAL EXCEPTION: main
    Process: osu.Framework.Tests.Android, PID: 10374
    android.runtime.JavaProxyThrowable: osuTK.Graphics.GraphicsErrorException: InvalidFramebufferOperationExt
        at osuTK.Graphics.ES30.ErrorHelper.CheckErrors()
        at osuTK.Graphics.ES30.ErrorHelper.Dispose()
        at osuTK.Graphics.ES30.GL.Clear(ClearBufferMask mask)
        at osu.Framework.Graphics.OpenGL.GLWrapper.Clear(ClearInfo clearInfo)
        at osu.Framework.Graphics.OpenGL.GLWrapper.Reset(Vector2 size)
        at osu.Framework.Threading.DrawThread.OnInitialize()
        at osu.Framework.Threading.GameThread.Initialize(Boolean withThrottling)
        at osu.Framework.Platform.ThreadRunner.ensureCorrectExecutionMode()
        at osu.Framework.Platform.ThreadRunner.Start()
        at osu.Framework.Platform.GameHost.Resume()
        at osu.Framework.Android.AndroidGameActivity.OnRestart()
        at Android.App.Activity.n_OnRestart(IntPtr jnienv, IntPtr native__this)
        at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PP_V(_JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
        at crc64eb0f9b4bca91a892.AndroidGameActivity.n_onRestart(Native Method)
        at crc64eb0f9b4bca91a892.AndroidGameActivity.onRestart(AndroidGameActivity.java:61)
        at android.app.Instrumentation.callActivityOnRestart(Instrumentation.java:1445)
        at android.app.Activity.performRestart(Activity.java:8261)
        at android.app.ActivityThread.performRestartActivity(ActivityThread.java:5305)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:243)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2328)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:255)
        at android.app.ActivityThread.main(ActivityThread.java:8212)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:632)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1049)

Susko3 avatar Mar 28 '22 20:03 Susko3

No, it works fine on my phone, using framework visual tests in Debug. The device is Android 11.

Note: I didn't use the updated osuTK binary, but instead create a facade (mentioned in osuTK PR) to make it work. I don't think this can be a different though, but you can do a try for this method too.

huoyaoyuan avatar Mar 29 '22 07:03 huoyaoyuan

Seems the crashes are caused by running/building ppy.osuTK.NS20 in debug configuration. So it doesn't crash with your method.

In my original testing, I changed both ppy.osuTK.NS20 and ppy.osuTK.Android to use a local checkout (at your PR). With only ppy.osuTK.Android being a local checkout (and ppy.osuTK.NS20 using the same nuget package as master), it doesn't crash in debug.

Crashes with your method if using local checkout of both osuTK.NS20 and osuTK.Android (local checkout at master)

But this doesn't seem like a blocker for this PR, as osuTK is used in release configuration.

Susko3 avatar Mar 29 '22 11:03 Susko3

Going to block this until the osu!-side blocking issue is fixed. We typically keep osu! and osu!framework in sync and it's hard to say how long the blocked issue will take to be resolved (or even what the rollout timetable looks like for dotnet android).

smoogipoo avatar Mar 31 '22 04:03 smoogipoo

I've pushed out an osuTK build to nuget including https://github.com/ppy/osuTK/pull/75, so packages can be updated after packages are indexed (needs 30 minutes or so).

peppy avatar Mar 31 '22 04:03 peppy

After updating osuTK to net6 compiled version, the Realm issue disappears (tested with framework visual test), but another null pointer dereference appears when testing with full game, even before touching full game specific code. Aiming to identify it.

Edit: it's a stupid issue that I forgot to update common framework together with android framework. However, the Realms issue only reproduces for full game, not framework test. It's getting more and more complicated.

Now I've clarified the crash happens if any monoandroid reference exists. Identifying what to do at full game side.

huoyaoyuan avatar Apr 03 '22 12:04 huoyaoyuan

Highlight the TBD here: should we (try to) move android native assets into NativeLibs?

huoyaoyuan avatar Apr 03 '22 14:04 huoyaoyuan

probably best to leave as is in this pull and move them as a follow up even if it is decided to be the right direction.

bdach avatar Apr 03 '22 14:04 bdach

Confirmed with debugger and decompiled code that InvocationPointer is java_vm.

huoyaoyuan avatar Apr 08 '22 08:04 huoyaoyuan

I've attempted to test this today and am not sure where to start, because:

  • I can no longer debug any of the android projects. The option just isn't there, because I can't deploy projects in debug onto android:

    image

    The above screenshot has a bit of Polish but hopefully you get the point - the run button is supposed to be listing off all available devices to deploy to. It doesn't anymore if I'm not on a release config. And if I try to debug a release build, then it just doesn't work anymore, the debugger does not attach.

  • On both my test devices, I am seeing native crashes in logcat when attempting to run visual testts.

Device 1 (Samsung SM-J710F, api level 24):

04-10 14:08:36.421	Samsung SM-J710F	Error	31958	DEBUG	pid: 31910, tid: 31910, name: k.Tests.Android  >>> osu.Framework.Tests.Android <<<
04-10 14:08:36.421	Samsung SM-J710F	Error	31958	DEBUG	signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x3a127fd7
    r0 3a127fcb  r1 00000000  r2 00000000  r3 00000000
    r4 ffa2ff8c  r5 3a127fcb  r6 00000000  r7 ffa2ffd0
    r8 00000000  r9 f3bbce17  sl 00000043  fp 00000000
    ip f3f78b20  sp ffa2feb8  lr f3e387c1  pc f3bbfe64  cpsr 600e0030
04-10 14:08:36.425	Samsung SM-J710F	Error	31958	DEBUG	backtrace:
    #00 pc 000b0e64  /system/lib/libart.so (_ZN3art9ArtMethod23GetOatQuickMethodHeaderEj+19)
    #01 pc 003297bd  /system/lib/libart.so (_ZN3art12StackVisitor9WalkStackEb+116)
    #02 pc 003348d7  /system/lib/libart.so (_ZNK3art6Thread16GetCurrentMethodEPjb+54)
    #03 pc 00261975  /system/lib/libart.so (_ZN3art3JNI9FindClassEP7_JNIEnvPKc+1152)
    #04 pc 0002ad0b  /data/app/osu.Framework.Tests.Android-1/lib/arm/libmonodroid.so (java_interop_jnienv_find_class+24)
    #05 pc 00003f14  <anonymous:eae25000>

Device 2 (OnePlus KB2003, api 30):

04-10 14:13:24.124	OnePlus KB2003	Error	23722	DEBUG	pid: 28032, tid: 28032, name: k.Tests.Android  >>> osu.Framework.Tests.Android <<<
04-10 14:13:24.124	OnePlus KB2003	Error	23722	DEBUG	signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4
04-10 14:13:24.124	OnePlus KB2003	Error	23722	DEBUG	Cause: null pointer dereference
    x0  4060000000000030  x1  0000000000000000  x2  0000007fc0b93400  x3  0000000000000000
    x4  00000000132d73c0  x5  0000000013a80ea8  x6  4200000400000000  x7  4200000400000000
    x8  0000007fc0b8af60  x9  0000000000000000  x10 0000007fc0b8af60  x11 0000000040600000
    x12 0000000000000000  x13 0000000000000038  x14 0000000040600000  x15 40600000320f3016
    x16 0000000040600000  x17 fffffffffffffffe  x18 00000070952b4000  x19 0000007fc0b93640
    x20 0000000000000000  x21 0000007fc0b936b0  x22 0000007fc0b940f0  x23 0000007fc0b94050
    x24 0000006d9dd05e40  x25 0000006d9dd06210  x26 0000000000000000  x27 0000000000000030
    x28 0000007094646000  x29 0000007fc0b93590
    lr  0000006d9e14de54  sp  0000007fc0b933c0  pc  0000006d9e14dc48  pst 0000000000001000
04-10 14:13:24.170	OnePlus KB2003	Error	23722	DEBUG	backtrace:
      #00 pc 0000000000584c48  /apex/com.android.art/lib64/libart.so (void art::StackVisitor::WalkStack<(art::StackVisitor::CountTransitions)1>(bool)+136) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #01 pc 00000000005b9fb8  /apex/com.android.art/lib64/libart.so (void art::Thread::VisitRoots<false>(art::RootVisitor*)+1320) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #02 pc 0000000000268090  /apex/com.android.art/lib64/libart.so (art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::Run(art::Thread*)+308) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #03 pc 00000000005c4454  /apex/com.android.art/lib64/libart.so (art::ThreadList::FlipThreadRoots(art::Closure*, art::Closure*, art::gc::collector::GarbageCollector*, art::gc::GcPauseListener*)+1152) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #04 pc 0000000000254ff4  /apex/com.android.art/lib64/libart.so (art::gc::collector::ConcurrentCopying::FlipThreadRoots()+292) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #05 pc 00000000002536a0  /apex/com.android.art/lib64/libart.so (art::gc::collector::ConcurrentCopying::RunPhases()+1024) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #06 pc 000000000027700c  /apex/com.android.art/lib64/libart.so (art::gc::collector::GarbageCollector::Run(art::gc::GcCause, bool)+300) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #07 pc 0000000000293e80  /apex/com.android.art/lib64/libart.so (art::gc::Heap::CollectGarbageInternal(art::gc::collector::GcType, art::gc::GcCause, bool)+4144) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #08 pc 0000000000004f08  /apex/com.android.art/lib64/libopenjdkjvm.so (JVM_GC+164) (BuildId: ef162aca91c49e678fee75b29fa972fb)
      #09 pc 00000000000815fc  /apex/com.android.art/javalib/arm64/boot.oat (art_jni_trampoline+124) (BuildId: aece9284df80b1815bdaf34e52f290399c49da97)
      #10 pc 000000000009b620  /apex/com.android.art/javalib/arm64/boot.oat (java.lang.Runtime.gc+96) (BuildId: aece9284df80b1815bdaf34e52f290399c49da97)
      #11 pc 0000000000133564  /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #12 pc 00000000001a8a78  /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #13 pc 0000000000555598  /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeVirtualOrInterfaceWithVarArgs<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, std::__va_list)+468) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #14 pc 0000000000555738  /apex/com.android.art/lib64/libart.so (art::JValue art::InvokeVirtualOrInterfaceWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+92) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #15 pc 00000000003aba24  /apex/com.android.art/lib64/libart.so (art::JNI<false>::CallVoidMethodV(_JNIEnv*, _jobject*, _jmethodID*, std::__va_list)+660) (BuildId: e841be9816817e37b70ebf4a461a916e)
      #16 pc 00000000000283ac  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonodroid.so (_JNIEnv::CallVoidMethod(_jobject*, _jmethodID*, ...)+116) (BuildId: 6e521e11e0c16c91d944e41591484727b79a9f43)
      #17 pc 0000000000026be0  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonodroid.so (xamarin::android::internal::OSBridge::gc_cross_references(int, MonoGCBridgeSCC**, int, MonoGCBridgeXRef*)+224) (BuildId: 6e521e11e0c16c91d944e41591484727b79a9f43)
      #18 pc 00000000000d8030  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #19 pc 000000000011e0c8  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #20 pc 000000000011aff0  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #21 pc 000000000011abcc  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #22 pc 0000000000110074  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #23 pc 00000000000e3568  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #24 pc 00000000000a2950  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #25 pc 00000000000a51e8  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #26 pc 00000000001ecd04  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #27 pc 00000000001eb12c  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #28 pc 00000000001e9e24  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #29 pc 00000000002260a0  /data/app/~~QwSnIcJf7DGwRKt9OCBJBg==/osu.Framework.Tests.Android-ib8ldIfSgmF9U3iLixWhKA==/lib/arm64/libmonosgen-2.0.so (BuildId: 7310e0da105e448374721a35c0841ba1673fb87a)
      #30 pc 0000000000005ba8  <anonymous:6d9600a000>

I thought maybe it was the video stuff from the most recent commits, but it doesn't appear to be, still happens if I build a revision before that.

Test game does work, surprisingly, but I would very much like to be able to run visual tests on a device. Going to hold off on testing the game-side pull for now.

My setup is windows + visual studio 2022 + dotnet 6.0.102 with the android workload enabled.

bdach avatar Apr 10 '22 12:04 bdach

@bdach Since the workload is effectively preview, I'd suggest to update .NET SDK to latest first. I'm using SDK 6.0.201, and the version of Microsoft.Android.Sdk.Windows (located in C:\Program Files\dotnet\packs) is 31.0.200-preview.14.106.

I've installed the Xamarin workload in Visual Studio, and the deployment just works like before. Maybe restarting Visual Studio/rebuilding or whatever can update the deployment action.

Is your crash happening on startup? As a remainder, fully uninstall previous versions and do full rebuild may cause difference.

huoyaoyuan avatar Apr 10 '22 12:04 huoyaoyuan

Crash occurs immediately on startup yes. Uninstalled previous app versions from devices before.

bdach avatar Apr 10 '22 13:04 bdach

Hopefully you can bring debugger working. Try to do dotnet build and reopen VS or whatever. Since sample game is working, at least you should be able to step in until it crashes. Video related issues shouldn't throw until you touch the video decoder tests. You can also see if release build is working. The call stacks indicates something really bad. java_interop_jnienv_find_class looks like misconfigured reflection, and the second one like crash from GC. Anyway, updating .NET SDK may resolve issue in mono itself.

huoyaoyuan avatar Apr 10 '22 14:04 huoyaoyuan

I've jiggled everything I could think of (updated VS 2022, updated dotnet SDK, rebooted machine, cleaned solution), and looks like it helped for whatever reason, both issues are gone.

bdach avatar Apr 10 '22 17:04 bdach

We may need to fast forward this a bit, as android builds started inexplicably failing yesterday and this pull seems to fix it.

bdach avatar May 15 '22 13:05 bdach

Reminder: this requires deployment for Framework, Framework.Android and NativeLibs. After deploying NativeLibs, WarningAsError could be turned on for Android CI.

huoyaoyuan avatar May 16 '22 08:05 huoyaoyuan

Just to confirm, there are no longer any issues with this across both osu and osu-framework projects, in either debug or release mode configs?

smoogipoo avatar May 18 '22 02:05 smoogipoo

Confirmed following actions with latest commits of both PRs, in Debug and Release:

  • Run framework test, play video and audio
  • Login into production server
  • Download a beatmap with video, play and run autoplay

If the SSL issue with downlevel OS found by @bdach is no longer present, I can't see any issue now.

huoyaoyuan avatar May 18 '22 03:05 huoyaoyuan

Didn't find any issues last time I tested this.

bdach avatar May 18 '22 19:05 bdach

Shall we look at getting this in after the next framework release?

peppy avatar May 19 '22 05:05 peppy

With the interim android build fix merged I don't see any particular reason to hurry too much with these changes anymore. I don't have any more code/testing issues to point out at this juncture, so it all boils down as to when you get the time/space to figure out the tooling/deployment end issues (if any).

bdach avatar May 19 '22 19:05 bdach

Unfortunately my android test device will no longer boot (suffered from a bulging battery but totally conked it now). Will test with emulator to make sure the tooling works I guess.

peppy avatar Jul 08 '22 11:07 peppy

Using HttpClientHandler or SocketsHttpHandler was previously discussed. There were problems with both handlers on @bdach, @huoyaoyuan and my (Realme 6) test devices. Results were inconclusive and could change with different dotnet workload install android versions. I've done the following testing to get a conclusive answer of which to use.

I've tested osu! with a local framework checkout with a matrix of devices and configurations. Main focus was testing the difference of HttpClientHandler and SocketsHttpHandler on Release and Debug build configurations. Additionally, Release was tested with and without trimming.

HttpClientHandler or SocketsHttpHandler?

Changing HttpClientHandler (the current default in this PR) to SocketsHttpHandler (used on desktop net6.0):

diff --git a/osu.Framework/IO/Network/WebRequest.cs b/osu.Framework/IO/Network/WebRequest.cs
index 4fea6062b..f139ac6a7 100644
--- a/osu.Framework/IO/Network/WebRequest.cs
+++ b/osu.Framework/IO/Network/WebRequest.cs
@@ -154,7 +154,7 @@ private set
 #if NET6_0_OR_GREATER
             // SocketsHttpHandler causes crash in Android Debug, and seems to have compatibility issue on SSL
             // Use platform HTTP handler which is invoked by HttpClientHandler for better compatibility and app size
-            RuntimeInfo.IsMobile
+            RuntimeInfo.IsMobile && false
                 ? new HttpClientHandler
                 {
                     Credentials = CredentialCache.DefaultCredentials,

Trimming

By default, Android will build with trimming enabled. To disable trimming, the following was added to both osu.Framework.Android.props and osu.Android.props:

<PropertyGroup>
  <PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>

The data

Realme 6 (API 30) Release not trimmed Release trimmed Debug
HttpClientHandler works fine (!) login fails with MethodInfo nullref works fine
SocketsHttpHandler works fine works fine works fine
Samsung A02s (API 30) Release not trimmed Release trimmed Debug
HttpClientHandler works fine (!) login fails with MethodInfo nullref works fine
SocketsHttpHandler works fine works fine works fine
LG Q6 (API 27) Release not trimmed Release trimmed Debug
HttpClientHandler works fine (!) login fails with MethodInfo nullref works fine
SocketsHttpHandler (!) login fails with SSL error (!) login fails with SSL error (!) login fails with SSL error

network.log excerpts:

LG Q6, SocketsHttpHandler, all configurations:

2022-07-08 21:11:22 [verbose]: Request to https://osu.ppy.sh/oauth/token failed with System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
2022-07-08 21:11:22 [verbose]: ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
2022-07-08 21:11:22 [verbose]: ---> Interop+AndroidCrypto+SslException: Exception of type 'Interop+AndroidCrypto+SslException' was thrown.
2022-07-08 21:11:22 [verbose]: --- End of inner exception stack trace ---
2022-07-08 21:11:22 [verbose]: at System.Net.Security.SslStream.<ForceAuthenticationAsync>d__175`1[[System.Net.Security.AsyncReadWriteAdapter, System.Net.Security, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
2022-07-08 21:11:22 [verbose]: at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: --- End of inner exception stack trace ---
2022-07-08 21:11:22 [verbose]: at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(HttpRequestMessage request)
2022-07-08 21:11:22 [verbose]: at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.<WaitWithCancellationAsync>d__1[[System.Net.Http.HttpConnection, System.Net.Http, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]].MoveNext()
2022-07-08 21:11:22 [verbose]: at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
2022-07-08 21:11:22 [verbose]: at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
2022-07-08 21:11:22 [verbose]: at osu.Framework.IO.Network.WebRequest.internalPerform(CancellationToken cancellationToken).
2022-07-08 21:11:22 [verbose]: Login failed!

All devices, HttpClientHandler, Release trimmed:

2022-07-08 19:26:36 [verbose]: Request to https://osu.ppy.sh/oauth/token failed with System.ArgumentNullException: ArgumentNull_Generic Arg_ParamName_Name, method
2022-07-08 19:26:36 [verbose]: at System.Delegate.CreateDelegate(Type type, Object firstArgument, MethodInfo method, Boolean throwOnBindFailure, Boolean allowClosed)
2022-07-08 19:26:36 [verbose]: at System.Delegate.CreateDelegate(Type type, MethodInfo method, Boolean throwOnBindFailure)
2022-07-08 19:26:36 [verbose]: at System.Delegate.CreateDelegate(Type type, MethodInfo method)
2022-07-08 19:26:36 [verbose]: at Java.Interop.JavaConvert.GetJniHandleConverterForType(Type )
2022-07-08 19:26:36 [verbose]: at Java.Interop.JavaConvert.GetJniHandleConverter(Type )
2022-07-08 19:26:36 [verbose]: at Java.Interop.JavaConvert.FromJniHandle[IList`1](IntPtr , JniHandleOwnership , Boolean& )
2022-07-08 19:26:36 [verbose]: at Java.Interop.JavaConvert.FromJniHandle[IList`1](IntPtr , JniHandleOwnership )
2022-07-08 19:26:36 [verbose]: at Android.Runtime.JavaDictionary`2[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Collections.Generic.IList`1[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Get(String )
2022-07-08 19:26:36 [verbose]: at Android.Runtime.JavaDictionary`2[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Collections.Generic.IList`1[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Item(String )
2022-07-08 19:26:36 [verbose]: at Xamarin.Android.Net.AndroidMessageHandler.CopyHeaders(HttpURLConnection , HttpResponseMessage )
2022-07-08 19:26:36 [verbose]: at Xamarin.Android.Net.AndroidMessageHandler.DoProcessRequest(HttpRequestMessage , URL , HttpURLConnection , CancellationToken , RequestRedirectionState )
2022-07-08 19:26:36 [verbose]: at Xamarin.Android.Net.AndroidMessageHandler.SendAsync(HttpRequestMessage , CancellationToken )
2022-07-08 19:26:36 [verbose]: at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage , HttpCompletionOption , CancellationTokenSource , Boolean , CancellationTokenSource , CancellationToken )
2022-07-08 19:26:36 [verbose]: at osu.Framework.IO.Network.WebRequest.internalPerform(CancellationToken cancellationToken).
2022-07-08 19:26:36 [verbose]: Login failed!

Building methodology

  1. o!f commit: b198d30726520bd3e3e1c257dc591c944ba098d7
  2. osu! commit: 10269b7613b3f913f1dcf9c9d5059ad6b7cbd614
  3. Latest dotnet workload install android
    • (If I'm reading this right, it is version 6.0.6: Microsoft.AOT.win-x64.Cross.android-arm64.6.0.6.msi)
  4. Local checkout of o!f on osu.Android.slnf
  5. Clean osu! and osu!framework solutions
  6. Build osu.Framework.Android with the specified configuration
  • For Debug
    • Run osu! with the specified configuration
    • (debug apks created with Archive for Publish would crash on startup)
  • For Release
    • Right click on osu.Android and select Archive for Publish
    • Install .apks on tested device using adb install

Testing methodology

  1. Open osu!
  2. Log in
  3. Download a single beatmap from my profile / beatmap listing
  4. Play it
  5. Check that the results loaded / beatmap successfuly finished
  6. Put the game to background
  7. Repeat for the next version in the test matrix

For my own sanity, I played a different song each time :)

For the LG Q6, I only did steps 1.-3., phone was too laggy to play properly.

Conclusion

SocketsHttpHandler is used on desktop platforms, and works fine on modern Android versions (Android 11), but has problems with SSL on older version (Android 8.1). HttpClientHandler does not work with trimming enabled. Trimming is not currently enables on desktop (as far as I'm aware).

Going forward, trimming should be disabled in Android .props. HttpClientHandler should be kept for best compatibility. If using SocketsHttpHandler is beneficial, it could be used, but on modern Android versions only.

Concerns about SocketsHttpHandler increasing file size were brought up, but the file sizes don't really differ much. (Except debug, probably why the app crashed -- missing libraries.)

image

Closing question

Does (not) using SocketsHttpHandler have any effect on (future) use of websockets? (I've seen talk of using websockets for chat.)

Susko3 avatar Jul 08 '22 22:07 Susko3

I can login with the default trimming option in Release. I didn't build framework in Release though.

The trimming issue looks like https://github.com/ppy/osu/pull/17462/commits/9b50fa346ff2868e160e2f8f9cd669593a180f1f , which is already resolved newer SDK. @Susko3 can you confirm your android SDK version at dotnet\packs\Microsoft.Android.Runtime.32.android-arm64? Mine is 32.0.415. Can you run dotnet workload update to ensure you are at latest version?

If you decompile the assembly at 32.0.xxx\runtimes\android-arm64\lib\net6.0\Mono.Android.dll, can you see the trimmer annotation on the method? image

Or, does it work if you redo the workaround in that commit? I reverted it because it was fixed in SDK.

huoyaoyuan avatar Jul 09 '22 03:07 huoyaoyuan

I was on 31.0.200-preview.14.106. No amount of dotnet workload update and install would get me to version 32. Probably because I was using dotnet version 6.0.203. Downloading the newer version (6.0.301) now (will take some time, crappy internet).

Can confirm that the aforementioned method does not have the trimmer annotation.

This is really fucking stupid. Is there some way to block or show a warning/error to people (like me) who are using older versions.

Susko3 avatar Jul 09 '22 08:07 Susko3

Using global.json in the root of the repo works (errors on build). Using global.json in osu.Framework.Android folder does not work. iOS seems to be using global.json in the subfolder.

Specifying the exact SDK version in the .csproj is hit or miss, sometimes giving warnings, sometimes building fine:

diff --git a/osu.Framework.Android/osu.Framework.Android.csproj b/osu.Framework.Android/osu.Framework.Android.csproj
index 819f5306f..d6a6063fe 100644
--- a/osu.Framework.Android/osu.Framework.Android.csproj
+++ b/osu.Framework.Android/osu.Framework.Android.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk/6.0.301">
   <PropertyGroup Label="Project">
     <TargetFramework>net6.0-android</TargetFramework>
     <SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>

(I've done this testing with 6.0.203 installed only, I didn't have 6.0.301 installed.)

Susko3 avatar Jul 09 '22 08:07 Susko3