قابلیتهای Wi-Fi Aware به دستگاههایی که اندروید ۸.۰ (سطح API ۲۶) و بالاتر را اجرا میکنند، این امکان را میدهد که بدون هیچ نوع اتصال دیگری بین آنها، مستقیماً یکدیگر را کشف و به آنها متصل شوند. Wi-Fi Aware همچنین به عنوان شبکهسازی با آگاهی از همسایه (NAN) شناخته میشود.
شبکه Wi-Fi Aware با تشکیل خوشهها با دستگاههای همسایه یا با ایجاد یک خوشه جدید در صورتی که دستگاه اولین دستگاه در یک منطقه باشد، کار میکند. این رفتار خوشهبندی برای کل دستگاه اعمال میشود و توسط سرویس سیستم Wi-Fi Aware مدیریت میشود؛ برنامهها هیچ کنترلی بر رفتار خوشهبندی ندارند. برنامهها از APIهای Wi-Fi Aware برای ارتباط با سرویس سیستم Wi-Fi Aware استفاده میکنند که سختافزار Wi-Fi Aware را در دستگاه مدیریت میکند.
رابطهای برنامهنویسی کاربردی (API) مربوط به Wi-Fi Aware به برنامهها اجازه میدهند عملیات زیر را انجام دهند:
کشف دستگاههای دیگر: این API مکانیزمی برای یافتن دستگاههای مجاور دارد. این فرآیند زمانی شروع میشود که یک دستگاه، یک یا چند سرویس قابل کشف را منتشر میکند . سپس، هنگامی که یک دستگاه در یک یا چند سرویس مشترک میشود و وارد محدوده Wi-Fi ناشر میشود، مشترک اعلانی دریافت میکند مبنی بر اینکه یک ناشر منطبق کشف شده است. پس از اینکه مشترک، ناشر را کشف کرد، میتواند یک پیام کوتاه ارسال کند یا با دستگاه کشف شده اتصال شبکه برقرار کند. دستگاهها میتوانند همزمان هم ناشر و هم مشترک باشند.
ایجاد اتصال شبکه: پس از اینکه دو دستگاه یکدیگر را کشف کردند، میتوانند بدون نیاز به نقطه دسترسی، یک اتصال شبکه دو طرفه مبتنی بر Wi-Fi Aware ایجاد کنند.
اتصالات شبکه Wi-Fi Aware از نرخهای انتقال داده بالاتری در فواصل طولانیتر نسبت به اتصالات بلوتوث پشتیبانی میکنند. این نوع اتصالات برای برنامههایی که حجم زیادی از دادهها را بین کاربران به اشتراک میگذارند، مانند برنامههای اشتراکگذاری عکس، مفید هستند.
پیشرفتهای اندروید ۱۳ (سطح API ۳۳)
در دستگاههایی که اندروید ۱۳ (سطح API ۳۳) و بالاتر دارند و از حالت ارتباط فوری پشتیبانی میکنند، برنامهها میتوانند از متدهای PublishConfig.Builder.setInstantCommunicationModeEnabled() و SubscribeConfig.Builder.setInstantCommunicationModeEnabled() برای فعال یا غیرفعال کردن حالت ارتباط فوری برای یک جلسه کشف ناشر یا مشترک استفاده کنند. حالت ارتباط فوری، تبادل پیام، کشف سرویس و هر مسیر دادهای را که به عنوان بخشی از جلسه کشف ناشر یا مشترک تنظیم شده است، سرعت میبخشد. برای تعیین اینکه آیا یک دستگاه از حالت ارتباط فوری پشتیبانی میکند، از متد isInstantCommunicationModeSupported() استفاده کنید.
پیشرفتهای اندروید ۱۲ (سطح API ۳۱)
اندروید ۱۲ (سطح API 31) برخی پیشرفتها را به Wi-Fi Aware اضافه میکند:
- در دستگاههایی که اندروید ۱۲ (سطح API 31) یا بالاتر را اجرا میکنند، میتوانید از فراخوانی
onServiceLost()برای هشدار دادن به برنامه خود در زمانی که سرویس کشف شدهای را به دلیل توقف یا خارج شدن از محدوده از دست داده است، استفاده کنید. - راهاندازی مسیرهای داده Wi-Fi Aware سادهتر شده است. نسخههای قبلی از پیامرسانی L2 برای ارائه آدرس MAC آغازگر استفاده میکردند که باعث تأخیر میشد. در دستگاههایی که اندروید ۱۲ و بالاتر دارند، پاسخدهنده (سرور) را میتوان طوری پیکربندی کرد که هر نظیری را بپذیرد - یعنی نیازی به دانستن آدرس MAC آغازگر از قبل ندارد. این امر سرعت ایجاد مسیر داده را افزایش میدهد و چندین لینک نقطه به نقطه را تنها با یک درخواست شبکه فعال میکند.
- برنامههایی که روی اندروید ۱۲ یا بالاتر اجرا میشوند میتوانند از متد
WifiAwareManager.getAvailableAwareResources()برای دریافت تعداد مسیرهای داده، جلسات انتشار و جلسات اشتراک در حال حاضر موجود استفاده کنند. این میتواند به برنامه کمک کند تا تعیین کند که آیا منابع موجود کافی برای اجرای عملکرد مورد نظر آنها وجود دارد یا خیر.
راه اندازی اولیه
برای تنظیم برنامه خود برای استفاده از کشف و شبکهسازی Wi-Fi Aware، مراحل زیر را انجام دهید:
مجوزهای زیر را در مانیفست برنامه خود درخواست کنید:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- If your app targets Android 13 (API level 33) or higher, you must declare the NEARBY_WIFI_DEVICES permission. --> <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" <!-- If your app derives location information from Wi-Fi APIs, don't include the "usesPermissionFlags" attribute. --> android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" <!-- If any feature in your app relies on precise location information, don't include the "maxSdkVersion" attribute. --> android:maxSdkVersion="32" />
بررسی کنید که آیا دستگاه از Wi-Fi Aware با استفاده از
PackageManagerAPI پشتیبانی میکند یا خیر، همانطور که در زیر نشان داده شده است:کاتلین
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
جاوا
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
بررسی کنید که آیا Wi-Fi Aware در حال حاضر در دسترس است یا خیر. Wi-Fi Aware ممکن است در دستگاه وجود داشته باشد، اما در حال حاضر در دسترس نباشد زیرا کاربر Wi-Fi یا موقعیت مکانی را غیرفعال کرده است. بسته به قابلیتهای سختافزاری و میانافزار، برخی از دستگاهها ممکن است در صورت استفاده از Wi-Fi Direct، SoftAP یا اتصال به اینترنت، از Wi-Fi Aware پشتیبانی نکنند. برای بررسی اینکه آیا Wi-Fi Aware در حال حاضر در دسترس است،
isAvailable()را فراخوانی کنید.در دسترس بودن Wi-Fi Aware میتواند در هر زمانی تغییر کند. برنامه شما باید یک
BroadcastReceiverبرای دریافتACTION_WIFI_AWARE_STATE_CHANGEDثبت کند، که هر زمان که در دسترس بودن تغییر کند، ارسال میشود. هنگامی که برنامه شما قصد پخش را دریافت میکند، باید تمام جلسات موجود را حذف کند (فرض کنید که سرویس Wi-Fi Aware مختل شده است)، سپس وضعیت فعلی در دسترس بودن را بررسی کرده و رفتار خود را بر اساس آن تنظیم کند. به عنوان مثال:کاتلین
val wifiAwareManager = context.getSystemService(Context.WIFI_AWARE_SERVICE) as WifiAwareManager? val filter = IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED) val myReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // discard current sessions if (wifiAwareManager?.isAvailable) { ... } else { ... } } } context.registerReceiver(myReceiver, filter)
جاوا
WifiAwareManager wifiAwareManager = (WifiAwareManager)context.getSystemService(Context.WIFI_AWARE_SERVICE) IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED); BroadcastReceiver myReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // discard current sessions if (wifiAwareManager.isAvailable()) { ... } else { ... } } }; context.registerReceiver(myReceiver, filter);
برای اطلاعات بیشتر، به بخش پخشها مراجعه کنید.
دریافت جلسه
برای شروع استفاده از Wi-Fi Aware، برنامه شما باید با فراخوانی attach() یک WifiAwareSession دریافت کند. این متد موارد زیر را انجام میدهد:
- سختافزار Wi-Fi Aware را روشن میکند.
- به یک خوشه Wi-Fi Aware میپیوندد یا آن را تشکیل میدهد.
- یک جلسه Wi-Fi Aware با یک فضای نام منحصر به فرد ایجاد میکند که به عنوان ظرفی برای تمام جلسات اکتشافی ایجاد شده در آن عمل میکند.
اگر برنامه با موفقیت پیوست شود، سیستم فراخوانی onAttached() را اجرا میکند. این فراخوانی یک شیء WifiAwareSession فراهم میکند که برنامه شما باید برای تمام عملیات بعدی session از آن استفاده کند. یک برنامه میتواند از session برای انتشار یک سرویس یا اشتراک در یک سرویس استفاده کند.
برنامه شما باید فقط یک بار تابع attach() را فراخوانی کند. اگر برنامه شما چندین بار attach() را فراخوانی کند، برنامه برای هر فراخوانی یک session متفاوت دریافت میکند که هر کدام فضای نام مخصوص به خود را دارد. این میتواند در سناریوهای پیچیده مفید باشد، اما به طور کلی باید از آن اجتناب شود.
انتشار یک سرویس
برای قابل کشف کردن یک سرویس، متد publish() را فراخوانی کنید که پارامترهای زیر را دریافت میکند:
-
PublishConfigنام سرویس و سایر ویژگیهای پیکربندی مانند فیلتر match را مشخص میکند. -
DiscoverySessionCallbackاقداماتی را که باید هنگام وقوع رویدادها اجرا شوند، مشخص میکند، مانند زمانی که مشترک پیامی دریافت میکند.
در اینجا یک مثال آورده شده است:
کاتلین
val config: PublishConfig = PublishConfig.Builder() .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME) .build() awareSession.publish(config, object : DiscoverySessionCallback() { override fun onPublishStarted(session: PublishDiscoverySession) { ... } override fun onMessageReceived(peerHandle: PeerHandle, message: ByteArray) { ... } })
جاوا
PublishConfig config = new PublishConfig.Builder() .setServiceName(“Aware_File_Share_Service_Name”) .build(); awareSession.publish(config, new DiscoverySessionCallback() { @Override public void onPublishStarted(PublishDiscoverySession session) { ... } @Override public void onMessageReceived(PeerHandle peerHandle, byte[] message) { ... } }, null);
اگر انتشار با موفقیت انجام شود، متد فراخوانی onPublishStarted() فراخوانی میشود.
پس از انتشار، هنگامی که دستگاههایی که برنامههای مشترک منطبق را اجرا میکنند، وارد محدوده Wi-Fi دستگاه منتشرکننده میشوند، مشترکین سرویس را کشف میکنند. هنگامی که یک مشترک، ناشر را کشف میکند، ناشر اعلانی دریافت نمیکند. با این حال، اگر مشترک پیامی به ناشر ارسال کند، ناشر اعلانی دریافت میکند. وقتی این اتفاق میافتد، متد فراخوانی onMessageReceived() فراخوانی میشود. میتوانید از آرگومان PeerHandle از این متد برای ارسال پیام به مشترک یا ایجاد اتصال به آن استفاده کنید.
برای توقف انتشار سرویس، تابع DiscoverySession.close() را فراخوانی کنید. جلسات Discovery با والد خود WifiAwareSession مرتبط هستند. اگر جلسه والد بسته شود، جلسات Discovery مرتبط با آن نیز بسته میشوند. در حالی که اشیاء حذف شده نیز بسته میشوند، سیستم تضمین نمیکند که جلسات خارج از محدوده چه زمانی بسته میشوند، بنابراین توصیه میکنیم که به طور صریح متدهای close() را فراخوانی کنید.
اشتراک در یک سرویس
برای عضویت در یک سرویس، متد subscribe() را فراخوانی کنید که پارامترهای زیر را دریافت میکند:
-
SubscribeConfigنام سرویسی که باید در آن مشترک شوید و سایر ویژگیهای پیکربندی مانند فیلتر match را مشخص میکند. -
DiscoverySessionCallbackاقداماتی را که باید هنگام وقوع رویدادها اجرا شوند، مانند زمانی که یک ناشر کشف میشود، مشخص میکند.
در اینجا یک مثال آورده شده است:
کاتلین
val config: SubscribeConfig = SubscribeConfig.Builder() .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME) .build() awareSession.subscribe(config, object : DiscoverySessionCallback() { override fun onSubscribeStarted(session: SubscribeDiscoverySession) { ... } override fun onServiceDiscovered( peerHandle: PeerHandle, serviceSpecificInfo: ByteArray, matchFilter: List<ByteArray> ) { ... } }, null)
جاوا
SubscribeConfig config = new SubscribeConfig.Builder() .setServiceName("Aware_File_Share_Service_Name") .build(); awareSession.subscribe(config, new DiscoverySessionCallback() { @Override public void onSubscribeStarted(SubscribeDiscoverySession session) { ... } @Override public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter) { ... } }, null);
اگر عملیات اشتراک با موفقیت انجام شود، سیستم فراخوانی onSubscribeStarted() را در برنامه شما فراخوانی میکند. از آنجا که میتوانید پس از کشف یک ناشر توسط برنامه خود، از آرگومان SubscribeDiscoverySession در فراخوانی برای برقراری ارتباط با آن استفاده کنید، باید این ارجاع را ذخیره کنید. میتوانید در هر زمانی با فراخوانی updateSubscribe() در جلسه کشف، جلسه اشتراک را بهروزرسانی کنید.
در این مرحله، اشتراک شما منتظر میماند تا ناشران منطبق در محدوده Wi-Fi قرار گیرند. وقتی این اتفاق میافتد، سیستم متد فراخوانی onServiceDiscovered() اجرا میکند. میتوانید از آرگومان PeerHandle از این فراخوانی برای ارسال پیام یا ایجاد اتصال به آن ناشر استفاده کنید.
برای توقف اشتراک در یک سرویس، تابع DiscoverySession.close() را فراخوانی کنید. جلسات Discovery با والد خود WifiAwareSession مرتبط هستند. اگر جلسه والد بسته شود، جلسات Discovery مرتبط با آن نیز بسته میشوند. در حالی که اشیاء حذف شده نیز بسته میشوند، سیستم تضمین نمیکند که جلسات خارج از محدوده چه زمانی بسته میشوند، بنابراین توصیه میکنیم که به طور صریح متدهای close() را فراخوانی کنید.
ارسال پیام
برای ارسال پیام به دستگاه دیگر، به اشیاء زیر نیاز دارید:
یک
DiscoverySession. این شیء به شما امکان میدهدsendMessage()فراخوانی کنید. برنامه شما با انتشار یک سرویس یا عضویت در یک سرویس،DiscoverySessionدریافت میکند.PeerHandleدستگاه دیگر، برای مسیریابی پیام. برنامه شماPeerHandleدستگاه دیگر را به یکی از دو روش زیر دریافت میکند:- برنامه شما یک سرویس منتشر میکند و پیامی را از یک مشترک دریافت میکند. برنامه شما
PeerHandleمشترک را از فراخوانیonMessageReceived()دریافت میکند. - برنامه شما در یک سرویس مشترک میشود. سپس، هنگامی که یک ناشر منطبق پیدا میکند، برنامه شما
PeerHandleناشر را از فراخوانیonServiceDiscovered()دریافت میکند.
- برنامه شما یک سرویس منتشر میکند و پیامی را از یک مشترک دریافت میکند. برنامه شما
برای ارسال پیام، تابع sendMessage() را فراخوانی کنید. سپس ممکن است فراخوانیهای زیر رخ دهد:
- وقتی پیام با موفقیت توسط نظیر دریافت شد، سیستم تابع
onMessageSendSucceeded()را در برنامه ارسال کننده فراخوانی میکند. - وقتی نظیر پیامی را دریافت میکند، سیستم تابع
onMessageReceived()را در برنامهی گیرنده فراخوانی میکند.
اگرچه PeerHandle برای ارتباط با peerها مورد نیاز است، اما نباید به آن به عنوان یک شناسه دائمی peerها تکیه کنید. شناسههای سطح بالاتر میتوانند توسط برنامه - که در خود سرویس کشف یا در پیامهای بعدی تعبیه شدهاند - استفاده شوند. میتوانید یک شناسه را در سرویس کشف با استفاده از متدهای setMatchFilter() یا setServiceSpecificInfo() از PublishConfig یا SubscribeConfig تعبیه کنید. متد setMatchFilter() بر کشف تأثیر میگذارد، در حالی که متد setServiceSpecificInfo() بر کشف تأثیری ندارد.
جاسازی یک شناسه در یک پیام به معنای تغییر آرایه بایتهای پیام برای گنجاندن یک شناسه (مثلاً به عنوان چند بایت اول) است.
ایجاد یک اتصال
Wi-Fi Aware از شبکهسازی کلاینت-سرور بین دو دستگاه Wi-Fi Aware پشتیبانی میکند.
برای تنظیم اتصال کلاینت-سرور:
از قابلیت کشف آگاهانهی وایفای برای انتشار یک سرویس (روی سرور) و اشتراک در یک سرویس (روی کلاینت) استفاده کنید.
به محض اینکه مشترک، ناشر را پیدا کرد، پیامی از طرف مشترک به ناشر ارسال کنید .
یک
ServerSocketروی دستگاه ناشر شروع کنید و پورت آن را تنظیم یا دریافت کنید:کاتلین
val ss = ServerSocket(0) val port = ss.localPort
جاوا
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
از
ConnectivityManagerبرای درخواست یک شبکه Wi-Fi Aware در ناشر با استفاده ازWifiAwareNetworkSpecifierاستفاده کنید، که جلسه کشف وPeerHandleمشترک را که از پیام ارسال شده توسط مشترک به دست آوردهاید، مشخص میکند:کاتلین
val networkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("somePassword") .setPort(port) .build() val myNetworkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build() val callback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { ... } override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) { ... } override fun onLost(network: Network) { ... } } connMgr.requestNetwork(myNetworkRequest, callback);
جاوا
NetworkSpecifier networkSpecifier = new WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle) .setPskPassphrase("somePassword") .setPort(port) .build(); NetworkRequest myNetworkRequest = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .setNetworkSpecifier(networkSpecifier) .build(); ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { ... } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) { ... } @Override public void onLost(Network network) { ... } }; ConnectivityManager connMgr.requestNetwork(myNetworkRequest, callback);
به محض اینکه ناشر درخواست شبکهای را میدهد، باید پیامی را برای مشترک ارسال کند .
به محض اینکه مشترک پیام را از ناشر دریافت کرد، با استفاده از همان روشی که در ناشر استفاده شده بود، یک شبکه Wi-Fi Aware را روی مشترک درخواست کنید. هنگام ایجاد
NetworkSpecifierپورت را مشخص نکنید. متدهای فراخوانی مناسب زمانی فراخوانی میشوند که اتصال شبکه در دسترس باشد، تغییر کند یا از بین برود.زمانی که متد
onAvailable()برای مشترک فراخوانی میشود، یک شیءNetworkدر دسترس قرار میگیرد که با آن میتوانید یکSocketبرای ارتباط باServerSocketروی ناشر باز کنید، اما باید آدرس IPv6 و پورتServerSocketرا بدانید. این موارد را از شیءNetworkCapabilitiesارائه شده در فراخوانیonCapabilitiesChanged()دریافت میکنید:کاتلین
val peerAwareInfo = networkCapabilities.transportInfo as WifiAwareNetworkInfo val peerIpv6 = peerAwareInfo.peerIpv6Addr val peerPort = peerAwareInfo.port ... val socket = network.getSocketFactory().createSocket(peerIpv6, peerPort)
جاوا
WifiAwareNetworkInfo peerAwareInfo = (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo(); Inet6Address peerIpv6 = peerAwareInfo.getPeerIpv6Addr(); int peerPort = peerAwareInfo.getPort(); ... Socket socket = network.getSocketFactory().createSocket(peerIpv6, peerPort);
وقتی کارتان با اتصال شبکه تمام شد، تابع
unregisterNetworkCallback()را فراخوانی کنید.
جفتهای در حال تغییر و کشف مبتنی بر مکان
دستگاهی با قابلیتهای موقعیت مکانی Wi-Fi RTT میتواند مستقیماً فاصله تا دستگاههای همتا را اندازهگیری کند و از این اطلاعات برای محدود کردن کشف سرویس Wi-Fi Aware استفاده کند.
رابط برنامهنویسی کاربردی Wi-Fi RTT امکان تعیین محدوده مستقیم به یک همتای Wi-Fi Aware را با استفاده از آدرس MAC یا PeerHandle آن فراهم میکند.
کشف آگاه از طریق Wi-Fi میتواند محدود به کشف سرویسهایی باشد که در یک محدوده جغرافیایی خاص قرار دارند. برای مثال، میتوانید یک محدوده جغرافیایی تنظیم کنید که امکان کشف دستگاهی را که سرویس "Aware_File_Share_Service_Name" را منتشر میکند، فراهم کند، به طوری که این دستگاه از فاصله ۳ متر (۳۰۰۰ میلیمتر) و ۱۰ متر (۱۰۰۰۰ میلیمتر) دورتر نباشد.
برای فعال کردن geofencing، ناشر و مشترک هر دو باید اقدامات زیر را انجام دهند:
ناشر باید با استفاده از setRangingEnabled(true) قابلیت محدودهیابی را در سرویس منتشر شده فعال کند.
اگر ناشر، قابلیت مسافتیابی را فعال نکند، هرگونه محدودیت جغرافیایی مشخصشده توسط مشترک نادیده گرفته میشود و کشف عادی، صرفنظر از فاصله، انجام میشود.
مشترک باید با استفاده از ترکیبی از setMinDistanceMm و setMaxDistanceMm یک geofence مشخص کند.
برای هر دو مقدار، فاصله نامشخص به معنای هیچ محدودیتی نیست. فقط مشخص کردن حداکثر فاصله به معنای حداقل فاصله ۰ است. فقط مشخص کردن حداقل فاصله به معنای هیچ حداکثری نیست.
وقتی یک سرویس نظیر در یک محدوده جغرافیایی کشف میشود، فراخوانی onServiceDiscoveredWithinRange آغاز میشود که فاصله اندازهگیری شده تا نظیر را ارائه میدهد. سپس میتوان در صورت لزوم، API مستقیم Wi-Fi RTT را برای اندازهگیری فاصله در زمانهای بعدی فراخوانی کرد.