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.Utils; 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 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 if (networkDetail != null) { 64 hessid = networkDetail.getHESSID(); 65 anqpDomainId = networkDetail.getAnqpDomainID(); 66 osuProviders = networkDetail.getOsuProviders(); 67 channelWidth = networkDetail.getChannelWidth(); 68 centerFreq0 = networkDetail.getCenterfreq0(); 69 centerFreq1 = networkDetail.getCenterfreq1(); 70 isPasspoint = caps.contains("EAP") 71 && networkDetail.isInterworking() && networkDetail.getHSRelease() != null; 72 is80211McResponder = networkDetail.is80211McResponderSupport(); 73 } 74 mScanResult = new ScanResult(wifiSsid, bssid, hessid, anqpDomainId, osuProviders, caps, 75 level, frequency, tsf); 76 mSeen = System.currentTimeMillis(); 77 mScanResult.seen = mSeen; 78 mScanResult.channelWidth = channelWidth; 79 mScanResult.centerFreq0 = centerFreq0; 80 mScanResult.centerFreq1 = centerFreq1; 81 mScanResult.informationElements = informationElements; 82 mScanResult.anqpLines = anqpLines; 83 if (is80211McResponder) { 84 mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER); 85 } 86 if (isPasspoint) { 87 mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); 88 } 89 mInformationElementRawData = informationElementRawData; 90 } 91 92 /** 93 * Creates a ScanDetail without NetworkDetail for unit testing 94 */ 95 @VisibleForTesting ScanDetail(@ullable WifiSsid wifiSsid, @Nullable String bssid, String caps, int level, int frequency, long tsf, long seen)96 public ScanDetail(@Nullable WifiSsid wifiSsid, @Nullable String bssid, String caps, int level, 97 int frequency, long tsf, long seen) { 98 this(null, wifiSsid, bssid, caps, level, frequency, tsf, null, null, null); 99 mSeen = seen; 100 mScanResult.seen = seen; 101 } 102 103 /** 104 * Create a ScanDetail from a ScanResult 105 */ ScanDetail(@onNull ScanResult scanResult)106 public ScanDetail(@NonNull ScanResult scanResult) { 107 mScanResult = scanResult; 108 mNetworkDetail = new NetworkDetail( 109 scanResult.BSSID, 110 scanResult.informationElements, 111 scanResult.anqpLines, 112 scanResult.frequency); 113 // Only inherit |mScanResult.seen| if it was previously set. This ensures that |mSeen| 114 // will always contain a valid timestamp. 115 mSeen = (mScanResult.seen == 0) ? System.currentTimeMillis() : mScanResult.seen; 116 } 117 118 /** 119 * Copy constructor 120 */ ScanDetail(@onNull ScanDetail scanDetail)121 public ScanDetail(@NonNull ScanDetail scanDetail) { 122 mScanResult = new ScanResult(scanDetail.mScanResult); 123 mNetworkDetail = new NetworkDetail(scanDetail.mNetworkDetail); 124 mSeen = scanDetail.mSeen; 125 mInformationElementRawData = scanDetail.mInformationElementRawData; 126 } 127 128 /** 129 * Store ANQ element information 130 * 131 * @param anqpElements Map<Constants.ANQPElementType, ANQPElement> 132 */ propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements)133 public void propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> anqpElements) { 134 if (anqpElements.isEmpty()) { 135 return; 136 } 137 mNetworkDetail = mNetworkDetail.complete(anqpElements); 138 HSFriendlyNameElement fne = (HSFriendlyNameElement) anqpElements.get( 139 Constants.ANQPElementType.HSFriendlyName); 140 // !!! Match with language 141 if (fne != null && !fne.getNames().isEmpty()) { 142 mScanResult.venueName = fne.getNames().get(0).getText(); 143 } else { 144 VenueNameElement vne = 145 (((VenueNameElement) anqpElements.get( 146 Constants.ANQPElementType.ANQPVenueName))); 147 if (vne != null && !vne.getNames().isEmpty()) { 148 mScanResult.venueName = vne.getNames().get(0).getText(); 149 } 150 } 151 RawByteElement osuProviders = (RawByteElement) anqpElements 152 .get(Constants.ANQPElementType.HSOSUProviders); 153 if (osuProviders != null) { 154 mScanResult.anqpElements = new AnqpInformationElement[1]; 155 mScanResult.anqpElements[0] = 156 new AnqpInformationElement(AnqpInformationElement.HOTSPOT20_VENDOR_ID, 157 AnqpInformationElement.HS_OSU_PROVIDERS, osuProviders.getPayload()); 158 } 159 } 160 getScanResult()161 public ScanResult getScanResult() { 162 return mScanResult; 163 } 164 getNetworkDetail()165 public NetworkDetail getNetworkDetail() { 166 return mNetworkDetail; 167 } 168 getSSID()169 public String getSSID() { 170 return mNetworkDetail == null ? mScanResult.SSID : mNetworkDetail.getSSID(); 171 } 172 getBSSIDString()173 public String getBSSIDString() { 174 return mNetworkDetail == null ? mScanResult.BSSID : mNetworkDetail.getBSSIDString(); 175 } 176 177 /** 178 * Return the network detail key string. 179 */ toKeyString()180 public String toKeyString() { 181 NetworkDetail networkDetail = mNetworkDetail; 182 if (networkDetail != null) { 183 return networkDetail.toKeyString(); 184 } else { 185 return "'" + mScanResult.BSSID + "':" + Utils.macToSimpleString( 186 Utils.parseMac(mScanResult.BSSID)); 187 } 188 } 189 190 /** 191 * Return the time this network was last seen. 192 */ getSeen()193 public long getSeen() { 194 return mSeen; 195 } 196 197 /** 198 * Update the time this network was last seen to the current system time. 199 */ setSeen()200 public long setSeen() { 201 mSeen = System.currentTimeMillis(); 202 mScanResult.seen = mSeen; 203 return mSeen; 204 } 205 206 /** 207 * Return the network information element raw data. 208 */ getInformationElementRawData()209 public byte[] getInformationElementRawData() { 210 return mInformationElementRawData; 211 } 212 213 @Override toString()214 public String toString() { 215 try { 216 return "'" + mScanResult.BSSID + "'/" + Utils.macToSimpleString( 217 Utils.parseMac(mScanResult.BSSID)); 218 } catch (IllegalArgumentException iae) { 219 return "'" + mScanResult.BSSID + "'/----"; 220 } 221 } 222 } 223