1 /*
2  * Copyright (C) 2015 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.server.wifi;
18 
19 import android.net.wifi.AnqpInformationElement;
20 import android.net.wifi.ScanResult;
21 import android.net.wifi.WifiSsid;
22 
23 import androidx.annotation.NonNull;
24 import androidx.annotation.Nullable;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.server.wifi.hotspot2.NetworkDetail;
28 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
29 import com.android.server.wifi.hotspot2.anqp.Constants;
30 import com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement;
31 import com.android.server.wifi.hotspot2.anqp.RawByteElement;
32 import com.android.server.wifi.hotspot2.anqp.VenueNameElement;
33 
34 import java.util.List;
35 import java.util.Map;
36 
37 /**
38  * Wifi scan result details.
39  */
40 public class ScanDetail {
41     private final ScanResult mScanResult;
42     private volatile NetworkDetail mNetworkDetail;
43     private long mSeen = 0;
44     private byte[] mInformationElementRawData;
45     private static final ScanResult.Builder sBuilder = new ScanResult.Builder();
46 
47     /**
48      * Main constructor used when converting from NativeScanResult
49      */
ScanDetail(@ullable NetworkDetail networkDetail, @Nullable WifiSsid wifiSsid, @Nullable String bssid, @Nullable String caps, int level, int frequency, long tsf, @Nullable ScanResult.InformationElement[] informationElements, @Nullable List<String> anqpLines, @Nullable byte[] informationElementRawData)50     public ScanDetail(@Nullable NetworkDetail networkDetail, @Nullable WifiSsid wifiSsid,
51             @Nullable String bssid, @Nullable String caps, int level, int frequency, long tsf,
52             @Nullable ScanResult.InformationElement[] informationElements,
53             @Nullable List<String> anqpLines, @Nullable byte[] informationElementRawData) {
54         mNetworkDetail = networkDetail;
55         long hessid = 0L;
56         int anqpDomainId = ScanResult.UNSPECIFIED;
57         byte[] osuProviders = null;
58         int channelWidth = ScanResult.UNSPECIFIED;
59         int centerFreq0 = ScanResult.UNSPECIFIED;
60         int centerFreq1 = ScanResult.UNSPECIFIED;
61         boolean isPasspoint = false;
62         boolean is80211McResponder = false;
63         boolean isTwtResponder = false;
64         boolean is11azNtbResponder = false;
65         if (networkDetail != null) {
66             hessid = networkDetail.getHESSID();
67             anqpDomainId = networkDetail.getAnqpDomainID();
68             osuProviders = networkDetail.getOsuProviders();
69             channelWidth = networkDetail.getChannelWidth();
70             centerFreq0 = networkDetail.getCenterfreq0();
71             centerFreq1 = networkDetail.getCenterfreq1();
72             isPasspoint =
73                     caps.contains("EAP")
74                             && !caps.contains("SUITE_B_192")
75                             && networkDetail.isInterworking()
76                             && networkDetail.getHSRelease() != null;
77             is80211McResponder = networkDetail.is80211McResponderSupport();
78             isTwtResponder = networkDetail.isIndividualTwtSupported();
79             is11azNtbResponder = networkDetail.is80211azNtbResponder();
80         }
81         sBuilder.clear();
82         mScanResult = sBuilder
83                 .setWifiSsid(wifiSsid)
84                 .setBssid(bssid)
85                 .setHessid(hessid)
86                 .setAnqpDomainId(anqpDomainId)
87                 .setOsuProviders(osuProviders)
88                 .setCaps(caps)
89                 .setRssi(level)
90                 .setFrequency(frequency)
91                 .setTsf(tsf)
92                 .setIsTwtResponder(isTwtResponder)
93                 .setIs80211azNtbRTTResponder(is11azNtbResponder)
94                 .build();
95         mSeen = System.currentTimeMillis();
96         mScanResult.seen = mSeen;
97         mScanResult.channelWidth = channelWidth;
98         mScanResult.centerFreq0 = centerFreq0;
99         mScanResult.centerFreq1 = centerFreq1;
100         mScanResult.informationElements = informationElements;
101         mScanResult.anqpLines = anqpLines;
102         if (is80211McResponder) {
103             mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
104         }
105         if (isPasspoint) {
106             mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
107         }
108         mInformationElementRawData = informationElementRawData;
109     }
110 
111     /**
112      * Creates a ScanDetail without NetworkDetail for unit testing
113      */
114     @VisibleForTesting
ScanDetail(@ullable WifiSsid wifiSsid, @Nullable String bssid, String caps, int level, int frequency, long tsf, long seen)115     public ScanDetail(@Nullable WifiSsid wifiSsid, @Nullable String bssid, String caps, int level,
116             int frequency, long tsf, long seen) {
117         this(null, wifiSsid, bssid, caps, level, frequency, tsf, null, null, null);
118         mSeen = seen;
119         mScanResult.seen = seen;
120     }
121 
122     /**
123      * Create a ScanDetail from a ScanResult
124      */
ScanDetail(@onNull ScanResult scanResult)125     public ScanDetail(@NonNull ScanResult scanResult) {
126         mScanResult = scanResult;
127         mNetworkDetail = new NetworkDetail(
128                 scanResult.BSSID,
129                 scanResult.informationElements,
130                 scanResult.anqpLines,
131                 scanResult.frequency);
132         // Only inherit |mScanResult.seen| if it was previously set. This ensures that |mSeen|
133         // will always contain a valid timestamp.
134         mSeen = (mScanResult.seen == 0) ? System.currentTimeMillis() : mScanResult.seen;
135     }
136 
137     /**
138      * Copy constructor
139      */
ScanDetail(@onNull ScanDetail scanDetail)140     public ScanDetail(@NonNull ScanDetail scanDetail) {
141         mScanResult = new ScanResult(scanDetail.mScanResult);
142         mNetworkDetail = new NetworkDetail(scanDetail.mNetworkDetail);
143         mSeen = scanDetail.mSeen;
144         mInformationElementRawData = scanDetail.mInformationElementRawData;
145     }
146 
147     /**
148      * Store ANQ element information
149      *
150      * @param anqpElements Map<Constants.ANQPElementType, ANQPElement>
151      */
propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements)152     public void propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
153         if (anqpElements.isEmpty()) {
154             return;
155         }
156         mNetworkDetail = mNetworkDetail.complete(anqpElements);
157         HSFriendlyNameElement fne = (HSFriendlyNameElement) anqpElements.get(
158                 Constants.ANQPElementType.HSFriendlyName);
159         // !!! Match with language
160         if (fne != null && !fne.getNames().isEmpty()) {
161             mScanResult.venueName = fne.getNames().get(0).getText();
162         } else {
163             VenueNameElement vne =
164                     (((VenueNameElement) anqpElements.get(
165                             Constants.ANQPElementType.ANQPVenueName)));
166             if (vne != null && !vne.getNames().isEmpty()) {
167                 mScanResult.venueName = vne.getNames().get(0).getText();
168             }
169         }
170         RawByteElement osuProviders = (RawByteElement) anqpElements
171                 .get(Constants.ANQPElementType.HSOSUProviders);
172         if (osuProviders != null) {
173             mScanResult.anqpElements = new AnqpInformationElement[1];
174             mScanResult.anqpElements[0] =
175                     new AnqpInformationElement(AnqpInformationElement.HOTSPOT20_VENDOR_ID,
176                             AnqpInformationElement.HS_OSU_PROVIDERS, osuProviders.getPayload());
177         }
178     }
179 
getScanResult()180     public ScanResult getScanResult() {
181         return mScanResult;
182     }
183 
getNetworkDetail()184     public NetworkDetail getNetworkDetail() {
185         return mNetworkDetail;
186     }
187 
getSSID()188     public String getSSID() {
189         return mNetworkDetail == null ? mScanResult.SSID : mNetworkDetail.getSSID();
190     }
191 
getBSSIDString()192     public String getBSSIDString() {
193         return  mNetworkDetail == null ? mScanResult.BSSID : mNetworkDetail.getBSSIDString();
194     }
195 
196     /**
197      *  Return the network detail key string.
198      */
toKeyString()199     public String toKeyString() {
200         NetworkDetail networkDetail = mNetworkDetail;
201         if (networkDetail != null) {
202             return networkDetail.toKeyString();
203         } else {
204             return "'" + mScanResult.SSID + "':" + mScanResult.BSSID;
205         }
206     }
207 
208     /**
209      * Return the time this network was last seen.
210      */
getSeen()211     public long getSeen() {
212         return mSeen;
213     }
214 
215     /**
216      * Update the time this network was last seen to the current system time.
217      */
setSeen()218     public long setSeen() {
219         mSeen = System.currentTimeMillis();
220         mScanResult.seen = mSeen;
221         return mSeen;
222     }
223 
224     /**
225      * Return the network information element raw data.
226      */
getInformationElementRawData()227     public byte[] getInformationElementRawData() {
228         return mInformationElementRawData;
229     }
230 
231     @Override
toString()232     public String toString() {
233         return "'" + mScanResult.SSID + "'/" + mScanResult.BSSID;
234     }
235 }
236