• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 package com.android.tradefed.device;
18 
19 import com.android.tradefed.log.LogUtil.CLog;
20 
21 import java.util.LinkedHashMap;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 
28 /** A utility class that can parse wifi command outputs. */
29 public class WifiCommandUtil {
30 
31     private static final Pattern SSID_PATTERN =
32             Pattern.compile(".*WifiInfo:.*SSID:\\s*\"([^,]*)\".*");
33     private static final Pattern BSSID_PATTERN =
34             Pattern.compile(".*WifiInfo:.*BSSID:\\s*([^,]*).*");
35     private static final Pattern LINK_SPEED_PATTERN =
36             Pattern.compile(
37                     ".*WifiInfo:.*(?<!\\bTx\\s\\b|\\bRx\\s\\b)Link speed:\\s*([^,]*)Mbps.*");
38     private static final Pattern RSSI_PATTERN = Pattern.compile(".*WifiInfo:.*RSSI:\\s*([^,]*).*");
39     private static final Pattern MAC_ADDRESS_PATTERN =
40             Pattern.compile(".*WifiInfo:.*MAC:\\s*([^,]*).*");
41     private static final Pattern NETWORK_ID_PATTERN =
42             Pattern.compile(".*WifiInfo:.*Net ID:\\s*([^,]*).*");
43 
44     /** Represents a wifi network containing its related info. */
45     public static class ScanResult {
46         private Map<String, String> scanInfo = new LinkedHashMap<>();
47         /** Adds an info related to the current wifi network. */
addInfo(String key, String value)48         private void addInfo(String key, String value) {
49             scanInfo.put(key, value);
50         }
51         /** Returns the wifi network information related to the key */
getInfo(String infoKey)52         public String getInfo(String infoKey) {
53             return scanInfo.get(infoKey);
54         }
55 
56         @Override
toString()57         public String toString() {
58             return scanInfo.toString();
59         }
60     }
61 
62     /**
63      * Parse the `wifi list-scan-results` command output and returns a list of {@link ScanResult}s.
64      *
65      * @param input Output of the list-scan-results command to parse.
66      * @return List of {@link ScanResult}s.
67      */
parseScanResults(String input)68     public static List<ScanResult> parseScanResults(String input) {
69         // EXAMPLE INPUT:
70 
71         // BSSID             Frequency   RSSI           Age(sec)   SSID         Flags
72         // 20:9c:b4:16:09:f2   5580    -70(0:-70/1:-79)  4.731   GoogleGuest-2  [ESS]
73         // 20:9c:b4:16:09:f0   5580    -69(0:-70/1:-78)  4.732   Google-A       [WPA2-PSK-CCMP][ESS]
74         // 20:9c:b4:16:09:f1   5580    -69(0:-69/1:-78)  4.731   GoogleGuest    [ESS]
75 
76         if (input == null || input.isEmpty()) {
77             return new LinkedList<>();
78         }
79         List<ScanResult> results = new LinkedList<>();
80 
81         // Figure out the column names from the first line of the output
82         String[] scanResultLines = input.split("\n");
83         String[] columnNames = scanResultLines[0].split("\\s+");
84 
85         // All lines after that should be related to wifi networks
86         for (int i = 1; i < scanResultLines.length; i++) {
87             if (scanResultLines[i].trim().isEmpty()) {
88                 continue;
89             }
90             String[] columnValues = scanResultLines[i].split("\\s+");
91             if (columnValues.length != columnNames.length) {
92                 CLog.d(
93                         "Skipping scan result since one or more of its value is undetermined:\n%s",
94                         scanResultLines[i]);
95             } else {
96                 ScanResult scanResult = new ScanResult();
97                 for (int j = 0; j < columnNames.length; j++) {
98                     scanResult.addInfo(columnNames[j], columnValues[j]);
99                 }
100                 results.add(scanResult);
101             }
102         }
103         return results;
104     }
105 
106     /** Resolves the network type given the flags returned from list-scan-result cmd. */
resolveNetworkType(String flags)107     public static String resolveNetworkType(String flags) {
108         if (flags.contains("WEP")) {
109             return "wep";
110         } else if (flags.contains("OWE")) {
111             return "owe";
112         } else if (flags.contains("WPA2")) {
113             return "wpa2";
114         } else if (flags.contains("SAE")) {
115             return "wpa3";
116         } else {
117             return "open";
118         }
119     }
120 
121     /**
122      * Parse the 'wifi status' output and returns a map of info about connected wifi network.
123      *
124      * @param input Output of the 'wifi status' command to parse.
125      * @return a map of info about the connected network.
126      */
parseWifiInfo(String input)127     public static Map<String, String> parseWifiInfo(String input) {
128         Map<String, String> wifiInfo = new LinkedHashMap<>();
129 
130         Matcher ssidMatcher = SSID_PATTERN.matcher(input);
131         if (ssidMatcher.find()) {
132             wifiInfo.put("ssid", ssidMatcher.group(1));
133         }
134 
135         Matcher bssidMatcher = BSSID_PATTERN.matcher(input);
136         if (bssidMatcher.find()) {
137             wifiInfo.put("bssid", bssidMatcher.group(1));
138         }
139 
140         // TODO: also gather ip address, which is not availabled in the 'wifi status" output
141 
142         Matcher linkSpeedMatcher = LINK_SPEED_PATTERN.matcher(input);
143         if (linkSpeedMatcher.find()) {
144             wifiInfo.put("linkSpeed", linkSpeedMatcher.group(1));
145         }
146 
147         Matcher rssiMatcher = RSSI_PATTERN.matcher(input);
148         if (rssiMatcher.find()) {
149             wifiInfo.put("rssi", rssiMatcher.group(1));
150         }
151 
152         Matcher macAddressMatcher = MAC_ADDRESS_PATTERN.matcher(input);
153         if (macAddressMatcher.find()) {
154             wifiInfo.put("macAddress", macAddressMatcher.group(1));
155         }
156 
157         Matcher networkIdMatcher = NETWORK_ID_PATTERN.matcher(input);
158         if (networkIdMatcher.find()) {
159             wifiInfo.put("netId", networkIdMatcher.group(1));
160         }
161 
162         return wifiInfo;
163     }
164 }
165