From bc984f78bf420c15c7935359320bb21f26f0ed30 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 12 Feb 2024 19:46:03 -0500 Subject: [PATCH] android: Remove blocking permission request code. Async only in SDL3! (this actually still blocks at our internal points of usage, though, for replacement at a later time.) --- docs/README-migration.md | 2 ++ include/SDL3/SDL_system.h | 25 +--------------- src/audio/aaudio/SDL_aaudio.c | 19 +++++++++++- src/audio/openslES/SDL_openslES.c | 25 ++++++++++++++-- src/core/SDL_core_unsupported.c | 12 ++------ src/core/android/SDL_android.c | 30 +------------------ src/core/android/SDL_android.h | 3 -- src/dynapi/SDL_dynapi.sym | 1 - src/dynapi/SDL_dynapi_overrides.h | 1 - src/dynapi/SDL_dynapi_procs.h | 3 +- src/hidapi/android/hid.cpp | 25 +++++++++++++++- src/video/android/SDL_android_video_capture.c | 4 +-- 12 files changed, 73 insertions(+), 77 deletions(-) diff --git a/docs/README-migration.md b/docs/README-migration.md index dfd68bf65..5f8555a31 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -1424,6 +1424,8 @@ SDL_WindowsMessageHook has changed signatures so the message may be modified and SDL_AndroidGetExternalStorageState() takes the state as an output parameter and returns 0 if the function succeeds or a negative error code if there was an error. +SDL_AndroidRequestPermission is no longer a blocking call; the caller now provides a callback function that fires when a response is available. + The following functions have been removed: * SDL_RenderGetD3D11Device() - replaced with the "SDL.renderer.d3d11.device" property * SDL_RenderGetD3D12Device() - replaced with the "SDL.renderer.d3d12.device" property diff --git a/include/SDL3/SDL_system.h b/include/SDL3/SDL_system.h index 277f512a5..40483fc64 100644 --- a/include/SDL3/SDL_system.h +++ b/include/SDL3/SDL_system.h @@ -399,29 +399,6 @@ extern DECLSPEC int SDLCALL SDL_AndroidGetExternalStorageState(Uint32 *state); */ extern DECLSPEC const char * SDLCALL SDL_AndroidGetExternalStoragePath(void); -/** - * Request permissions at runtime. - * - * You do not need to call this for built-in functionality of SDL; recording - * from a microphone or reading images from a camera, using standard SDL - * APIs, will manage permission requests for you. - * - * This blocks the calling thread until the permission is granted or denied. - * if the app already has the requested permission, this returns immediately, - * but may block indefinitely until the user responds to the system's - * permission request dialog. - * - * If possible, you should _not_ use this function. You should use - * SDL_AndroidRequestPermissionAsync and deal with the response in a callback - * at a later time, and possibly in a different thread. - * - * \param permission The permission to request. - * \returns SDL_TRUE if the permission was granted, SDL_FALSE otherwise. - * - * \since This function is available since SDL 3.0.0. - */ -extern DECLSPEC SDL_bool SDLCALL SDL_AndroidRequestPermission(const char *permission); - typedef void (SDLCALL *SDL_AndroidRequestPermissionCallback)(void *userdata, const char *permission, SDL_bool granted); @@ -453,7 +430,7 @@ typedef void (SDLCALL *SDL_AndroidRequestPermissionCallback)(void *userdata, con * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC int SDLCALL SDL_AndroidRequestPermissionAsync(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata); +extern DECLSPEC int SDLCALL SDL_AndroidRequestPermission(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata); /** * Shows an Android toast notification. diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c index 4445ba8c8..265eaacaf 100644 --- a/src/audio/aaudio/SDL_aaudio.c +++ b/src/audio/aaudio/SDL_aaudio.c @@ -381,6 +381,12 @@ static int BuildAAudioStream(SDL_AudioDevice *device) return 0; } +// !!! FIXME: make this non-blocking! +static void SDLCALL AndroidRequestPermissionBlockingCallback(void *userdata, const char *permission, SDL_bool granted) +{ + SDL_AtomicSet((SDL_AtomicInt *) userdata, granted ? 1 : -1); +} + static int AAUDIO_OpenDevice(SDL_AudioDevice *device) { #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES @@ -390,7 +396,18 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device) LOGI(__func__); if (device->iscapture) { - if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) { + // !!! FIXME: make this non-blocking! + SDL_AtomicInt permission_response; + SDL_AtomicSet(&permission_response, 0); + if (SDL_AndroidRequestPermission("android.permission.RECORD_AUDIO", AndroidRequestPermissionBlockingCallback, &permission_response) == -1) { + return -1; + } + + while (SDL_AtomicGet(&permission_response) == 0) { + SDL_Delay(10); + } + + if (SDL_AtomicGet(&permission_response) < 0) { LOGI("This app doesn't have RECORD_AUDIO permission"); return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); } diff --git a/src/audio/openslES/SDL_openslES.c b/src/audio/openslES/SDL_openslES.c index 84f92a41b..bda1b91c9 100644 --- a/src/audio/openslES/SDL_openslES.c +++ b/src/audio/openslES/SDL_openslES.c @@ -228,6 +228,12 @@ static void OPENSLES_DestroyPCMRecorder(SDL_AudioDevice *device) } } +// !!! FIXME: make this non-blocking! +static void SDLCALL AndroidRequestPermissionBlockingCallback(void *userdata, const char *permission, SDL_bool granted) +{ + SDL_AtomicSet((SDL_AtomicInt *) userdata, granted ? 1 : -1); +} + static int OPENSLES_CreatePCMRecorder(SDL_AudioDevice *device) { struct SDL_PrivateAudioData *audiodata = device->hidden; @@ -241,9 +247,22 @@ static int OPENSLES_CreatePCMRecorder(SDL_AudioDevice *device) SLresult result; int i; - if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) { - LOGE("This app doesn't have RECORD_AUDIO permission"); - return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); + // !!! FIXME: make this non-blocking! + { + SDL_AtomicInt permission_response; + SDL_AtomicSet(&permission_response, 0); + if (SDL_AndroidRequestPermission("android.permission.RECORD_AUDIO", AndroidRequestPermissionBlockingCallback, &permission_response) == -1) { + return -1; + } + + while (SDL_AtomicGet(&permission_response) == 0) { + SDL_Delay(10); + } + + if (SDL_AtomicGet(&permission_response) < 0) { + LOGE("This app doesn't have RECORD_AUDIO permission"); + return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); + } } // Just go with signed 16-bit audio as it's the most compatible diff --git a/src/core/SDL_core_unsupported.c b/src/core/SDL_core_unsupported.c index 6ab9ff54e..2196aa404 100644 --- a/src/core/SDL_core_unsupported.c +++ b/src/core/SDL_core_unsupported.c @@ -161,17 +161,9 @@ void *SDL_AndroidGetJNIEnv() return NULL; } -DECLSPEC SDL_bool SDLCALL SDL_AndroidRequestPermission(const char *permission); -SDL_bool SDL_AndroidRequestPermission(const char *permission) -{ - (void)permission; - SDL_Unsupported(); - return SDL_FALSE; -} - typedef void (SDLCALL *SDL_AndroidRequestPermissionCallback)(void *userdata, const char *permission, SDL_bool granted); -DECLSPEC int SDLCALL SDL_AndroidRequestPermissionAsync(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata); -int SDL_AndroidRequestPermissionAsync(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata) +DECLSPEC int SDLCALL SDL_AndroidRequestPermission(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata); +int SDL_AndroidRequestPermission(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata) { (void)permission; (void)cb; diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index 9c94e2549..a8a9cddb7 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -2557,11 +2557,6 @@ const char *SDL_AndroidGetExternalStoragePath(void) return s_AndroidExternalFilesPath; } -SDL_bool SDL_AndroidRequestPermission(const char *permission) -{ - return Android_JNI_RequestPermission(permission); -} - int SDL_AndroidShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset) { return Android_JNI_ShowToast(message, duration, gravity, xOffset, yOffset); @@ -2662,7 +2657,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)( SDL_assert(!"Shouldn't have hit this code"); // we had a permission response for a request we never made...? } -int SDL_AndroidRequestPermissionAsync(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata) +int SDL_AndroidRequestPermission(const char *permission, SDL_AndroidRequestPermissionCallback cb, void *userdata) { if (!permission) { return SDL_InvalidParamError("permission"); @@ -2700,29 +2695,6 @@ int SDL_AndroidRequestPermissionAsync(const char *permission, SDL_AndroidRequest return 0; } -static void SDLCALL AndroidRequestPermissionBlockingCallback(void *userdata, const char *permission, SDL_bool granted) -{ - SDL_AtomicSet((SDL_AtomicInt *) userdata, granted ? 1 : -1); -} - -SDL_bool Android_JNI_RequestPermission(const char *permission) -{ - SDL_AtomicInt response; - SDL_AtomicSet(&response, 0); - - if (SDL_AndroidRequestPermissionAsync(permission, AndroidRequestPermissionBlockingCallback, &response) == -1) { - return SDL_FALSE; - } - - /* Wait for the request to complete */ - while (SDL_AtomicGet(&response) == 0) { - SDL_Delay(10); - } - - return (SDL_AtomicGet(&response) < 0) ? SDL_FALSE : SDL_TRUE; -} - - /* Show toast notification */ int Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset) { diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index b611bc52d..c51fb4db9 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -125,9 +125,6 @@ SDL_bool Android_JNI_SetSystemCursor(int cursorID); SDL_bool Android_JNI_SupportsRelativeMouse(void); SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled); -/* Request permission */ -SDL_bool Android_JNI_RequestPermission(const char *permission); - /* Show toast notification */ int Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset); diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 821ad6f22..2ba1f7be6 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -972,7 +972,6 @@ SDL3_0.0.0 { SDL_RenderGeometryRawFloat; SDL_SetWindowShape; SDL_RenderViewportSet; - SDL_AndroidRequestPermissionAsync; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 8168d51ff..b297a3026 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -997,4 +997,3 @@ #define SDL_RenderGeometryRawFloat SDL_RenderGeometryRawFloat_REAL #define SDL_SetWindowShape SDL_SetWindowShape_REAL #define SDL_RenderViewportSet SDL_RenderViewportSet_REAL -#define SDL_AndroidRequestPermissionAsync SDL_AndroidRequestPermissionAsync_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 15bd3cf28..3969fda9c 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -93,7 +93,7 @@ SDL_DYNAPI_PROC(const char*,SDL_AndroidGetExternalStoragePath,(void),(),return) SDL_DYNAPI_PROC(int,SDL_AndroidGetExternalStorageState,(Uint32 *a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_AndroidGetInternalStoragePath,(void),(),return) SDL_DYNAPI_PROC(void*,SDL_AndroidGetJNIEnv,(void),(),return) -SDL_DYNAPI_PROC(SDL_bool,SDL_AndroidRequestPermission,(const char *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_AndroidRequestPermission,(const char *a, SDL_AndroidRequestPermissionCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_AndroidSendMessage,(Uint32 a, int b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_AndroidShowToast,(const char *a, int b, int c, int d, int e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_GetAndroidSDKVersion,(void),(),return) @@ -1022,4 +1022,3 @@ SDL_DYNAPI_PROC(int,SDL_GetRenderColorScale,(SDL_Renderer *a, float *b),(a,b),re SDL_DYNAPI_PROC(int,SDL_RenderGeometryRawFloat,(SDL_Renderer *a, SDL_Texture *b, const float *c, int d, const SDL_FColor *e, int f, const float *g, int h, int i, const void *j, int k, int l),(a,b,c,d,e,f,g,h,i,j,k,l),return) SDL_DYNAPI_PROC(int,SDL_SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_RenderViewportSet,(SDL_Renderer *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_AndroidRequestPermissionAsync,(const char *a, SDL_AndroidRequestPermissionCallback b, void *c),(a,b,c),return) diff --git a/src/hidapi/android/hid.cpp b/src/hidapi/android/hid.cpp index 098feb85d..ea8d27972 100644 --- a/src/hidapi/android/hid.cpp +++ b/src/hidapi/android/hid.cpp @@ -1029,6 +1029,29 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReportResponse extern "C" { +// !!! FIXME: make this non-blocking! +static void SDLCALL AndroidRequestPermissionBlockingCallback(void *userdata, const char *permission, SDL_bool granted) +{ + SDL_AtomicSet((SDL_AtomicInt *) userdata, granted ? 1 : -1); +} + +static SDL_bool RequestBluetoothPermissions(const char *permission) +{ + // !!! FIXME: make this non-blocking! + SDL_AtomicInt permission_response; + SDL_AtomicSet(&permission_response, 0); + if (SDL_AndroidRequestPermission(permission, AndroidRequestPermissionBlockingCallback, &permission_response) == -1) { + return SDL_FALSE; + } + + while (SDL_AtomicGet(&permission_response) == 0) { + SDL_Delay(10); + } + + return SDL_AtomicGet(&permission_response) > 0; +} + + int hid_init(void) { if ( !g_initialized && g_HIDDeviceManagerCallbackHandler ) @@ -1046,7 +1069,7 @@ int hid_init(void) bool init_bluetooth = false; if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_FALSE)) { if (SDL_GetAndroidSDKVersion() < 31 || - Android_JNI_RequestPermission("android.permission.BLUETOOTH_CONNECT")) { + RequestBluetoothPermissions("android.permission.BLUETOOTH_CONNECT")) { init_bluetooth = true; } } diff --git a/src/video/android/SDL_android_video_capture.c b/src/video/android/SDL_android_video_capture.c index 3ec5cd1ba..ff157cbd4 100644 --- a/src/video/android/SDL_android_video_capture.c +++ b/src/video/android/SDL_android_video_capture.c @@ -68,12 +68,12 @@ static void create_cameraMgr(void) { if (cameraMgr == NULL) { - + #if 0 // !!! FIXME: this is getting replaced in a different branch. if (!Android_JNI_RequestPermission("android.permission.CAMERA")) { SDL_SetError("This app doesn't have CAMERA permission"); return; } - + #endif cameraMgr = ACameraManager_create(); if (cameraMgr == NULL) { SDL_Log("Error creating ACameraManager");