ARTICLE AD BOX
The system has permission to display on top of other apps.
But when I check with canDrawOverlays with Android internal code, I always get false.
val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { appOpsManager.unsafeCheckOpNoThrow( AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName ) } else { @Suppress("DEPRECATION") appOpsManager.checkOpNoThrow( AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName ) } Log.d(TAG, "AppOpsManager mode: $mode")So when I run the code above, the mode value comes out as MODE_ERRORED (2).
However, if React-Native calls the same function to the Native Module, it is normally true.
The same is true even if you remove and re-allow permissions.
I used the same apk, but it only happened on certain devices.
Certain devices are versions of Android 16, but it also happened on other devices so I don't think it's just an Android 16 issue.
What should I do?
CheckPermission.kt
object CheckPermission { private const val TAG = "customNative - CheckPermission" fun hasAllRequiredPermissions(context: Context): Boolean { val requiredPermissions = mutableListOf( Manifest.permission.READ_CALL_LOG, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_CONTACTS, Manifest.permission.READ_PHONE_NUMBERS ) if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { requiredPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE) requiredPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) } val allPermissionsGranted = requiredPermissions.all { perm -> ContextCompat.checkSelfPermission(context, perm) == PackageManager.PERMISSION_GRANTED } val canDrawOverlays = checkOverlayPermissionViaAppOps(context); val hasFilePermission = MMKVHelper.hasRecordingPermission(context) return allPermissionsGranted && canDrawOverlays && hasFilePermission } fun checkOverlayPermissionViaAppOps(context: Context): Boolean { return try { val appContext = context.applicationContext ?: return false val appOpsManager = appContext.getSystemService(Context.APP_OPS_SERVICE) as? AppOpsManager if (appOpsManager == null) { Log.e(TAG, "❌ AppOpsManager null") return false } val packageName = appContext.packageName val uid = appContext.applicationInfo.uid Log.d(TAG, "Package: $packageName, UID: $uid") val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { appOpsManager.unsafeCheckOpNoThrow( AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName ) } else { @Suppress("DEPRECATION") appOpsManager.checkOpNoThrow( AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName ) } Log.d(TAG, "AppOpsManager mode: $mode (0=ALLOW, 1=MODE_IGNORED, 2=ERRORED, 3=DEFAULT, 4=FOREGROUND)") val result = mode == AppOpsManager.MODE_ALLOWED result } catch (e: Exception) { Log.e(TAG, "❌ AppOpsManager 확인 실패", e) e.printStackTrace() false } } }NativeModule.kt
@ReactMethod fun canDrawOverlays(promise: Promise) { try { val canDraw = CheckPermission.checkOverlayPermissionViaAppOps(reactApplicationContext) promise.resolve(canDraw) } catch (e: Exception) { promise.reject("OVERLAY_CHECK_ERROR", e.message) } }PhoneStateReceiver.kt
class PhoneStateReceiver : BroadcastReceiver() { companion object { private const val TAG = "customNative - PhoneStateReceiver" var isIncomingCall = false private var callState = "" private var lastCallNumber = "" } @RequiresApi(Build.VERSION_CODES.O) @SuppressLint("ScheduleExactAlarm", "UnsafeProtectedBroadcastReceiver") override fun onReceive(context: Context, intent: Intent) { val appContext = MainApplication.getAppContext() ?: context.applicationContext if (!CheckPermission.hasAllRequiredPermissions(appContext)) { return //always false! } } }