1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package com.google.snippet.wifi.aware; 19 20 import android.net.MacAddress; 21 import android.net.NetworkCapabilities; 22 import android.net.NetworkRequest; 23 import android.net.wifi.aware.AwarePairingConfig; 24 import android.net.wifi.aware.PeerHandle; 25 import android.net.wifi.aware.PublishConfig; 26 import android.net.wifi.aware.SubscribeConfig; 27 import android.net.wifi.aware.WifiAwareDataPathSecurityConfig; 28 import android.net.wifi.aware.WifiAwareNetworkSpecifier; 29 import android.net.wifi.rtt.RangingRequest; 30 import android.util.Base64; 31 32 import androidx.annotation.NonNull; 33 34 import com.android.modules.utils.build.SdkLevel; 35 36 import org.json.JSONArray; 37 import org.json.JSONException; 38 import org.json.JSONObject; 39 40 import java.nio.charset.StandardCharsets; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.Objects; 44 import java.util.concurrent.ConcurrentHashMap; 45 46 /** 47 * Deserializes JSONObject into data objects defined in Wi-Fi Aware API. 48 */ 49 public class WifiAwareJsonDeserializer { 50 51 private static final String SERVICE_NAME = "service_name"; 52 private static final String SERVICE_SPECIFIC_INFO = "service_specific_info"; 53 private static final String MATCH_FILTER = "match_filter"; 54 private static final String MATCH_FILTER_LIST = "MatchFilterList"; 55 private static final String SUBSCRIBE_TYPE = "subscribe_type"; 56 private static final String TERMINATE_NOTIFICATION_ENABLED = "terminate_notification_enabled"; 57 private static final String MAX_DISTANCE_MM = "max_distance_mm"; 58 private static final String PAIRING_CONFIG = "pairing_config"; 59 private static final String TTL_SEC = "TtlSec"; 60 private static final String INSTANTMODE_ENABLE = "InstantModeEnabled"; 61 private static final String BAND_5 = "5G"; 62 // PublishConfig special 63 private static final String PUBLISH_TYPE = "publish_type"; 64 private static final String RANGING_ENABLED = "ranging_enabled"; 65 // AwarePairingConfig specific 66 private static final String PAIRING_CACHE_ENABLED = "pairing_cache_enabled"; 67 private static final String PAIRING_SETUP_ENABLED = "pairing_setup_enabled"; 68 private static final String PAIRING_VERIFICATION_ENABLED = "pairing_verification_enabled"; 69 private static final String BOOTSTRAPPING_METHODS = "bootstrapping_methods"; 70 // WifiAwareNetworkSpecifier specific 71 private static final String IS_ACCEPT_ANY = "is_accept_any"; 72 private static final String PMK = "pmk"; 73 private static final String CHANNEL_IN_MHZ = "channel_in_mhz"; 74 private static final String CHANNEL_REQUIRE = "channel_require"; 75 private static final String PSK_PASSPHRASE = "psk_passphrase"; 76 private static final String PORT = "port"; 77 private static final String TRANSPORT_PROTOCOL = "transport_protocol"; 78 private static final String DATA_PATH_SECURITY_CONFIG = "data_path_security_config"; 79 private static final String CHANNEL_FREQUENCY_M_HZ = "channel_frequency_m_hz"; 80 //NetworkRequest specific 81 private static final String TRANSPORT_TYPE = "transport_type"; 82 private static final String CAPABILITY = "capability"; 83 private static final String NETWORK_SPECIFIER_PARCEL = "network_specifier_parcel"; 84 //WifiAwareDataPathSecurityConfig specific 85 private static final String CIPHER_SUITE = "cipher_suite"; 86 private static final String SECURITY_CONFIG_PMK = "pmk"; 87 /** 2.4 GHz band */ 88 public static final int WIFI_BAND_24_GHZ = 1; 89 /** 5 GHz band excluding DFS channels */ 90 public static final int WIFI_BAND_5_GHZ = 1; 91 /** DFS channels from 5 GHz band only */ 92 public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 1; 93 94 // Fields for rangingRequest 95 private static final String RANGING_REQUEST_PEER_IDS = "peer_ids"; 96 private static final String RANGING_REQUEST_PEER_MACS = "peer_mac_addresses"; 97 98 WifiAwareJsonDeserializer()99 private WifiAwareJsonDeserializer() { 100 } 101 102 /** 103 * Converts Python dict to {@link SubscribeConfig}. 104 * 105 * @param jsonObject corresponding to SubscribeConfig in 106 * tests/hostsidetests/multidevices/test/aware/constants.py 107 */ jsonToSubscribeConfig(JSONObject jsonObject)108 public static SubscribeConfig jsonToSubscribeConfig(JSONObject jsonObject) 109 throws JSONException { 110 SubscribeConfig.Builder builder = new SubscribeConfig.Builder(); 111 if (jsonObject == null) { 112 return builder.build(); 113 } 114 if (jsonObject.has(SERVICE_NAME)) { 115 String serviceName = jsonObject.getString(SERVICE_NAME); 116 builder.setServiceName(serviceName); 117 } 118 if (jsonObject.has(SERVICE_SPECIFIC_INFO)) { 119 byte[] serviceSpecificInfo = 120 jsonObject.getString(SERVICE_SPECIFIC_INFO).getBytes(StandardCharsets.UTF_8); 121 builder.setServiceSpecificInfo(serviceSpecificInfo); 122 } 123 if (jsonObject.has(MATCH_FILTER)) { 124 List<byte[]> matchFilter = new ArrayList<>(); 125 for (int i = 0; i < jsonObject.getJSONArray(MATCH_FILTER).length(); i++) { 126 matchFilter.add(jsonObject.getJSONArray(MATCH_FILTER).getString(i) 127 .getBytes(StandardCharsets.UTF_8)); 128 } 129 builder.setMatchFilter(matchFilter); 130 } 131 if (jsonObject.has(MATCH_FILTER_LIST)) { 132 byte[] bytes = Base64.decode( 133 jsonObject.getString(MATCH_FILTER_LIST).getBytes(StandardCharsets.UTF_8), 134 Base64.DEFAULT); 135 136 List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList(); 137 builder.setMatchFilter(mf); 138 } 139 if (jsonObject.has(SUBSCRIBE_TYPE)) { 140 int subscribeType = jsonObject.getInt(SUBSCRIBE_TYPE); 141 builder.setSubscribeType(subscribeType); 142 } 143 if (jsonObject.has(TERMINATE_NOTIFICATION_ENABLED)) { 144 boolean terminateNotificationEnabled = 145 jsonObject.getBoolean(TERMINATE_NOTIFICATION_ENABLED); 146 builder.setTerminateNotificationEnabled(terminateNotificationEnabled); 147 } 148 if (jsonObject.has(MAX_DISTANCE_MM)) { 149 int maxDistanceMm = jsonObject.getInt(MAX_DISTANCE_MM); 150 if (maxDistanceMm > 0) { 151 builder.setMaxDistanceMm(maxDistanceMm); 152 } 153 } 154 if (jsonObject.has(PAIRING_CONFIG)) { 155 JSONObject pairingConfigObject = jsonObject.getJSONObject(PAIRING_CONFIG); 156 AwarePairingConfig pairingConfig = jsonToAwarePairingConfig(pairingConfigObject); 157 builder.setPairingConfig(pairingConfig); 158 } 159 if (jsonObject.has(TTL_SEC)) { 160 builder.setTtlSec(jsonObject.getInt(TTL_SEC)); 161 } 162 if (SdkLevel.isAtLeastT() && jsonObject.has(INSTANTMODE_ENABLE)) { 163 builder.setInstantCommunicationModeEnabled(true, 164 Objects.equals(jsonObject.getString(INSTANTMODE_ENABLE), BAND_5) 165 ? WIFI_BAND_5_GHZ :WIFI_BAND_24_GHZ); 166 } 167 return builder.build(); 168 } 169 170 /** 171 * Converts JSONObject to {@link AwarePairingConfig}. 172 * 173 * @param jsonObject corresponding to SubscribeConfig in 174 * tests/hostsidetests/multidevices/test/aware/constants.py 175 */ jsonToAwarePairingConfig(JSONObject jsonObject)176 private static AwarePairingConfig jsonToAwarePairingConfig(JSONObject jsonObject) 177 throws JSONException { 178 AwarePairingConfig.Builder builder = new AwarePairingConfig.Builder(); 179 if (jsonObject == null) { 180 return builder.build(); 181 } 182 if (jsonObject.has(PAIRING_CACHE_ENABLED)) { 183 boolean pairingCacheEnabled = jsonObject.getBoolean(PAIRING_CACHE_ENABLED); 184 builder.setPairingCacheEnabled(pairingCacheEnabled); 185 } 186 if (jsonObject.has(PAIRING_SETUP_ENABLED)) { 187 boolean pairingSetupEnabled = jsonObject.getBoolean(PAIRING_SETUP_ENABLED); 188 builder.setPairingSetupEnabled(pairingSetupEnabled); 189 } 190 if (jsonObject.has(PAIRING_VERIFICATION_ENABLED)) { 191 boolean pairingVerificationEnabled = 192 jsonObject.getBoolean(PAIRING_VERIFICATION_ENABLED); 193 builder.setPairingVerificationEnabled(pairingVerificationEnabled); 194 } 195 if (jsonObject.has(BOOTSTRAPPING_METHODS)) { 196 int bootstrappingMethods = jsonObject.getInt(BOOTSTRAPPING_METHODS); 197 builder.setBootstrappingMethods(bootstrappingMethods); 198 } 199 return builder.build(); 200 } 201 202 /** 203 * Converts Python dict to {@link PublishConfig}. 204 * 205 * @param jsonObject corresponding to PublishConfig in 206 * tests/hostsidetests/multidevices/test/aware/constants.py 207 */ jsonToPublishConfig(JSONObject jsonObject)208 public static PublishConfig jsonToPublishConfig(JSONObject jsonObject) throws JSONException { 209 PublishConfig.Builder builder = new PublishConfig.Builder(); 210 if (jsonObject == null) { 211 return builder.build(); 212 } 213 if (jsonObject.has(SERVICE_NAME)) { 214 String serviceName = jsonObject.getString(SERVICE_NAME); 215 builder.setServiceName(serviceName); 216 } 217 if (jsonObject.has(SERVICE_SPECIFIC_INFO)) { 218 byte[] serviceSpecificInfo = 219 jsonObject.getString(SERVICE_SPECIFIC_INFO).getBytes(StandardCharsets.UTF_8); 220 builder.setServiceSpecificInfo(serviceSpecificInfo); 221 } 222 if (jsonObject.has(MATCH_FILTER)) { 223 List<byte[]> matchFilter = new ArrayList<>(); 224 for (int i = 0; i < jsonObject.getJSONArray(MATCH_FILTER).length(); i++) { 225 matchFilter.add(jsonObject.getJSONArray(MATCH_FILTER).getString(i) 226 .getBytes(StandardCharsets.UTF_8)); 227 } 228 builder.setMatchFilter(matchFilter); 229 } 230 if (jsonObject.has(MATCH_FILTER_LIST)) { 231 byte[] bytes = Base64.decode( 232 jsonObject.getString(MATCH_FILTER_LIST).getBytes(StandardCharsets.UTF_8), 233 Base64.DEFAULT); 234 List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList(); 235 builder.setMatchFilter(mf); 236 } 237 if (jsonObject.has(PUBLISH_TYPE)) { 238 int publishType = jsonObject.getInt(PUBLISH_TYPE); 239 builder.setPublishType(publishType); 240 } 241 if (jsonObject.has(TERMINATE_NOTIFICATION_ENABLED)) { 242 boolean terminateNotificationEnabled = 243 jsonObject.getBoolean(TERMINATE_NOTIFICATION_ENABLED); 244 builder.setTerminateNotificationEnabled(terminateNotificationEnabled); 245 } 246 if (jsonObject.has(RANGING_ENABLED)) { 247 boolean rangingEnabled = jsonObject.getBoolean(RANGING_ENABLED); 248 builder.setRangingEnabled(rangingEnabled); 249 } 250 if (jsonObject.has(PAIRING_CONFIG)) { 251 JSONObject pairingConfigObject = jsonObject.getJSONObject(PAIRING_CONFIG); 252 AwarePairingConfig pairingConfig = jsonToAwarePairingConfig(pairingConfigObject); 253 builder.setPairingConfig(pairingConfig); 254 } 255 if (jsonObject.has(TTL_SEC)) { 256 builder.setTtlSec(jsonObject.getInt(TTL_SEC)); 257 } 258 if (SdkLevel.isAtLeastT() && jsonObject.has(INSTANTMODE_ENABLE)) { 259 builder.setInstantCommunicationModeEnabled(true, 260 Objects.equals(jsonObject.getString(INSTANTMODE_ENABLE), BAND_5) 261 ? WIFI_BAND_5_GHZ :WIFI_BAND_24_GHZ); 262 } 263 return builder.build(); 264 } 265 266 /** 267 * Converts request from JSON object to {@link NetworkRequest}. 268 * 269 * @param jsonObject corresponding to WifiAwareNetworkSpecifier in 270 * tests/hostsidetests/multidevices/test/aware/constants.py 271 */ jsonToNetworkRequest(JSONObject jsonObject)272 public static NetworkRequest jsonToNetworkRequest(JSONObject jsonObject) throws JSONException { 273 NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder(); 274 if (jsonObject == null) { 275 return requestBuilder.build(); 276 } 277 int transportType; 278 if (jsonObject.has(TRANSPORT_TYPE)) { 279 transportType = jsonObject.getInt(TRANSPORT_TYPE); 280 } else { 281 // Returns null for request of unknown type. 282 return null; 283 } 284 if (transportType == NetworkCapabilities.TRANSPORT_WIFI_AWARE) { 285 requestBuilder.addTransportType(transportType); 286 if (jsonObject.has(NETWORK_SPECIFIER_PARCEL)) { 287 String specifierParcelableStr = jsonObject.getString(NETWORK_SPECIFIER_PARCEL); 288 WifiAwareNetworkSpecifier wifiAwareNetworkSpecifier = 289 SerializationUtil.stringToParcelable( 290 specifierParcelableStr, 291 WifiAwareNetworkSpecifier.CREATOR 292 ); 293 // Set the network specifier in the request builder 294 requestBuilder.setNetworkSpecifier(wifiAwareNetworkSpecifier); 295 } 296 if (jsonObject.has(CAPABILITY)) { 297 int capability = jsonObject.getInt(CAPABILITY); 298 requestBuilder.addCapability(capability); 299 } 300 return requestBuilder.build(); 301 } 302 return null; 303 } 304 305 /** 306 * Converts JSON object to {@link WifiAwareNetworkSpecifier}. 307 * 308 * @param jsonObject corresponding to WifiAwareNetworkSpecifier in 309 * @param builder builder to build the WifiAwareNetworkSpecifier 310 * @return WifiAwareNetworkSpecifier object 311 */ jsonToNetworkSpecifier( JSONObject jsonObject, WifiAwareNetworkSpecifier.Builder builder )312 public static WifiAwareNetworkSpecifier jsonToNetworkSpecifier( 313 JSONObject jsonObject, WifiAwareNetworkSpecifier.Builder builder 314 ) throws JSONException { 315 if (jsonObject == null) { 316 return builder.build(); 317 } 318 if (jsonObject.has(PSK_PASSPHRASE)) { 319 String pskPassphrase = jsonObject.getString(PSK_PASSPHRASE); 320 builder.setPskPassphrase(pskPassphrase); 321 } 322 if (jsonObject.has(PORT)) { 323 builder.setPort(jsonObject.getInt(PORT)); 324 } 325 if (jsonObject.has(TRANSPORT_PROTOCOL)) { 326 builder.setTransportProtocol(jsonObject.getInt(TRANSPORT_PROTOCOL)); 327 } 328 if (jsonObject.has(PMK)) { 329 builder.setPmk(jsonObject.getString(PMK).getBytes(StandardCharsets.UTF_8)); 330 } 331 if (jsonObject.has(DATA_PATH_SECURITY_CONFIG)) { 332 builder.setDataPathSecurityConfig(jsonToDataPathSSecurityConfig( 333 jsonObject.getJSONObject(DATA_PATH_SECURITY_CONFIG))); 334 } 335 if (jsonObject.has(CHANNEL_FREQUENCY_M_HZ)) { 336 builder.setChannelFrequencyMhz(jsonObject.getInt(CHANNEL_FREQUENCY_M_HZ), true); 337 } 338 339 return builder.build(); 340 341 } 342 343 /** 344 * Converts request from JSON object to {@link WifiAwareDataPathSecurityConfig}. 345 * 346 * @param jsonObject corresponding to WifiAwareNetworkSpecifier in 347 * tests/hostsidetests/multidevices/test/aware/constants.py 348 */ jsonToDataPathSSecurityConfig( @onNull JSONObject jsonObject )349 private static WifiAwareDataPathSecurityConfig jsonToDataPathSSecurityConfig( 350 @NonNull JSONObject jsonObject 351 ) throws JSONException { 352 WifiAwareDataPathSecurityConfig.Builder builder = null; 353 354 if (jsonObject.has(CIPHER_SUITE)) { 355 int cipherSuite = jsonObject.getInt(CIPHER_SUITE); 356 builder = new WifiAwareDataPathSecurityConfig.Builder(cipherSuite); 357 } else { 358 throw new RuntimeException("Missing 'cipher_suite' in data path security jsonObject " 359 + "config"); 360 } 361 if (jsonObject.has(SECURITY_CONFIG_PMK)) { 362 byte[] pmk = jsonObject.getString(SECURITY_CONFIG_PMK).getBytes(StandardCharsets.UTF_8); 363 builder.setPmk(pmk); 364 } 365 return builder.build(); 366 367 } 368 369 /** 370 * Converts the ranging request from JSONObject to {@link android.net.wifi.rtt.RangingRequest}. 371 * This converts peer IDs in the request to Wi-Fi Aware peer handles in 372 * {@link #mPeerHandles mPeerHandles}. 373 * 374 * @param jsonObject The ranging request in JSONObject type. 375 * @param peerHandles All Wi-Fi Aware peers. 376 * @return The converted ranging request. 377 */ jsonToRangingRequest( @onNull JSONObject jsonObject, ConcurrentHashMap<Integer, PeerHandle> peerHandles )378 public static RangingRequest jsonToRangingRequest( 379 @NonNull JSONObject jsonObject, ConcurrentHashMap<Integer, PeerHandle> peerHandles 380 ) throws JSONException, IllegalArgumentException { 381 RangingRequest.Builder builder = new RangingRequest.Builder(); 382 if (jsonObject.has(RANGING_REQUEST_PEER_IDS)) { 383 JSONArray values = jsonObject.getJSONArray(RANGING_REQUEST_PEER_IDS); 384 for (int i = 0; i < values.length(); i++) { 385 int peerId = values.getInt(i); 386 PeerHandle handle = peerHandles.get(peerId); 387 if (handle == null) { 388 throw new IllegalArgumentException( 389 "Got an invalid peerId. peerId: " + peerId + ", all peer Handles: " 390 + peerHandles 391 ); 392 } 393 builder.addWifiAwarePeer(handle); 394 } 395 } 396 if (jsonObject.has(RANGING_REQUEST_PEER_MACS)) { 397 JSONArray values = jsonObject.getJSONArray(RANGING_REQUEST_PEER_MACS); 398 for (int i = 0; i < values.length(); i++) { 399 String macAddressStr = values.getString(i); 400 MacAddress macAddress = MacAddress.fromString(macAddressStr); 401 builder.addWifiAwarePeer(macAddress); 402 } 403 } 404 return builder.build(); 405 } 406 } 407