Tính năng Wi-Fi Aware cho phép các thiết bị chạy Android 8.0 (API cấp 26) trở lên khám phá 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 các thiết bị. Wi-Fi Aware còn được gọi là Mạng nhận biết thiết bị lân cận (NAN).
Kết nối mạng Nhận biết Wi-Fi hoạt động bằng cách hình thành 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 phân cụm này áp dụng cho toàn bộ thiết bị và do dịch vụ hệ thống Wi-Fi Aware quản lý; các ứng dụng không có quyền kiểm soát hành vi phân cụm. Các ứng dụng sử dụng API Wi-Fi Aware để giao tiếp với dịch vụ hệ thống Wi-Fi Aware. Dịch vụ này quản lý phần cứng Wi-Fi Aware trên thiết bị.
API Wi-Fi Aware cho phép ứng dụng thực hiện các thao tác sau:
Khám phá các thiết bị khác: API có cơ chế tìm các thiết bị khác ở gần. Quá trình này bắt đầu khi một thiết bị phát hành một hoặc nhiều dịch vụ có thể phát hiện. 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, người đăng ký sẽ nhận được thông báo rằng đã phát hiện một nhà xuất bản phù hợp. Sau khi phát hiện một trình xuất bản, trình đăng ký có thể gửi một thông báo ngắn hoặc thiết lập kết nối mạng với thiết bị đã phát hiện. Thiết bị có thể đồng thời là nhà xuất bản và người đăng ký.
Tạo kết nối mạng: Sau khi hai thiết bị phát hiện lẫn nhau, chúng có thể tạo kết nối mạng Wi-Fi Aware hai chiều mà không cần điểm truy cập.
Kết nối mạng Wi-Fi Aware hỗ trợ tốc độ truyền tải cao hơn ở khoảng cách xa hơn so với kết nối Bluetooth. Những 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 tính năng nâng cao của Android 13 (API cấp 33)
Trên các thiết bị chạy Android 13 (API cấp 33) trở lên hỗ trợ chế độ giao tiếp tức thì, ứng dụng có thể sử dụng các phương thức PublishConfig.Builder.setInstantCommunicationModeEnabled()
và SubscribeConfig.Builder.setInstantCommunicationModeEnabled()
để bật hoặc tắt chế độ giao tiếp tức thì cho phiên khám phá của nhà xuất bản hoặc người đăng ký. Chế độ giao tiếp tức thì giúp tăng tốc việc trao đổi thông báo, khám phá dịch vụ và mọi đường dẫn dữ liệu được thiết lập trong phiên khám phá của nhà xuất bản hoặc người đăng ký. Để xác định xem 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 tính năng nâng cao của Android 12 (API cấp 31)
Android 12 (API cấp 31) bổ sung một số tính năng nâng cao cho tính năng Nhận biết Wi-Fi:
- Trên các thiết bị chạy Android 12 (API cấp 31) trở lên, bạn có thể sử dụng lệnh gọi lại
onServiceLost()
để nhận thông báo khi ứng dụng của bạn mất một dịch vụ đã phát hiện do dịch vụ đó 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ị đồng cấp, tức là không cần biết trước địa chỉ MAC của thiết bị khởi tạo. Điều này giúp tăng tốc độ 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ỉ bằng 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 phương thức
WifiAwareManager.getAvailableAwareResources()
để nhận số lượng đường dẫn dữ liệu hiện có, phiên phát hành và đăng ký phiên. Đ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 để dùng tính năng kết nối và khám phá Wi-Fi, hãy thực hiện các bước sau:
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" />
Kiểm tra xem thiết bị có hỗ trợ Wi-Fi Aware 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);
Kiểm tra xem tính năng Wi-Fi Aware có đang hoạt động 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 hiện có thể không hoạt động vì người dùng đã tắt Wi-Fi hoặc Vị trí. Tuỳ thuộc vào chức năng phần cứng và chương trình cơ sở, một số thiết bị có thể không hỗ trợ tính năng Nhận biết Wi-Fi nếu Wi-Fi Direct, SoftAP hoặc tính năng chia sẻ Internet đang được sử dụng. Để kiểm tra xem Wi-Fi Aware có đang hoạt động hay không, hãy gọi
isAvailable()
.Tình trạng cung cấp tính năng Wi-Fi Aware có thể thay đổi bất cứ lúc nào. Ứng dụng của bạn nên đăng ký
BroadcastReceiver
để nhậnACTION_WIFI_AWARE_STATE_CHANGED
. Lệnh này được gửi bất cứ khi nào tình trạng còn hàng thay đổi. Khi nhận được ý định truyền tin, ứng dụng của bạn sẽ loại bỏ tất cả các phiên hiện có (giả sử dịch vụ Wi-Fi Aware bị gián đoạn), sau đó kiểm tra trạng thái hiện tại của dịch vụ 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 Thông báo 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 có 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 Wi-Fi Aware.
- Tham gia hoặc tạo một cụm Wi-Fi Aware.
- Tạo một phiên Wi-Fi Aware có không gian tên riêng biệt đóng vai trò là một vùng chứa cho tất cả các phiên khám phá đượ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 sẽ sử dụng cho tất cả các thao tác tiếp theo trong phiên. Ứng dụng có thể sử dụng phiên để phát hành dịch vụ hoặc đăng ký 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, thì ứng dụng sẽ nhận được một phiên khác nhau 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 trường hợp phức tạp, nhưng nói chung bạn nên tránh.
Phát hành dịch vụ
Để cho phép phát hiện một dịch vụ, hãy gọi phương thức publish()
. Phương thức này sẽ 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 sự kiện xảy ra, chẳng hạn như khi người đăng ký nhận được thông báo.
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 phát hành thành công, thì phương thức gọi lại onPublishStarted()
sẽ được gọi.
Sau khi phát hành, khi các thiết bị chạy ứng dụng của người đăng ký phù hợp di chuyển vào phạm vi Wi-Fi của thiết bị phát hành, người đăng ký sẽ khám phá dịch vụ. Khi người đăng ký khám phá 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 người đăng ký gửi 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 thông báo trở lại cho người đăng ký hoặc tạo kết nối với người đăng ký đó.
Để ngừng phát hành dịch vụ, hãy gọi DiscoverySession.close()
.
Các phiên Khám phá được liên kết với WifiAwareSession
mẹ. Nếu phiên mẹ bị đóng, các phiên khám phá liên quan 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 các phiên hoạt động nằm ngoài phạm vi bị đóng. 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 sẽ 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 khớp. DiscoverySessionCallback
chỉ định các hành động cần thực thi khi sự kiện xảy ra, chẳng hạn như khi phát hiện 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 nhà xuất bản sau khi ứng dụng của bạn phát hiện một nhà xuất bản, nên bạn nên lưu tệp 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 khám phá.
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 nằm trong 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 thông báo hoặc tạo kết nối với nhà xuất bản đó.
Để ngừng đăng ký một dịch vụ, hãy gọi DiscoverySession.close()
.
Phiên khám phá được liên kết với WifiAwareSession
mẹ. Nếu phiên mẹ bị đóng, các phiên khám phá liên quan 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 các phiên hoạt động nằm ngoài phạm vi bị đóng. 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 thông báo đến một thiết bị khác, bạn cần có các đối tượng sau:
DiscoverySession
. Đối tượng này cho phép bạn gọisendMessage()
. Ứng dụng của bạn nhận đượcDiscoverySession
bằng cách phát hành một dịch vụ hoặc đăng ký một dịch vụ.PeerHandle
của thiết bị khác để định tuyến thông báo. Ứng dụng của bạn nhận đượcPeerHandle
của một thiết bị khác theo một trong hai cách sau:- Ứng dụng của bạn sẽ xuất bản một dịch vụ và nhận tin nhắn từ người đăng ký.
Ứng dụng của bạn nhận được
PeerHandle
của người đăng ký từ lệnh gọi lạionMessageReceived()
. - Ứng dụng của bạn đăng ký một dịch vụ. Sau đó, khi phát hiện một nhà xuất bản phù hợp, ứng dụng của bạn sẽ nhận được
PeerHandle
của nhà xuất bản từ lệnh gọi lạionServiceDiscovered()
.
- Ứng dụng của bạn sẽ xuất bản một dịch vụ và nhận tin nhắn từ người đăng ký.
Ứng dụng của bạn nhận được
Để gửi tin nhắn, hãy gọi sendMessage()
. Sau đó, các lệnh gọi lại sau đây có thể xảy ra:
- Khi máy khách nhận được thông báo thành công, hệ thống sẽ gọi lệnh gọi lại
onMessageSendSucceeded()
trong ứng dụng gửi. - Khi máy ngang hàng nhận được thông báo, hệ thống sẽ gọi lệnh gọi lại
onMessageReceived()
trong ứng dụng nhận.
Mặc dù PeerHandle
là bắt buộc để giao tiếp với các ứng dụng ngang hàng, nhưng bạn không nên coi đây là giá trị nhận dạng cố định của các ứng dụng ngang hàng. Ứng dụng có thể sử dụng giá trị nhận dạng cấp cao hơn – được nhúng trong chính dịch vụ khám phá hoặc trong các thông báo tiếp theo. Bạn có thể nhúng giá trị nhận dạng vào dịch vụ khám phá 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 khả năng khám phá, còn phương thức setServiceSpecificInfo()
không ảnh hưởng đến khả năng khám phá.
Việc nhúng giá trị nhận dạng vào một thông báo có nghĩa là sửa đổi mảng byte của thông báo để bao gồm giá trị nhận dạng (ví dụ: dưới dạng một vài byte đầu tiên).
Tạo kết nối
Wi-Fi Aware hỗ trợ kết nối máy khách-máy chủ giữa hai thiết bị Wi-Fi Aware.
Cách thiết lập kết nối máy khách-máy chủ:
Sử dụng tính năng khám phá Wi-Fi Aware để phát hành một dịch vụ (trên máy chủ) và đăng ký một dịch vụ (trên ứng dụng).
Sau khi người đăng ký phát hiện ra nhà xuất bản, hãy gửi thông báo của người đăng ký cho nhà xuất bản.
Bắt đầu một
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();
Sử dụng
ConnectivityManager
để yêu cầu mạng Wi-Fi Aware trên trình xuất bản bằng cách sử dụngWifiAwareNetworkSpecifier
, chỉ định phiên khám phá vàPeerHandle
của người đăng ký mà bạn nhận được từ thông báo do người đăng ký truyền: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);
Sau khi nhà xuất bản yêu cầu một mạng, nhà xuất bản sẽ gửi một thông báo đến người đăng ký.
Sau khi người đăng ký nhận được thông báo từ nhà xuất bản, hãy yêu cầu mạng Wi-Fi Aware trên người đăng ký bằng cách sử dụ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 có, thay đổi hoặc mất kết nối mạng.Sau khi phương thức
onAvailable()
được gọi trên trình đăng ký, đối tượngNetwork
sẽ có sẵn để bạn có thể mởSocket
nhằm giao tiếp vớiServerSocket
trên nhà xuất bản, nhưng bạn cần biết địa chỉ IPv6 và cổng củaServerSocket
. Bạn nhận được các giá trị này từ đối tượngNetworkCapabilities
được cung cấp trong lệnh gọi lạionCapabilitiesChanged()
: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);
Khi bạn kết nối mạng xong, hãy gọi
unregisterNetworkCallback()
.
Đo khoảng cách với các thiết bị đồng cấp và tính năng khám phá nhận biết vị trí
Một thiết bị có chức năng vị trí Wi-Fi RTT có thể trực tiếp đo khoảng cách đến các thiết bị khác và sử dụng thông tin này để hạn chế việc khám phá dịch vụ Wi-Fi Aware.
API RTT Wi-Fi cho phép đo khoảng cách trực tiếp đến một thiết bị ngang hàng có tính năng Wi-Fi Aware bằng cách sử dụng địa chỉ MAC hoặc PeerHandle của thiết bị đó.
Tính năng khám phá Wi-Fi Aware có thể bị hạn chế để chỉ khám phá các dịch vụ trong một khoanh vùng địa lý cụ thể. Ví dụ: bạn có thể thiết lập khoanh vùng địa lý cho phép phát hiện một thiết bị phát hành dịch vụ "Aware_File_Share_Service_Name"
cách không quá 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 khoanh vùng địa lý, cả nhà xuất bản và người đă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 đo khoảng cách trên dịch vụ đã phát hành bằng cách sử dụng setRangingEnabled(true).
Nếu nhà xuất bản không bật tính năng sắp xếp khoảng cách, thì mọi giới hạn khoanh vùng địa lý do người đăng ký chỉ định sẽ bị bỏ qua và quá trình khám phá thông thường sẽ được thực hiện, bỏ qua khoảng cách.
Người đăng ký phải chỉ định khoanh vùng địa lý bằng cách kết hợp một số phương thức setMinDistanceMm và setMaxDistanceMm.
Đối với một trong hai giá trị, khoảng cách không xác định nghĩa là không có giới hạn. Việc chỉ chỉ định khoảng cách tối đa ngụ ý khoảng cách tối thiểu là 0. Nếu chỉ xác định khoảng cách tối thiểu thì tức là không có khoảng cách tối đa.
Khi phát hiện một dịch vụ ngang hàng trong 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 đến thiết bị ngang hàng. Sau đó, bạn có thể gọi API RTT Wi-Fi trực tiếp nếu cần để đo khoảng cách vào các thời điểm sau.