<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Kanunnikov</title>
    <description>The latest articles on DEV Community by Alex Kanunnikov (@lifeart).</description>
    <link>https://dev.to/lifeart</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F102818%2Fcb0e0e5c-8e47-409a-9d9d-01e4bb3a0f27.jpeg</url>
      <title>DEV Community: Alex Kanunnikov</title>
      <link>https://dev.to/lifeart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lifeart"/>
    <language>en</language>
    <item>
      <title>Inside Apple AirPods Auto-Switching: Bluetooth Continuity, AAP &amp; Python Packet Sniffing</title>
      <dc:creator>Alex Kanunnikov</dc:creator>
      <pubDate>Mon, 14 Jul 2025 13:56:36 +0000</pubDate>
      <link>https://dev.to/lifeart/inside-apple-airpods-auto-switching-bluetooth-continuity-aap-python-packet-sniffing-jf6</link>
      <guid>https://dev.to/lifeart/inside-apple-airpods-auto-switching-bluetooth-continuity-aap-python-packet-sniffing-jf6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; — AirPods jump between your iPhone, iPad &amp;amp; Mac by juggling two Bluetooth links (Classic for audio, BLE for control), iCloud-synced keys, Continuity advertisements and a secret L2CAP channel (AAP). This post explains every layer and shows how to watch the magic with Python &amp;amp; Wireshark.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1 · Architecture in 90 seconds
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What AirPods use&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bluetooth Classic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A2DP (music) · HFP (calls)&lt;/td&gt;
&lt;td&gt;Actual audio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bluetooth LE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom service ➜ L2CAP PSM &lt;code&gt;0x1001&lt;/code&gt; (AAP)&lt;/td&gt;
&lt;td&gt;Battery, sensors, ANC, control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Continuity BLE ads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;“Proximity Pairing” &amp;amp; device-activity beacons&lt;/td&gt;
&lt;td&gt;Lets every Apple device &lt;em&gt;know&lt;/em&gt; who’s doing what&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;iCloud Keychain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shares pairing keys across your Apple ID&lt;/td&gt;
&lt;td&gt;Zero-config pairing on all devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;H1/H2 chip&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast link setup &amp;amp; encrypted state&lt;/td&gt;
&lt;td&gt;Switch in ~1 s instead of normal 5–10 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The result: only &lt;strong&gt;one audio stream at a time&lt;/strong&gt;, but the OS hands off ownership so quickly that it feels like multipoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  2 · Continuity BLE: Finding Your Pods in the Ether
&lt;/h2&gt;

&lt;p&gt;Apple devices broadcast &amp;amp; consume proprietary BLE packets (doc-type &lt;em&gt;0x0220&lt;/em&gt; for AirPods).&lt;br&gt;&lt;br&gt;
Open a terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo timeout &lt;/span&gt;15s btmon | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-A2&lt;/span&gt; &lt;span class="nt"&gt;-B2&lt;/span&gt; 0x004c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll catch lines like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; ADV_IND, Apple, Inc. (0x004C), RSSI -43
  22 20 19 aa bb cc dd ee ...   # 0x2220 = AirPods advert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fields decoded by Martin et al. (PETS 2020):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Byte(s)&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;+0&lt;/code&gt;-&lt;code&gt;+1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Message type &lt;code&gt;0x0220&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;+4&lt;/code&gt;-&lt;code&gt;+6&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Serial hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lid-open counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Battery L&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Battery R&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Battery Case + flags&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Apple devices &lt;em&gt;listen&lt;/em&gt;; when your Mac starts media playback it yells “I need the Pods!” and your iPhone politely disconnects.&lt;/p&gt;




&lt;h2&gt;
  
  
  3 · AAP: The Secret L2CAP Channel (0x1001)
&lt;/h2&gt;

&lt;p&gt;Reverse-engineered by &lt;strong&gt;LibrePods&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Handshake
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bluetooth&lt;/span&gt;  &lt;span class="c1"&gt;# PyBluez
&lt;/span&gt;&lt;span class="n"&gt;ADDR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XX:XX:XX:XX:XX:XX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# AirPods MAC from `bluetoothctl`
&lt;/span&gt;&lt;span class="n"&gt;PSM&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x1001&lt;/span&gt;                &lt;span class="c1"&gt;# Apple Audio Protocol
&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bluetooth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BluetoothSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bluetooth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L2CAP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ADDR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PSM&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# Stage-1: 4-byte magic
&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\x01\x00\x00\x00&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Handshake-1:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full spec: (see &lt;a href="https://raw.githubusercontent.com/kavishdevar/librepods/main/AAP%20Definitions.md" rel="noopener noreferrer"&gt;“AAP Definitions”&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Subscribing for notifications
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Tell Pods we want battery/in-ear updates
&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromhex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;07 00 00 00 00 00 0A 00 01 00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;pkt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Notify:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A battery frame looks like:&lt;br&gt;&lt;br&gt;
&lt;code&gt;08 00 07 00 &amp;lt;left%&amp;gt; &amp;lt;right%&amp;gt; &amp;lt;case%&amp;gt; &amp;lt;flags&amp;gt;&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4 · What Actually Triggers a Switch?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;BLE/Wi-Fi Message&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Incoming phone call&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Telephony Continuity&lt;/strong&gt; ad&lt;/td&gt;
&lt;td&gt;🚨 High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media play on other device&lt;/td&gt;
&lt;td&gt;“Activity-level” &lt;code&gt;Active=YES&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User taps device in AirPlay menu&lt;/td&gt;
&lt;td&gt;UI event → OS forces takeover&lt;/td&gt;
&lt;td&gt;Guaranteed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Current host drops AAP &amp;amp; A2DP → target host connects in ~1 s.&lt;br&gt;&lt;br&gt;
No real “multipoint”: just lightning-fast hand-offs.&lt;/p&gt;




&lt;h2&gt;
  
  
  5 · Watch It Live: Python + Bleak
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bleak&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BleakScanner&lt;/span&gt;

&lt;span class="n"&gt;APPLE_MFG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x004C&lt;/span&gt;
&lt;span class="n"&gt;TYPE_AIRPODS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\x22\x20&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_airpods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;serial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;lid&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;BleakScanner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;scanner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;scanner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_discovered_device&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;adv&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;adv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manufacturer_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APPLE_MFG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TYPE_AIRPODS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_airpods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manufacturer_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;APPLE_MFG&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AirPods &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; lid=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; L=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% R=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% Case=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit &lt;strong&gt;Play&lt;/strong&gt; on your Mac → watch the adverts stop (Pods connected), then resume from iPhone when call ends.&lt;/p&gt;




&lt;h2&gt;
  
  
  6 · Opening AAP on Linux (BlueZ ≥ 5.66)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# kernel must allow PSM 0x1001&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;1 | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /sys/kernel/debug/bluetooth/allowed_psm/0x1001
python3 aap_client.py  &lt;span class="c"&gt;# from LibrePods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;aap_client.py&lt;/code&gt; dumps live events: ANC mode, in-ear detect, etc.&lt;br&gt;&lt;br&gt;
Great starting point if you’re building &lt;strong&gt;multipoint for Android&lt;/strong&gt; (CAPod is doing exactly that).&lt;/p&gt;




&lt;h2&gt;
  
  
  7 · FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q · Can I keep two devices connected at once?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Not today. Only one A2DP stream. Apple optimises &lt;em&gt;switching speed&lt;/em&gt;, not true multipoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q · Why don’t W1 (1st-gen) Pods auto-switch?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The W1 lacks BLE packet-routing logic &amp;amp; enough RAM for Continuity states. H1/H2 only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q · Will clones work?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Many fakes replay the same Continuity advert, so the pop-up appears, but they can’t speak AAP or fast-switch.&lt;/p&gt;




&lt;h2&gt;
  
  
  8 · Further Reading &amp;amp; Credits
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;What you’ll find&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;“Handoff All Your Privacy” (PETS 2020) &lt;a href="https://petsymposium.org/2020/files/papers/issue4/popets-2020-0067.pdf" rel="noopener noreferrer"&gt;https://petsymposium.org/2020/files/papers/issue4/popets-2020-0067.pdf&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Continuity advert dissections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LibrePods &lt;a href="https://github.com/kavishdevar/librepods" rel="noopener noreferrer"&gt;https://github.com/kavishdevar/librepods&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;AAP definitions, sample clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAPod &lt;a href="https://github.com/d4rken-org/capod" rel="noopener noreferrer"&gt;https://github.com/d4rken-org/capod&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;A companion app for AirPods on Android.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SoundGuys: H1 vs H2 deep dive &lt;a href="https://www.soundguys.com/how-does-apple-h1-chip-work-21049/" rel="noopener noreferrer"&gt;https://www.soundguys.com/how-does-apple-h1-chip-work-21049/&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Chipset capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9to5Mac guide &lt;a href="https://9to5mac.com/2020/09/27/disable-airpods-automatic-switching/" rel="noopener noreferrer"&gt;https://9to5mac.com/2020/09/27/disable-airpods-automatic-switching/&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;How to disable auto-switch per device&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Educational purposes only. Respect Apple’s terms and local laws when experimenting.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>bluetooth</category>
      <category>reverseengineering</category>
      <category>apple</category>
      <category>airpods</category>
    </item>
  </channel>
</rss>
