Technically, it's true, as far as I am aware. Android has low level functions which themselves then directly call Linux, but you are not allowed to make a direct Linux call.
In the Android-y part of Android, yes, they use ART, which is abstracted.
But, they also give app authors the NDK. How do you think Android knows whether it's libc or your app making a syscall from native code?
It doesn't - you're allowed access to full Linux-land. The syscall numbers and userland EABI for Android are the same as for any other Linux. They use a different `libc` from most GNU/Linux (one of a few places this wonderful turn of phrase actually makes sense) flavors, but this is no different from Alpine Linux using `musl` instead of `glibc` for example.
As such, you can use `musl`, `glibc`, or non-libc based environments (Rust, Zig) on Android without issue. You can run any C you want, either by porting it to `bionic` (most termux apps, although they support glibc now), statically linking your own runtime (Rust, Zig, etc.), or abusing the dynamic linker into making glibc work (termux-glibc, I think).
Yes, but there are strict SELinux policies forbidding you from accessing certain "dangerous" stuff like execve(), which may end up killing native terminal emulators like Termux (despite Android not forbidding dynamic code execution elsewhere, Play Store policies aside).
Sure, and this VM solution is the exact path forward for “install stuff in a box” solutions as Android move towards trying to enforce w^x, which is probably why they chose a full Linux VM as their demo app. Emulators and games and apps with embedded JIT will be harder to deal with.
What do you imagine happens when you call fopen()? At some point there's a transition from user mode into kernel mode. What can the system do to prevent normal "app" code from making the transition but not "low level" code, when it's all user mode stuff running in the same process?
I could easily be completely wrong though.