יכולות של מודעות ל-Wi-Fi מאפשרות למכשירים עם 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 פשוט יותר. בגרסאות קודמות נעשה שימוש בהודעות L2 כדי לספק את כתובת ה-MAC של הגורם שהתחיל את הבקשה, וכתוצאה מכך נוצרה זמן אחזור. במכשירים עם Android מגרסה 12 ואילך, אפשר להגדיר את המכשיר המגיב (השרת) לקבלת כל מכשיר אחר – כלומר, הוא לא צריך לדעת מראש את כתובת ה-MAC של המכשיר שהתחיל את ההתחברות. כך אפשר להאיץ את ההפעלה של נתיב הנתונים ולאפשר מספר קישורים מקצה לקצה באמצעות בקשת רשת אחת בלבד.
- אפליקציות שפועלות ב-Android בגרסה 12 ואילך יכולות להשתמש ב-method
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
, שנשלחת בכל פעם שהזמינות משתנה. כשהאפליקציה מקבלת את ה-intent של השידור, היא צריכה לבטל את כל הסשנים הקיימים (בהנחה שהשירות של 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 עם מרחב שמות ייחודי שמשמש כמאגר לכל סשני הגילוי שנוצרים בתוכו.
אם האפליקציה מצורפת בהצלחה, המערכת מבצעת את הקריאה החוזרת (callback) של onAttached()
.
פונקציית ה-callback הזו מספקת אובייקט 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()
.
סשנים של Discovery משויכים לרכיב ההורה שלהם WifiAwareSession
. אם הסשן הראשי נסגר, גם סשני הגילוי המשויכים אליו נסגרים. למרות שאובייקטים שנמחקו נסגרים גם כן, המערכת לא מבטיחה מתי סשנים מחוץ לטווח ייסגרו, ולכן אנחנו ממליצים לקרוא באופן מפורש ל-methods 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. במקרה כזה, המערכת מפעילה את שיטת הקריאה החוזרת (callback) של onServiceDiscovered()
. אפשר להשתמש בארגומנט PeerHandle
מהקריאה החוזרת (callback) הזו כדי לשלוח הודעה או ליצור חיבור למפרסם הזה.
כדי לבטל את המינוי לשירות, צריך להתקשר למספר DiscoverySession.close()
.
סשנים של Discovery משויכים לרכיב ההורה שלהם WifiAwareSession
. אם הסשן הראשי נסגר, גם סשני הגילוי המשויכים אליו נסגרים. אובייקטים שהוצאו משימוש נסגרים גם כן, אבל המערכת לא מבטיחה מתי סשנים מחוץ להיקף נסגרים, לכן מומלץ להפעיל את השיטות close()
באופן מפורש.
שליחת הודעה
כדי לשלוח הודעה למכשיר אחר, צריך את האובייקטים הבאים:
DiscoverySession
. האובייקט הזה מאפשר לכם לבצע קריאה ל-sendMessage()
. האפליקציה מקבלת את המאפייןDiscoverySession
על ידי פרסום שירות או הרשמה למינוי לשירות.PeerHandle
של המכשיר השני, כדי לנתב את ההודעה. האפליקציה מקבלת את הערך שלPeerHandle
של מכשיר אחר באחת משתי דרכים:- האפליקציה מפרסמת שירות ומקבלת הודעה ממנוי.
האפליקציה מקבלת את הערך של
PeerHandle
של המנוי מהפונקציה החוזרתonMessageReceived()
. - האפליקציה רשומה לשירות. לאחר מכן, כשהיא מוצאת בעל אתר תואם, האפליקציה מקבלת את
PeerHandle
של בעל האפליקציה מהקריאה החוזרת (callback) שלonServiceDiscovered()
.
- האפליקציה מפרסמת שירות ומקבלת הודעה ממנוי.
האפליקציה מקבלת את הערך של
כדי לשלוח הודעה, צריך להתקשר למספר sendMessage()
. לאחר מכן עשויות להתרחש הקריאות החוזרות הבאות:
- כשההודעה השכנה מתקבלת, המערכת מפעילה את הקריאה החוזרת (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
. שיטות ה-callback המתאימות נקראות כשהחיבור לרשת זמין, משתנה או נותק.אחרי שמפעילים את השיטה
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()
.
טווח של אפליקציות להשוואה וגילוי מבוסס-מיקום
מכשיר עם יכולות של מיקום לפי זמן אחזור (RTT) של Wi-Fi יכול למדוד ישירות את המרחק למכשירים אחרים ולהשתמש במידע הזה כדי להגביל את גילוי השירותים של 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).
אם בעל התוכן הדיגיטלי לא מפעיל את הטווח, המערכת תתעלם מאילוצים של גדר גיאוגרפית שצוינו על ידי המנוי ותבצע חיפוש רגיל, בלי להתחשב במרחק.
המנוי צריך לציין גבולות וירטואליים באמצעות שילוב כלשהו של setMinDestinationMm ו-setMaxDestinationMm.
בכל אחד מהערכים, מרחק לא מצוין פירושו שאין הגבלה. רק ציון המרחק המקסימלי מעיד על מרחק מינימלי של 0. אם מציינים רק את המרחק המינימלי, המשמעות היא שאין מרחק מקסימלי.
כשמתגלה שירות של אפליקציות להשוואה בתוך גבולות וירטואליים, מופעלת קריאה חוזרת (callback) מסוג onServiceDiscoveredWithinRange, שמספקת את המרחק שנמדד בין הרשת השכנה. לאחר מכן אפשר להפעיל את ה-API הישיר של RTT ב-Wi-Fi לפי הצורך כדי למדוד את המרחק במועדים מאוחרים יותר.