fix(macos): reliable Local Network permission denial detection and re-check
- Replace broad POSIX error checks (EACCES/EPERM/ENETDOWN) with the canonical kDNSServiceErr_PolicyDenied DNS error in the NWBrowser state handler, matching the pattern used by Expo, Pulse, Strongbox, and WLED. Detect denial in both .failed and .waiting states. - Add checkLocalNetworkAccess(host:port:) — a read-only NWConnection probe (Sequel-Ace pattern) that checks NWPath.unsatisfiedReason == .localNetworkDenied without triggering a new system prompt. Useful for confirming denial against a specific destination before attempting to connect. - In _onConnect, after the prompt resolves to Denied, confirm with checkLocalNetworkAccess against the target host. If confirmed, abort the connect attempt and show a non-modal snackbar with an 'Open System Settings' action that deep-links to Privacy_LocalNetwork. Previously the app would proceed to connect, fail with PermissionDenied, and surface a redundant in-app modal. - Drop the now-orphaned _openIosAppSettings helper and _iosPlatformChannel constant (the only caller was the removed in-app permission dialog). - Add unit tests for checkLocalNetworkAccess covering outbound MethodCall arguments and state parsing for Granted/Denied. Trace: SRS-300.
This commit is contained in:
@@ -63,6 +63,8 @@ const String methodTriggerLocalNetworkPrompt = 'triggerLocalNetworkPrompt';
|
||||
@visibleForTesting
|
||||
const String methodCheckLocalNetwork = 'checkLocalNetwork';
|
||||
@visibleForTesting
|
||||
const String methodCheckLocalNetworkAccess = 'checkLocalNetworkAccess';
|
||||
@visibleForTesting
|
||||
const String methodRequestNotifications = 'requestNotifications';
|
||||
@visibleForTesting
|
||||
const String methodCheckNotifications = 'checkNotifications';
|
||||
@@ -440,6 +442,37 @@ class MacOSPermissionsService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Probe whether Local Network access is currently denied for [host]:[port]
|
||||
/// by creating a short-lived NWConnection and checking
|
||||
/// `unsatisfiedReason == .localNetworkDenied`.
|
||||
///
|
||||
/// This does NOT trigger a new system prompt — it is a read-only check.
|
||||
/// Returns [MacOSLocalNetworkState.unsupported] on non-macOS platforms.
|
||||
Future<MacOSLocalNetworkState> checkLocalNetworkAccess({
|
||||
required String host,
|
||||
required int port,
|
||||
}) async {
|
||||
final ch = _channel;
|
||||
if (ch == null) return MacOSLocalNetworkState.unsupported;
|
||||
try {
|
||||
final raw = await ch.invokeMethod<String>(
|
||||
methodCheckLocalNetworkAccess,
|
||||
<String, dynamic>{'host': host, 'port': port},
|
||||
);
|
||||
final state = _parseLocalNetworkState(raw);
|
||||
_localNetworkState.value = state;
|
||||
return state;
|
||||
} catch (e, st) {
|
||||
developer.log(
|
||||
'checkLocalNetworkAccess failed',
|
||||
name: 'MacOSPermissionsService',
|
||||
error: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
return _localNetworkState.value;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Outbound: Notifications ----------------------------------------------
|
||||
|
||||
Future<MacOSPermissionState> _checkNotifications() async {
|
||||
|
||||
Reference in New Issue
Block a user