היכולות של Wi-Fi Aware מאפשרות למכשירים עם Android 8.0 (רמת API 26) ומעלה לגלות מכשירים אחרים ולהתחבר אליהם ישירות, ללא צורך בחיבור מסוג אחר ביניהם. Wi-Fi Aware נקרא גם Neighbor Awareness Networking (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 תומכים בקצבי העברה גבוהים יותר למרחקים ארוכים יותר מאשר חיבורי Bluetooth. סוגי החיבורים האלה שימושיים לאפליקציות שמשתפות כמויות גדולות של נתונים בין משתמשים, כמו אפליקציות לשיתוף תמונות.
שיפורים ב-Android 13 (רמת API 33)
במכשירים שמותקנת בהם גרסת Android 13 (רמת API 33) ומעלה שתומכים במצב תקשורת מיידית, אפליקציות יכולות להשתמש בשיטות PublishConfig.Builder.setInstantCommunicationModeEnabled()
ו-SubscribeConfig.Builder.setInstantCommunicationModeEnabled()
כדי להפעיל או להשבית את מצב התקשורת המיידית בסשן של גילוי מפרסם או מנוי. מצב תקשורת מיידית מזרז את חילופי ההודעות, את גילוי השירותים ואת ההגדרה של נתיב הנתונים כחלק מהפעלת גילוי של מפרסם או מנוי. כדי לקבוע אם מכשיר תומך במצב תקשורת מיידית, משתמשים בשיטה isInstantCommunicationModeSupported()
.
שיפורים ב-Android 12 (רמת API 31)
ב-Android 12 (רמת API 31) נוספו שיפורים ל-Wi-Fi Aware:
- במכשירים עם Android 12 (רמת API 31) ומעלה, אפשר להשתמש בקריאה החוזרת (callback) של
onServiceLost()
כדי לקבל התראה כשהאפליקציה מאבדת שירות שזוהה בגלל שהשירות מפסיק לפעול או יוצא מטווח הקליטה. - הגדרת נתיבי נתונים ב-Wi-Fi Aware פשוטה יותר. בגרסאות קודמות נעשה שימוש בהעברת הודעות ברמה 2 כדי לספק את כתובת ה-MAC של היוזם, מה שהוביל לזמן אחזור. במכשירים עם Android מגרסה 12 ואילך, אפשר להגדיר את המשיב (השרת) כך שיקבל כל עמית – כלומר, הוא לא צריך לדעת מראש את כתובת ה-MAC של היוזם. האפשרות הזו מאיצה את ההפעלה של נתיב הנתונים ומאפשרת ליצור כמה קישורים מנקודה לנקודה עם בקשת רשת אחת בלבד.
- אפליקציות שפועלות ב-Android בגרסה 12 ומעלה יכולות להשתמש בשיטה
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
, כמו שמוצג בהמשך: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 של המכשיר המפרסם, המנויים מגלים את השירות. כשמשתמש רשום מגלה אתר חדשות, האתר לא מקבל על כך הודעה. עם זאת, אם המשתמש הרשום שולח הודעה לאתר, האתר מקבל הודעה. במקרה כזה, המערכת קוראת ל-callback method onMessageReceived()
. אפשר להשתמש בארגומנט PeerHandle
מהשיטה הזו כדי לשלוח הודעה בחזרה למנוי או ליצור חיבור אליו.
כדי להפסיק את הפרסום של השירות, צריך להתקשר אל DiscoverySession.close()
.
סשנים של גילוי משויכים לרכיב ההורה שלהם, WifiAwareSession
. אם הסשן הראשי נסגר, גם סשנים של גילוי שמשויכים אליו נסגרים. גם אובייקטים שהוצאו משימוש נסגרים, אבל המערכת לא מבטיחה מתי סשנים שלא נמצאים בהיקף נסגרים, ולכן מומלץ לקרוא באופן מפורש לשיטות close()
.
הרשמה למינוי לשירות
כדי להירשם לשירות, מפעילים את method 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
של המנוי מהקריאה החוזרת (callback) שלonMessageReceived()
. - האפליקציה שלך נרשמת לשירות. לאחר מכן, כשהמערכת מזהה בעל תוכן דיגיטלי תואם, האפליקציה מקבלת את
PeerHandle
של בעל התוכן הדיגיטלי מהקריאה החוזרת (callback) שלonServiceDiscovered()
.
- האפליקציה מפרסמת שירות ומקבלת הודעה ממנוי.
האפליקציה מקבלת את
כדי לשלוח הודעה, מתקשרים אל
sendMessage()
. אחרי זה יכולות להתבצע הקריאות החוזרות (callback) הבאות:
- כשההודעה מתקבלת בהצלחה על ידי העמית, המערכת קוראת ל-callback באפליקציית השליחה.
onMessageSendSucceeded()
- כשהעמית מקבל הודעה, המערכת קוראת ל-callback
onMessageReceived()
באפליקציה המקבלת.
למרות שנדרש PeerHandle
כדי לתקשר עם עמיתים, לא מומלץ להסתמך עליו כמזהה קבוע של עמיתים. האפליקציה יכולה להשתמש במזהים ברמה גבוהה יותר – הם מוטמעים בשירות הגילוי עצמו או בהודעות הבאות. אפשר להטמיע מזהה בשירות הגילוי באמצעות השיטה setMatchFilter()
או setServiceSpecificInfo()
של PublishConfig
או SubscribeConfig
. השיטה
setMatchFilter()
משפיעה על הגילוי, ואילו השיטה
setServiceSpecificInfo()
לא משפיעה על הגילוי.
הטמעה של מזהה בהודעה מרמזת על שינוי של מערך הבייטים של ההודעה כדי לכלול מזהה (למשל, בתור כמה הבייטים הראשונים).
יצירת חיבור
Wi-Fi Aware תומך ברשתות לקוח-שרת בין שני מכשירי 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. אם מציינים רק את המרחק המינימלי, המשמעות היא שאין מרחק מקסימלי.
כשמתגלה שירות עמית בתוך גדר וירטואלית, מופעלת קריאה חוזרת (callback) של onServiceDiscoveredWithinRange, שמספקת את המרחק שנמדד עד לעמית. לאחר מכן אפשר להפעיל את ה-API הישיר של Wi-Fi RTT לפי הצורך כדי למדוד מרחק בשלבים מאוחרים יותר.