Repository: kivy/kivy

Issue:
Platform linux on some android devices
<!--
The issue tracker is a tool to address bugs.
Please use the Disord community or Stack Overflow for support questions,
more information at https://github.com/kivy/python-for-android#support

Before opening a new issue, make sure you do the following:
    * check that your issue isn't already filed: https://github.com/kivy/python-for-android/issues
    * prepare a short, runnable example that reproduces the issue
    * make sure to have `log_level = 2` in your `buildozer.spec`
    * reproduce the problem with the latest development version (`p4a.branch = master`)
    * double-check that the issue is indeed a bug and not a support request
    * please use backticks to format code or logs
-->

### Versions

* Kivy: master (2.0.0rc1)

### Description

Last week I had to rebuild a kivy app that was working in many android devices. I normally do it with buildozer docker image.
I was able to create the apk again and it works as expected ... in some devices! In other ones the app crashes during splash screen due to a PermissionError. 
After debugging with adb logcat I noticed kivy is not recognizing the platform correctly as it return 'linux' instead of 'android'.
This causes that [ANDROID_APP_PATH is not used](https://github.com/kivy/kivy/blob/b6eddbb2fcf97a21b7f0c4cfbf0d8ccdb74fb3ec/kivy/__init__.py#L339) and it tries to write on /data/ path.

I'm using kivy==master on buildozer.spec.

### Logs

```
04-27 14:54:56.946  7788  7807 I python  : Android kivy bootstrap done. name is main
04-27 14:54:56.946  7788  7807 I python  : AND: Ran string
04-27 14:54:56.946  7788  7807 I python  : Run user program, change dir and execute entrypoint
04-27 14:54:57.303  7788  7807 I python  :  Traceback (most recent call last):
04-27 14:54:57.303  7788  7807 I python  :    File "/home/user/hostcwd/.buildozer/android/app/main.py", line 10, in <module>
04-27 14:54:57.304  7788  7807 I python  :    File "/home/user/hostcwd/.buildozer/android/app/app.py", line 4, in <module>
04-27 14:54:57.305  7788  7807 I python  :    File "/home/use
r/hostcwd/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/myapp/kivy/__init__.py", line 351, in <module>
04-27 14:54:57.306  7788  7807 I python  :  PermissionError: [Errno 13] Permission denied: '/data/.kivy'
04-27 14:54:57.306  7788  7807 I python  : Python for android ended.
```



Discussion:
> In other ones the app crashes during splash screen due to a PermissionError.

Some models in which app crashes:
* Xiaomi Mi A2 Lite
* Samsung Galaxy S8
Thanks for the bug report. That could be related to https://github.com/kivy/kivy/pull/6798
@misl6 and @opacam have confirmed the bug.
Could you try to revert build prior that commit and see if it solves the bug?
I'm transferring out the issue to kivy/kivy
yep, likely related! I guess maybe it would be safer to replace "and" with "or" in this test, maybe expecting all of these paths to exists is a bit much, any of them should be a good enough indicator that we are on android.
My proposal is to add a env variable on p4a bootstrappers like we do in `kivy-ios`, it seems more clean to me:

https://github.com/kivy/kivy/blob/26228b202e821824afd8b2536234b26c23ae1b9d/kivy/utils.py#L434
Well, on p4a we set an environ variable to let apps know that we are in p4a `P4A_BOOTSTRAP`:

https://github.com/kivy/python-for-android/blob/develop/pythonforandroid/bootstraps/common/build/jni/application/src/start.c#L86
@opacam Never seen that! So basically we could already detect if We're on android without any change on `p4a`.

I don't think that anyone is actually using `kivy` on Android without the aid of `p4a`, and also if there's such situation, the developer could easely set `P4A_BOOTSTRAP=1` in it's startup routine.

Resolving patch:
diff --git a/kivy/utils.py b/kivy/utils.py
--- a/kivy/utils.py
+++ b/kivy/utils.py
@@ -424,15 +424,17 @@ def format_bytes_to_human(size, precision=2):
 
 def _get_platform():
     # On Android sys.platform returns 'linux2', so prefer to check the
-    # existence of system specific commands and files
-    if (
-            path.exists('/default.prop')
-            and path.exists('/system/bin/logcat')
-            and path.exists('/system/xbin')
-    ):
+    # existence of environ variables set during Python initialization
+    kivy_build = environ.get('KIVY_BUILD', '')
+    if kivy_build in {'android', 'ios'}:
+        return environ['KIVY_BUILD']
+    elif 'P4A_BOOTSTRAP' in environ:
+        return 'android'
+    elif 'ANDROID_ARGUMENT' in environ:
+        # We used to use this method to detect android platform,
+        # leaving it here to be backwards compatible with `pydroid3`
+        # and similar tools outside kivy's ecosystem
         return 'android'
-    elif environ.get('KIVY_BUILD', '') == 'ios':
-        return 'ios'
     elif _sys_platform in ('win32', 'cygwin'):
         return 'win'
     elif _sys_platform == 'darwin':


Test patch:
diff --git a/kivy/tests/test_utils.py b/kivy/tests/test_utils.py
--- a/kivy/tests/test_utils.py
+++ b/kivy/tests/test_utils.py
@@ -191,21 +191,19 @@ def test_reify(self):
         assert first == second
 
     def test_Platform_android(self):
-        # modified os.path.exists function
-        # for test purpose
-        def modified_path_exists(p):
-            valid_paths = ['/default.prop',
-                           '/system/bin/logcat',
-                           '/system/xbin']
-            if p in valid_paths:
-                return True
-            return False
-
-        with patch(target='os.path.exists',
-                   side_effect=modified_path_exists):
-            pf = _get_platform()
-            self.assertTrue(pf == 'android')
-        self.assertFalse(os.path.exists('/default.prop'))
+        with patch.dict('os.environ', {'KIVY_BUILD': 'android'}):
+            self.assertEqual(_get_platform(), 'android')
+        self.assertNotIn('KIVY_BUILD', os.environ)
+
+    def test_Platform_android_with_p4a(self):
+        with patch.dict('os.environ', {'P4A_BOOTSTRAP': 'sdl2'}):
+            self.assertEqual(_get_platform(), 'android')
+        self.assertNotIn('P4A_BOOTSTRAP', os.environ)
+
+    def test_Platform_android_with_android_argument(self):
+        with patch.dict('os.environ', {'ANDROID_ARGUMENT': ''}):
+            self.assertEqual(_get_platform(), 'android')
+        self.assertNotIn('ANDROID_ARGUMENT', os.environ)
 
     def test_Platform_ios(self):
         with patch.dict('os.environ', {'KIVY_BUILD': 'ios'}):