• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 MIN_DISTANCE_MM = "min_distance_mm";
59     private static final String PAIRING_CONFIG = "pairing_config";
60     private static final String TTL_SEC = "TtlSec";
61     private static final String INSTANTMODE_ENABLE = "InstantModeEnabled";
62     private static final String BAND_5 = "5G";
63     // PublishConfig special
64     private static final String PUBLISH_TYPE = "publish_type";
65     private static final String RANGING_ENABLED = "ranging_enabled";
66     // AwarePairingConfig specific
67     private static final String PAIRING_CACHE_ENABLED = "pairing_cache_enabled";
68     private static final String PAIRING_SETUP_ENABLED = "pairing_setup_enabled";
69     private static final String PAIRING_VERIFICATION_ENABLED = "pairing_verification_enabled";
70     private static final String BOOTSTRAPPING_METHODS = "bootstrapping_methods";
71     // WifiAwareNetworkSpecifier specific
72     private static final String IS_ACCEPT_ANY = "is_accept_any";
73     private static final String PMK = "pmk";
74     private static final String CHANNEL_IN_MHZ = "channel_in_mhz";
75     private static final String CHANNEL_REQUIRE = "channel_require";
76     private static final String PSK_PASSPHRASE = "psk_passphrase";
77     private static final String PORT = "port";
78     private static final String TRANSPORT_PROTOCOL = "transport_protocol";
79     private static final String DATA_PATH_SECURITY_CONFIG = "data_path_security_config";
80     private static final String CHANNEL_FREQUENCY_M_HZ = "channel_frequency_m_hz";
81     //NetworkRequest specific
82     private static final String TRANSPORT_TYPE = "transport_type";
83     private static final String CAPABILITY = "capability";
84     private static final String NETWORK_SPECIFIER_PARCEL = "network_specifier_parcel";
85     //WifiAwareDataPathSecurityConfig specific
86     private static final String CIPHER_SUITE = "cipher_suite";
87     private static final String SECURITY_CONFIG_PMK = "pmk";
88     /** 2.4 GHz band */
89     public static final int WIFI_BAND_24_GHZ = 1;
90     /** 5 GHz band excluding DFS channels */
91     public static final int WIFI_BAND_5_GHZ = 1;
92     /** DFS channels from 5 GHz band only */
93     public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 1;
94 
95     // Fields for rangingRequest
96     private static final String RANGING_REQUEST_PEER_IDS = "peer_ids";
97     private static final String RANGING_REQUEST_PEER_MACS = "peer_mac_addresses";
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(MIN_DISTANCE_MM)) {
155             int minDistanceMm = jsonObject.getInt(MIN_DISTANCE_MM);
156             if (minDistanceMm >= 0) {
157                 builder.setMinDistanceMm(minDistanceMm);
158             }
159         }
160         if (jsonObject.has(PAIRING_CONFIG)) {
161             JSONObject pairingConfigObject = jsonObject.getJSONObject(PAIRING_CONFIG);
162             AwarePairingConfig pairingConfig = jsonToAwarePairingConfig(pairingConfigObject);
163             builder.setPairingConfig(pairingConfig);
164         }
165         if (jsonObject.has(TTL_SEC)) {
166             builder.setTtlSec(jsonObject.getInt(TTL_SEC));
167         }
168         if (SdkLevel.isAtLeastT() && jsonObject.has(INSTANTMODE_ENABLE)) {
169             builder.setInstantCommunicationModeEnabled(true,
170                     Objects.equals(jsonObject.getString(INSTANTMODE_ENABLE), BAND_5)
171                             ? WIFI_BAND_5_GHZ :WIFI_BAND_24_GHZ);
172         }
173         return builder.build();
174     }
175 
176     /**
177      * Converts JSONObject to {@link AwarePairingConfig}.
178      *
179      * @param jsonObject corresponding to SubscribeConfig in
180      *                   tests/hostsidetests/multidevices/test/aware/constants.py
181      */
jsonToAwarePairingConfig(JSONObject jsonObject)182     private static AwarePairingConfig jsonToAwarePairingConfig(JSONObject jsonObject)
183             throws JSONException {
184         AwarePairingConfig.Builder builder = new AwarePairingConfig.Builder();
185         if (jsonObject == null) {
186             return builder.build();
187         }
188         if (jsonObject.has(PAIRING_CACHE_ENABLED)) {
189             boolean pairingCacheEnabled = jsonObject.getBoolean(PAIRING_CACHE_ENABLED);
190             builder.setPairingCacheEnabled(pairingCacheEnabled);
191         }
192         if (jsonObject.has(PAIRING_SETUP_ENABLED)) {
193             boolean pairingSetupEnabled = jsonObject.getBoolean(PAIRING_SETUP_ENABLED);
194             builder.setPairingSetupEnabled(pairingSetupEnabled);
195         }
196         if (jsonObject.has(PAIRING_VERIFICATION_ENABLED)) {
197             boolean pairingVerificationEnabled =
198                     jsonObject.getBoolean(PAIRING_VERIFICATION_ENABLED);
199             builder.setPairingVerificationEnabled(pairingVerificationEnabled);
200         }
201         if (jsonObject.has(BOOTSTRAPPING_METHODS)) {
202             int bootstrappingMethods = jsonObject.getInt(BOOTSTRAPPING_METHODS);
203             builder.setBootstrappingMethods(bootstrappingMethods);
204         }
205         return builder.build();
206     }
207 
208     /**
209      * Converts Python dict to {@link PublishConfig}.
210      *
211      * @param jsonObject corresponding to PublishConfig in
212      *                   tests/hostsidetests/multidevices/test/aware/constants.py
213      */
jsonToPublishConfig(JSONObject jsonObject)214     public static PublishConfig jsonToPublishConfig(JSONObject jsonObject) throws JSONException {
215         PublishConfig.Builder builder = new PublishConfig.Builder();
216         if (jsonObject == null) {
217             return builder.build();
218         }
219         if (jsonObject.has(SERVICE_NAME)) {
220             String serviceName = jsonObject.getString(SERVICE_NAME);
221             builder.setServiceName(serviceName);
222         }
223         if (jsonObject.has(SERVICE_SPECIFIC_INFO)) {
224             byte[] serviceSpecificInfo =
225                     jsonObject.getString(SERVICE_SPECIFIC_INFO).getBytes(StandardCharsets.UTF_8);
226             builder.setServiceSpecificInfo(serviceSpecificInfo);
227         }
228         if (jsonObject.has(MATCH_FILTER)) {
229             List<byte[]> matchFilter = new ArrayList<>();
230             for (int i = 0; i < jsonObject.getJSONArray(MATCH_FILTER).length(); i++) {
231                 matchFilter.add(jsonObject.getJSONArray(MATCH_FILTER).getString(i)
232                         .getBytes(StandardCharsets.UTF_8));
233             }
234             builder.setMatchFilter(matchFilter);
235         }
236         if (jsonObject.has(MATCH_FILTER_LIST)) {
237             byte[] bytes = Base64.decode(
238                     jsonObject.getString(MATCH_FILTER_LIST).getBytes(StandardCharsets.UTF_8),
239                      Base64.DEFAULT);
240             List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList();
241             builder.setMatchFilter(mf);
242         }
243         if (jsonObject.has(PUBLISH_TYPE)) {
244             int publishType = jsonObject.getInt(PUBLISH_TYPE);
245             builder.setPublishType(publishType);
246         }
247         if (jsonObject.has(TERMINATE_NOTIFICATION_ENABLED)) {
248             boolean terminateNotificationEnabled =
249                     jsonObject.getBoolean(TERMINATE_NOTIFICATION_ENABLED);
250             builder.setTerminateNotificationEnabled(terminateNotificationEnabled);
251         }
252         if (jsonObject.has(RANGING_ENABLED)) {
253             boolean rangingEnabled = jsonObject.getBoolean(RANGING_ENABLED);
254             builder.setRangingEnabled(rangingEnabled);
255         }
256         if (jsonObject.has(PAIRING_CONFIG)) {
257             JSONObject pairingConfigObject = jsonObject.getJSONObject(PAIRING_CONFIG);
258             AwarePairingConfig pairingConfig = jsonToAwarePairingConfig(pairingConfigObject);
259             builder.setPairingConfig(pairingConfig);
260         }
261         if (jsonObject.has(TTL_SEC)) {
262             builder.setTtlSec(jsonObject.getInt(TTL_SEC));
263         }
264         if (SdkLevel.isAtLeastT() && jsonObject.has(INSTANTMODE_ENABLE)) {
265             builder.setInstantCommunicationModeEnabled(true,
266                     Objects.equals(jsonObject.getString(INSTANTMODE_ENABLE), BAND_5)
267                             ? WIFI_BAND_5_GHZ :WIFI_BAND_24_GHZ);
268         }
269         return builder.build();
270     }
271 
272     /**
273      * Converts request from JSON object to {@link NetworkRequest}.
274      *
275      * @param jsonObject corresponding to WifiAwareNetworkSpecifier in
276      *                   tests/hostsidetests/multidevices/test/aware/constants.py
277      */
jsonToNetworkRequest(JSONObject jsonObject)278     public static NetworkRequest jsonToNetworkRequest(JSONObject jsonObject) throws JSONException {
279         NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
280         if (jsonObject == null) {
281             return requestBuilder.build();
282         }
283         int transportType;
284         if (jsonObject.has(TRANSPORT_TYPE)) {
285             transportType = jsonObject.getInt(TRANSPORT_TYPE);
286         } else {
287             // Returns null for request of unknown type.
288             return null;
289         }
290         if (transportType == NetworkCapabilities.TRANSPORT_WIFI_AWARE) {
291             requestBuilder.addTransportType(transportType);
292             if (jsonObject.has(NETWORK_SPECIFIER_PARCEL)) {
293                 String specifierParcelableStr = jsonObject.getString(NETWORK_SPECIFIER_PARCEL);
294                 WifiAwareNetworkSpecifier wifiAwareNetworkSpecifier =
295                         SerializationUtil.stringToParcelable(
296                                 specifierParcelableStr,
297                                 WifiAwareNetworkSpecifier.CREATOR
298                         );
299                 // Set the network specifier in the request builder
300                 requestBuilder.setNetworkSpecifier(wifiAwareNetworkSpecifier);
301             }
302             if (jsonObject.has(CAPABILITY)) {
303                 int capability = jsonObject.getInt(CAPABILITY);
304                 requestBuilder.addCapability(capability);
305             }
306             return requestBuilder.build();
307         }
308         return null;
309     }
310 
311     /**
312      * Converts JSON object to {@link WifiAwareNetworkSpecifier}.
313      *
314      * @param jsonObject corresponding to WifiAwareNetworkSpecifier in
315      * @param builder    builder to build the WifiAwareNetworkSpecifier
316      * @return WifiAwareNetworkSpecifier object
317      */
jsonToNetworkSpecifier( JSONObject jsonObject, WifiAwareNetworkSpecifier.Builder builder )318     public static WifiAwareNetworkSpecifier jsonToNetworkSpecifier(
319             JSONObject jsonObject, WifiAwareNetworkSpecifier.Builder builder
320     ) throws JSONException {
321         if (jsonObject == null) {
322             return builder.build();
323         }
324         if (jsonObject.has(PSK_PASSPHRASE)) {
325             String pskPassphrase = jsonObject.getString(PSK_PASSPHRASE);
326             builder.setPskPassphrase(pskPassphrase);
327         }
328         if (jsonObject.has(PORT)) {
329             builder.setPort(jsonObject.getInt(PORT));
330         }
331         if (jsonObject.has(TRANSPORT_PROTOCOL)) {
332             builder.setTransportProtocol(jsonObject.getInt(TRANSPORT_PROTOCOL));
333         }
334         if (jsonObject.has(PMK)) {
335             builder.setPmk(jsonObject.getString(PMK).getBytes(StandardCharsets.UTF_8));
336         }
337         if (jsonObject.has(DATA_PATH_SECURITY_CONFIG)) {
338             builder.setDataPathSecurityConfig(jsonToDataPathSSecurityConfig(
339                     jsonObject.getJSONObject(DATA_PATH_SECURITY_CONFIG)));
340         }
341         if (jsonObject.has(CHANNEL_FREQUENCY_M_HZ)) {
342             builder.setChannelFrequencyMhz(jsonObject.getInt(CHANNEL_FREQUENCY_M_HZ), true);
343         }
344 
345         return builder.build();
346 
347     }
348 
349     /**
350      * Converts request from JSON object to {@link WifiAwareDataPathSecurityConfig}.
351      *
352      * @param jsonObject corresponding to WifiAwareNetworkSpecifier in
353      *                   tests/hostsidetests/multidevices/test/aware/constants.py
354      */
jsonToDataPathSSecurityConfig( @onNull JSONObject jsonObject )355     private static WifiAwareDataPathSecurityConfig jsonToDataPathSSecurityConfig(
356             @NonNull JSONObject jsonObject
357     ) throws JSONException {
358         WifiAwareDataPathSecurityConfig.Builder builder = null;
359 
360         if (jsonObject.has(CIPHER_SUITE)) {
361             int cipherSuite = jsonObject.getInt(CIPHER_SUITE);
362             builder = new WifiAwareDataPathSecurityConfig.Builder(cipherSuite);
363         } else {
364             throw new RuntimeException("Missing 'cipher_suite' in data path security jsonObject "
365                     + "config");
366         }
367         if (jsonObject.has(SECURITY_CONFIG_PMK)) {
368             byte[] pmk = jsonObject.getString(SECURITY_CONFIG_PMK).getBytes(StandardCharsets.UTF_8);
369             builder.setPmk(pmk);
370         }
371         return builder.build();
372 
373     }
374 
375     /**
376      * Converts the ranging request from JSONObject to {@link android.net.wifi.rtt.RangingRequest}.
377      * This converts peer IDs in the request to Wi-Fi Aware peer handles in
378      * {@link #mPeerHandles mPeerHandles}.
379      *
380      * @param jsonObject        The ranging request in JSONObject type.
381      * @param peerHandles       All Wi-Fi Aware peers.
382      * @return The converted ranging request.
383      */
jsonToRangingRequest( @onNull JSONObject jsonObject, ConcurrentHashMap<Integer, PeerHandle> peerHandles )384     public static RangingRequest jsonToRangingRequest(
385             @NonNull JSONObject jsonObject, ConcurrentHashMap<Integer, PeerHandle> peerHandles
386     ) throws JSONException, IllegalArgumentException {
387         RangingRequest.Builder builder = new RangingRequest.Builder();
388         if (jsonObject.has(RANGING_REQUEST_PEER_IDS)) {
389             JSONArray values = jsonObject.getJSONArray(RANGING_REQUEST_PEER_IDS);
390             for (int i = 0; i < values.length(); i++) {
391                 int peerId = values.getInt(i);
392                 PeerHandle handle = peerHandles.get(peerId);
393                 if (handle == null) {
394                     throw new IllegalArgumentException(
395                         "Got an invalid peerId. peerId: " + peerId + ", all peer Handles: "
396                             + peerHandles
397                     );
398                 }
399                 builder.addWifiAwarePeer(handle);
400             }
401         }
402         if (jsonObject.has(RANGING_REQUEST_PEER_MACS)) {
403             JSONArray values = jsonObject.getJSONArray(RANGING_REQUEST_PEER_MACS);
404             for (int i = 0; i < values.length(); i++) {
405                 String macAddressStr = values.getString(i);
406                 MacAddress macAddress = MacAddress.fromString(macAddressStr);
407                 builder.addWifiAwarePeer(macAddress);
408             }
409         }
410         return builder.build();
411     }
412 }
413