برای درک عمق تحولی که AOSP 16 برای توسعهدهندگان بلوتوث به ارمغان آورده، باید سفری به گذشته داشته باشیم و ببینیم این فناوری در اکوسیستم اندروید چگونه تکامل یافته است. این سفر از روزهای پرمصرف و پرچالش بلوتوث کلاسیک آغاز میشود و با ظهور قهرمانی به نام بلوتوث کممصرف (BLE) ادامه مییابد؛ مسیری که در نهایت ما را به دروازههای عصر جدید اسکن غیرفعال میرساند.
در ابتدا، بلوتوث کلاسیک بود. این فناوری را میتوان به مهمانی تشبیه کرد که صدایش از همه بلندتر است و توجه همه را به خود جلب میکند. بلوتوث کلاسیک توانایی انتقال حجم زیادی از داده (مانند پخش موسیقی مورد علاقه شما روی یک اسپیکر) را داشت، اما در عین حال به شدت به باتری دستگاه فشار وارد میکرد. برای کارهایی مانند استریم کردن صوت عالی بود، اما برای انتقال دادههای کوچک و نامتناوب؟ مانند این بود که از شلنگ آتشنشانی برای آبیاری یک گلدان استفاده کنید. کاری افراطی و در نهایت، کمی آشفته.
توسعهدهندگان در آن دوران روزهای خود را با کلنجار رفتن با `BluetoothAdapter`، `BluetoothDevice` و کابوس به نام `BluetoothSocket` سپری میکردند. این دوره، دوران عدم قطعیت بزرگ بود؛ زمانی که یک اتصال ساده میتوانست ثانیهها طول بکشد، یا حتی بدتر، آنقدر طولانی شود که شما فرصت درست کردن یک فنجان قهوه را داشته باشید. و مسئله مصرف باتری؟ کاربران شما شاهد بودند که سطح باتری گوشیشان سریعتر از یک سربالونی سقوط میکند.
سپس، با عرضه اندروید ۴.۳ (API level 18)، یک قهرمان جدید پا به عرصه گذاشت: بلوتوث کممصرف یا BLE. این دیگر بلوتوث قدیمی و پرمصرف نبود. BLE، فناوریی بود ظریف، بهینه و مرموز. این فناوری برای انتقال داده در حجمهای کم و به صورت جهشی طراحی شده بود و قدرت باتری را مانند جرعهای از یک شراب خوب مینوشید، نه اینکه آن را یکجا سر بکشد. BLE همان بچهخوب محله بود.
این فناوری دنیایی کاملاً جدید از امکانها را به روی ما گشود: مانیتورهای ضربان قلب، ساعتهای هوشمند و میلیونها دستگاه اینترنت اشیاء (IoT) که میتوانستند ماهها با تنها یک باتری سکهای کار کنند. BLE یک بازیساز واقعی بود. اما با قدرت بزرگ، پیچیدگی بزرگ نیز همراه میشود. ما مجبور بودیم زبان کاملاً جدیدی از مفاهیم مانند GATT، GAP، سرویسها (Services) و مشخصهها (Characteristics) را یاد بگیریم. این گذار مانند رفتن از نوشتن اسکریپتهای ساده به سمت تصنیف یک اپرای کامل بود. پتانسیل آن عظیم بود، اما شیب منحنی یادگیری بسیار تند.
و سپس مسئله «اسکن» مطرح شد. عملیات پیدا کردن این دستگاههای جدید و کممصرف. در روزهای اولیه BLE، فرآیند اسکن همچون یک منطقه بیقانون بود. این یک فرآیند فعال و پرسرصدا محسوب میشد. گوشی شما در خلأ فریاد میزد که "آیا کسی آن بیرون هست؟" و سپس به پاسخها گوش میداد. این روش کار میکرد، اما همچنان یک عامل قابل توجه در تخلیه باتری بود، به ویژه اگر اپلیکیشن شما نیاز به اسکن برای دورههای طولانی داشت.
این همان معمای همیشگی توسعهدهندگان بود: شما نیاز دارید دستگاهها را پیدا کنید، اما نمیخواهید دلیل خالی شدن باتری گوشی کاربرتان تا وقت ناهار باشید. برای سالها، ما بر این طناب باریک راه میرفتیم و بین نیاز به کشف دستگاهها و التماس برای حفظ باتری تعادل برقرار میکردیم. این دقیقاً همان دنیایی است که AOSP 16 در آن متولد شد. دنیایی که فریاد میزد برای یک راه بهتر برای اسکن. دنیایی که آماده یک قهرمان بود. و آن قهرمان، دوستان من، «اسکن غیرفعال» است.
این تاریخچه پر فراز و نشیب، اهمیت پیشرفتهای معرفی شده در AOSP 16 را پررنگ میکند. سالها تجربه با چالشهای بلوتوث کلاسیک و سپس پیچیدگیهای BLE، جامعه توسعهدهندگان را به نقطهای رساند که نیاز مبرمی به بهینهسازی در مصرف انرژی و سادهسازی فرآیندها احساس میشد. تیم اندروید با درک این دردهای مشترک، در AOSP 16 پایههای تحولی را بنا نهاد که توسعه برنامههای بلوتوثمحور را متحول کرد. این نسخه نه یک بهروزرسانی ساده، که پاسخی مستقیم به سالها چالش بود و راه را برای ساخت اپلیکیشنهای هوشمندتر، کارآمدتر و در نهایت، کاربرپسندتر هموار کرد.
سیستم عامل اندروید در نسخهی AOSP 16، با یک تغییر استراتژی در زمانبندی انتشار، تحول چشمگیری را در حوزهی بلوتوث برای توسعهدهندگان به ارمغان آورده است. برخلاف روال گذشته، در سال ۲۰۲۵ شاهد دو انتشار اصلی بودیم: انتشار اصلی "باقلوا" (Android 16) در سهماههی دوم که شامل تغییرات رفتاری بود، و یک انتشار جزئی در سهامه چهارم که مخصوصاً برای بلوتوث، مجموعهای از ویژگیهای جدید و بهبودهای کیفی را بدون تغییرات مخرب ارائه کرد. این به روزرسانیها که پاسخ مستقیمی به سالها چالش توسعهدهندگان هستند، سه قابلیت کلیدی را معرفی میکنند که میتوان آنها را "سه تفنگدار" بلوتوث AOSP 16 نامید.
اسکن غیرفعال، مهمترین و تحولآفرینترین ویژگی جدید است. برای درک آن، کتابخانهای را تصور کنید. در روش سنتی (اسکن فعال)، اپلیکیشن شما مانند کسی است که در وسط کتابخانه میایستد و فریاد میزند "استیو! اینجایی؟" این روش موثر اما پرسر و صدا و مصرفکنندهی انرژی (باتری کاربر) است. در مقابل، اسکن غیرفعال مانند این است که شما ساکت در کتابخانه قدم بزنید و فقط به صدای خندهی متمایز دوستتان گوش دهید. در دنیای فنی، دستگاههای BLE بستههای اطلاعاتی به نام "آگهی" (Advertisement) پخش میکنند. در اسکن فعال، گوشی پس از دریافت آگهی، یک درخواست اسکن میفرستد تا اطلاعات بیشتری دریافت کند، اما در اسکن غیرفعال، گوشی فقط به آگهیها گوش میدهد و پاسخی ارسال نمیکند. این تغییر از ارسال به دریافت صرف، مصرف انرژی را به شکل چشمگیری کاهش میدهد و برای اپلیکیشنهایی که نیاز به مانیتورینگ طولانیمدت دستگاههای اطراف دارند (مانند ردیابهای هوشمند یا سیستمهای حضور و غیاب) ایدهآل است. پیادهسازی آن با استفاده از متد جدید setScanType(SCAN_TYPE_PASSIVE) در کلاس ScanSettings.Builder انجام میشود.
یکی از بزرگترین دردسرهای توسعه بلوتوث، تشخیص علت قطع شدن پیوند (Bond) بین دستگاهها بوده است. پیش از این، اپلیکیشن شما فقط متوجه میشد که ارتباط قطع شده، اما هیچ اطلاعی از دلیل آن (مثلاً خطای احراز هویت، اقدام کاربر یا مشکل در دستگاه مقصد) نداشت. AOSP 16 با معرفی BluetoothDevice.EXTRA_BOND_LOSS_REASON این مشکل را حل کرده است. اکنون، هنگامی که یک پیوند از بین میرود، این "دلیل" به همراه Broadcast مربوط به تغییر وضعیت پیوند ارسال میشود. توسعهدهنده میتواند با ثبت یک BroadcastReceiver، این دلیل را دریافت کرده و عکسالعمل مناسب را نشان دهد. برای مثال، اگر دلیل، خطای احراز هویت از سمت دستگاه هدف باشد، میتوان به کاربر اطلاع داد که "هدفون شما ارتباط را قطع کرده، لطفاً مجدداً pairing کنید." این قابلیت، دیباگ کردن مشکلات اتصال را از یک بازی حدسزنی به یک فرآیند دقیق و قابل مدیریت تبدیل میکند.
UUID سرویس یک شناسهی منحصربهفرد است که مشخص میکند یک دستگاه BLE چه کاری انجام میدهد (مثلاً سرویس اندازهگیری ضربان قلب). پیش از این، برای فهمیدن این موضوع، اپلیکیشن مجبور بود مراحل زمانبر و پرمصرف اتصال به دستگاه و جستجوی سرویسهای آن (GATT Discovery) را انجام دهد، مانند این که برای فهمیدن شغل یک نفر، مجبور باشید با او به شام بروید. در AOSP 16، بسیاری از دستگاهها UUID سرویس اصلی خود را در بستههای آگهی قرار میدهند و سیستم عامل به طور خودکار این اطلاعات را استخراج کرده و در آبجکت ScanResult (از طریق scanRecord?.serviceUuids) در اختیار شما میگذارد. این امکان، شناسایی دستگاههای مورد نظر را در مرحلهی اسکن و بدون نیاز به اتصال فراهم میکند. اپلیکیشن شما میتواند بلافاصله پس از پیدا کردن یک دستگاه، UUID سرویس آن را بررسی کند و تنها در صورت مطابقت، فرآیند اتصال را آغاز نماید. این امر سرعت کشف دستگاه را به شدت افزایش داده و از اتلاف باتری برای اتصال به دستگاههای نامرتبط جلوگیری میکند.
این ویژگیها در ترکیب با یکدیگر، اپلیکیشنهای بلوتوث را متحول میکنند:
به طور خلاصه، ویژگیهای جدید AOSP 16، توسعهی برنامههای بلوتوث را از یک نبرد طاقتفرسا با مصرف باتری و دیباگهای مبهم، به یک تجربهی کارآمد، قابل کنترل و کاربرپسند تبدیل کردهاند و راه را برای نسل جدیدی از اپلیکیشنهای هوشمند و کممصرف هموار میسازند.
برای درک اهمیت اسکن غیرفعال (Passive Scanning) در AOSP 16، بهتر است ابتدا عملکرد اسکن سنتی یا فعال (Active Scanning) را مرور کنیم. در دنیای Bluetooth Low Energy (BLE)، دستگاههای پیرامونی به طور مداوم بستههای اطلاعاتی کوچکی به نام «آگهی» (Advertisement) پخش میکنند تا حضور و قابلیتهای خود را اعلام کنند. در اسکن فعال، وقتی گوشی شما یکی از این آگهیها را دریافت میکند، بلافاصله یک درخواست اسکن (SCAN_REQ) به سمت دستگاه پیرامونی میفرستد و در پاسخ، اطلاعات تکمیلی بیشتری (SCAN_RSP) دریافت میکند. این فرآیند شبیه به این است که در یک کتابخانه با صدای بلند اسم دوست خود را صدا بزنید و منتظر پاسخ او بمانید. اگرچه مؤثر است، اما این «حرف زدن» مداوم رادیو بلوتوث، مصرف انرژی قابل توجهی دارد. در مقابل، اسکن غیرفعال یک رویکرد کاملاً متفاوت دارد. در این حالت، گوشی شما فقط گوش میدهد. آگهیها را دریافت میکند، اما هیچ پاسخی ارسال نمیکند. این یک مکالمه یکطرفه و فوقالعاده کممصرف است که برنامه شما را به یک نینجای صرفهجو در باتری تبدیل میکند.
خوشبختانه، استفاده از این قابلیت در AOSP 16 بسیار ساده است. تمام جادو در هنگام تعریف شیء `ScanSettings` و با استفاده از متد جدید `setScanType()` رخ میدهد. در گذشته، تنظیمات اسکن شما ممکن است به این شکل باشد:
اما برای فعال کردن حالت غیرفعال، کافی است یک خط کد به builder اضافه کنید:
با این تنظیم، اسکنر شما به جای مشارکت فعال در ارتباط، به یک شنونده محض تبدیل میشود. این تغییر به ظاهر کوچک، تأثیر شگرفی بر مصرف باتری دارد، به ویژه برای برنامههایی که نیاز به اسکن مداوم یا طولانیمدت دارند، مانند برنامههای ردیابی دارایی یا نظارت بر حضور. البته یک نکته مهم وجود دارد: از آنجا که در اسکن غیرفعال درخواستی برای اطلاعات بیشتر ارسال نمیشود، دادهی موجود در SCAN_RSP را دریافت نخواهید کرد. اما برای بسیاری از کاربردها، اطلاعات اولیه موجود در خود آگهی کافی است و صرفهجویی در انرژی به مراتب ارزشمندتر است.
استفاده از اسکن غیرفعال تنها یکی از راههای بهینهسازی است. برای ساخت یک برنامه نمونه که به باتری کاربر احترام میگذارد، رعایت این اصول طلایی ضروری است:
اسکن غیرفعال در AOSP 16 تنها یک ویژگی جدید نیست، بلکه نشاندهنده یک تغییر فلسفه در توسعه برنامههای بلوتوثی است. این قابلیت به ما توسعهدهندگان این قدرت را میدهد که به جای «فریاد زدن» در دنیای امواج رادیویی، «شنوندگان» هوشمند و کممصرفی باشیم. این پیشرفت، راه را برای برنامههای پیچیده، باهوش و همیشهحواسجمعی هموار میکند که میتوانند برای مدتها به نظارت محیط اطراف بپردازند، بدون آنکه هزینه گزافی برای باتری کاربر داشته باشند. با ترکیب این قابلیت با دیگر ابزارهای بهینهسازی مانند فیلترهای سختافزاری و گزارشدهی دستهای، میتوانید تجربههای بلوتوثی بسازید که هم قدرتمند هستند و هم مودب.
اتصال بلوتوث یا "Bond" را میتوان به یک رابطه دوستانه تشبیه کرد. زمانی که گوشی شما با هدفون جفت میشود، این دو دستگاه یک پیوند امن و مورد اعتماد ایجاد میکنند. کلیدهای رمزنگاری را به اشتراک میگذارند و قول میدهند که در دفعات بعدی به طور خودکار به هم متصل شوند تا کاربر مجبور نباشد هر بار فرآیند جفتسازی را تکرار کند. این یک رمانس زیبا است. تا زمانی که ناگهان، یک روز، آنها یکدیگر را فراموش میکنند. ارتباط قطع میشود، اعتماد از بین میرود و اپلیکیشن شما در وسط ماجرا میماند، در حالی که هیچ نظری ندارد که مشکل از کجاست. تا به امروز، اندروید کمکی به توسعهدهندگان نمیکرد. فقط اعلانی دریافت میکردید که وضعیت پیوند به BOND_NONE تغییر کرده، بدون هیچ توضیحی. اما با AOSP 16، بالاخره این سکون شکسته شد.
در AOSP 16، تیم اندروید یک ویژگی حیاتی برای دیباگ به نام `BluetoothDevice.EXTRA_BOND_LOSS_REASON` معرفی کرده است. این یک "اکسترا" (Extra) جدید است که همراه با برادکست `ACTION_BOND_STATE_CHANGED` ارسال میشود و دقیقاً به شما میگوید که چرا پیوند بلوتوث قطع شده است. این مانند دریافت یک پیام متنی پس از جدایی است که واقعاً توضیح میدهد چه اتفاقی افتاده است! اکنون، هنگام قطع پیوند، میتوانید یک کد دلیل مشخص دریافت کنید. این کدها مانند دلایل کلاسیک قطع ارتباط، اما برای بلوتوث هستند:
برای دریافت این اطلاعات ارزشمند، باید یک `BroadcastReceiver` تنظیم کنید تا تغییرات وضعیت پیوند (Bond State) را گوش دهد. این رسیور نقش یک کارآگاه را بازی میکند. زمانی که یک رویداد `ACTION_BOND_STATE_CHANGED` دریافت میشود، کد بررسی میکند که آیا انتقال از حالت "Bonded" (متصل) به "None" (قطع) رخ داده است یا خیر. اگر چنین باشد، دلیل جدید `EXTRA_BOND_LOSS_REASON` از intent استخراج میشود. سپس میتوانید با استفاده از یک عبارت شرطی (مانند when در کاتلین) هر دلیل را به طور جداگانه مدیریت کنید.
به عنوان مثال، اگر دلیل `BOND_LOSS_REASON_LE_INCOMING_PAIRING` باشد، میدانید که دستگاه مقابل (مانند هدفون) فرآیند جدایی را آغاز کرده است. در این صورت میتوانید یک پیام مفید و قابل اقدام به کاربر نشان دهید، مثلاً: "به نظر میرسد هدفون شما این دستگاه را فراموش کرده است. لطفاً دوباره سعی کنید جفتسازی کنید." مدیریت چرخه عمر رسیور (ثبت در `onResume()` و لغو ثبت در `onPause()`) برای جلوگیری از نشتی حافظه بسیار مهم است.
قبل از این قابلیت، دیباگ کردن قطعشدن اتصال بلوتوث یک فرآیند مبتنی بر حدس و آزمایش بود. آیا مشکل از اپلیکیشن است؟ از سیستم عامل؟ باتری دستگاه جانبی تمام شده؟ کاربر به صورت دستی دستگاه را "فراموش" کرده است؟ این ویژگی جدید این فرآیند را متحول میکند. اکنون شما یک بازخورد مستقیم از سیستم دریافت میکنید. این نه تنها زمان دیباگ را به شدت کاهش میدهد، بلکه تجربه کاربری را نیز بهبود میبخشد. به جای نمایش یک پیغام مبهم مانند "اتصال شکست خورد"، میتوانید راهنمایی مشخصی به کاربر ارائه دهید.
این موضوع به ویژه برای برنامههای حرفهای و enterprise که با دستگاههای بلوتوثی متعدد سروکار دارند (مانند سیستمهای رهگیری دارایی یا دستگاههای پزشکی) حیاتی است. دانستن دلیل دقیق قطع ارتباط به حفظ ثبات سیستم و عیبیابی سریع کمک شایانی میکند. با AOSP 16، توسعهدهندگان دیگر یک تماشاگر سردرگم نیستند، بلکه مانند یک مشاور رابطه آگاه میتوانند مشکلات اتصال بلوتوث را تشخیص داده و برطرف کنند.
در دنیای Bluetooth Low Energy، UUID سرویس مانند یک کارت شناسایی منحصر به فرد عمل میکند. این شناسه ۱۲۸ بیتی دقیقاً مشخص میکند که یک دستگاه چه خدماتی ارائه میدهد. برای مثال، UUID سرویس ضربان قلب (0000180D-0000-1000-8000-00805F9B34FB) به برنامه شما میگوید که این دستگاه یک مانیتور ضربان قلب است، بدون اینکه نیاز باشد به آن متصل شوید.
پیش از AOSP 16، فرآیند شناسایی سرویسهای یک دستگاه بسیار زمانبر و پر هزینه بود. ابتدا باید دستگاه را اسکن میکردید، سپس به آن متصل میشدید، پس از آن فرآیند کشف سرویسها را انجام میدادید و در نهایت چک میکردید که آیا سرویس مورد نظر شما وجود دارد یا خیر. این فرآیند نه تنها باتری زیادی مصرف میکرد، بلکه در محیطهای شلوغ با دهها دستگاه BLE غیرعملی بود.
با AOSP 16، این فرآیند به طور کامل متحول شده است. بسیاری از دستگاههای BLE، UUID سرویس اصلی خود را در بستههای advertisement قرار میدهند. AOSP 16 این اطلاعات را به صورت خودکار تجزیه و تحلیل کرده و مستقیماً در ScanResult در اختیار شما قرار میدهد. این یعنی شما میتوانید تنها با اسکن کردن و بدون اتصال، تشخیص دهید که یک دستگاه چه قابلیتهایی دارد.
برای استفاده از این قابلیت، کافی است در ScanCallback خود، scanRecord دستگاه را بررسی کنید. با فراخوانی result.scanRecord?.serviceUuids میتوانید لیست UUIDهای سرویسهای موجود را دریافت کنید. سپس میتوانید چک کنید که آیا UUID مورد نظر شما در این لیست وجود دارد یا خیر. این روش سرعت شناسایی دستگاههای مناسب را به میزان چشمگیری افزایش میدهد.
این قابلیت به خصوص در محیطهای شلوغ مانند بیمارستانها، کارخانهها و خانههای هوشمند بسیار کاربردی است. شما میتوانید از میان دهها دستگاه BLE، تنها دستگاههایی را که سرویس مورد نظر شما را ارائه میدهند، به سرعت شناسایی کرده و به آنها متصل شوید. این نه تنها در زمان صرفهجویی میکند، بلکه مصرف باتری را نیز به حداقل میرساند.
قابلیت خواندن UUID سرویسها از advertisement در AOSP 16، یکی از مهمترین پیشرفتها در توسعه برنامههای بلوتوث محور است. این ویژگی به شما امکان میدهد دستگاههای مناسب را با کارایی بسیار بالاتری شناسایی کنید، مصرف باتری را کاهش دهید و تجربه کاربری بهتری ارائه دهید. برای نتیجهگیری، توصیه میکنیم حتماً از این قابلیت در برنامههای خود استفاده کنید، به خصوص اگر با محیطهای شلوغ از دستگاههای BLE سروکار دارید. ترکیب این ویژگی با اسکن غیرفعال و فیلترهای سختافزاری، میتواند برنامه شما را به یک ابزار حرفهای و بهینه تبدیل کند.