نمای کلی Wi-Fi Aware

قابلیت‌های 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 مکانیزمی برای یافتن سایر دستگاه‌های مجاور دارد. The process starts when one device publishes one or more discoverable services. سپس، هنگامی که دستگاهی مشترک یک یا چند سرویس می شود و وارد محدوده 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، مراحل زیر را انجام دهید:

  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 پشتیبانی می کند یا خیر:

    کاتلین

    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)

    جاوا

    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. بررسی کنید که آیا 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 با یک فضای نام منحصر به فرد ایجاد می کند که به عنوان یک محفظه برای تمام جلسات کشف ایجاد شده در آن عمل می کند.

If the app attaches successfully, the system executes the onAttached() callback. این پاسخ تماس یک شی WifiAwareSession را ارائه می دهد که برنامه شما باید برای تمام عملیات های جلسه بعدی از آن استفاده کند. یک برنامه می تواند از جلسه برای انتشار یک سرویس یا اشتراک در یک سرویس استفاده کند.

برنامه شما باید فقط یک بار با attach() تماس بگیرد. If your app calls attach() multiple times, the app receives a different session for each call, each with its own namespace. این می تواند در سناریوهای پیچیده مفید باشد، اما به طور کلی باید از آن اجتناب کرد.

یک سرویس را منتشر کنید

برای اینکه یک سرویس قابل کشف باشد، متد 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);

If publication succeeds, then the onPublishStarted() callback method is called.

پس از انتشار، وقتی دستگاه‌هایی که برنامه‌های مشترک منطبق را اجرا می‌کنند به محدوده 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() را فراخوانی کنید. Your app gets a DiscoverySession by either publishing a service or subscribing to a service .

  • PeerHandle دستگاه دیگر، برای مسیریابی پیام. برنامه شما PeerHandle دستگاه دیگری را به یکی از دو روش دریافت می کند:

    • برنامه شما سرویسی را منتشر می کند و پیامی از یک مشترک دریافت می کند. برنامه شما PeerHandle مشترک را از پاسخ تماس onMessageReceived() دریافت می کند.
    • برنامه شما در یک سرویس مشترک می شود. سپس، هنگامی که یک ناشر منطبق را پیدا کرد، برنامه شما PeerHandle ناشر را از پاسخ تماس onServiceDiscovered() دریافت می کند.

برای ارسال پیام، با sendMessage() تماس بگیرید. پس از آن ممکن است تماس های زیر رخ دهد:

  • هنگامی که پیام با موفقیت توسط همتا دریافت شد، سیستم در برنامه ارسال ، callback onMessageSendSucceeded() را فراخوانی می کند.
  • هنگامی که همتا پیامی را دریافت می کند، سیستم در برنامه دریافت کننده ، پاسخ تماس onMessageReceived() را فرا می خواند.

اگرچه PeerHandle برای برقراری ارتباط با همتایان مورد نیاز است، اما نباید به آن به عنوان یک شناسه دائمی همتایان اعتماد کنید. شناسه های سطح بالاتر را می توان توسط برنامه استفاده کرد - که در خود سرویس کشف یا در پیام های بعدی تعبیه شده است. می‌توانید با متد setMatchFilter() یا setServiceSpecificInfo() PublishConfig یا SubscribeConfig یک شناسه را در سرویس کشف جاسازی کنید. متد setMatchFilter() بر روی اکتشاف تاثیر می گذارد، در حالی که متد setServiceSpecificInfo() بر کشف تاثیر نمی گذارد.

جاسازی یک شناسه در یک پیام مستلزم تغییر آرایه بایت پیام برای گنجاندن یک شناسه (مثلاً به عنوان دو بایت اول) است.

ارتباط ایجاد کنید

Wi-Fi Aware از شبکه سرویس گیرنده-سرور بین دو دستگاه Wi-Fi Aware پشتیبانی می کند.

برای راه اندازی اتصال مشتری-سرور:

  1. Use Wi-Fi Aware discovery to publish a service (on the server) and subscribe to a service (on the client).

  2. هنگامی که مشترک ناشر را پیدا کرد، پیامی از طرف مشترک برای ناشر ارسال کنید .

  3. یک ServerSocket در دستگاه ناشر راه اندازی کنید و پورت آن را تنظیم یا دریافت کنید:

    کاتلین

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

    جاوا

    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. از 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);
  5. هنگامی که ناشر درخواست یک شبکه می کند، باید پیامی برای مشترک ارسال کند .

  6. هنگامی که مشترک پیام را از ناشر دریافت کرد، با همان روشی که در ناشر وجود دارد، درخواست یک شبکه Wi-Fi Aware روی مشترک را بدهید. هنگام ایجاد NetworkSpecifier پورتی را مشخص نکنید. زمانی که اتصال شبکه در دسترس باشد، تغییر کند یا از بین برود، روش های پاسخ به تماس مناسب فراخوانی می شوند.

  7. هنگامی که متد onAvailable() روی مشترک فراخوانی شد، یک آبجکت Network در دسترس است که با آن می توانید یک Socket را برای برقراری ارتباط با ServerSocket در ناشر باز کنید، اما باید آدرس و پورت IPv6 ServerSocket را بدانید. شما اینها را از شی 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);
  8. وقتی اتصال شبکه به پایان رسید، unregisterNetworkCallback() را فراخوانی کنید.

رتبه بندی همتایان و کشف مکان آگاه

دستگاهی با قابلیت مکان یابی Wi-Fi RTT می تواند مستقیماً فاصله با همتایان را اندازه گیری کند و از این اطلاعات برای محدود کردن کشف سرویس Wi-Fi Aware استفاده کند.

Wi-Fi RTT API اجازه می دهد تا با استفاده از آدرس MAC یا PeerHandle، به یک همتای Wi-Fi Aware دسترسی داشته باشید.

کشف Wi-Fi Aware می‌تواند فقط به کشف سرویس‌ها در یک geofence خاص محدود شود. For example, you can set up a geofence that allows discovery of a device publishing an "Aware_File_Share_Service_Name" service that is no closer than 3 meters (specified as 3,000 mm) and no further than 10 meters (specified as 10,000 mm).

برای فعال کردن geofencing، ناشر و مشترک هر دو باید اقدامات زیر را انجام دهند:

  • ناشر باید محدوده را در سرویس منتشر شده با استفاده از setRangingEnabled(true) فعال کند.

    اگر ناشر محدوده را فعال نکند، هر گونه محدودیت جغرافیایی مشخص شده توسط مشترک نادیده گرفته می شود و بدون توجه به فاصله، کشف عادی انجام می شود.

  • مشترک باید یک geofence را با استفاده از ترکیبی از setMinDistanceMm و setMaxDistanceMm مشخص کند.

    برای هر یک از مقادیر، یک فاصله نامشخص به معنای هیچ محدودیتی نیست. فقط تعیین حداکثر فاصله به معنای حداقل فاصله 0 است. فقط تعیین حداقل فاصله به معنای عدم حداکثر است.

هنگامی که یک سرویس همتا در یک geofence کشف می شود، پاسخ تماس onServiceDiscoveredWithinRange فعال می شود که فاصله اندازه گیری شده را برای همتا فراهم می کند. سپس می‌توان برای اندازه‌گیری فاصله در زمان‌های بعدی، API مستقیم Wi-Fi RTT را فراخوانی کرد.