قابلیتهای Wi-Fi Aware دستگاههای دارای Android 8.0 (سطح API 26) و بالاتر را قادر میسازد تا مستقیماً به یکدیگر و بدون هیچ نوع اتصال دیگری بین آنها شناسایی و متصل شوند. Wi-Fi Aware با نام Neighbor Awareness Network (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 نسبت به اتصالات بلوتوث از نرخ توان بالاتر در فواصل طولانیتر پشتیبانی میکنند. این نوع اتصالات برای برنامه هایی مفید هستند که حجم زیادی از داده را بین کاربران به اشتراک می گذارند، مانند برنامه های اشتراک عکس.
پیشرفت های اندروید 13 (سطح API 33).
در دستگاههای دارای Android 13 (سطح API 33) و بالاتر که از حالت ارتباط فوری پشتیبانی میکنند، برنامهها میتوانند از روشهای PublishConfig.Builder.setInstantCommunicationModeEnabled()
و SubscribeConfig.Builder.setInstantCommunicationModeEnabled()
برای فعال یا غیرفعال کردن حالت ارتباط فوری یا مشترک برای ناشر استفاده کنند. جلسه کشف حالت ارتباط فوری تبادل پیام، کشف سرویس و هر مسیر داده ای را که به عنوان بخشی از جلسه کشف ناشر یا مشترک تنظیم شده است، سرعت می بخشد. برای تعیین اینکه آیا یک دستگاه از حالت ارتباط فوری پشتیبانی می کند یا خیر، از متد isInstantCommunicationModeSupported()
استفاده کنید.
پیشرفت های اندروید 12 (سطح API 31).
Android 12 (سطح API 31) برخی از پیشرفتها را به Wi-Fi Aware اضافه میکند:
- در دستگاههای دارای Android 12 (سطح API 31) یا بالاتر، میتوانید از پاسخ تماس
onServiceLost()
استفاده کنید تا زمانی که برنامهتان سرویس کشفشدهای را به دلیل توقف یا خارج شدن از محدوده سرویس از دست داده است، هشدار داده شود. - راه اندازی مسیرهای داده Wi-Fi Aware ساده شده است. نسخههای قبلی از پیامرسانی L2 برای ارائه آدرس MAC آغازگر استفاده میکردند که تأخیر را معرفی میکرد. در دستگاههای دارای Android 12 و بالاتر، پاسخدهنده (سرور) را میتوان طوری پیکربندی کرد که هر همتای را بپذیرد - یعنی نیازی به دانستن آدرس MAC آغازگر از قبل ندارد. این امر به بالا آوردن مسیر داده سرعت می بخشد و چندین پیوند نقطه به نقطه را تنها با یک درخواست شبکه فعال می کند.
- برنامههایی که روی Android نسخه ۱۲ یا بالاتر اجرا میشوند میتوانند از روش
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 با API
PackageManager
پشتیبانی می کند یا خیر:کاتلین
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
را ارائه می دهد که برنامه شما باید برای تمام عملیات های جلسه بعدی از آن استفاده کند. یک برنامه می تواند از جلسه برای انتشار یک سرویس یا اشتراک در یک سرویس استفاده کند.
برنامه شما باید فقط یک بار با attach()
تماس بگیرد. اگر برنامه شما چندین بار attach()
تماس بگیرد، برنامه برای هر تماس جلسه متفاوتی دریافت میکند که هر کدام فضای نام خاص خود را دارند. این می تواند در سناریوهای پیچیده مفید باشد، اما به طور کلی باید از آن اجتناب کرد.
یک سرویس را منتشر کنید
برای اینکه یک سرویس قابل کشف باشد، متد publish()
را فراخوانی کنید که پارامترهای زیر را می گیرد:
-
PublishConfig
نام سرویس و سایر ویژگی های پیکربندی، مانند فیلتر مطابقت را مشخص می کند. -
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()
تماس بگیرید. جلسات کشف با WifiAwareSession
والد خود مرتبط است. اگر جلسه والد بسته شود، جلسات کشف مرتبط با آن نیز بسته می شود. در حالی که اشیاء دور ریخته شده نیز بسته هستند، سیستم تضمین نمیکند که جلسات خارج از محدوده بسته میشوند، بنابراین توصیه میکنیم صریحاً متدهای close()
را فراخوانی کنید.
مشترک شدن در یک سرویس
برای عضویت در یک سرویس، متد subscribe()
را فراخوانی کنید که پارامترهای زیر را می گیرد:
-
SubscribeConfig
نام سرویس مورد نظر برای اشتراک و سایر ویژگی های پیکربندی مانند فیلتر مطابقت را مشخص می کند. -
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()
تماس بگیرید. جلسات کشف با WifiAwareSession
والد خود مرتبط است. اگر جلسه والد بسته شود، جلسات کشف مرتبط با آن نیز بسته می شود. در حالی که اشیاء دور ریخته شده نیز بسته هستند، سیستم تضمین نمیکند که جلسات خارج از محدوده بسته میشوند، بنابراین توصیه میکنیم صریحاً متدهای close()
را فراخوانی کنید.
ارسال پیام
برای ارسال پیام به دستگاه دیگری، به اشیاء زیر نیاز دارید:
یک
DiscoverySession
. این شی به شما اجازه می دهد کهsendMessage()
را فراخوانی کنید. برنامه شما با انتشار یک سرویس یا اشتراک در یک سرویس،DiscoverySession
دریافت می کند.PeerHandle
دستگاه دیگر، برای مسیریابی پیام. برنامه شماPeerHandle
دستگاه دیگری را به یکی از دو روش دریافت می کند:- برنامه شما سرویسی را منتشر می کند و پیامی از یک مشترک دریافت می کند. برنامه شما
PeerHandle
مشترک را از پاسخ تماسonMessageReceived()
دریافت می کند. - برنامه شما در یک سرویس مشترک می شود. سپس، هنگامی که یک ناشر منطبق را پیدا کرد، برنامه شما
PeerHandle
ناشر را از پاسخ تماسonServiceDiscovered()
دریافت می کند.
- برنامه شما سرویسی را منتشر می کند و پیامی از یک مشترک دریافت می کند. برنامه شما
برای ارسال پیام، با sendMessage()
تماس بگیرید. پس از آن ممکن است تماس های زیر رخ دهد:
- هنگامی که پیام با موفقیت توسط همتا دریافت شد، سیستم در برنامه ارسال ، callback
onMessageSendSucceeded()
را فراخوانی می کند. - هنگامی که همتا پیامی را دریافت می کند، سیستم در برنامه دریافت کننده ، پاسخ تماس
onMessageReceived()
را فرا می خواند.
اگرچه PeerHandle
برای برقراری ارتباط با همتایان مورد نیاز است، اما نباید به آن به عنوان یک شناسه دائمی همتایان اعتماد کنید. شناسه های سطح بالاتر را می توان توسط برنامه استفاده کرد - که در خود سرویس کشف یا در پیام های بعدی تعبیه شده است. میتوانید با متد setMatchFilter()
یا setServiceSpecificInfo()
PublishConfig
یا SubscribeConfig
یک شناسه را در سرویس کشف جاسازی کنید. متد setMatchFilter()
بر روی اکتشاف تاثیر می گذارد، در حالی که متد setServiceSpecificInfo()
بر کشف تاثیر نمی گذارد.
جاسازی یک شناسه در یک پیام مستلزم تغییر آرایه بایت پیام برای گنجاندن یک شناسه (مثلاً به عنوان دو بایت اول) است.
ارتباط ایجاد کنید
Wi-Fi Aware از شبکه سرویس گیرنده-سرور بین دو دستگاه 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
در ناشر باز کنید، اما باید آدرس و پورت IPv6ServerSocket
را بدانید. شما اینها را از شی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 API اجازه می دهد تا با استفاده از آدرس MAC یا PeerHandle، به یک همتای Wi-Fi Aware دسترسی داشته باشید.
کشف Wi-Fi Aware میتواند فقط به کشف سرویسها در یک geofence خاص محدود شود. به عنوان مثال، میتوانید یک geofence راهاندازی کنید که امکان کشف دستگاهی را فراهم میکند که سرویس "Aware_File_Share_Service_Name"
را منتشر میکند که نزدیکتر از 3 متر (مشخص شده به عنوان 3000 میلیمتر) و بیش از 10 متر (مشخص به عنوان 10000 میلیمتر) نباشد.
برای فعال کردن geofencing، ناشر و مشترک هر دو باید اقدامات زیر را انجام دهند:
ناشر باید محدوده را در سرویس منتشر شده با استفاده از setRangingEnabled(true) فعال کند.
اگر ناشر محدوده را فعال نکند، هر گونه محدودیت جغرافیایی مشخص شده توسط مشترک نادیده گرفته می شود و بدون توجه به فاصله، کشف عادی انجام می شود.
مشترک باید یک geofence را با استفاده از ترکیبی از setMinDistanceMm و setMaxDistanceMm مشخص کند.
برای هر یک از مقادیر، یک فاصله نامشخص به معنای هیچ محدودیتی نیست. فقط تعیین حداکثر فاصله به معنای حداقل فاصله 0 است. فقط تعیین حداقل فاصله به معنای عدم حداکثر است.
هنگامی که یک سرویس همتا در یک geofence کشف می شود، پاسخ تماس onServiceDiscoveredWithinRange فعال می شود که فاصله اندازه گیری شده را برای همتا فراهم می کند. سپس میتوان برای اندازهگیری فاصله در زمانهای بعدی، API مستقیم Wi-Fi RTT را فراخوانی کرد.