Apple Nearby Actions: The Protocol Behind iOS BLE Popups
The Popup That Appears Out of Nowhere
You're sitting near someone's iPhone and a sheet slides up from the bottom of the screen: "AirPods Pro - Connect." No one tapped anything. The iPhone wasn't actively searching. The popup just appeared.
This is Apple's Continuity Protocol at work. It's the same system behind AirDrop, Handoff, and the instant AirPods pairing experience. It also happens to be why iOS devices are trivially easy to flood with fake BLE popups - because the protocol has no authentication at the advertisement layer.
This article breaks down how Nearby Actions work at the packet level, what byte values trigger which UI prompts, how iOS handles (and sometimes mishandles) rate limiting, and how the BLEShark Nano's BLESpam mode interacts with it.
What Is the Continuity Protocol?
Apple's Continuity Protocol is a proprietary suite of BLE-based proximity features. It covers device pairing (AirPods, Apple Watch), sharing (AirDrop), clipboard sync (Universal Clipboard), handoff between devices, and various setup flows for HomePod, Apple TV, and other hardware.
All of these features share one underlying mechanism: Bluetooth Low Energy advertisement packets with a manufacturer-specific payload that iOS recognizes and acts on in the background, even when the screen is off.
At the BLE layer, these are completely standard ADV_IND packets - the same broadcast format any BLE peripheral uses. What makes them Apple-specific is the content of one particular AD structure.
BLE Manufacturer Specific Data: The Hook
graph TD
subgraph "BLE Advertisement Packet"
PREAMBLE["Preamble
(1 byte)"]
ACCESS["Access Address
(4 bytes)
0x8E89BED6"]
PDU["PDU Header
(2 bytes)"]
ADDR["Advertiser Address
(6 bytes, randomized)"]
AD["AD Structures"]
end
subgraph "AD Structure: Manufacturer Specific Data"
LEN["Length byte"]
TYPE["Type: 0xFF
(Manufacturer Specific)"]
CID["Company ID: 0x004C
(Apple Inc.)"]
CONT["Continuity Type"]
PAYLOAD["Payload Data"]
end
subgraph "Continuity Message Types"
T0F["0x0F: Nearby Action
(triggers popup)"]
T07["0x07: Proximity Pairing
(AirPods popup)"]
T10["0x10: Nearby Info
(device status)"]
T0C["0x0C: Handoff
(cross-device continuity)"]
T05["0x05: AirDrop
(file sharing discovery)"]
end
PREAMBLE --> ACCESS --> PDU --> ADDR --> AD
AD --> LEN --> TYPE --> CID --> CONT --> PAYLOAD
CONT --> T0F & T07 & T10 & T0C & T05
BLE advertisement packet structure - Apple embeds Continuity protocol data in Manufacturer Specific Data fields using company ID 0x004C
BLE advertisement payloads are made up of AD structures - type-length-value records. Each record starts with a length byte, then a type byte, then the data. One important AD type is 0xFF: Manufacturer Specific Data.
This type begins with a 16-bit company identifier assigned by the Bluetooth SIG. Apple's assigned company ID is 0x004C. In little-endian byte order, that's 0x4C 0x00.
So the header for every Apple Continuity advertisement looks like this:
Length: 0x?? (total length of this AD structure)
AD Type: 0xFF (Manufacturer Specific Data)
Company ID: 0x4C 0x00 (Apple, Inc.)
Message Type: 0x?? (identifies the Continuity feature)
Msg Length: 0x?? (payload byte count)
Payload: [varies by message type]
When iOS receives a BLE advertisement containing this structure, it parses the message type and decides what UI action, if any, to trigger.
Continuity Message Types
Apple has never officially published the Continuity Protocol specification. What's known comes from open-source reverse engineering efforts, primarily the work behind projects like Apple BLE Research and OpenHaystack. The currently documented message types include:
- 0x05 - AirDrop
- 0x07 - Proximity Pairing (AirPods and Beats headphones)
- 0x0A - Hey Siri
- 0x0B - AirPlay Target
- 0x0C - AirPlay Source
- 0x0D - Magic Switch (automatic device switching)
- 0x0E - Handoff
- 0x0F - Nearby Action (setup and pairing prompts)
- 0x10 - Nearby Info
- 0x12 - FindMy
For BLE popup spam, type 0x0F (Nearby Action) is the most relevant. This is the type that triggers full-screen or sheet-style UI prompts on iOS - the kind that slide up from the bottom and demand attention.
Nearby Action (0x0F): The Popup Engine
The Nearby Action message structure after the company ID looks like this:
Message Type: 0x0F
Length: 0x05 (typically 5 bytes of payload)
Action Code: 0x?? (determines which popup appears)
Flags: 0x?? (state flags)
Auth Tag: 0x?? 0x?? 0x?? (3-byte authentication field)
The action code byte is what determines the popup type. Documented action codes include:
- 0x01 - Apple TV Setup
- 0x06 - Instant Hotspot
- 0x09 - Apple TV New User
- 0x0B - Apple TV Apple ID Setup
- 0x0C - Apple TV Wireless Audio Sync
- 0x0D - Apple TV Homekit Setup
- 0x0E - Apple TV Keyboard
- 0x13 - Apple Watch Setup
- 0x20 - AirPods
- 0x27 - Apple TV Pair
- 0x2B - Apple Vision Pro
- 0x47 - HomePod Setup
Each code triggers a different popup with different wording. The authentication tag bytes are supposed to verify the action originated from a real Apple device. In practice, iOS accepts arbitrary values in these fields for most action types - the verification is either not implemented or relies on other mechanisms that aren't enforced at the advertisement level.
Proximity Pairing (0x07): The AirPods Flow
Type 0x07 handles the "AirPods detected nearby" popup - the one that shows the device name, battery levels, and a "Connect" button. This message type has a more complex structure because it carries actual device state:
Message Type: 0x07
Length: 0x19 (25 bytes)
Device Model: 0x?? 0x?? (2 bytes - identifies AirPods model)
Status: 0x?? (bitmask: case open, ears in, etc.)
Battery: 0x?? 0x?? (left/right pod charge, case charge packed)
Lid Open: 0x?? (lid open counter)
Color: 0x?? (case color ID)
Suffix: 0x00
Encrypted: [remaining bytes - device-specific encrypted data]
The model ID determines which AirPods graphic iOS displays in the popup. Model IDs include:
- 0x0220 - AirPods 1st generation
- 0x0F20 - AirPods 2nd generation
- 0x1320 - AirPods 3rd generation
- 0x0E20 - AirPods Pro
- 0x1420 - AirPods Pro 2nd generation
- 0x0A20 - AirPods Max
- 0x0620 - Beats X
- 0x0320 - Beats Solo3
This is why BLESpam can impersonate specific AirPods models - you just need to set the right 2-byte model ID in the advertisement. The battery fields and status bytes don't need to be accurate; iOS renders whatever values it receives.
Why This Works Without Authentication
The fundamental problem is that BLE advertisement channels are open broadcast. Any device within range can transmit any advertisement payload, and receiving devices can't verify the transmitter's identity before parsing the payload and rendering UI.
For the Proximity Pairing type (0x07), there's an encrypted section that a real AirPods case generates using a device-specific key. iOS can verify this when establishing an actual connection - but the popup appears before that verification happens. The UI is triggered by the advertisement alone.
For Nearby Actions (0x0F), the authentication tag is present in the spec but appears to be minimally validated for many action codes. The popup triggers on advertisement receipt, not after any handshake.
This is a deliberate tradeoff. Requiring cryptographic verification before showing a "HomePod detected" prompt would add latency and complexity. Apple designed these features for convenience first. The security assumption was that advertising random action codes wouldn't be useful to an attacker - a reasonable assumption until BLE spam tools became widely available.
iOS Version Behavior
Apple has adjusted how aggressively iOS responds to Nearby Action advertisements across versions:
iOS 16 and earlier: Most action codes trigger popups with minimal rate limiting. A device broadcasting action codes continuously could trigger repeated popups on nearby iPhones.
iOS 17.0 to 17.1: Similar behavior, though Apple quietly reduced the popup frequency for some action codes. The AirPods popup (0x07) still triggers reliably.
iOS 17.2 and later: Apple introduced more aggressive rate limiting on Nearby Action popups. After a popup is dismissed, the same device (identified by its MAC address) is suppressed for a cooldown period. However, since BLE devices can use randomly generated MAC addresses that change on every advertisement, this rate limiting can be bypassed by rotating the advertised MAC.
iOS 17.3+: Apple further tightened restrictions. Some action codes that previously triggered full popups now trigger smaller, less intrusive notifications. The AirPods Proximity Pairing (0x07) still generates visible UI because Apple needs it to work reliably for actual AirPods.
The core issue hasn't been fully resolved - it can't be without breaking the legitimate proximity pairing experience that millions of users rely on.
BLEShark Nano and iOS BLESpam
sequenceDiagram
participant BN as BLEShark Nano
participant AIR as 2.4GHz Airspace
participant IP as iPhone (Victim)
participant AP as AirPods (Spoofed)
Note over BN: User selects BLESpam mode
loop Every ~20ms
BN->>AIR: Broadcast Nearby Action (0x0F)
Random action type + flags
AIR->>IP: BLE advertisement received
IP->>IP: Parse Apple Continuity header
IP->>IP: Match action type to UI template
IP-->>IP: Display popup notification
end
Note over IP: User dismisses popup
loop Continuous
BN->>AIR: Broadcast Proximity Pairing (0x07)
Spoofed AirPods model ID
AIR->>IP: Advertisement received
IP->>IP: Render "Not Your AirPods" popup
IP-->>IP: Another popup appears
end
Note over BN: Cycles through device models:
AirPods Pro, Beats, AirTag,
Apple TV, etc.
Note over IP: Continuous popup storm
(DoS on user attention)
BLESpam attack sequence - the BLEShark Nano floods BLE advertisements mimicking Apple Continuity packets, triggering cascading popups on nearby iOS devices
The BLEShark Nano's BLESpam mode includes an iOS-specific option that cycles through the documented Nearby Action codes and Proximity Pairing model IDs. The device broadcasts successive advertisements on BLE advertising channels 37, 38, and 39, varying the action code and MAC address per cycle.
At the packet level, BLEShark is generating valid BLE ADV_IND frames with correctly structured Continuity Protocol payloads. It isn't exploiting a bug - it's using the protocol exactly as documented, just with arbitrary source identities.
The practical use case for security teams is demonstrating the Bluetooth attack surface in a controlled environment - showing stakeholders that proximity-based UI prompts can be triggered by nearby hardware, and that there's no user action required to see them. For iOS specifically, this can be relevant in high-density environments where fake prompts could distract or confuse users.
For testing at scale across a space, BLEShark Nanos can be deployed as part of a Shiver mesh (up to 16 nodes, ESP-NOW, 20-50m range). Each node operates its BLE radio independently, which means you can run coordinated BLESpam across multiple physical locations simultaneously - or compare popup trigger rates across different iOS versions in the same room.
Note: BLE and mesh connectivity are mutually exclusive on a single BLEShark Nano due to the shared radio. When BLESpam is active, the device uses its BLE radio for advertising. It hops back briefly for mesh synchronization windows, so mesh coordination still works, but continuous BLE advertising does pause during those windows.
What BLE Scanners Can See
If you want to see these advertisements directly, you can use nRF Connect (Android or iOS) or run a BLE scanner in passive mode. You'll see devices advertising with company ID 0x004C and can decode the message type and payload manually.
On the BLEShark Nano's BLE scanning mode, devices broadcasting Apple Continuity advertisements show up with manufacturer data decoded by OUI lookup. The scan results identify the advertisement as Apple manufacturer-specific data, though full Continuity Protocol decoding depends on firmware version.
Defense and Mitigation
graph TD
POPUP["Unexpected BLE popup
on your iPhone"] --> Q1{"Recognize the
device name?"}
Q1 -->|"Yes"| LEGIT["Likely legitimate
Continuity event"]
Q1 -->|"No"| Q2{"Multiple popups
in quick succession?"}
Q2 -->|"Yes"| SPAM["Likely BLESpam
attack"]
Q2 -->|"No"| Q3{"In public
location?"}
Q3 -->|"Yes"| CAUTION["Treat as suspicious
(do not interact)"]
Q3 -->|"No"| LEGIT
SPAM --> D1["Dismiss all popups"]
SPAM --> D2["Disable Bluetooth
temporarily"]
SPAM --> D3["Move away from
attacker range (~10m)"]
subgraph "iOS Version Mitigations"
IOS17["iOS 17.2+
Reduced popup frequency"]
IOS18["iOS 18+
Further rate limiting"]
OLDER["iOS 16 and earlier
No mitigation"]
end
D2 --> IOS17 & IOS18 & OLDER
User decision tree for unexpected BLE popups - distinguishing legitimate Continuity events from BLESpam attacks
From Apple's side, the only real mitigation path is tighter rate limiting and better suppression logic - which iOS 17.2+ has moved toward. Complete prevention would require authentication at the advertisement layer, which isn't part of the BLE spec and would break the instant pairing experience.
For end users: keeping iOS updated is the most effective step. Apple has consistently reduced popup frequency with each point release since the issue became widely known.
For enterprise environments where Bluetooth popup spam could be a distraction in meetings or public demos, disabling Bluetooth when it isn't needed eliminates the attack surface entirely. MDM-managed devices can enforce Bluetooth restrictions via configuration profile.
The Bigger Picture
The Apple Continuity Protocol is a good example of a design where convenience and security are in direct tension. The zero-friction pairing experience that makes AirPods feel magical is powered by the same open advertisement system that makes popup spam possible. There's no version of this protocol that delivers instant pairing UI while also being immune to spoofed advertisements - at least not without a hardware root of trust in the advertisement itself, which BLE doesn't provide.
Understanding the packet structure matters for security researchers evaluating environments where Bluetooth proximity features are in use. Knowing exactly which byte triggers which popup, and which iOS versions respond to which action codes, is the difference between a vague "BLE is potentially exploitable" note in a report and a specific, actionable finding.
The BLEShark Nano makes that kind of hands-on protocol exploration accessible without needing a full SDR setup or a custom Linux BLE stack.
Get BLEShark Nano - $36.99+