/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wifi; import android.net.wifi.AnqpInformationElement; import android.net.wifi.ScanResult; import android.net.wifi.WifiSsid; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.Constants; import com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement; import com.android.server.wifi.hotspot2.anqp.RawByteElement; import com.android.server.wifi.hotspot2.anqp.VenueNameElement; import java.util.List; import java.util.Map; /** * Wifi scan result details. */ public class ScanDetail { private final ScanResult mScanResult; private volatile NetworkDetail mNetworkDetail; private long mSeen = 0; private byte[] mInformationElementRawData; private static final ScanResult.Builder sBuilder = new ScanResult.Builder(); /** * Main constructor used when converting from NativeScanResult */ public ScanDetail(@Nullable NetworkDetail networkDetail, @Nullable WifiSsid wifiSsid, @Nullable String bssid, @Nullable String caps, int level, int frequency, long tsf, @Nullable ScanResult.InformationElement[] informationElements, @Nullable List anqpLines, @Nullable byte[] informationElementRawData) { mNetworkDetail = networkDetail; long hessid = 0L; int anqpDomainId = ScanResult.UNSPECIFIED; byte[] osuProviders = null; int channelWidth = ScanResult.UNSPECIFIED; int centerFreq0 = ScanResult.UNSPECIFIED; int centerFreq1 = ScanResult.UNSPECIFIED; boolean isPasspoint = false; boolean is80211McResponder = false; boolean isTwtResponder = false; boolean is11azNtbResponder = false; if (networkDetail != null) { hessid = networkDetail.getHESSID(); anqpDomainId = networkDetail.getAnqpDomainID(); osuProviders = networkDetail.getOsuProviders(); channelWidth = networkDetail.getChannelWidth(); centerFreq0 = networkDetail.getCenterfreq0(); centerFreq1 = networkDetail.getCenterfreq1(); isPasspoint = caps.contains("EAP") && !caps.contains("SUITE_B_192") && networkDetail.isInterworking() && networkDetail.getHSRelease() != null; is80211McResponder = networkDetail.is80211McResponderSupport(); isTwtResponder = networkDetail.isIndividualTwtSupported(); is11azNtbResponder = networkDetail.is80211azNtbResponder(); } sBuilder.clear(); mScanResult = sBuilder .setWifiSsid(wifiSsid) .setBssid(bssid) .setHessid(hessid) .setAnqpDomainId(anqpDomainId) .setOsuProviders(osuProviders) .setCaps(caps) .setRssi(level) .setFrequency(frequency) .setTsf(tsf) .setIsTwtResponder(isTwtResponder) .setIs80211azNtbRTTResponder(is11azNtbResponder) .build(); mSeen = System.currentTimeMillis(); mScanResult.seen = mSeen; mScanResult.channelWidth = channelWidth; mScanResult.centerFreq0 = centerFreq0; mScanResult.centerFreq1 = centerFreq1; mScanResult.informationElements = informationElements; mScanResult.anqpLines = anqpLines; if (is80211McResponder) { mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER); } if (isPasspoint) { mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK); } mInformationElementRawData = informationElementRawData; } /** * Creates a ScanDetail without NetworkDetail for unit testing */ @VisibleForTesting public ScanDetail(@Nullable WifiSsid wifiSsid, @Nullable String bssid, String caps, int level, int frequency, long tsf, long seen) { this(null, wifiSsid, bssid, caps, level, frequency, tsf, null, null, null); mSeen = seen; mScanResult.seen = seen; } /** * Create a ScanDetail from a ScanResult */ public ScanDetail(@NonNull ScanResult scanResult) { mScanResult = scanResult; mNetworkDetail = new NetworkDetail( scanResult.BSSID, scanResult.informationElements, scanResult.anqpLines, scanResult.frequency); // Only inherit |mScanResult.seen| if it was previously set. This ensures that |mSeen| // will always contain a valid timestamp. mSeen = (mScanResult.seen == 0) ? System.currentTimeMillis() : mScanResult.seen; } /** * Copy constructor */ public ScanDetail(@NonNull ScanDetail scanDetail) { mScanResult = new ScanResult(scanDetail.mScanResult); mNetworkDetail = new NetworkDetail(scanDetail.mNetworkDetail); mSeen = scanDetail.mSeen; mInformationElementRawData = scanDetail.mInformationElementRawData; } /** * Store ANQ element information * * @param anqpElements Map */ public void propagateANQPInfo(Map anqpElements) { if (anqpElements.isEmpty()) { return; } mNetworkDetail = mNetworkDetail.complete(anqpElements); HSFriendlyNameElement fne = (HSFriendlyNameElement) anqpElements.get( Constants.ANQPElementType.HSFriendlyName); // !!! Match with language if (fne != null && !fne.getNames().isEmpty()) { mScanResult.venueName = fne.getNames().get(0).getText(); } else { VenueNameElement vne = (((VenueNameElement) anqpElements.get( Constants.ANQPElementType.ANQPVenueName))); if (vne != null && !vne.getNames().isEmpty()) { mScanResult.venueName = vne.getNames().get(0).getText(); } } RawByteElement osuProviders = (RawByteElement) anqpElements .get(Constants.ANQPElementType.HSOSUProviders); if (osuProviders != null) { mScanResult.anqpElements = new AnqpInformationElement[1]; mScanResult.anqpElements[0] = new AnqpInformationElement(AnqpInformationElement.HOTSPOT20_VENDOR_ID, AnqpInformationElement.HS_OSU_PROVIDERS, osuProviders.getPayload()); } } public ScanResult getScanResult() { return mScanResult; } public NetworkDetail getNetworkDetail() { return mNetworkDetail; } public String getSSID() { return mNetworkDetail == null ? mScanResult.SSID : mNetworkDetail.getSSID(); } public String getBSSIDString() { return mNetworkDetail == null ? mScanResult.BSSID : mNetworkDetail.getBSSIDString(); } /** * Return the network detail key string. */ public String toKeyString() { NetworkDetail networkDetail = mNetworkDetail; if (networkDetail != null) { return networkDetail.toKeyString(); } else { return "'" + mScanResult.SSID + "':" + mScanResult.BSSID; } } /** * Return the time this network was last seen. */ public long getSeen() { return mSeen; } /** * Update the time this network was last seen to the current system time. */ public long setSeen() { mSeen = System.currentTimeMillis(); mScanResult.seen = mSeen; return mSeen; } /** * Return the network information element raw data. */ public byte[] getInformationElementRawData() { return mInformationElementRawData; } @Override public String toString() { return "'" + mScanResult.SSID + "'/" + mScanResult.BSSID; } }