نمای کلی 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 مکانیزمی برای یافتن سایر دستگاه‌های مجاور دارد. این فرآیند زمانی شروع می شود که یک دستگاه یک یا چند سرویس قابل کشف را منتشر کند . سپس، هنگامی که دستگاهی مشترک یک یا چند سرویس می شود و وارد محدوده 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 با یک فضای نام منحصر به فرد ایجاد می کند که به عنوان یک محفظه برای تمام جلسات کشف ایجاد شده در آن عمل می کند.

اگر برنامه با موفقیت متصل شود، سیستم پاسخ تماس onAttached() را اجرا می کند. این پاسخ تماس یک شی WifiAwareSession را ارائه می دهد که برنامه شما باید برای تمام عملیات های جلسه بعدی از آن استفاده کند. یک برنامه می تواند از جلسه برای انتشار یک سرویس یا اشتراک در یک سرویس استفاده کند.

برنامه شما باید فقط یک بار attach() تماس بگیرد. اگر برنامه شما چندین بار attach() تماس بگیرد، برنامه برای هر تماس جلسه متفاوتی دریافت می‌کند که هر کدام فضای نام خاص خود را دارند. این می تواند در سناریوهای پیچیده مفید باشد، اما به طور کلی باید از آن اجتناب کرد.

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

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

اگر انتشار موفقیت آمیز باشد، متد onPublishStarted() فراخوانی می شود.

پس از انتشار، وقتی دستگاه‌هایی که برنامه‌های مشترک منطبق را اجرا می‌کنند به محدوده 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() را فراخوانی کنید. برنامه شما با انتشار یک سرویس یا اشتراک در یک سرویس، DiscoverySession دریافت می کند.

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

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

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

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

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

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

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

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

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

  1. از کشف Wi-Fi Aware برای انتشار یک سرویس (در سرور) و اشتراک در یک سرویس (در مشتری) استفاده کنید.

  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 خاص محدود شود. به عنوان مثال، می‌توانید یک geofence راه‌اندازی کنید که امکان کشف دستگاهی را فراهم می‌کند که سرویس "Aware_File_Share_Service_Name" را منتشر می‌کند که نزدیک‌تر از 3 متر (مشخص شده به عنوان 3000 میلی‌متر) و بیش از 10 متر (مشخص به عنوان 10000 میلی‌متر) نباشد.

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

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

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

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

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

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

،

قابلیت‌های 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 مکانیزمی برای یافتن سایر دستگاه‌های مجاور دارد. این فرآیند زمانی شروع می شود که یک دستگاه یک یا چند سرویس قابل کشف را منتشر کند . سپس، هنگامی که دستگاهی مشترک یک یا چند سرویس می شود و وارد محدوده 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 با یک فضای نام منحصر به فرد ایجاد می کند که به عنوان یک محفظه برای تمام جلسات کشف ایجاد شده در آن عمل می کند.

اگر برنامه با موفقیت متصل شود، سیستم پاسخ تماس onAttached() را اجرا می کند. این پاسخ تماس یک شی WifiAwareSession را ارائه می دهد که برنامه شما باید برای تمام عملیات های جلسه بعدی از آن استفاده کند. یک برنامه می تواند از جلسه برای انتشار یک سرویس یا اشتراک در یک سرویس استفاده کند.

برنامه شما باید فقط یک بار attach() تماس بگیرد. اگر برنامه شما چندین بار attach() تماس بگیرد، برنامه برای هر تماس جلسه متفاوتی دریافت می‌کند که هر کدام فضای نام خاص خود را دارند. این می تواند در سناریوهای پیچیده مفید باشد، اما به طور کلی باید از آن اجتناب کرد.

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

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

اگر انتشار موفقیت آمیز باشد، متد onPublishStarted() فراخوانی می شود.

پس از انتشار، وقتی دستگاه‌هایی که برنامه‌های مشترک منطبق را اجرا می‌کنند به محدوده 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() را فراخوانی کنید. برنامه شما با انتشار یک سرویس یا اشتراک در یک سرویس، DiscoverySession دریافت می کند.

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

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

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

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

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

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

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

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

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

  1. از کشف Wi-Fi Aware برای انتشار یک سرویس (در سرور) و اشتراک در یک سرویس (در مشتری) استفاده کنید.

  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 خاص محدود شود. به عنوان مثال، می‌توانید یک geofence راه‌اندازی کنید که امکان کشف دستگاهی را فراهم می‌کند که سرویس "Aware_File_Share_Service_Name" را منتشر می‌کند که نزدیک‌تر از 3 متر (مشخص شده به عنوان 3000 میلی‌متر) و بیش از 10 متر (مشخص به عنوان 10000 میلی‌متر) نباشد.

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

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

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

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

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

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

،

قابلیت‌های 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 مکانیزمی برای یافتن سایر دستگاه‌های مجاور دارد. این فرآیند زمانی شروع می شود که یک دستگاه یک یا چند سرویس قابل کشف را منتشر کند . سپس، هنگامی که دستگاهی مشترک یک یا چند سرویس می شود و وارد محدوده 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 از مبتدی است. این کار DataPath را سرعت می بخشد و چندین پیوند نقطه به نقطه را تنها با یک درخواست شبکه امکان پذیر می کند.
  • برنامه هایی که در Android 12 یا بالاتر اجرا می شوند می توانند از روش WifiAwareManager.getAvailableAwareResources() استفاده کنند تا تعداد مسیرهای داده موجود ، انتشار جلسات و جلسات مشترک را دریافت کنند. این می تواند به برنامه کمک کند تا تعیین کند که آیا منابع کافی برای اجرای عملکرد مورد نظر خود وجود دارد یا خیر.

راه اندازی اولیه

برای تنظیم برنامه خود برای استفاده از Wi-Fi And Discovery و Networking ، مراحل زیر را انجام دهید:

  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 از API PackageManager پشتیبانی می کند ، همانطور که در زیر آمده است:

    کاتلین

    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)

    جاوا

    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. بررسی کنید که آیا Wi-Fi And در حال حاضر در دسترس است یا خیر. Wi-Fi And ممکن است در دستگاه وجود داشته باشد ، اما ممکن است در حال حاضر در دسترس نباشد زیرا کاربر Wi-Fi یا موقعیت مکانی را غیرفعال کرده است. بسته به قابلیت سخت افزاری و سیستم عامل آنها ، برخی از دستگاه ها ممکن است از Wi-Fi آگاه نباشند اگر Wi-Fi Direct ، SoftAP یا اتصال دهنده استفاده شود. برای بررسی اینکه آیا Wi-Fi And در حال حاضر در دسترس است ، با isAvailable() تماس بگیرید.

    در دسترس بودن Wi-Fi آگاه می تواند در هر زمان تغییر کند. برنامه شما باید برای دریافت ACTION_WIFI_AWARE_STATE_CHANGED ، یک BroadcastReceiver را ثبت کند ، که هر زمان که در دسترس باشد ارسال می شود. هنگامی که برنامه شما قصد پخش را دریافت می کند ، باید تمام جلسات موجود را دور بیندازد (فرض کنید که سرویس آگاهی از Wi-Fi مختل شده است) ، سپس وضعیت فعلی در دسترس بودن را بررسی کرده و رفتار آن را مطابق با آن تنظیم کنید. به عنوان مثال:

    کاتلین

    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 ، برنامه شما باید با فراخوانی attach() WifiAwareSession را بدست آورید. این روش موارد زیر را انجام می دهد:

  • سخت افزار آگاه Wi-Fi را روشن می کند.
  • به یک خوشه آگاه Wi-Fi می پیوندد یا تشکیل می دهد.
  • یک جلسه آگاهانه Wi-Fi با یک فضای نام منحصر به فرد ایجاد می کند که به عنوان یک ظرف برای تمام جلسات کشف ایجاد شده در آن عمل می کند.

اگر برنامه با موفقیت وصل شود ، سیستم پاسخ onAttached() انجام می دهد. این پاسخ به یک شیء WifiAwareSession است که برنامه شما باید برای کلیه عملیات جلسه بیشتر استفاده کند. یک برنامه می تواند از جلسه برای انتشار یک سرویس یا مشترک شدن در یک سرویس استفاده کند.

برنامه شما فقط یک بار باید attach() تماس بگیرد. اگر برنامه شما چندین بار attach() ، برنامه برای هر تماس جلسه متفاوتی دریافت می کند که هر کدام دارای فضای نام خود هستند. این می تواند در سناریوهای پیچیده مفید باشد ، اما به طور کلی باید از آن جلوگیری کرد.

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

برای کشف یک سرویس ، با روش publish() تماس بگیرید ، که پارامترهای زیر را می گیرد:

  • PublishConfig نام سرویس و سایر خصوصیات پیکربندی مانند فیلتر Match را مشخص می کند.
  • 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);

اگر انتشار موفق شود ، روش برگشت به تماس onPublishStarted() نامیده می شود.

پس از انتشار ، هنگامی که دستگاه هایی که برنامه های مشترک را اجرا می کنند به محدوده Wi-Fi دستگاه انتشارات منتقل می شوند ، مشترکان این سرویس را کشف می کنند. هنگامی که یک مشترک ناشر را کشف می کند ، ناشر اطلاعیه ای دریافت نمی کند. اگر مشترک پیامی را به ناشر ارسال کند ، با این حال ناشر اعلان دریافت می کند. وقتی این اتفاق بیفتد ، روش پاسخ به تماس با onMessageReceived() نامیده می شود. برای ارسال پیام به مشترک یا ایجاد اتصال به آن می توانید از این PeerHandle از این روش استفاده کنید.

برای متوقف کردن انتشار سرویس ، با DiscoverySession.close() تماس بگیرید. جلسات Discovery با WifiAwareSession والدین همراه است. اگر جلسه والدین بسته شود ، جلسات کشف مرتبط با آن نیز بسته می شود. در حالی که اشیاء دور ریخته شده نیز بسته هستند ، سیستم هنگام بسته شدن جلسات خارج از مقیاس تضمین نمی شود ، بنابراین توصیه می کنیم به طور صریح روش های close() فراخوانی کنید.

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

برای عضویت در یک سرویس ، با روش subscribe() تماس بگیرید ، که پارامترهای زیر را می گیرد:

  • SubscribeConfig نام سرویس را برای عضویت در و سایر خصوصیات پیکربندی مانند فیلتر Match مشخص می کند.
  • 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() در جلسه Discovery به روز کنید.

در این مرحله ، اشتراک شما منتظر است تا ناشران را به دامنه Wi-Fi برسانند. وقتی این اتفاق بیفتد ، سیستم روش onServiceDiscovered() انجام می دهد. برای ارسال پیام یا ایجاد ارتباطی به آن ناشر می توانید از آرگومان PeerHandle استفاده کنید.

برای متوقف کردن مشترک شدن در یک سرویس ، با DiscoverySession.close() تماس بگیرید. جلسات Discovery با WifiAwareSession والدین همراه است. اگر جلسه والدین بسته شود ، جلسات کشف مرتبط با آن نیز بسته می شود. در حالی که اشیاء دور ریخته شده نیز بسته هستند ، سیستم هنگام بسته شدن جلسات خارج از مقیاس تضمین نمی شود ، بنابراین توصیه می کنیم به طور صریح روش های close() فراخوانی کنید.

ارسال پیام

برای ارسال پیام به دستگاه دیگر ، به اشیاء زیر نیاز دارید:

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

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

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

تعبیه یک شناسه در یک پیام حاکی از اصلاح آرایه بایت پیام است که شامل یک شناسه (به عنوان مثال ، به عنوان اولین زن و شوهر بایت) است.

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

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

برای تنظیم اتصال مشتری-سرور:

  1. از Wi-Fi Discovery برای انتشار یک سرویس (در سرور) استفاده کنید و در یک سرویس (در مشتری) مشترک شوید .

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

  3. یک ServerSocket در دستگاه ناشر شروع کنید و درگاه خود را تنظیم یا بدست آورید:

    کاتلین

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

    جاوا

    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. از ConnectivityManager برای درخواست یک شبکه آگاهی Wi-Fi در ناشر با استفاده از یک WifiAwareNetworkSpecifier استفاده کنید ، جلسه Discovery و 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 را در مشترکین درخواست کنید. هنگام ایجاد 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 استفاده کند.

Wi-Fi RTT API اجازه می دهد تا مستقیماً به یک همسالان Wi-Fi با استفاده از آدرس MAC یا Peerhandle آن بپردازد.

کشف آگاهانه Wi-Fi می تواند فقط به کشف خدمات در یک Geofence خاص محدود شود. به عنوان مثال ، شما می توانید یک Geofence را تنظیم کنید که امکان کشف دستگاهی را که در حال انتشار یک سرویس "Aware_File_Share_Service_Name" است که نزدیکتر از 3 متر (مشخص شده 3000 میلی متر) و بیشتر از 10 متر (مشخص شده به عنوان 10،000 میلی متر) است.

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

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

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

  • مشترک باید با استفاده از ترکیبی از SetMindistanceMM و SetMaxDistanceMM ، یک Geofence را مشخص کند.

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

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

،

قابلیت های آگاهی Wi-Fi دستگاه هایی را که Android 8.0 (API سطح 26) دارند و بالاتر می توانند بدون هیچ نوع اتصال دیگری بین آنها ، به طور مستقیم به یکدیگر کشف و متصل شوند. Wi-Fi And همچنین به عنوان شبکه آگاهی همسایه (NAN) شناخته می شود.

شبکه آگاهی Wi-Fi با تشکیل خوشه هایی با دستگاه های همسایه یا ایجاد یک خوشه جدید در صورتی که دستگاه اولین دستگاه در یک منطقه باشد ، کار می کند. این رفتار خوشه بندی برای کل دستگاه اعمال می شود و توسط سرویس سیستم آگاهی Wi-Fi اداره می شود. برنامه ها هیچ کنترلی بر رفتار خوشه بندی ندارند. برنامه ها از API های آگاه Wi-Fi برای گفتگو با سرویس سیستم آگاهی Wi-Fi استفاده می کنند ، که سخت افزار آگاهی Wi-Fi را در دستگاه مدیریت می کند.

API های آگاه Wi-Fi به برنامه ها اجازه می دهد تا عملیات زیر را انجام دهند:

  • کشف دستگاه های دیگر: API مکانیسم برای یافتن سایر دستگاه های اطراف دارد. این فرایند هنگامی شروع می شود که یک دستگاه یک یا چند سرویس کشف شده را منتشر کند . سپس ، هنگامی که یک دستگاه در یک یا چند سرویس مشترک می شود و وارد محدوده Wi-Fi ناشر می شود ، مشترکین اعلان هایی را دریافت می کند که یک ناشر تطبیق یافته کشف شده است. پس از اینکه مشترک یک ناشر را کشف کرد ، مشترک می تواند پیام کوتاه ارسال کند یا با دستگاه کشف شده ارتباط شبکه برقرار کند. دستگاه ها همزمان می توانند ناشر و مشترک باشند.

  • ایجاد یک اتصال شبکه: بعد از اینکه دو دستگاه یکدیگر را کشف کردند ، می توانند بدون یک نقطه دسترسی ، یک اتصال شبکه آگاهی دو جهته Wi-Fi ایجاد کنند.

اتصالات شبکه آگاه Wi-Fi از نرخ توان بالاتر در مسافت های طولانی تر از اتصالات بلوتوث پشتیبانی می کند. این نوع اتصالات برای برنامه هایی که مقادیر زیادی از داده ها بین کاربران دارند ، مانند برنامه های اشتراک عکس مفید هستند.

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 Away اضافه می کند:

  • در دستگاه هایی که Android 12 (API سطح 31) یا بالاتر را اجرا می کنند ، می توانید از پاسخ به تماس onServiceLost() استفاده کنید تا وقتی برنامه شما به دلیل متوقف کردن سرویس یا خارج شدن از محدوده ، سرویس کشف شده را از دست داده است ، هشدار داده شود.
  • راه اندازی مسیرهای داده آگاه Wi-Fi Simiplified شده است. نسخه های اولیه از پیام رسانی L2 برای تهیه آدرس MAC از آغازگر استفاده می کردند ، که تأخیر را معرفی می کرد. در دستگاه هایی که Android 12 و بالاتر را اجرا می کنند ، پاسخ دهنده (سرور) می تواند پیکربندی شود تا هر یک از همسالان را بپذیرد - یعنی نیازی به دانستن آدرس MAC از مبتدی است. این کار DataPath را سرعت می بخشد و چندین پیوند نقطه به نقطه را تنها با یک درخواست شبکه امکان پذیر می کند.
  • برنامه هایی که در Android 12 یا بالاتر اجرا می شوند می توانند از روش WifiAwareManager.getAvailableAwareResources() استفاده کنند تا تعداد مسیرهای داده موجود ، انتشار جلسات و جلسات مشترک را دریافت کنند. این می تواند به برنامه کمک کند تا تعیین کند که آیا منابع کافی برای اجرای عملکرد مورد نظر خود وجود دارد یا خیر.

راه اندازی اولیه

برای تنظیم برنامه خود برای استفاده از Wi-Fi And Discovery و Networking ، مراحل زیر را انجام دهید:

  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 از API PackageManager پشتیبانی می کند ، همانطور که در زیر آمده است:

    کاتلین

    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)

    جاوا

    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. بررسی کنید که آیا Wi-Fi And در حال حاضر در دسترس است یا خیر. Wi-Fi And ممکن است در دستگاه وجود داشته باشد ، اما ممکن است در حال حاضر در دسترس نباشد زیرا کاربر Wi-Fi یا موقعیت مکانی را غیرفعال کرده است. بسته به قابلیت سخت افزاری و سیستم عامل آنها ، برخی از دستگاه ها ممکن است از Wi-Fi آگاه نباشند اگر Wi-Fi Direct ، SoftAP یا اتصال دهنده استفاده شود. برای بررسی اینکه آیا Wi-Fi And در حال حاضر در دسترس است ، با isAvailable() تماس بگیرید.

    در دسترس بودن Wi-Fi آگاه می تواند در هر زمان تغییر کند. برنامه شما باید برای دریافت ACTION_WIFI_AWARE_STATE_CHANGED ، یک BroadcastReceiver را ثبت کند ، که هر زمان که در دسترس باشد ارسال می شود. هنگامی که برنامه شما قصد پخش را دریافت می کند ، باید تمام جلسات موجود را دور بیندازد (فرض کنید که سرویس آگاهی از Wi-Fi مختل شده است) ، سپس وضعیت فعلی در دسترس بودن را بررسی کرده و رفتار آن را مطابق با آن تنظیم کنید. به عنوان مثال:

    کاتلین

    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 ، برنامه شما باید با فراخوانی attach() WifiAwareSession را بدست آورید. این روش موارد زیر را انجام می دهد:

  • سخت افزار آگاه Wi-Fi را روشن می کند.
  • به یک خوشه آگاه Wi-Fi می پیوندد یا تشکیل می دهد.
  • یک جلسه آگاهانه Wi-Fi با یک فضای نام منحصر به فرد ایجاد می کند که به عنوان یک ظرف برای تمام جلسات کشف ایجاد شده در آن عمل می کند.

اگر برنامه با موفقیت وصل شود ، سیستم پاسخ onAttached() انجام می دهد. این پاسخ به یک شیء WifiAwareSession است که برنامه شما باید برای کلیه عملیات جلسه بیشتر استفاده کند. یک برنامه می تواند از جلسه برای انتشار یک سرویس یا مشترک شدن در یک سرویس استفاده کند.

برنامه شما فقط یک بار باید attach() تماس بگیرد. اگر برنامه شما چندین بار attach() ، برنامه برای هر تماس جلسه متفاوتی دریافت می کند که هر کدام دارای فضای نام خود هستند. این می تواند در سناریوهای پیچیده مفید باشد ، اما به طور کلی باید از آن جلوگیری کرد.

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

برای کشف یک سرویس ، با روش publish() تماس بگیرید ، که پارامترهای زیر را می گیرد:

  • PublishConfig نام سرویس و سایر خصوصیات پیکربندی مانند فیلتر Match را مشخص می کند.
  • 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);

اگر انتشار موفق شود ، روش برگشت به تماس onPublishStarted() نامیده می شود.

پس از انتشار ، هنگامی که دستگاه هایی که برنامه های مشترک را اجرا می کنند به محدوده Wi-Fi دستگاه انتشارات منتقل می شوند ، مشترکان این سرویس را کشف می کنند. هنگامی که یک مشترک ناشر را کشف می کند ، ناشر اطلاعیه ای دریافت نمی کند. اگر مشترک پیامی را به ناشر ارسال کند ، با این حال ناشر اعلان دریافت می کند. وقتی این اتفاق بیفتد ، روش پاسخ به تماس با onMessageReceived() نامیده می شود. برای ارسال پیام به مشترک یا ایجاد اتصال به آن می توانید از این PeerHandle از این روش استفاده کنید.

برای متوقف کردن انتشار سرویس ، با DiscoverySession.close() تماس بگیرید. جلسات Discovery با WifiAwareSession والدین همراه است. اگر جلسه والدین بسته شود ، جلسات کشف مرتبط با آن نیز بسته می شود. در حالی که اشیاء دور ریخته شده نیز بسته هستند ، سیستم هنگام بسته شدن جلسات خارج از مقیاس تضمین نمی شود ، بنابراین توصیه می کنیم به طور صریح روش های close() فراخوانی کنید.

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

برای عضویت در یک سرویس ، با روش subscribe() تماس بگیرید ، که پارامترهای زیر را می گیرد:

  • SubscribeConfig نام سرویس را برای عضویت در و سایر خصوصیات پیکربندی مانند فیلتر Match مشخص می کند.
  • 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() در جلسه Discovery به روز کنید.

در این مرحله ، اشتراک شما منتظر است تا ناشران را به دامنه Wi-Fi برسانند. وقتی این اتفاق بیفتد ، سیستم روش onServiceDiscovered() انجام می دهد. برای ارسال پیام یا ایجاد ارتباطی به آن ناشر می توانید از آرگومان PeerHandle استفاده کنید.

برای متوقف کردن مشترک شدن در یک سرویس ، با DiscoverySession.close() تماس بگیرید. جلسات Discovery با WifiAwareSession والدین همراه است. اگر جلسه والدین بسته شود ، جلسات کشف مرتبط با آن نیز بسته می شود. در حالی که اشیاء دور ریخته شده نیز بسته هستند ، سیستم هنگام بسته شدن جلسات خارج از مقیاس تضمین نمی شود ، بنابراین توصیه می کنیم به طور صریح روش های close() فراخوانی کنید.

ارسال پیام

برای ارسال پیام به دستگاه دیگر ، به اشیاء زیر نیاز دارید:

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

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

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

تعبیه یک شناسه در یک پیام حاکی از اصلاح آرایه بایت پیام است که شامل یک شناسه (به عنوان مثال ، به عنوان اولین زن و شوهر بایت) است.

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

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

برای تنظیم اتصال مشتری-سرور:

  1. از Wi-Fi Discovery برای انتشار یک سرویس (در سرور) استفاده کنید و در یک سرویس (در مشتری) مشترک شوید .

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

  3. یک ServerSocket در دستگاه ناشر شروع کنید و درگاه خود را تنظیم یا بدست آورید:

    کاتلین

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

    جاوا

    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. از ConnectivityManager برای درخواست یک شبکه آگاهی Wi-Fi در ناشر با استفاده از یک WifiAwareNetworkSpecifier استفاده کنید ، جلسه Discovery و 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 را در مشترکین درخواست کنید. هنگام ایجاد 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 استفاده کند.

Wi-Fi RTT API اجازه می دهد تا مستقیماً به یک همسالان Wi-Fi با استفاده از آدرس MAC یا Peerhandle آن بپردازد.

کشف آگاهانه Wi-Fi می تواند فقط به کشف خدمات در یک Geofence خاص محدود شود. به عنوان مثال ، شما می توانید یک Geofence را تنظیم کنید که امکان کشف دستگاهی را که در حال انتشار یک سرویس "Aware_File_Share_Service_Name" است که نزدیکتر از 3 متر (مشخص شده 3000 میلی متر) و بیشتر از 10 متر (مشخص شده به عنوان 10،000 میلی متر) است.

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

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

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

  • مشترک باید با استفاده از ترکیبی از SetMindistanceMM و SetMaxDistanceMM ، یک Geofence را مشخص کند.

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

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