KEY1 toggle: bring up the phone-access AP that serves the minimal wardriving page, WITHOUT stopping wardriving. The AP runs on a spare adapter when one exists; otherwise it BORROWS a radio from wardriving's scan set (removing it so the scan loop's _prepare_interface
(self)
| 2458 | |
| 2459 | # Stop current AP if running |
| 2460 | if self.ap_mode_active: |
| 2461 | self.stop_ap_mode() |
| 2462 | |
| 2463 | # Start AP mode (endless loop will handle timeout management) |
| 2464 | if self.endless_loop_active: |
| 2465 | # Use endless loop AP mode |
| 2466 | self._endless_loop_start_ap_mode() |
| 2467 | return True |
| 2468 | else: |
| 2469 | # Fallback to regular AP mode if endless loop not active |
| 2470 | return self.start_ap_mode() |
| 2471 | |
| 2472 | def start_ap_mode(self, captive=True): |
| 2473 | """Start Access Point mode. |
| 2474 | |
| 2475 | captive=False starts an accessory AP that advertises no gateway and no |
| 2476 | DNS, so a phone joining it keeps using cellular for internet (see |
| 2477 | _dnsmasq_config_no_internet). Used by the wardriving phone-access AP. |
| 2478 | """ |
| 2479 | if self.ap_mode_active: |
| 2480 | self.logger.info("AP mode already active") |
| 2481 | self.ap_logger.info("AP mode start requested but already active") |
| 2482 | return True |
| 2483 | |
| 2484 | try: |
| 2485 | self.logger.info(f"Starting AP mode: {self.ap_ssid}") |
| 2486 | self.ap_logger.info(f"Starting AP mode: SSID={self.ap_ssid}, Interface={self.ap_interface}") |
| 2487 | self.ap_logger.info(f"AP Configuration: IP={self.ap_ip}, Subnet={self.ap_subnet}") |
| 2488 | |
| 2489 | # Scan and cache WiFi networks before starting AP mode |
| 2490 | self.logger.info("Scanning for WiFi networks before starting AP mode...") |
| 2491 | self.ap_logger.info("Pre-AP scan: Scanning for available networks to cache") |
| 2492 | try: |
| 2493 | self.available_networks = self.scan_networks() |
| 2494 | cached_count = len([n for n in self.available_networks if not n.get('instruction')]) |
| 2495 | self.logger.info(f"Cached {cached_count} networks before starting AP mode") |
| 2496 | self.ap_logger.info(f"Pre-AP scan complete: Cached {cached_count} networks") |
| 2497 | except Exception as scan_error: |
| 2498 | self.logger.warning(f"Pre-AP network scan failed: {scan_error}") |
| 2499 | self.ap_logger.warning(f"Pre-AP scan failed but continuing: {scan_error}") |
| 2500 | self.available_networks = [] |
| 2501 | |
| 2502 | # Radio must not be rfkill-blocked or hostapd fails to start |
| 2503 | self._ensure_radios_unblocked() |
| 2504 | |
| 2505 | # Create hostapd configuration |
| 2506 | self.ap_logger.info("Creating hostapd configuration...") |
| 2507 | if not self._create_hostapd_config(): |
| 2508 | self.ap_logger.error("Failed to create hostapd configuration") |
| 2509 | return False |
| 2510 | |
| 2511 | # Create dnsmasq configuration |
| 2512 | self.ap_logger.info("Creating dnsmasq configuration...") |
| 2513 | if not self._create_dnsmasq_config(dns_enabled=True, captive=captive): |
| 2514 | self.ap_logger.error("Failed to create dnsmasq configuration") |
| 2515 | return False |
| 2516 | |
| 2517 | # Configure network interface |
nothing calls this directly
no test coverage detected