Tổng quan về nhận biết Wi-Fi

Các tính năng Nhận biết Wi-Fi cho phép các thiết bị chạy Android 8.0 (API cấp độ 26) trở lên tìm và kết nối trực tiếp với nhau mà không cần bất kỳ loại kết nối nào khác giữa chúng. Tính năng Nhận biết Wi-Fi còn được gọi là Mạng nhận biết thiết bị lân cận (NAN).

Mạng Nhận biết Wi-Fi hoạt động bằng cách tạo các cụm với các thiết bị lân cận hoặc bằng cách tạo một cụm mới nếu thiết bị là thiết bị đầu tiên trong một khu vực. Hành vi tạo cụm này áp dụng cho toàn bộ thiết bị và do dịch vụ hệ thống Nhận biết Wi-Fi quản lý; các ứng dụng không có quyền kiểm soát hành vi tạo cụm. Các ứng dụng sử dụng API Nhận biết Wi-Fi để giao tiếp với dịch vụ hệ thống Nhận biết Wi-Fi. Dịch vụ này quản lý phần cứng Nhận biết Wi-Fi trên thiết bị.

API Nhận biết Wi-Fi cho phép các ứng dụng thực hiện các thao tác sau:

  • Khám phá các thiết bị khác: API này có một cơ chế để tìm các thiết bị ở gần đó. Quá trình này bắt đầu khi một thiết bị xuất bản một hoặc nhiều dịch vụ có thể tìm thấy. Sau đó, khi một thiết bị đăng ký một hoặc nhiều dịch vụ và đi vào phạm vi Wi-Fi của nhà xuất bản, thiết bị đăng ký sẽ nhận được thông báo rằng một nhà xuất bản phù hợp đã được tìm thấy. Sau khi tìm thấy một nhà xuất bản, thiết bị đăng ký có thể gửi một tin nhắn ngắn hoặc thiết lập kết nối mạng với thiết bị đã tìm thấy. Các thiết bị có thể đồng thời vừa là nhà xuất bản vừa là thiết bị đăng ký.

  • Tạo kết nối mạng: Sau khi tìm thấy nhau, hai thiết bị có thể tạo kết nối mạng Nhận biết Wi-Fi hai chiều mà không cần điểm truy cập.

Kết nối mạng Nhận biết Wi-Fi hỗ trợ tốc độ truyền dữ liệu cao hơn trên khoảng cách xa hơn so với Bluetooth kết nối. Các loại kết nối này hữu ích cho các ứng dụng chia sẻ lượng lớn dữ liệu giữa người dùng, chẳng hạn như ứng dụng chia sẻ ảnh.

Các điểm cải tiến của Android 13 (cấp độ API 33)

Trên các thiết bị chạy Android 13 (cấp độ API 33) trở lên hỗ trợ chế độ giao tiếp tức thì, các ứng dụng có thể sử dụng phương thức PublishConfig.Builder.setInstantCommunicationModeEnabled()SubscribeConfig.Builder.setInstantCommunicationModeEnabled() để bật hoặc tắt chế độ giao tiếp tức thì cho phiên tìm kiếm nhà xuất bản hoặc người đăng ký. Chế độ giao tiếp tức thì giúp tăng tốc quá trình trao đổi tin nhắn, tìm dịch vụ và thiết lập mọi đường dẫn dữ liệu trong phiên tìm kiếm nhà xuất bản hoặc thiết bị đăng ký. Để xác định xem một thiết bị có hỗ trợ chế độ giao tiếp tức thì hay không, hãy sử dụng phương thức isInstantCommunicationModeSupported().

Các điểm cải tiến của Android 12 (cấp độ API 31)

Android 12 (cấp độ API 31) bổ sung một số điểm cải tiến cho tính năng Nhận biết Wi-Fi:

  • Trên các thiết bị chạy Android 12 (cấp độ API 31) trở lên, bạn có thể sử dụng onServiceLost() để nhận thông báo khi ứng dụng của bạn bị mất một dịch vụ đã tìm thấy do dừng hoặc di chuyển ra khỏi phạm vi.
  • Quy trình thiết lập đường dẫn dữ liệu Nhận biết Wi-Fi đã được đơn giản hoá. Các phiên bản trước sử dụng tính năng nhắn tin L2 để cung cấp địa chỉ MAC của trình khởi tạo, gây ra độ trễ. Trên các thiết bị chạy Android 12 trở lên, bạn có thể định cấu hình trình phản hồi (máy chủ) để chấp nhận mọi thiết bị ngang hàng, tức là không cần biết trước địa chỉ MAC của trình khởi tạo. Điều này giúp tăng tốc quá trình khởi động đường dẫn dữ liệu và cho phép nhiều đường liên kết điểm-điểm chỉ với một yêu cầu mạng.
  • Các ứng dụng chạy trên Android 12 trở lên có thể sử dụng WifiAwareManager.getAvailableAwareResources() để lấy số lượng đường dẫn dữ liệu, phiên xuất bản, và phiên đăng ký hiện có. Điều này có thể giúp ứng dụng xác định xem có đủ tài nguyên để thực thi chức năng mong muốn hay không.

Thiết lập ban đầu

Để thiết lập ứng dụng sử dụng tính năng tìm kiếm và kết nối mạng Nhận biết Wi-Fi, hãy thực hiện các bước sau:

  1. Yêu cầu các quyền sau trong tệp kê khai của ứng dụng:

    <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. Kiểm tra xem thiết bị có hỗ trợ tính năng Nhận biết Wi-Fi bằng API PackageManager hay không, như minh hoạ bên dưới:

    Kotlin

    context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)

    Java

    context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
  3. Kiểm tra xem tính năng Nhận biết Wi-Fi hiện có dùng được hay không. Tính năng Nhận biết Wi-Fi có thể tồn tại trên thiết bị nhưng có thể hiện không dùng được vì người dùng đã tắt Wi-Fi hoặc Dịch vụ vị trí. Tuỳ thuộc vào khả năng của phần cứng và phần sụn, một số thiết bị có thể không hỗ trợ tính năng Nhận biết Wi-Fi nếu đang sử dụng Wi-Fi Direct, SoftAP hoặc chia sẻ Internet. Để kiểm tra xem tính năng Nhận biết Wi-Fi hiện có dùng được hay không, hãy gọi isAvailable().

    Tính năng Nhận biết Wi-Fi có thể thay đổi bất cứ lúc nào. Ứng dụng của bạn phải đăng ký BroadcastReceiver để nhận ACTION_WIFI_AWARE_STATE_CHANGED. Thông báo này được gửi bất cứ khi nào tính năng này thay đổi. Khi ứng dụng của bạn nhận được ý định truyền tin, ứng dụng sẽ loại bỏ tất cả các phiên hiện có (giả sử dịch vụ Nhận biết Wi-Fi bị gián đoạn), sau đó kiểm tra trạng thái hiện tại của tính năng này và điều chỉnh hành vi của ứng dụng cho phù hợp. Ví dụ:

    Kotlin

    val wifiAwareManager = context.getSystemService(Context.WIFI_AWARE_SERVICE) as WifiAwareManager?
    val filter = IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED)
    val myReceiver = object : BroadcastReceiver() {
    
        override fun onReceive(context: Context, intent: Intent) {
            // discard current sessions
            if (wifiAwareManager?.isAvailable) {
                ...
            } else {
                ...
            }
        }
    }
    context.registerReceiver(myReceiver, filter)

    Java

    WifiAwareManager wifiAwareManager = 
            (WifiAwareManager)context.getSystemService(Context.WIFI_AWARE_SERVICE)
    IntentFilter filter =
            new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
    BroadcastReceiver myReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // discard current sessions
            if (wifiAwareManager.isAvailable()) {
                ...
            } else {
                ...
            }
        }
    };
    context.registerReceiver(myReceiver, filter);

Để biết thêm thông tin, hãy xem phần Truyền tin.

Lấy một phiên

Để bắt đầu sử dụng tính năng Nhận biết Wi-Fi, ứng dụng của bạn phải lấy WifiAwareSession bằng cách gọi attach(). Phương thức này thực hiện những việc sau:

  • Bật phần cứng Nhận biết Wi-Fi.
  • Tham gia hoặc tạo một cụm Nhận biết Wi-Fi.
  • Tạo một phiên Nhận biết Wi-Fi có không gian tên duy nhất đóng vai trò là vùng chứa cho tất cả các phiên tìm kiếm được tạo trong đó.

Nếu ứng dụng đính kèm thành công, hệ thống sẽ thực thi lệnh gọi lại onAttached(). Lệnh gọi lại này cung cấp một đối tượng WifiAwareSession mà ứng dụng của bạn nên sử dụng cho tất cả các thao tác phiên tiếp theo. Ứng dụng có thể sử dụng phiên này để xuất bản một dịch vụ hoặc đăng ký một dịch vụ.

Ứng dụng của bạn chỉ nên gọi attach() một lần. Nếu ứng dụng gọi attach() nhiều lần, ứng dụng sẽ nhận được một phiên khác cho mỗi lệnh gọi, mỗi phiên có không gian tên riêng. Điều này có thể hữu ích trong các tình huống phức tạp, nhưng thường nên tránh.

Xuất bản một dịch vụ

Để làm cho một dịch vụ có thể tìm thấy, hãy gọi phương thức publish(). Phương thức này nhận các tham số sau:

  • PublishConfig chỉ định tên của dịch vụ và các thuộc tính cấu hình khác, chẳng hạn như bộ lọc so khớp.
  • DiscoverySessionCallback chỉ định các hành động cần thực thi khi xảy ra sự kiện, chẳng hạn như khi thiết bị đăng ký nhận được một tin nhắn.

Ví dụ:

Kotlin

val config: PublishConfig = PublishConfig.Builder()
        .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME)
        .build()
awareSession.publish(config, object : DiscoverySessionCallback() {

    override fun onPublishStarted(session: PublishDiscoverySession) {
        ...
    }

    override fun onMessageReceived(peerHandle: PeerHandle, message: ByteArray) {
        ...
    }
})

Java

PublishConfig config = new PublishConfig.Builder()
    .setServiceName(Aware_File_Share_Service_Name)
    .build();

awareSession.publish(config, new DiscoverySessionCallback() {
    @Override
    public void onPublishStarted(PublishDiscoverySession session) {
        ...
    }
    @Override
    public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
        ...
    }
}, null);

Nếu quá trình xuất bản thành công, phương thức gọi lại onPublishStarted() sẽ được gọi.

Sau khi xuất bản, khi các thiết bị chạy ứng dụng đăng ký phù hợp di chuyển vào phạm vi Wi-Fi của thiết bị xuất bản, thiết bị đăng ký sẽ tìm thấy dịch vụ. Khi một thiết bị đăng ký tìm thấy một nhà xuất bản, nhà xuất bản sẽ không nhận được thông báo; tuy nhiên, nếu thiết bị đăng ký gửi một tin nhắn đến nhà xuất bản, thì nhà xuất bản sẽ nhận được thông báo. Khi điều đó xảy ra, phương thức gọi lại onMessageReceived() sẽ được gọi. Bạn có thể sử dụng đối số PeerHandle từ phương thức này để gửi tin nhắn trở lại thiết bị đăng ký hoặc tạo kết nối với thiết bị đó.

Để ngừng xuất bản dịch vụ, hãy gọi DiscoverySession.close(). Các phiên tìm kiếm được liên kết với WifiAwareSession mẹ. Nếu phiên mẹ bị đóng, thì các phiên tìm kiếm được liên kết cũng sẽ bị đóng. Mặc dù các đối tượng bị loại bỏ cũng bị đóng, nhưng hệ thống không đảm bảo thời điểm đóng các phiên ngoài phạm vi, vì vậy, bạn nên gọi rõ ràng các phương thức close().

Đăng ký một dịch vụ

Để đăng ký một dịch vụ, hãy gọi phương thức subscribe(). Phương thức này nhận các tham số sau:

  • SubscribeConfig chỉ định tên của dịch vụ cần đăng ký và các thuộc tính cấu hình khác, chẳng hạn như bộ lọc so khớp.
  • DiscoverySessionCallback chỉ định các hành động cần thực thi khi xảy ra sự kiện, chẳng hạn như khi tìm thấy một nhà xuất bản.

Ví dụ:

Kotlin

val config: SubscribeConfig = SubscribeConfig.Builder()
        .setServiceName(AWARE_FILE_SHARE_SERVICE_NAME)
        .build()
awareSession.subscribe(config, object : DiscoverySessionCallback() {

    override fun onSubscribeStarted(session: SubscribeDiscoverySession) {
        ...
    }

    override fun onServiceDiscovered(
            peerHandle: PeerHandle,
            serviceSpecificInfo: ByteArray,
            matchFilter: List<ByteArray>
    ) {
        ...
    }
}, null)

Java

SubscribeConfig config = new SubscribeConfig.Builder()
    .setServiceName("Aware_File_Share_Service_Name")
    .build();

awareSession.subscribe(config, new DiscoverySessionCallback() {
    @Override
    public void onSubscribeStarted(SubscribeDiscoverySession session) {
        ...
    }

    @Override
    public void onServiceDiscovered(PeerHandle peerHandle,
            byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
        ...
    }
}, null);

Nếu thao tác đăng ký thành công, hệ thống sẽ gọi lệnh gọi lại onSubscribeStarted() trong ứng dụng của bạn. Vì bạn có thể sử dụng đối số SubscribeDiscoverySession trong lệnh gọi lại để giao tiếp với một nhà xuất bản sau khi ứng dụng của bạn tìm thấy một nhà xuất bản, nên bạn cần lưu tham chiếu này. Bạn có thể cập nhật phiên đăng ký bất cứ lúc nào bằng cách gọi updateSubscribe() trên phiên tìm kiếm.

Tại thời điểm này, gói thuê bao của bạn sẽ chờ các nhà xuất bản phù hợp đi vào phạm vi Wi-Fi. Khi điều này xảy ra, hệ thống sẽ thực thi phương thức gọi lại onServiceDiscovered(). Bạn có thể sử dụng đối số PeerHandle từ lệnh gọi lại này để gửi tin nhắn hoặc tạo kết nối đến nhà xuất bản đó.

Để ngừng đăng ký một dịch vụ, hãy gọi DiscoverySession.close(). Các phiên tìm kiếm được liên kết với WifiAwareSession mẹ. Nếu phiên mẹ bị đóng, thì các phiên tìm kiếm được liên kết cũng sẽ bị đóng. Mặc dù các đối tượng bị loại bỏ cũng bị đóng, nhưng hệ thống không đảm bảo thời điểm đóng các phiên ngoài phạm vi, vì vậy, bạn nên gọi rõ ràng các phương thức close().

Gửi tin nhắn

Để gửi tin nhắn đến một thiết bị khác, bạn cần có các đối tượng sau:

Để gửi tin nhắn, hãy gọi sendMessage(). Sau đó, các lệnh gọi lại sau có thể xảy ra:

  • Khi tin nhắn được thiết bị ngang hàng nhận thành công, hệ thống sẽ gọi lệnh gọi lại onMessageSendSucceeded() trong ứng dụng gửi.
  • Khi thiết bị ngang hàng nhận được một tin nhắn, hệ thống sẽ gọi lệnh gọi lại onMessageReceived() trong ứng dụng nhận.

Mặc dù bạn phải có PeerHandle để giao tiếp với các thiết bị ngang hàng, nhưng bạn không nên dựa vào đó làm mã nhận dạng vĩnh viễn của các thiết bị ngang hàng. Ứng dụng có thể sử dụng các mã nhận dạng cấp cao hơn – được nhúng trong chính dịch vụ tìm kiếm hoặc trong các tin nhắn tiếp theo. Bạn có thể nhúng một mã nhận dạng vào dịch vụ tìm kiếm bằng phương thức setMatchFilter() hoặc setServiceSpecificInfo() của PublishConfig hoặc SubscribeConfig. Phương thức setMatchFilter() ảnh hưởng đến quá trình tìm kiếm, trong khi phương thức setServiceSpecificInfo() không ảnh hưởng đến quá trình tìm kiếm.

Việc nhúng một mã nhận dạng vào tin nhắn ngụ ý sửa đổi mảng byte tin nhắn để đưa vào một mã nhận dạng (ví dụ: dưới dạng vài byte đầu tiên).

Tạo kết nối

Tính năng Nhận biết Wi-Fi hỗ trợ kết nối mạng máy khách-máy chủ giữa hai thiết bị Nhận biết Wi-Fi.

Cách thiết lập kết nối máy khách-máy chủ:

  1. Sử dụng tính năng tìm kiếm Nhận biết Wi-Fi để xuất bản một dịch vụ (trên máy chủ) và đăng ký một dịch vụ (trên máy khách).

  2. Sau khi thiết bị đăng ký tìm thấy nhà xuất bản, hãy gửi một tin nhắn từ thiết bị đăng ký đến nhà xuất bản.

  3. Khởi động ServerSocket trên thiết bị của nhà xuất bản và đặt hoặc lấy cổng của thiết bị đó:

    Kotlin

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

    Java

    ServerSocket ss = new ServerSocket(0);
    int port = ss.getLocalPort();
  4. Sử dụng ConnectivityManager để yêu cầu một mạng Nhận biết Wi-Fi trên nhà xuất bản bằng cách sử dụng WifiAwareNetworkSpecifier, chỉ định phiên tìm kiếm và PeerHandle của thiết bị đăng ký, mà bạn đã lấy từ tin nhắn do thiết bị đăng ký truyền đi:

    Kotlin

    val networkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle)
        .setPskPassphrase("somePassword")
        .setPort(port)
        .build()
    val myNetworkRequest = NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
        .setNetworkSpecifier(networkSpecifier)
        .build()
    val callback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            ...
        }
    
        override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
            ...
        }
    
        override fun onLost(network: Network) {
            ...
        }
    }
    
    connMgr.requestNetwork(myNetworkRequest, callback);

    Java

    NetworkSpecifier networkSpecifier = new WifiAwareNetworkSpecifier.Builder(discoverySession, peerHandle)
        .setPskPassphrase("somePassword")
        .setPort(port)
        .build();
    NetworkRequest myNetworkRequest = new NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
        .setNetworkSpecifier(networkSpecifier)
        .build();
    ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            ...
        }
    
        @Override
        public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
            ...
        }
    
        @Override
        public void onLost(Network network) {
            ...
        }
    };
    
    ConnectivityManager connMgr.requestNetwork(myNetworkRequest, callback);
  5. Sau khi yêu cầu một mạng, nhà xuất bản sẽ gửi một tin nhắn đến thiết bị đăng ký.

  6. Sau khi nhận được tin nhắn từ nhà xuất bản, thiết bị đăng ký sẽ yêu cầu một mạng Nhận biết Wi-Fi trên thiết bị đăng ký bằng cùng một phương thức như trên nhà xuất bản. Không chỉ định cổng khi tạo NetworkSpecifier. Các phương thức gọi lại thích hợp sẽ được gọi khi kết nối mạng có sẵn, thay đổi hoặc bị mất.

  7. Sau khi phương thức onAvailable() được gọi trên thiết bị đăng ký, một Network đối tượng sẽ có sẵn để bạn có thể mở một Socket để giao tiếp với ServerSocket trên nhà xuất bản, nhưng bạn cần phải biết địa chỉ IPv6 và cổng của ServerSocket. Bạn nhận được các thông tin này từ đối tượng NetworkCapabilities được cung cấp trong lệnh gọi lại onCapabilitiesChanged():

    Kotlin

    val peerAwareInfo = networkCapabilities.transportInfo as WifiAwareNetworkInfo
    val peerIpv6 = peerAwareInfo.peerIpv6Addr
    val peerPort = peerAwareInfo.port
    ...
    val socket = network.getSocketFactory().createSocket(peerIpv6, peerPort)

    Java

    WifiAwareNetworkInfo peerAwareInfo = (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo();
    Inet6Address peerIpv6 = peerAwareInfo.getPeerIpv6Addr();
    int peerPort = peerAwareInfo.getPort();
    ...
    Socket socket = network.getSocketFactory().createSocket(peerIpv6, peerPort);
  8. Khi bạn hoàn tất kết nối mạng, hãy gọi unregisterNetworkCallback().

Phạm vi thiết bị ngang hàng và tìm kiếm dựa trên vị trí

Một thiết bị có khả năng xác định vị trí Wi-Fi RTT có thể trực tiếp đo khoảng cách đến các thiết bị ngang hàng và sử dụng thông tin này để giới hạn phạm vi tìm kiếm dịch vụ Nhận biết Wi-Fi.

API Wi-Fi RTT cho phép xác định phạm vi trực tiếp đến một thiết bị ngang hàng Nhận biết Wi-Fi bằng cách sử dụng địa chỉ MAC hoặc PeerHandle của thiết bị đó.

Bạn có thể giới hạn phạm vi tìm kiếm Nhận biết Wi-Fi để chỉ tìm thấy các dịch vụ trong một hàng rào địa lý cụ thể. Ví dụ: bạn có thể thiết lập một khoanh vùng địa lý cho phép tìm thấy một thiết bị xuất bản dịch vụ "Aware_File_Share_Service_Name" không gần hơn 3 mét (được chỉ định là 3.000 mm) và không xa hơn 10 mét (được chỉ định là 10.000 mm).

Để bật tính năng hàng rào địa lý, cả nhà xuất bản và thiết bị đăng ký đều phải thực hiện hành động:

  • Nhà xuất bản phải bật tính năng xác định phạm vi trên dịch vụ đã xuất bản bằng cách sử dụng setRangingEnabled(true).

    Nếu nhà xuất bản không bật tính năng xác định phạm vi, thì mọi ràng buộc về khoanh vùng địa lý do thiết bị đăng ký chỉ định sẽ bị bỏ qua và quá trình tìm kiếm thông thường sẽ được thực hiện, bỏ qua khoảng cách.

  • Thiết bị đăng ký phải chỉ định một hàng rào địa lý bằng cách sử dụng một số tổ hợp setMinDistanceMmsetMaxDistanceMm.

    Đối với giá trị bất kỳ, khoảng cách không xác định ngụ ý không có giới hạn. Chỉ chỉ định khoảng cách tối đa ngụ ý khoảng cách tối thiểu là 0. Chỉ chỉ định khoảng cách tối thiểu ngụ ý không có khoảng cách tối đa.

Khi một dịch vụ ngang hàng được tìm thấy trong một khoanh vùng địa lý, lệnh gọi lại onServiceDiscoveredWithinRange sẽ được kích hoạt, cung cấp khoảng cách đo được đến thiết bị ngang hàng. Sau đó, bạn có thể gọi API Wi-Fi RTT trực tiếp khi cần để đo khoảng cách vào những thời điểm sau.