cordova.plugins.diagnostic 7.2.5 → 7.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ **v7.2.7**
4
+ (ios) bugfix: Handle DNS PolicyDenied like EPERM for local network permission detection
5
+
6
+ **v7.2.6**
7
+ * (ios) fix: Local Network status check now accepts a configurable timeout, detects permission denials via NWBrowser errors/NSNetService failures, and returns `UNKNOWN` instead of `DENIED_ALWAYS` when the probe times out. This works around an issue where, if the network stack is under load, local network permission may be falsely reported as denied because iOS fails to resolve the Bonjour service within the default timeout period.
8
+
3
9
  **v7.2.5**
4
10
  * (ios) feat: add check/request local network authorization to Wifi module
5
11
  * Resolves [#524](https://github.com/dpa99c/cordova-diagnostic-plugin/issues/524)
package/README.md CHANGED
@@ -639,14 +639,17 @@ App can never ask for permission again.
639
639
  The only way around this is to instruct the user to manually change the permission in Settings.
640
640
  - `RESTRICTED` - Permission is unavailable and user cannot enable it.
641
641
  For example, when parental controls are in effect for the current user.
642
- - `GRANTED` - User granted access to this permission.
643
- For location permission, this indicates the user has granted access to the permission "always" (when app is both in foreground and background).
644
- - `GRANTED_WHEN_IN_USE` - Used only for location permission.
645
- Indicates the user has granted access to the permission "when in use" (only when the app is in the foreground).
646
-
647
- Addtionally, for notifications permissions:
648
- - `PROVISIONAL` - The app is provisionally authorized to post non-interruptive user notifications.
649
- - `EPHEMERAL` - The app is authorized to schedule or receive notifications for a limited amount of time.
642
+ - `GRANTED` - User granted access to this permission.
643
+ For location permission, this indicates the user has granted access to the permission "always" (when app is both in foreground and background).
644
+ - `GRANTED_WHEN_IN_USE` - Used only for location permission.
645
+ Indicates the user has granted access to the permission "when in use" (only when the app is in the foreground).
646
+
647
+ Addtionally, for notifications permissions:
648
+ - `PROVISIONAL` - The app is provisionally authorized to post non-interruptive user notifications.
649
+ - `EPHEMERAL` - The app is authorized to schedule or receive notifications for a limited amount of time.
650
+
651
+ For cases where the platform cannot return a definitive answer, the plugin also exposes:
652
+ - `UNKNOWN` (Android and iOS) - Returned when the underlying OS has not yet provided a concrete status (for example, if an iOS Local Network probe timed out). Treat this as a transient state and retry before surfacing a denial to the user.
650
653
 
651
654
  #### Example
652
655
 
@@ -2455,61 +2458,80 @@ The function is passed a single string parameter containing the error message.
2455
2458
 
2456
2459
  Platforms: iOS
2457
2460
 
2458
- Checks if the app is authorised to access devices on the local network (iOS 14+).
2459
- On iOS versions prior to 14 this will always return TRUE as no local network authorization is required.
2461
+ Checks if the app is authorised to access devices on the local network (iOS 14+).
2462
+ On iOS versions prior to 14 this will always return TRUE as no local network authorization is required.
2463
+ An optional third argument allows you to override the fallback timeout (defaults to 2 seconds) by passing `{ timeoutMs: <number> }`.
2460
2464
 
2461
2465
  cordova.plugins.diagnostic.isLocalNetworkAuthorized(successCallback, errorCallback);
2462
2466
 
2463
- #### Parameters
2464
-
2465
- - {Function} successCallback - The callback which will be called when operation is successful.
2466
- The function is passed a single boolean parameter which is TRUE if the app is authorised to use the Local Network.
2467
- - {Function} errorCallback - The callback which will be called when operation encounters an error.
2468
- The function is passed a single string parameter containing the error message.
2467
+ #### Parameters
2468
+
2469
+ - {Function} successCallback - The callback which will be called when operation is successful.
2470
+ The function is passed a single boolean parameter which is TRUE if the app is authorised to use the Local Network.
2471
+ - {Function} errorCallback - The callback which will be called when operation encounters an error.
2472
+ The function is passed a single string parameter containing the error message.
2473
+ - {Object} [options] - Optional timeout control object. Provide `timeoutMs` (milliseconds) to override the default 2000 ms timeout.
2469
2474
 
2470
2475
 
2471
2476
  #### Example usage
2472
2477
 
2473
- cordova.plugins.diagnostic.isLocalNetworkAuthorized(function(authorized){
2474
- console.log("Local Network is " + (authorized ? "authorized" : "unauthorised"));
2475
- }, function(error){
2476
- console.error("The following error occurred: "+error);
2477
- });
2478
+ cordova.plugins.diagnostic.isLocalNetworkAuthorized(function(authorized){
2479
+ console.log("Local Network is " + (authorized ? "authorized" : "unauthorised"));
2480
+ }, function(error){
2481
+ console.error("The following error occurred: "+error);
2482
+ });
2483
+
2484
+ To wait longer before treating a slow response as indeterminate:
2485
+
2486
+ ```
2487
+ cordova.plugins.diagnostic.isLocalNetworkAuthorized(
2488
+ function(authorized){
2489
+ console.log("Local Network authorized? " + authorized);
2490
+ },
2491
+ console.error,
2492
+ { timeoutMs: 8000 }
2493
+ );
2494
+ ```
2478
2495
 
2479
2496
  ### getLocalNetworkAuthorizationStatus()
2480
2497
 
2481
2498
  Platforms: iOS
2482
2499
 
2483
- Returns the app's Local Network authorization status.
2484
- On iOS 14+ this returns one of the values in `cordova.plugins.diagnostic.permissionStatus`: `NOT_REQUESTED`, `GRANTED`, `DENIED_ALWAYS`.
2485
- On iOS versions prior to 14 this will always return `GRANTED` as no authorization is required.
2500
+ Returns the app's Local Network authorization status.
2501
+ On iOS 14+ this returns one of the values in `cordova.plugins.diagnostic.permissionStatus`: `NOT_REQUESTED`, `GRANTED`, `DENIED_ALWAYS`, `UNKNOWN`.
2502
+ `UNKNOWN` indicates that iOS did not return a definitive answer before the timeout elapsed, so the app can retry before warning the user.
2503
+ On iOS versions prior to 14 this will always return `GRANTED` as no authorization is required.
2486
2504
 
2487
2505
  cordova.plugins.diagnostic.getLocalNetworkAuthorizationStatus(successCallback, errorCallback);
2488
2506
 
2489
2507
  #### Parameters
2490
2508
 
2491
- - {Function} successCallback - The callback which will be called when operation is successful.
2492
- The function is passed a single string parameter which is one of the values in `cordova.plugins.diagnostic.permissionStatus`:
2493
- `NOT_REQUESTED`, `GRANTED`, `DENIED_ALWAYS`.
2494
- - {Function} errorCallback - The callback which will be called when operation encounters an error.
2495
- The function is passed a single string parameter containing the error message.
2509
+ - {Function} successCallback - The callback which will be called when operation is successful.
2510
+ The function is passed a single string parameter which is one of the values in `cordova.plugins.diagnostic.permissionStatus`:
2511
+ `NOT_REQUESTED`, `GRANTED`, `DENIED_ALWAYS`, `UNKNOWN`.
2512
+ - {Function} errorCallback - The callback which will be called when operation encounters an error.
2513
+ The function is passed a single string parameter containing the error message.
2514
+ - {Object} [options] - Optional timeout override (defaults to 2 seconds). Provide `timeoutMs` (milliseconds) to customize the fallback duration.
2496
2515
 
2497
2516
 
2498
2517
  #### Example usage
2499
2518
 
2500
2519
  cordova.plugins.diagnostic.getLocalNetworkAuthorizationStatus(function(status){
2501
- switch(status){
2502
- case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
2503
- console.log("Local Network permission not requested");
2504
- break;
2505
- case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS:
2506
- console.log("Local Network permission denied");
2507
- break;
2508
- case cordova.plugins.diagnostic.permissionStatus.GRANTED:
2509
- console.log("Local Network permission granted");
2510
- break;
2511
- }
2512
- }, function(error){
2520
+ switch(status){
2521
+ case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
2522
+ console.log("Local Network permission not requested");
2523
+ break;
2524
+ case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS:
2525
+ console.log("Local Network permission denied");
2526
+ break;
2527
+ case cordova.plugins.diagnostic.permissionStatus.UNKNOWN:
2528
+ console.log("Local Network permission could not be determined (retry recommended)");
2529
+ break;
2530
+ case cordova.plugins.diagnostic.permissionStatus.GRANTED:
2531
+ console.log("Local Network permission granted");
2532
+ break;
2533
+ }
2534
+ }, function(error){
2513
2535
  console.error("The following error occurred: "+error);
2514
2536
  });
2515
2537
 
@@ -2517,31 +2539,34 @@ On iOS versions prior to 14 this will always return `GRANTED` as no authorizatio
2517
2539
 
2518
2540
  Platforms: iOS
2519
2541
 
2520
- Requests the user to authorise the app to access devices on the local network (iOS 14+).
2521
- On iOS versions prior to 14 this does nothing and will return success as no authorization is required.
2542
+ Requests the user to authorise the app to access devices on the local network (iOS 14+).
2543
+ On iOS versions prior to 14 this does nothing and will return success as no authorization is required.
2544
+ May return `UNKNOWN` if iOS does not respond before the native APIs time out, allowing the app to retry.
2522
2545
 
2523
2546
  cordova.plugins.diagnostic.requestLocalNetworkAuthorization(successCallback, errorCallback);
2524
2547
 
2525
2548
  #### Parameters
2526
2549
 
2527
- - {Function} successCallback - The callback which will be called when operation is successful.
2528
- The function is passed a single string parameter which is one of the values in `cordova.plugins.diagnostic.permissionStatus`:
2529
- `NOT_REQUESTED`, `GRANTED`, `DENIED_ALWAYS`.
2550
+ - {Function} successCallback - The callback which will be called when operation is successful.
2551
+ The function is passed a single string parameter which is one of the values in `cordova.plugins.diagnostic.permissionStatus`:
2552
+ `NOT_REQUESTED`, `GRANTED`, `DENIED_ALWAYS`, `UNKNOWN`.
2530
2553
  - {Function} errorCallback - The callback which will be called when operation encounters an error.
2531
2554
  The function is passed a single string parameter containing the error message.
2532
2555
 
2533
2556
 
2534
2557
  #### Example usage
2535
2558
 
2536
- cordova.plugins.diagnostic.requestLocalNetworkAuthorization(function(status){
2537
- if(status === cordova.plugins.diagnostic.permissionStatus.GRANTED){
2538
- console.log("Local Network access granted");
2539
- }else{
2540
- console.log("Local Network access not granted: " + status);
2541
- }
2542
- }, function(error){
2543
- console.error("The following error occurred: "+error);
2544
- });
2559
+ cordova.plugins.diagnostic.requestLocalNetworkAuthorization(function(status){
2560
+ if(status === cordova.plugins.diagnostic.permissionStatus.GRANTED){
2561
+ console.log("Local Network access granted");
2562
+ }else if(status === cordova.plugins.diagnostic.permissionStatus.UNKNOWN){
2563
+ console.log("Local Network status could not be determined, retrying may succeed");
2564
+ }else{
2565
+ console.log("Local Network access not granted: " + status);
2566
+ }
2567
+ }, function(error){
2568
+ console.error("The following error occurred: "+error);
2569
+ });
2545
2570
 
2546
2571
  ### setWifiState()
2547
2572
 
@@ -78,6 +78,7 @@ interface Diagnostic {
78
78
  "GRANTED_WHEN_IN_USE": "authorized_when_in_use";
79
79
  "NOT_REQUESTED": "not_determined"|"NOT_REQUESTED";
80
80
  "DENIED_ALWAYS": "denied_always"|"DENIED_ALWAYS";
81
+ "UNKNOWN": "unknown"|"UNKNOWN";
81
82
  };
82
83
 
83
84
  /**
@@ -1272,7 +1273,10 @@ interface Diagnostic {
1272
1273
  */
1273
1274
  isLocalNetworkAuthorized?: (
1274
1275
  successCallback: (authorized: boolean) => void,
1275
- errorCallback: (error: string) => void
1276
+ errorCallback: (error: string) => void,
1277
+ options?: {
1278
+ timeoutMs?: number;
1279
+ }
1276
1280
  ) => void;
1277
1281
 
1278
1282
  /**
@@ -1283,7 +1287,10 @@ interface Diagnostic {
1283
1287
  */
1284
1288
  getLocalNetworkAuthorizationStatus?: (
1285
1289
  successCallback: (status: string) => void,
1286
- errorCallback: (error: string) => void
1290
+ errorCallback: (error: string) => void,
1291
+ options?: {
1292
+ timeoutMs?: number;
1293
+ }
1287
1294
  ) => void;
1288
1295
 
1289
1296
  /**
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.2.5",
2
+ "version": "7.2.7",
3
3
  "name": "cordova.plugins.diagnostic",
4
4
  "cordova_name": "Diagnostic",
5
5
  "description": "Cordova/Phonegap plugin to check the state of Location/WiFi/Camera/Bluetooth device settings.",
package/plugin.xml CHANGED
@@ -2,7 +2,7 @@
2
2
  <plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
3
3
  xmlns:android="http://schemas.android.com/apk/res/android"
4
4
  id="cordova.plugins.diagnostic"
5
- version="7.2.5">
5
+ version="7.2.7">
6
6
 
7
7
  <name>Diagnostic</name>
8
8
  <description>Cordova/Phonegap plugin to check the state of Location/WiFi/Camera/Bluetooth device settings.</description>
@@ -13,6 +13,8 @@
13
13
  #import <net/if.h> // For IFF_LOOPBACK
14
14
  #import <Network/Network.h>
15
15
  #import <Network/browser.h>
16
+ #import <dns_sd.h>
17
+ #import <errno.h>
16
18
 
17
19
  // UserDefaults key for caching local network permission
18
20
  static NSString*const kLocalNetworkPermissionKey = @"Diagnostic_LocalNetworkPermission";
@@ -21,6 +23,7 @@ typedef NS_ENUM(NSInteger, LocalNetworkPermissionState) {
21
23
  LocalNetworkPermissionStateUnknown = 0,
22
24
  LocalNetworkPermissionStateGranted = 1,
23
25
  LocalNetworkPermissionStateDenied = -1,
26
+ LocalNetworkPermissionStateIndeterminate = -2,
24
27
  };
25
28
  @implementation Diagnostic_Wifi {
26
29
  nw_browser_t _browser;
@@ -41,6 +44,7 @@ static Diagnostic* diagnostic;
41
44
 
42
45
  // Internal constants
43
46
  static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
47
+ static NSTimeInterval const kLocalNetworkDefaultTimeoutSeconds = 2.0;
44
48
 
45
49
  - (void)pluginInitialize {
46
50
 
@@ -83,6 +87,8 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
83
87
  return;
84
88
  }
85
89
 
90
+ NSTimeInterval timeoutSeconds = [self resolveLocalNetworkTimeoutFromCommand:command];
91
+
86
92
  // Create parameters, and allow browsing over peer-to-peer link.
87
93
  if (@available(iOS 14.0, *)) {
88
94
  // Create parameters, and allow browsing over peer-to-peer link.
@@ -101,7 +107,7 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
101
107
  self->_isRequesting = YES;
102
108
  self->_isPublishing = NO;
103
109
 
104
- [diagnostic logDebug:@"Starting local network permission status check"];
110
+ [diagnostic logDebug:[NSString stringWithFormat:@"Starting local network permission status check (timeout %.2fs)", timeoutSeconds]];
105
111
  // Start the browsing/publish flow on the main queue immediately and create a single-shot timeout.
106
112
  dispatch_async(dispatch_get_main_queue(), ^{
107
113
  __weak __typeof__(self) weakSelf = self;
@@ -121,19 +127,11 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
121
127
  // Install a state handler so the browser emits state changes (silences the warning about missing handlers)
122
128
  if (strongSelf->_browser) {
123
129
  nw_browser_set_state_changed_handler(strongSelf->_browser, ^(nw_browser_state_t newState, nw_error_t error) {
124
- switch (newState) {
125
- case nw_browser_state_failed:
126
- if (error) {
127
- nw_error_domain_t errorDomain = nw_error_get_error_domain(error);
128
- [diagnostic logDebug:[NSString stringWithFormat:@"Browser failed (status check): domain=%d", (int)errorDomain]];
129
- }
130
- break;
131
- case nw_browser_state_ready:
132
- case nw_browser_state_cancelled:
133
- case nw_browser_state_waiting:
134
- default:
135
- break;
130
+ __strong __typeof__(weakSelf) innerSelf = weakSelf;
131
+ if (!innerSelf) {
132
+ return;
136
133
  }
134
+ [innerSelf handleBrowserState:newState error:error context:@"status check"];
137
135
  });
138
136
 
139
137
  nw_browser_start(strongSelf->_browser);
@@ -144,17 +142,17 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
144
142
  [strongSelf->_netService publish];
145
143
  [strongSelf->_netService scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
146
144
 
147
- // Set a single-shot timeout to consider the permission request failed
148
- strongSelf->_localNetworkTimer = [NSTimer scheduledTimerWithTimeInterval:2.0
149
- repeats:NO
150
- block:^(NSTimer * _Nonnull timer) {
151
- __strong __typeof__(weakSelf) innerSelf = weakSelf;
152
- if (!innerSelf) return;
153
-
154
- [diagnostic logDebug:@"Local network permission request timed out"];
155
- [self callLocalNetworkCallbacks:LocalNetworkPermissionStateDenied];
156
- [innerSelf resetLocalNetwork];
157
- }];
145
+ if (timeoutSeconds > 0) {
146
+ strongSelf->_localNetworkTimer = [NSTimer scheduledTimerWithTimeInterval:timeoutSeconds
147
+ repeats:NO
148
+ block:^(NSTimer * _Nonnull timer) {
149
+ __strong __typeof__(weakSelf) innerSelf = weakSelf;
150
+ if (!innerSelf) return;
151
+
152
+ [diagnostic logDebug:[NSString stringWithFormat:@"Local network permission status check timed out after %.2fs", timeoutSeconds]];
153
+ [innerSelf completeLocalNetworkFlowWithState:LocalNetworkPermissionStateIndeterminate shouldCache:NO];
154
+ }];
155
+ }
158
156
  });
159
157
  }else{
160
158
  [diagnostic logDebug:@"iOS version < 14.0, so local network permission is not required"];
@@ -199,33 +197,10 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
199
197
  __weak __typeof__(self) weakSelf = self;
200
198
  nw_browser_set_state_changed_handler(self->_browser, ^(nw_browser_state_t newState, nw_error_t error) {
201
199
  __strong __typeof__(weakSelf) strongSelf = weakSelf;
202
- switch (newState) {
203
- case nw_browser_state_failed:
204
- if (error) {
205
- nw_error_domain_t errorDomain = nw_error_get_error_domain(error);
206
- [diagnostic logDebug:[NSString stringWithFormat:@"Browser failed: domain=%d", (int)errorDomain]];
207
- }
208
- break;
209
- case nw_browser_state_ready:
210
- case nw_browser_state_cancelled:
211
- break;
212
- case nw_browser_state_waiting:
213
- if (error) {
214
- nw_error_domain_t errorDomain = nw_error_get_error_domain(error);
215
- [diagnostic logDebug:[NSString stringWithFormat:@"Local network permission has been denied: domain=%d", (int)errorDomain]];
216
- } else {
217
- [diagnostic logDebug:@"Local network permission has been denied"];
218
- }
219
- [strongSelf resetLocalNetwork];
220
- // cache denied
221
- [[NSUserDefaults standardUserDefaults] setInteger:LocalNetworkPermissionStateDenied forKey:kLocalNetworkPermissionKey];
222
- [[NSUserDefaults standardUserDefaults] synchronize];
223
- // send false result to all waiting commands
224
- [self callLocalNetworkCallbacks:LocalNetworkPermissionStateDenied];
225
- break;
226
- default:
227
- break;
200
+ if (!strongSelf) {
201
+ return;
228
202
  }
203
+ [strongSelf handleBrowserState:newState error:error context:@"authorization request"];
229
204
  });
230
205
 
231
206
  self->_netService = [[NSNetService alloc] initWithDomain:@"local." type:@"_lnp._tcp." name:@"LocalNetworkPrivacy" port:1100];
@@ -281,6 +256,93 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
281
256
  }
282
257
  }
283
258
 
259
+ - (void)completeLocalNetworkFlowWithState:(LocalNetworkPermissionState)state shouldCache:(BOOL)shouldCache
260
+ {
261
+ dispatch_block_t completion = ^{
262
+ [self resetLocalNetwork];
263
+ if (shouldCache && (state == LocalNetworkPermissionStateGranted || state == LocalNetworkPermissionStateDenied)) {
264
+ [[NSUserDefaults standardUserDefaults] setInteger:state forKey:kLocalNetworkPermissionKey];
265
+ [[NSUserDefaults standardUserDefaults] synchronize];
266
+ }
267
+ [self callLocalNetworkCallbacks:state];
268
+ };
269
+
270
+ if ([NSThread isMainThread]) {
271
+ completion();
272
+ } else {
273
+ dispatch_async(dispatch_get_main_queue(), completion);
274
+ }
275
+ }
276
+
277
+ - (NSTimeInterval)resolveLocalNetworkTimeoutFromCommand:(CDVInvokedUrlCommand*)command
278
+ {
279
+ NSTimeInterval timeout = kLocalNetworkDefaultTimeoutSeconds;
280
+ if (!command || !command.arguments || command.arguments.count == 0) {
281
+ return timeout;
282
+ }
283
+
284
+ id rawValue = command.arguments[0];
285
+ if (![rawValue isKindOfClass:[NSDictionary class]]) {
286
+ return timeout;
287
+ }
288
+
289
+ NSDictionary *dict = (NSDictionary *)rawValue;
290
+ id timeoutMsValue = dict[@"timeoutMs"];
291
+ if ([timeoutMsValue isKindOfClass:[NSNumber class]]) {
292
+ double milliseconds = [timeoutMsValue doubleValue];
293
+ if (milliseconds < 0) {
294
+ milliseconds = 0;
295
+ }
296
+ return milliseconds / 1000.0;
297
+ }
298
+
299
+ return timeout;
300
+ }
301
+
302
+ - (BOOL)isPermissionDeniedError:(nw_error_t)error
303
+ {
304
+ if (!error) {
305
+ return NO;
306
+ }
307
+
308
+ nw_error_domain_t errorDomain = nw_error_get_error_domain(error);
309
+ int errorCode = (int)nw_error_get_error_code(error);
310
+ if (errorDomain == nw_error_domain_posix && errorCode == EPERM) {
311
+ return YES;
312
+ }
313
+
314
+ if (errorDomain == nw_error_domain_dns && errorCode == kDNSServiceErr_PolicyDenied) {
315
+ return YES;
316
+ }
317
+
318
+ return NO;
319
+ }
320
+
321
+ - (void)handleBrowserState:(nw_browser_state_t)newState error:(nw_error_t)error context:(NSString *)context
322
+ {
323
+ if (newState == nw_browser_state_waiting || newState == nw_browser_state_failed) {
324
+ if ([self isPermissionDeniedError:error]) {
325
+ nw_error_domain_t domain = nw_error_get_error_domain(error);
326
+ int code = (int)nw_error_get_error_code(error);
327
+ [diagnostic logDebug:[NSString stringWithFormat:@"Local network permission denied during %@ (domain=%d, code=%d)", context, (int)domain, code]];
328
+ [self completeLocalNetworkFlowWithState:LocalNetworkPermissionStateDenied shouldCache:YES];
329
+ return;
330
+ }
331
+
332
+ if (error) {
333
+ nw_error_domain_t domain = nw_error_get_error_domain(error);
334
+ int code = (int)nw_error_get_error_code(error);
335
+ [diagnostic logDebug:[NSString stringWithFormat:@"Local network browser %@ state %ld error domain=%d code=%d", context, (long)newState, (int)domain, code]];
336
+ } else {
337
+ [diagnostic logDebug:[NSString stringWithFormat:@"Local network browser %@ entered state %ld without error", context, (long)newState]];
338
+ }
339
+
340
+ if (newState == nw_browser_state_failed) {
341
+ [self completeLocalNetworkFlowWithState:LocalNetworkPermissionStateIndeterminate shouldCache:NO];
342
+ }
343
+ }
344
+ }
345
+
284
346
  /********************************/
285
347
  #pragma mark - Wifi Plugin API
286
348
  /********************************/
@@ -365,17 +427,13 @@ static NSString*const LOG_TAG = @"Diagnostic_Wifi[native]";
365
427
 
366
428
  - (void)netServiceDidPublish:(NSNetService *)sender {
367
429
  [diagnostic logDebug:@"netServiceDidPublish: Local network permission has been granted"];
368
- [self resetLocalNetwork];
369
- if (_localNetworkTimer) {
370
- [_localNetworkTimer invalidate];
371
- _localNetworkTimer = nil;
372
- }
373
- _isPublishing = NO;
374
-
375
- // cache granted
376
- [[NSUserDefaults standardUserDefaults] setInteger:LocalNetworkPermissionStateGranted forKey:kLocalNetworkPermissionKey];
377
- [[NSUserDefaults standardUserDefaults] synchronize];
430
+ [self completeLocalNetworkFlowWithState:LocalNetworkPermissionStateGranted shouldCache:YES];
431
+ }
378
432
 
379
- [self callLocalNetworkCallbacks:LocalNetworkPermissionStateGranted];
433
+ - (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary<NSString *,NSNumber *> *)errorDict {
434
+ NSNumber *errorDomain = errorDict[NSNetServicesErrorDomain];
435
+ NSNumber *errorCode = errorDict[NSNetServicesErrorCode];
436
+ [diagnostic logDebug:[NSString stringWithFormat:@"netService didNotPublish (domain=%@, code=%@)", errorDomain, errorCode]];
437
+ [self completeLocalNetworkFlowWithState:LocalNetworkPermissionStateDenied shouldCache:YES];
380
438
  }
381
439
  @end
@@ -89,6 +89,8 @@ var Diagnostic = (function(){
89
89
  "DENIED_ALWAYS": "DENIED_ALWAYS",
90
90
  // App has not yet requested access to this permission.
91
91
  "NOT_REQUESTED": "NOT_REQUESTED",
92
+ // Platform has not provided a definitive status (e.g. timeout/indeterminate)
93
+ "UNKNOWN": "unknown",
92
94
  // Limited access to the photo library on Android 14 (API 34) and above
93
95
  "LIMITED": "LIMITED"
94
96
  };
@@ -22,6 +22,7 @@ var Diagnostic = (function(){
22
22
  */
23
23
  Diagnostic.permissionStatus = {
24
24
  "NOT_REQUESTED": "not_determined", // App has not yet requested this permission
25
+ "UNKNOWN": "unknown", // Platform has not provided a definitive status (e.g. timeout/indeterminate)
25
26
  "DENIED_ALWAYS": "denied_always", // User denied access to this permission
26
27
  "RESTRICTED": "restricted", // Permission is unavailable and user cannot enable it. For example, when parental controls are in effect for the current user.
27
28
  "GRANTED": "authorized", // User granted access to this permission
@@ -656,8 +657,9 @@ var Diagnostic = (function(){
656
657
  * This callback function is passed a single boolean parameter which is TRUE if the app is authorized to use Local Network.
657
658
  * @param {Function} errorCallback - The callback which will be called when operation encounters an error.
658
659
  * This callback function is passed a single string parameter containing the error message.
660
+ * @param {Object} [options] - Optional timeout control object containing an optional `timeoutMs` number, matching the options accepted by `getLocalNetworkAuthorizationStatus()`.
659
661
  */
660
- Diagnostic.isLocalNetworkAuthorized = function(successCallback, errorCallback) {
662
+ Diagnostic.isLocalNetworkAuthorized = function(successCallback, errorCallback, options) {
661
663
  if(cordova.plugins.diagnostic.wifi){
662
664
  cordova.plugins.diagnostic.wifi.isLocalNetworkAuthorized.apply(this, arguments);
663
665
  }else{
@@ -667,16 +669,18 @@ var Diagnostic = (function(){
667
669
 
668
670
  /**
669
671
  * Returns the app's Local Network authorization status.
670
- * On iOS 14+ this returns one of the values in Diagnostic.permissionStatus: NOT_REQUESTED, GRANTED, DENIED_ALWAYS.
672
+ * On iOS 14+ this returns one of the values in Diagnostic.permissionStatus: NOT_REQUESTED, GRANTED, DENIED_ALWAYS, UNKNOWN.
671
673
  * On iOS versions prior to 14, this always returns GRANTED as no authorization is required.
672
674
  *
673
675
  * @param {Function} successCallback - The callback which will be called when operation is successful.
674
676
  * This callback function is passed a single string parameter which is one of the values in Diagnostic.permissionStatus:
675
- * NOT_REQUESTED, GRANTED, DENIED_ALWAYS.
677
+ * NOT_REQUESTED, GRANTED, DENIED_ALWAYS, UNKNOWN.
676
678
  * @param {Function} errorCallback - The callback which will be called when operation encounters an error.
677
679
  * This callback function is passed a single string parameter containing the error message.
680
+ * @param {Object} [options] - Optional control over the timeout (defaults to 2 seconds) used when inferring the permission state.
681
+ * Provide `{ timeoutMs: <number> }` to override the timeout in milliseconds.
678
682
  */
679
- Diagnostic.getLocalNetworkAuthorizationStatus = function(successCallback, errorCallback) {
683
+ Diagnostic.getLocalNetworkAuthorizationStatus = function(successCallback, errorCallback, options) {
680
684
  if(cordova.plugins.diagnostic.wifi){
681
685
  cordova.plugins.diagnostic.wifi.getLocalNetworkAuthorizationStatus.apply(this, arguments);
682
686
  }else{
@@ -691,7 +695,7 @@ var Diagnostic = (function(){
691
695
  *
692
696
  * @param {Function} successCallback - The callback which will be called when operation is successful.
693
697
  * This callback function is passed a single string parameter which is one of the values in Diagnostic.permissionStatus:
694
- * NOT_REQUESTED, GRANTED, DENIED_ALWAYS.
698
+ * NOT_REQUESTED, GRANTED, DENIED_ALWAYS, UNKNOWN.
695
699
  * @param {Function} errorCallback - The callback which will be called when operation encounters an error.
696
700
  * This callback function is passed a single string parameter containing the error message.
697
701
  */
@@ -36,6 +36,9 @@ var Diagnostic_Wifi = (function(){
36
36
  case -1: // LocalNetworkPermissionStateDenied
37
37
  status = Diagnostic.permissionStatus.DENIED_ALWAYS;
38
38
  break;
39
+ case -2: // LocalNetworkPermissionStateIndeterminate
40
+ status = Diagnostic.permissionStatus.UNKNOWN;
41
+ break;
39
42
  case 0: // LocalNetworkPermissionStateUnknown
40
43
  default:
41
44
  status = Diagnostic.permissionStatus.NOT_REQUESTED;
@@ -44,6 +47,18 @@ var Diagnostic_Wifi = (function(){
44
47
  successCallback(status);
45
48
  }
46
49
 
50
+ function buildLocalNetworkArgs(options) {
51
+ if (options && typeof options === "object" && options.timeoutMs != null) {
52
+ var timeoutMs = Number(options.timeoutMs);
53
+ if (isFinite(timeoutMs)) {
54
+ return [{
55
+ timeoutMs: Math.max(0, timeoutMs)
56
+ }];
57
+ }
58
+ }
59
+ return [];
60
+ }
61
+
47
62
  /*****************************
48
63
  *
49
64
  * Protected member functions
@@ -100,8 +115,10 @@ var Diagnostic_Wifi = (function(){
100
115
  * This callback function is passed a single boolean parameter which is TRUE if the app is authorized to use Local Network.
101
116
  * @param {Function} errorCallback - The callback which will be called when operation encounters an error.
102
117
  * This callback function is passed a single string parameter containing the error message.
118
+ * @param {Object} [options] - Optional timeout control object containing an optional `timeoutMs` number.
103
119
  */
104
- Diagnostic_Wifi.isLocalNetworkAuthorized = function(successCallback, errorCallback) {
120
+ Diagnostic_Wifi.isLocalNetworkAuthorized = function(successCallback, errorCallback, options) {
121
+ var args = buildLocalNetworkArgs(options);
105
122
  return cordova.exec(function(status) {
106
123
  var authorized = (status === 1); // LocalNetworkPermissionStateAuthorized
107
124
  successCallback(authorized);
@@ -109,29 +126,32 @@ var Diagnostic_Wifi = (function(){
109
126
  errorCallback,
110
127
  'Diagnostic_Wifi',
111
128
  'getLocalNetworkAuthorizationStatus',
112
- []);
129
+ args);
113
130
  };
114
131
 
115
132
 
116
133
  /**
117
134
  * Returns the app's Local Network authorization status.
118
- * On iOS 14+ this returns one of the values in Diagnostic.permissionStatus: NOT_REQUESTED, GRANTED, DENIED_ALWAYS.
135
+ * On iOS 14+ this returns one of the values in Diagnostic.permissionStatus: NOT_REQUESTED, GRANTED, DENIED_ALWAYS, UNKNOWN.
119
136
  * On iOS versions prior to 14, this always returns GRANTED as no authorization is required.
120
137
  *
121
138
  * @param {Function} successCallback - The callback which will be called when operation is successful.
122
139
  * This callback function is passed a single string parameter which is one of the values in Diagnostic.permissionStatus:
123
- * NOT_REQUESTED, GRANTED, DENIED_ALWAYS.
140
+ * NOT_REQUESTED, GRANTED, DENIED_ALWAYS, UNKNOWN.
124
141
  * @param {Function} errorCallback - The callback which will be called when operation encounters an error.
125
142
  * This callback function is passed a single string parameter containing the error message.
143
+ * @param {Object} [options] - Optional control over the timeout used when inferring the permission state.
144
+ * Defaults to 2 seconds when omitted. Provide `{ timeoutMs: <number> }` to override the timeout in milliseconds.
126
145
  */
127
- Diagnostic_Wifi.getLocalNetworkAuthorizationStatus = function(successCallback, errorCallback) {
146
+ Diagnostic_Wifi.getLocalNetworkAuthorizationStatus = function(successCallback, errorCallback, options) {
147
+ var args = buildLocalNetworkArgs(options);
128
148
  return cordova.exec(function(nativeStatus) {
129
149
  processLocalNetworkStatus(nativeStatus, successCallback);
130
150
  },
131
151
  errorCallback,
132
152
  'Diagnostic_Wifi',
133
153
  'getLocalNetworkAuthorizationStatus',
134
- []);
154
+ args);
135
155
  };
136
156
 
137
157
  /**
@@ -141,7 +161,7 @@ var Diagnostic_Wifi = (function(){
141
161
  *
142
162
  * @param {Function} successCallback - The callback which will be called when operation is successful.
143
163
  * This callback function is passed a single string parameter which is one of the values in Diagnostic.permissionStatus:
144
- * NOT_REQUESTED, GRANTED, DENIED_ALWAYS.
164
+ * NOT_REQUESTED, GRANTED, DENIED_ALWAYS, UNKNOWN.
145
165
  * @param {Function} errorCallback - The callback which will be called when operation encounters an error.
146
166
  * This callback function is passed a single string parameter containing the error message.
147
167
  */
@@ -157,4 +177,4 @@ var Diagnostic_Wifi = (function(){
157
177
 
158
178
  return Diagnostic_Wifi;
159
179
  });
160
- module.exports = new Diagnostic_Wifi();
180
+ module.exports = new Diagnostic_Wifi();