סקירה כללית על Wi-Fi Aware

יכולות של מודעות ל-Wi-Fi מאפשרות למכשירים שפועלת בהם גרסת Android 8.0 (רמת API 26) וגם גבוה יותר כדי לגלות זה את זה ולהתחבר ישירות זה לזה ללא שום סוג אחר של את הקישוריות ביניהם. המודעות ל-Wi-Fi נקראת גם מוּדעוּת בסביבה Networking (NAN).

רשתות Wi-Fi Aware פועלות על ידי יצירת אשכולות עם מכשירים שכנים, או על ידי יצירת אשכול חדש אם המכשיר הוא הראשון באזור. הזה הקיבוץ באשכולות חל על כל המכשיר ומנוהל באמצעות ה-Wi-Fi שירות מערכת ידוע; לאפליקציות אין שליטה על ההתנהגות של אשכולות. שימוש באפליקציות את ממשקי ה-API של מודעות ל-Wi-Fi כדי לדבר עם שירות מערכת המודעות ל-Wi-Fi, שמנהל חומרת Wi-Fi Aware במכשיר.

ממשקי ה-API של Wi-Fi Aware מאפשרים לאפליקציות לבצע את הפעולות הבאות:

  • גילוי מכשירים אחרים: לממשק ה-API יש מנגנון לאיתור מכשירים אחרים בסביבה. התהליך מתחיל כשמכשיר אחד מפרסם אחד או שירותים נוספים לגילוי. לאחר מכן, כשמכשיר נרשם לשירות אחד או יותר ונכנס לטווח ה-Wi-Fi של בעל התוכן הדיגיטלי, המנוי מקבל התראה על כך שזוהה בעל תוכן דיגיטלי תואם. אחרי המנוי מגלה בעל אתר, והמנוי יכול לשלוח סרטון Shorts או ליצור חיבור לרשת עם המכשיר שאותר. מכשירים יכולים להיות גם מוצגים וגם מנויים בו-זמנית.

  • יצירת חיבור לרשת: אחרי ששני מכשירים מזהים זה את זה, הם יכולים ליצור חיבור לרשת דו-כיווני של 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, צריך לבצע את את השלבים הבאים:

  1. מבקשים את ההרשאות הבאות במניפסט של האפליקציה:

    <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" />
  2. בודקים אם המכשיר תומך ב-Wi-Fi Aware באמצעות ה-API‏ PackageManager, כפי שמתואר בהמשך:

    Kotlin

    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)

    Java

    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. בודקים אם התכונה Wi-Fi Aware זמינה כרגע. יכול להיות שחיבור Wi-Fi Aware פועל המכשיר, אבל ייתכן שהוא לא זמין כרגע כי המשתמש השבית Wi-Fi או מיקום. בהתאם ליכולות החומרה והקושחה שלהם, מכשירים מסוימים ייתכן שלא יתמוך ב-Wi-Fi Aware אם Wi-Fi ישיר, 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.
  • יצירת סשן 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() שיטת הקריאה החוזרת (callback) נקראת. אפשר להשתמש ארגומנט אחד (PeerHandle) משיטה זו עד לשלוח הודעה בחזרה למשתמש הרשום, או ליצור חיבור אליו.

כדי להפסיק את פרסום השירות, צריך להתקשר למספר DiscoverySession.close(). סשנים של Discovery משויכים לרכיב ההורה שלהם 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);

אם פעולת ההרשמה תצליח, המערכת תפנה ל-callback‏ onSubscribeStarted() באפליקציה. מכיוון שאפשר להשתמש בארגומנט SubscribeDiscoverySession ב-callback כדי לתקשר עם בעל תוכן דיגיטלי אחרי שהאפליקציה תזהה אותו, כדאי לשמור את ההפניה הזו. אפשר לעדכן את סשן ההרשמה בכל שלב על ידי קריאה לפונקציה updateSubscribe() בסשן הגילוי.

בשלב הזה, המינוי שלכם ממתין שפלטפורמות התוכן המתאימות ייכנסו לטווח ה-Wi-Fi. במקרה כזה, המערכת מריצה את שיטת ה-callback‏ onServiceDiscovered(). אפשר להשתמש ב-PeerHandle מהקריאה החוזרת לארגומנט הזה כדי לשלוח הודעה או ליצור חיבור אל המו"ל.

כדי להפסיק את ההרשמה לשירות, צריך להתקשר DiscoverySession.close() סשנים של Discovery משויכים לרכיב ההורה שלהם WifiAwareSession. אם סשן ההורה הוא נסגרה, וסשנים של גילוי שמשויכים אליו סגורים גם הם. אובייקטים שהוצאו משימוש נסגרים גם כן, אבל המערכת לא מבטיחה מתי סשנים מחוץ להיקף נסגרים, לכן מומלץ להפעיל את השיטות close() באופן מפורש.

שליחת הודעה

כדי לשלוח הודעה למכשיר אחר, צריך את האובייקטים הבאים:

  • DiscoverySession. האובייקט הזה מאפשר לכם לבצע קריאה ל-sendMessage(). האפליקציה שלך מקבלת DiscoverySession על ידי פרסום שירות או הרשמה לשירות .

  • PeerHandle של המכשיר השני, כדי לנתב את ההודעה. האפליקציה מקבלת את הערך של PeerHandle של מכשיר אחר באחת משתי דרכים:

    • האפליקציה מפרסמת שירות ומקבלת הודעה ממנוי. האפליקציה מקבלת את הערך של PeerHandle של המנוי מהפונקציה החוזרת onMessageReceived().
    • האפליקציה רשומה לשירות. לאחר מכן, כשהמערכת תזהה בעל תוכן דיגיטלי תואם, האפליקציה תקבל את הערך של PeerHandle של בעל התוכן הדיגיטלי מהקריאה החוזרת (callback) של onServiceDiscovered().

כדי לשלוח הודעה, צריך להתקשר למספר sendMessage(). לאחר מכן, יכול להיות שיתבצעו הקריאות החוזרות הבאות:

  • כשההודעה מתקבלת בהצלחה על ידי הצד השני, המערכת מפעילה את הקריאה החוזרת (callback) onMessageSendSucceeded() באפליקציה ששולחת.
  • כשהצד השני מקבל הודעה, המערכת מפעילה את הפונקציה החוזרת onMessageReceived() באפליקציה המתקבלת.

PeerHandle נדרש כדי לתקשר עם אפליקציות להשוואה, אבל לא כדאי מסתמכים עליו כמזהה קבוע של אפליקציות להשוואה. האפליקציה יכולה להשתמש במזהים ברמה גבוהה יותר – שמוטמעים בשירות הגילוי עצמו או בהודעות הבאות. אפשר להטמיע מזהה בשירות הגילוי באמצעות השיטה setMatchFilter() או השיטה setServiceSpecificInfo() של PublishConfig או SubscribeConfig. השיטה setMatchFilter() משפיעה על החשיפה, בעוד שהשיטה setServiceSpecificInfo() לא משפיעה על החשיפה.

הטמעת מזהה בהודעה מרמזת על שינוי מערך הבייטים של ההודעה לכלול מזהה (לדוגמה, כשני הבייטים הראשונים).

יצירת חיבור

Wi-Fi Aware תומך ברשת שרת לקוח בין שני מכשירי Wi-Fi Aware.

כדי להגדיר את החיבור לשרת-לקוח:

  1. להשתמש ב-Wi-Fi Aware Discovery כדי לפרסם שירות ( ולהירשם לשירות (ב לקוח).

  2. אחרי שהמנוי מגלה את המוציא לאור, לשלוח הודעה מהמנוי אל אתר החדשות.

  3. יצירת ServerSocket אצל בעל התוכן הדיגיטלי של המכשיר ולהגדיר או להשיג את היציאה שלו:

    Kotlin

    val ss = ServerSocket(0)
    val port = ss.localPort

    Java

    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. משתמשים ב-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);
  5. אחרי שבעל התוכן הדיגיטלי מבקש רשת, הוא צריך לשלוח הודעה למנוי.

  6. כשהמנוי יקבל את ההודעה מספק החדשות, עליך לבקש חיבור Wi-Fi רשת המודעות של המנוי שמשתמש באותה שיטה כמו באתר של בעל התוכן הדיגיטלי. מומלץ לא לציין יציאה בעת יצירת NetworkSpecifier שיטות ה-callback המתאימות נקראות כשהחיבור לרשת זמין, משתנה או נותק.

  7. אחרי שמפעילים את השיטה onAvailable() במנויים, זמין אובייקט Network שדרכו אפשר לפתוח Socket כדי לתקשר עם ServerSocket באתר החדשות, אבל צריך לדעת את כתובת ה-IPv6 והיציאה של ServerSocket. אתם מקבלים את אובייקט NetworkCapabilities סופקו בקריאה החוזרת (callback) של 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);
  8. כאשר מסיים עם החיבור לרשת, התקשר 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).

    אם בעל התוכן הדיגיטלי לא מפעיל את הטווח, המערכת מתעלמת מאילוצים של גדר גיאוגרפית שצוינו על ידי המנוי ומבצעת חיפוש רגיל בלי להתחשב במרחק.

  • המנוי צריך לציין גדר גיאוגרפי באמצעות שילוב כלשהו של setMinDistanceMm ו-setMaxDistanceMm.

    בכל אחד מהערכים האלה, מרחק שלא צוין לא מרמז על מגבלה. מציין רק המרחק המקסימלי מייצג מרחק מינימלי של 0. אם מציינים רק את המרחק המינימלי, המשמעות היא שאין מרחק מקסימלי.

כשמתגלה שירות אפליקציות להשוואה בתוך גבולות וירטואליים, onServiceDiscoveredWithinRange מופעלת קריאה חוזרת (callback), שמציינת את המרחק שנמדד מהעמית. לאחר מכן ניתן לקרוא לממשק ה-API של RTT ישיר ב-Wi-Fi לפי הצורך כדי למדוד את המרחק במועדים מאוחרים יותר.