Class: WifiWand::DisconnectManager

Inherits:
Object
  • Object
show all
Includes:
StringPredicates, Timing
Defined in:
lib/wifi_wand/services/disconnect_manager.rb

Constant Summary collapse

EXPECTED_NETWORK_ERRORS =
NetworkErrorConstants::EXPECTED_NETWORK_ERRORS
NETWORK_OPERATION_COMMAND_ERRORS =
NetworkErrorConstants::NETWORK_OPERATION_COMMAND_ERRORS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Timing

monotonic_now, status_deadline, status_timeout_for

Methods included from StringPredicates

string_nil_or_blank?, string_nil_or_empty?

Constructor Details

#initialize(model, runtime_config: nil) ⇒ DisconnectManager

Returns a new instance of DisconnectManager.



21
22
23
24
# File 'lib/wifi_wand/services/disconnect_manager.rb', line 21

def initialize(model, runtime_config: nil)
  @model = model
  @runtime_config = runtime_config || RuntimeConfig.new
end

Instance Attribute Details

#modelObject (readonly)

Returns the value of attribute model.



18
19
20
# File 'lib/wifi_wand/services/disconnect_manager.rb', line 18

def model
  @model
end

Instance Method Details

#disassociated_stable?Boolean

Returns:

  • (Boolean)


73
74
75
76
77
78
79
80
81
82
# File 'lib/wifi_wand/services/disconnect_manager.rb', line 73

def disassociated_stable?
  deadline = monotonic_now + disconnect_stability_window_in_secs

  loop do
    return false if disconnect_association_state.fetch(:associated)
    return true if monotonic_now >= deadline

    sleep(WifiWand::TimingConstants::DEFAULT_WAIT_INTERVAL)
  end
end

#disconnectObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/wifi_wand/services/disconnect_manager.rb', line 26

def disconnect
  original_network_name = nil
  return nil unless model.wifi_on?

  # Capture the SSID before asking the OS to disconnect so timeout handling
  # can still report which network we expected to leave.
  association_state = disconnect_association_state
  original_network_name = association_state.fetch(:network_name)
  return nil unless association_state.fetch(:associated)

  model._disconnect
  # A disconnect only counts as success once the interface actually reports
  # no active association, mirroring the postcondition checks used elsewhere.
  wait_until_disassociated!(timeout_in_secs: WifiWand::TimingConstants::STATUS_WAIT_TIMEOUT_SHORT)
  # On some systems the SSID can disappear briefly during state churn before
  # the radio re-associates. Require a short stable disassociation window so
  # a transient nil SSID does not count as a successful disconnect.
  unless disassociated_stable?
    raise(WifiWand::WaitTimeoutError.new(
      action:  :disassociated,
      timeout: disconnect_stability_window_in_secs
    ))
  end

  nil
rescue *NETWORK_OPERATION_COMMAND_ERRORS => e
  raise(disconnect_command_failure(original_network_name, e))
rescue WifiWand::WaitTimeoutError
  # Re-check the SSID after a timeout so callers get the best available
  # diagnostic when the disconnect command ran but the radio stayed associated.
  current_network_name = begin
    model.connected_network_name
  rescue WifiWand::Error
    nil
  end
  lingering_network_name = current_network_name || original_network_name
  reason = lingering_network_name ? "still associated with '#{lingering_network_name}'" :
    'interface remained associated'
  raise(NetworkDisconnectionError.new(network_name: lingering_network_name, reason: reason))
end

#disconnect_stability_window_in_secsObject

Returns true when the model considers the requested network fully usable. Subclasses may override this to require stronger OS-specific readiness.



69
70
71
# File 'lib/wifi_wand/services/disconnect_manager.rb', line 69

def disconnect_stability_window_in_secs
  WifiWand::TimingConstants::DEFAULT_WAIT_INTERVAL * 2
end