fix(android): improve exact alarm permission check with fallback strategies

Fix reflection-based permission check that was failing with NoSuchMethodException.
Add multiple fallback strategies to ensure permission check works reliably.

Changes:
- Add getDeclaredMethod() fallback when getMethod() fails
- Add heuristic fallback: if exact alarms not allowed, assume they can be requested
- Improve error handling: catch NoSuchMethodException separately from other exceptions
- Add debug logging to track which reflection path is taken
- Change reflection failure log level from ERROR to WARNING (we have fallback)

The heuristic fallback is safe because:
- If exact alarms are not currently allowed, we should try to request them
- Only edge case is permanently denied (rare), worst case is unnecessary Settings redirect
- Better than failing silently or blocking permission requests

Fixes reflection failures seen in logcat where Settings.canRequestScheduleExactAlarms()
method lookup was failing, causing unnecessary Settings redirects.
This commit is contained in:
Matthew Raymer
2025-11-10 06:12:22 +00:00
parent 5b61f18bd7
commit 3fa167cba0

View File

@@ -823,8 +823,10 @@ open class DailyNotificationPlugin : Plugin() {
* Check if exact alarm permission can be requested
* Helper method that handles API level differences
*
* Uses reflection to call Settings.canRequestScheduleExactAlarms() on Android 13+
* to avoid compilation issues with newer APIs.
* On Android 12 (API 31-32): Permission can always be requested
* On Android 13+ (API 33+): Uses reflection to call Settings.canRequestScheduleExactAlarms()
* If reflection fails, falls back to heuristic: if exact alarms are not currently allowed,
* we assume they can be requested (safe default).
*
* @param context Application context
* @return true if permission can be requested, false if permanently denied
@@ -833,17 +835,39 @@ open class DailyNotificationPlugin : Plugin() {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12+ (API 31+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ (API 33+) - use reflection to call canRequestScheduleExactAlarms
// Android 13+ (API 33+) - try reflection to call canRequestScheduleExactAlarms
try {
// Try getMethod first (for public static methods)
val method = Settings::class.java.getMethod(
"canRequestScheduleExactAlarms",
Context::class.java
)
method.invoke(null, context) as Boolean
val result = method.invoke(null, context) as Boolean
Log.d(TAG, "canRequestScheduleExactAlarms() returned: $result")
return result
} catch (e: NoSuchMethodException) {
// Method not found - try getDeclaredMethod as fallback
try {
val method = Settings::class.java.getDeclaredMethod(
"canRequestScheduleExactAlarms",
Context::class.java
)
method.isAccessible = true
val result = method.invoke(null, context) as Boolean
Log.d(TAG, "canRequestScheduleExactAlarms() (via getDeclaredMethod) returned: $result")
return result
} catch (e2: Exception) {
Log.w(TAG, "Failed to check exact alarm permission using reflection, using heuristic", e2)
// Fallback heuristic: if exact alarms are not currently allowed,
// assume we can request them (safe default)
// Only case where we can't request is if permanently denied, which is rare
return !canScheduleExactAlarms(context)
}
} catch (e: Exception) {
Log.e(TAG, "Failed to check exact alarm permission using reflection", e)
// Fallback to allowing request (safe default)
true
Log.w(TAG, "Failed to invoke canRequestScheduleExactAlarms(), using heuristic", e)
// Fallback heuristic: if exact alarms are not currently allowed,
// assume we can request them (safe default)
return !canScheduleExactAlarms(context)
}
} else {
// Android 12 (API 31-32) - permission can always be requested