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