content: /*
   * Copyright (C) 2016 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.hotspot2;
  
  import android.util.Log;
  import android.util.Pair;
  
  import com.android.server.wifi.WifiNative;
  import com.android.server.wifi.hotspot2.anqp.ANQPElement;
  import com.android.server.wifi.hotspot2.anqp.Constants;
  
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * This class handles passpoint specific interactions with the AP, such as ANQP
   * elements requests, passpoint icon requests, and wireless network management
   * event notifications.
   */
  public class PasspointEventHandler {
      private final WifiNative mSupplicantHook;
      private final Callbacks mCallbacks;
  
      /**
       * Interface to be implemented by the client to receive callbacks for passpoint
       * related events.
       */
      public interface Callbacks {
          /**
           * Invoked on received of ANQP response. |anqpElements| will be null on failure.
           * @param bssid BSSID of the AP
           * @param anqpElements ANQP elements to be queried
           */
          void onANQPResponse(long bssid,
                              Map<Constants.ANQPElementType, ANQPElement> anqpElements);
  
          /**
           * Invoked on received of icon response. |filename| and |data| will be null
           * on failure.
           * @param bssid BSSID of the AP
           * @param filename Name of the icon file
           * @data icon data bytes
           */
          void onIconResponse(long bssid, String filename, byte[] data);
  
          /**
           * Invoked on received of Hotspot 2.0 Wireless Network Management frame.
           * @param data Wireless Network Management frame data
           */
          void onWnmFrameReceived(WnmData data);
      }
  
      public PasspointEventHandler(WifiNative supplicantHook, Callbacks callbacks) {
          mSupplicantHook = supplicantHook;
          mCallbacks = callbacks;
      }
  
      /**
       * Request the specified ANQP elements |elements| from the specified AP |bssid|.
       * @param bssid BSSID of the AP
       * @param elements ANQP elements to be queried
       * @return true if request is sent successfully, false otherwise.
       */
      public boolean requestANQP(long bssid, List<Constants.ANQPElementType> elements) {
          Pair<Set<Integer>, Set<Integer>> querySets = buildAnqpIdSet(elements);
          if (bssid == 0 || querySets == null) return false;
          if (!mSupplicantHook.requestAnqp(
                  mSupplicantHook.getClientInterfaceName(),
                  Utils.macToString(bssid), querySets.first, querySets.second)) {
              Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " + Utils.macToString(bssid));
              return false;
          }
          Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + Utils.macToString(bssid));
          return true;
      }
  
      /**
       * Request a passpoint icon file |filename| from the specified AP |bssid|.
       * @param bssid BSSID of the AP
       * @param fileName name of the icon file
       * @return true if request is sent successfully, false otherwise
       */
      public boolean requestIcon(long bssid, String fileName) {
          if (bssid == 0 || fileName == null) return false;
          return mSupplicantHook.requestIcon(
                  mSupplicantHook.getClientInterfaceName(), Utils.macToString(bssid), fileName);
      }
  
      /**
       * Invoked when ANQP query is completed.
       * TODO(zqiu): currently ANQP completion notification is through WifiMonitor,
       * this shouldn't be needed once we switch over to wificond for ANQP requests.
       * @param anqpEvent ANQP result data retrieved. ANQP elements could be empty in the event to
       *                  indicate any failures.
       */
      public void notifyANQPDone(AnqpEvent anqpEvent) {
          if (anqpEvent == null) return;
          mCallbacks.onANQPResponse(anqpEvent.getBssid(), anqpEvent.getElements());
      }
  
      /**
       * Invoked when icon query is completed.
       * TODO(zqiu): currently icon completion notification is through WifiMonitor,
       * this shouldn't be needed once we switch over to wificond for icon requests.
       * @param iconEvent icon event data
       */
      public void notifyIconDone(IconEvent iconEvent) {
          if (iconEvent == null) return;
          mCallbacks.onIconResponse(
                  iconEvent.getBSSID(), iconEvent.getFileName(), iconEvent.getData());
      }
  
      /**
       * Invoked when a Wireless Network Management (WNM) frame is received.
       * TODO(zqiu): currently WNM frame notification is through WifiMonitor,
       * this shouldn't be needed once we switch over to wificond for WNM frame monitoring.
       * @param data WNM frame data
       */
      public void notifyWnmFrameReceived(WnmData data) {
          mCallbacks.onWnmFrameReceived(data);
      }
  
      /**
       * Create the set of ANQP ID's to query.
       *
       * @param querySet elements to query
       * @return Pair of <set of ANQP ID's, set of HS20 subtypes>
       */
      private static Pair<Set<Integer>, Set<Integer>> buildAnqpIdSet(
              List<Constants.ANQPElementType> querySet) {
          Set<Integer> anqpIds = new HashSet<>();
          Set<Integer> hs20Subtypes = new HashSet<>();
          for (Constants.ANQPElementType elementType : querySet) {
              Integer id = Constants.getANQPElementID(elementType);
              if (id != null) {
                  anqpIds.add(id);
              } else {
                  id = Constants.getHS20ElementID(elementType);
                  hs20Subtypes.add(id);
              }
          }
          return Pair.create(anqpIds, hs20Subtypes);
      }
  
  }
  