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