تتيح إمكانات Wi-Fi Aware للأجهزة التي تعمل بالإصدار 8.0 من نظام التشغيل Android (المستوى 26 من واجهة برمجة التطبيقات) والإصدارات الأحدث رصد الأجهزة الأخرى والاتصال بها مباشرةً بدون أي نوع آخر من الاتصال بينها. يُعرف Wi-Fi Aware أيضًا باسم اتصال مباشر بمحطات لاسلكية مجاورة (NAN).
تعمل شبكة Wi-Fi Aware من خلال إنشاء مجموعات مع الأجهزة المجاورة، أو من خلال إنشاء مجموعة جديدة إذا كان الجهاز هو الأول في منطقة معيّنة. ينطبق سلوك التجميع هذا على الجهاز بأكمله، وتديره خدمة نظام Wi-Fi Aware، ولا يمكن للتطبيقات التحكّم في سلوك التجميع. تستخدم التطبيقات واجهات برمجة تطبيقات Wi-Fi Aware للتواصل مع خدمة نظام Wi-Fi Aware التي تدير أجهزة Wi-Fi Aware على الجهاز.
تتيح واجهات برمجة التطبيقات Wi-Fi Aware للتطبيقات تنفيذ العمليات التالية:
استكشاف الأجهزة الأخرى: تتضمّن واجهة برمجة التطبيقات آلية للعثور على الأجهزة الأخرى القريبة. تبدأ العملية عندما ينشر جهاز واحد خدمة واحدة أو أكثر يمكن اكتشافها. بعد ذلك، عندما يشترك جهاز في خدمة واحدة أو أكثر ويدخل ضمن نطاق شبكة Wi-Fi الخاصة بالناشر، يتلقّى المشترك إشعارًا بأنّه تم العثور على ناشر مطابق. بعد أن يعثر المشترك على الناشر، يمكنه إرسال رسالة قصيرة أو إنشاء اتصال شبكة مع الجهاز الذي تم العثور عليه. يمكن أن تكون الأجهزة ناشرة ومشتركة في الوقت نفسه.
إنشاء اتصال شبكة: بعد أن يعثر جهازان على بعضهما البعض، يمكنهما إنشاء اتصال شبكة ثنائي الاتجاه باستخدام Wi-Fi Aware بدون نقطة وصول.
تتيح اتصالات شبكة Wi-Fi Aware معدّلات نقل بيانات أعلى على مسافات أطول مقارنةً باتصالات البلوتوث. تكون أنواع الاتصالات هذه مفيدة للتطبيقات التي تشارك كميات كبيرة من البيانات بين المستخدمين، مثل تطبيقات مشاركة الصور.
تحسينات في الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات)
على الأجهزة التي تعمل بالإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث التي تتوافق مع وضع الاتصال الفوري، يمكن للتطبيقات استخدام الطريقتَين PublishConfig.Builder.setInstantCommunicationModeEnabled() وSubscribeConfig.Builder.setInstantCommunicationModeEnabled() لتفعيل وضع الاتصال الفوري أو إيقافه لجلسة البحث عن الناشر أو المشترك. يؤدي وضع الاتصال الفوري إلى تسريع تبادل الرسائل واكتشاف الخدمات وإعداد أي مسار بيانات كجزء من جلسة اكتشاف الناشر أو المشترك. لتحديد ما إذا كان الجهاز يتيح وضع التواصل الفوري، استخدِم طريقة isInstantCommunicationModeSupported().
تحسينات في الإصدار 12 من نظام التشغيل Android (المستوى 31 لواجهة برمجة التطبيقات)
يضيف نظام التشغيل Android 12 (المستوى 31 لواجهة برمجة التطبيقات) بعض التحسينات على Wi-Fi Aware:
- على الأجهزة التي تعمل بنظام التشغيل Android 12 (المستوى 31 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمكنك استخدام
onServiceLost()للحصول على تنبيه عندما يفقد تطبيقك خدمة تم اكتشافها بسبب توقّف الخدمة أو خروجها عن النطاق. - تم تبسيط عملية إعداد مسارات بيانات Wi-Fi Aware. استخدمت الإصدارات السابقة ميزة المراسلة من الطبقة 2 لتوفير عنوان MAC الخاص بالمرسِل، ما أدى إلى حدوث تأخير. على الأجهزة التي تعمل بالإصدار 12 من نظام التشغيل Android والإصدارات الأحدث، يمكن ضبط الجهاز المستجيب (الخادم) لقبول أي جهاز آخر، أي أنّه لا يحتاج إلى معرفة عنوان MAC الخاص بالجهاز المبدئي مسبقًا. يؤدي ذلك إلى تسريع عملية إعداد مسار البيانات ويتيح إنشاء روابط متعددة بين نقطتين من خلال طلب شبكة واحد فقط.
- يمكن للتطبيقات التي تعمل على الإصدار 12 من نظام التشغيل 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 من خلال واجهة برمجة التطبيقات
PackageManagerكما هو موضّح أدناه:Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
تحقَّق ممّا إذا كانت ميزة Wi-Fi Aware متاحة حاليًا. قد تكون ميزة Wi-Fi Aware متوفّرة على الجهاز، ولكن قد لا تكون متاحة حاليًا لأنّ المستخدم أوقف Wi-Fi أو الموقع الجغرافي. قد لا تتوافق بعض الأجهزة مع Wi-Fi Aware إذا كانت تستخدم Wi-Fi Direct أو SoftAP أو الربط، وذلك حسب إمكانيات الأجهزة والبرامج الثابتة. للتحقّق مما إذا كانت ميزة Wi-Fi Aware متاحة حاليًا، اتّصِل بالرقم
isAvailable().قد تتغيّر إمكانية توفّر Wi-Fi Aware في أي وقت. يجب أن يسجّل تطبيقك
BroadcastReceiverلتلقّيACTION_WIFI_AWARE_STATE_CHANGED، والذي يتم إرساله كلما تغيّر مدى التوفّر. عندما يتلقّى تطبيقك الغرض من البث، عليه تجاهل جميع الجلسات الحالية (بافتراض أنّ خدمة Wi-Fi Aware قد تعرّضت لانقطاع)، ثم التحقّق من حالة التوفّر الحالية وتعديل سلوكه وفقًا لذلك. مثلاً:Kotlin
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)
Java
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، يجب أن يحصل تطبيقك على
WifiAwareSession من خلال استدعاء
attach(). تنفّذ هذه الطريقة ما يلي:
- يتم تفعيل أجهزة Wi-Fi Aware.
- الانضمام إلى مجموعة Wi-Fi Aware أو إنشاؤها
- تنشئ هذه الطريقة جلسة Wi-Fi Aware مع مساحة اسم فريدة تعمل كحاوية لجميع جلسات البحث التي تم إنشاؤها بداخلها.
في حال نجح التطبيق في إرفاق نفسه، ينفّذ النظام وظيفة الاستدعاء onAttached().
يوفّر هذا الإجراء ردًّا WifiAwareSession يجب أن يستخدمه تطبيقك في جميع عمليات الجلسة اللاحقة. يمكن لأحد التطبيقات استخدام الجلسة من أجل نشر خدمة أو الاشتراك في خدمة.
يجب أن يطلب تطبيقك
attach() مرة واحدة فقط. إذا كان تطبيقك يستدعي attach()
عدة مرات، سيتلقّى التطبيق جلسة مختلفة لكل عملية استدعاء، وستتضمّن كل جلسة مساحة الاسم الخاصة بها. قد يكون ذلك مفيدًا في السيناريوهات المعقّدة، ولكن يجب تجنُّبه بشكل عام.
نشر خدمة
لإتاحة إمكانية اكتشاف خدمة، استدعِ طريقة publish() التي تتطلّب المَعلمات التالية:
- تحدّد
PublishConfigاسم الخدمة وخصائص الإعداد الأخرى، مثل فلتر المطابقة. - تحدّد
DiscoverySessionCallbackالإجراءات التي سيتم تنفيذها عند وقوع أحداث، مثل عندما يتلقّى المشترك رسالة.
وفي ما يلي مثال لذلك:
Kotlin
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) { ... } })
Java
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الإجراءات التي سيتم تنفيذها عند وقوع أحداث، مثل اكتشاف ناشر.
وفي ما يلي مثال لذلك:
Kotlin
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)
Java
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(). قد تحدث عمليات معاودة الاتصال التالية:
- عندما يتلقّى الجهاز الآخر الرسالة بنجاح، يستدعي النظام دالة
onMessageSendSucceeded()في تطبيق الإرسال. - عندما يتلقّى الجهاز الآخر رسالة، يستدعي النظام دالة
onMessageReceived()callback في تطبيق الاستلام.
على الرغم من أنّ PeerHandle مطلوب للتواصل مع الأجهزة النظيرة، يجب عدم الاعتماد عليه كمعرّف دائم للأجهزة النظيرة. يمكن أن تستخدم التطبيقات معرّفات ذات مستوى أعلى، سواء كانت مضمّنة في خدمة البحث نفسها أو في الرسائل اللاحقة. يمكنك تضمين معرّف في خدمة الاكتشاف باستخدام الطريقتَين setMatchFilter() أو setServiceSpecificInfo() في PublishConfig أو SubscribeConfig. تؤثّر الطريقة
setMatchFilter() في إمكانية العثور على المحتوى، بينما لا تؤثّر الطريقة
setServiceSpecificInfo() في ذلك.
يتضمّن تضمين معرّف في رسالة تعديل مصفوفة بايت الرسالة لتضمين معرّف (على سبيل المثال، كأول وحدتَي بايت).
إنشاء اتصال
تتيح تقنية Wi-Fi Aware إنشاء شبكة بين جهازَين يستخدمان هذه التقنية.
لإعداد الاتصال بين العميل والخادم، اتّبِع الخطوات التالية:
استخدِم ميزة البحث في Wi-Fi Aware من أجل نشر خدمة (على الخادم) والاشتراك في خدمة (على العميل).
بعد أن يعثر المشترك على الناشر، عليه إرسال رسالة من المشترك إلى الناشر.
ابدأ
ServerSocketعلى جهاز الناشر، ثم اضبط المنفذ أو احصل عليه:Kotlin
val ss = ServerSocket(0) val port = ss.localPort
Java
ServerSocket ss = new ServerSocket(0); int port = ss.getLocalPort();
استخدِم
ConnectivityManagerلطلب شبكة Wi-Fi Aware من الناشر باستخدامWifiAwareNetworkSpecifier، مع تحديد جلسة البحث وPeerHandleللمشترك، والذي حصلت عليه من الرسالة التي أرسلها المشترك:Kotlin
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);
Java
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():Kotlin
val peerAwareInfo = networkCapabilities.transportInfo as WifiAwareNetworkInfo val peerIpv6 = peerAwareInfo.peerIpv6Addr val peerPort = peerAwareInfo.port ... val socket = network.getSocketFactory().createSocket(peerIpv6, peerPort)
Java
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 تحديد المسافة مباشرةً إلى جهاز Wi-Fi Aware باستخدام عنوان MAC أو PeerHandle.
يمكن حصر عملية اكتشاف الأجهزة المتوافقة مع Wi-Fi Aware على اكتشاف الخدمات ضمن نطاق جغرافي معيّن فقط. على سبيل المثال، يمكنك إعداد سياج جغرافي يسمح باكتشاف جهاز ينشر خدمة "Aware_File_Share_Service_Name" على مسافة لا تقل عن 3 أمتار (يتم تحديدها على أنّها 3,000 ملم) ولا تزيد عن 10 أمتار (يتم تحديدها على أنّها 10,000 ملم).
لتفعيل ميزة "السياج الجغرافي"، يجب أن يتّخذ كل من الناشر والمشترك إجراءً:
على الناشر تفعيل تحديد المدى في الخدمة المنشورة باستخدام setRangingEnabled(true).
إذا لم يفعّل الناشر تحديد المدى، سيتم تجاهل أي قيود على السياج الجغرافي يحدّدها المشترك، وسيتم إجراء عملية البحث العادية بدون مراعاة المسافة.
على المشترك تحديد سياج جغرافي باستخدام مجموعة من setMinDistanceMm و setMaxDistanceMm.
بالنسبة إلى أيّ من القيمتين، تشير المسافة غير المحدّدة إلى عدم وجود حدّ. يؤدي تحديد الحد الأقصى للمسافة فقط إلى ضِمنية الحد الأدنى للمسافة وهو 0. ويعني تحديد الحد الأدنى للمسافة فقط أنّه لا يوجد حد أقصى.
عندما يتم رصد خدمة جهاز قريب ضمن نطاق سياج جغرافي، يتم تشغيل معاودة الاتصال onServiceDiscoveredWithinRange، التي توفّر المسافة المقاسة إلى الجهاز القريب. يمكن بعد ذلك طلب بيانات من واجهة برمجة التطبيقات Wi-Fi RTT المباشرة حسب الحاجة لقياس المسافة في أوقات لاحقة.