content: /*
   * 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 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 com.android.server.wifi.hotspot2.NetworkDetail;
  import com.android.server.wifi.hotspot2.Utils;
  
  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;
  
      public ScanDetail(NetworkDetail networkDetail, WifiSsid wifiSsid, String bssid,
              String caps, int level, int frequency, long tsf,
              ScanResult.InformationElement[] informationElements, List<String> anqpLines) {
          mNetworkDetail = networkDetail;
          mScanResult = new ScanResult(wifiSsid, bssid, networkDetail.getHESSID(),
                  networkDetail.getAnqpDomainID(), networkDetail.getOsuProviders(),
                  caps, level, frequency, tsf);
          mSeen = System.currentTimeMillis();
          mScanResult.seen = mSeen;
          mScanResult.channelWidth = networkDetail.getChannelWidth();
          mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
          mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
          mScanResult.informationElements = informationElements;
          mScanResult.anqpLines = anqpLines;
          if (networkDetail.is80211McResponderSupport()) {
              mScanResult.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
          }
          if (networkDetail.isInterworking()) {
              mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
          }
      }
  
      public ScanDetail(WifiSsid wifiSsid, String bssid, String caps, int level, int frequency,
                        long tsf, long seen) {
          mNetworkDetail = null;
          mScanResult = new ScanResult(wifiSsid, bssid, 0L, -1, null, caps, level, frequency, tsf);
          mSeen = seen;
          mScanResult.seen = mSeen;
          mScanResult.channelWidth = 0;
          mScanResult.centerFreq0 = 0;
          mScanResult.centerFreq1 = 0;
          mScanResult.flags = 0;
      }
  
      public ScanDetail(ScanResult scanResult, NetworkDetail networkDetail) {
          mScanResult = scanResult;
          mNetworkDetail = networkDetail;
          // 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;
      }
  
      /**
       * Store ANQ element information
       *
       * @param anqpElements Map<Constants.ANQPElementType, ANQPElement>
       */
      public void propagateANQPInfo(Map<Constants.ANQPElementType, ANQPElement> 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 String.format("'%s':%012x",
                                   mScanResult.BSSID,
                                   Utils.parseMac(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;
      }
  
      @Override
      public String toString() {
          try {
              return String.format("'%s'/%012x",
                                   mScanResult.SSID,
                                   Utils.parseMac(mScanResult.BSSID));
          } catch (IllegalArgumentException iae) {
              return String.format("'%s'/----", mScanResult.BSSID);
          }
      }
  }
  