1 /*
2  * Copyright (C) 2016 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.hotspot2;
18 
19 import android.util.Log;
20 import android.util.Pair;
21 
22 import com.android.server.wifi.WifiInjector;
23 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
24 import com.android.server.wifi.hotspot2.anqp.Constants;
25 
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 
31 /**
32  * This class handles passpoint specific interactions with the AP, such as ANQP
33  * elements requests, passpoint icon requests, and wireless network management
34  * event notifications.
35  */
36 public class PasspointEventHandler {
37     private final WifiInjector mWifiInjector;
38     private final Callbacks mCallbacks;
39 
40     /**
41      * Interface to be implemented by the client to receive callbacks for passpoint
42      * related events.
43      */
44     public interface Callbacks {
45         /**
46          * Invoked on received of ANQP response. |anqpElements| will be null on failure.
47          * @param bssid BSSID of the AP
48          * @param anqpElements ANQP elements to be queried
49          */
onANQPResponse(long bssid, Map<Constants.ANQPElementType, ANQPElement> anqpElements)50         void onANQPResponse(long bssid,
51                             Map<Constants.ANQPElementType, ANQPElement> anqpElements);
52 
53         /**
54          * Invoked on received of icon response. |filename| and |data| will be null
55          * on failure.
56          * @param bssid BSSID of the AP
57          * @param filename Name of the icon file
58          * @data icon data bytes
59          */
onIconResponse(long bssid, String filename, byte[] data)60         void onIconResponse(long bssid, String filename, byte[] data);
61 
62         /**
63          * Invoked on received of Hotspot 2.0 Wireless Network Management frame.
64          * @param data Wireless Network Management frame data
65          */
onWnmFrameReceived(WnmData data)66         void onWnmFrameReceived(WnmData data);
67     }
68 
PasspointEventHandler(WifiInjector wifiInjector, Callbacks callbacks)69     public PasspointEventHandler(WifiInjector wifiInjector, Callbacks callbacks) {
70         mWifiInjector = wifiInjector;
71         mCallbacks = callbacks;
72     }
73 
74     /**
75      * Request the specified ANQP elements |elements| from the specified AP |bssid|.
76      * @param bssid BSSID of the AP
77      * @param elements ANQP elements to be queried
78      * @return true if request is sent successfully, false otherwise.
79      */
requestANQP(long bssid, List<Constants.ANQPElementType> elements)80     public boolean requestANQP(long bssid, List<Constants.ANQPElementType> elements) {
81         Pair<Set<Integer>, Set<Integer>> querySets = buildAnqpIdSet(elements);
82         if (bssid == 0 || querySets == null) return false;
83         if (!mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager().requestAnqp(
84                 Utils.macToString(bssid), querySets.first, querySets.second)) {
85             Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " + Utils.macToString(bssid));
86             return false;
87         }
88         Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + Utils.macToString(bssid));
89         return true;
90     }
91 
92     /**
93      * Request the Venue URL ANQP element from the specified AP |bssid|.
94      * @param bssid BSSID of the AP
95      * @return true if request is sent successfully, false otherwise
96      */
requestVenueUrlAnqp(long bssid)97     public boolean requestVenueUrlAnqp(long bssid) {
98         if (bssid == 0) return false;
99         return mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager()
100                 .requestVenueUrlAnqp(Utils.macToString(bssid));
101     }
102 
103     /**
104      * Request a passpoint icon file |filename| from the specified AP |bssid|.
105      * @param bssid BSSID of the AP
106      * @param fileName name of the icon file
107      * @return true if request is sent successfully, false otherwise
108      */
requestIcon(long bssid, String fileName)109     public boolean requestIcon(long bssid, String fileName) {
110         if (bssid == 0 || fileName == null) return false;
111         return mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager()
112                 .requestIcon(Utils.macToString(bssid), fileName);
113     }
114 
115     /**
116      * Invoked when ANQP query is completed.
117      * TODO(zqiu): currently ANQP completion notification is through WifiMonitor,
118      * this shouldn't be needed once we switch over to wificond for ANQP requests.
119      * @param anqpEvent ANQP result data retrieved. ANQP elements could be empty in the event to
120      *                  indicate any failures.
121      */
notifyANQPDone(AnqpEvent anqpEvent)122     public void notifyANQPDone(AnqpEvent anqpEvent) {
123         if (anqpEvent == null) return;
124         mCallbacks.onANQPResponse(anqpEvent.getBssid(), anqpEvent.getElements());
125     }
126 
127     /**
128      * Invoked when icon query is completed.
129      * TODO(zqiu): currently icon completion notification is through WifiMonitor,
130      * this shouldn't be needed once we switch over to wificond for icon requests.
131      * @param iconEvent icon event data
132      */
notifyIconDone(IconEvent iconEvent)133     public void notifyIconDone(IconEvent iconEvent) {
134         if (iconEvent == null) return;
135         mCallbacks.onIconResponse(
136                 iconEvent.getBSSID(), iconEvent.getFileName(), iconEvent.getData());
137     }
138 
139     /**
140      * Invoked when a Wireless Network Management (WNM) frame is received.
141      *
142      * @param data WNM frame data
143      */
notifyWnmFrameReceived(WnmData data)144     public void notifyWnmFrameReceived(WnmData data) {
145         mCallbacks.onWnmFrameReceived(data);
146     }
147 
148     /**
149      * Create the set of ANQP ID's to query.
150      *
151      * @param querySet elements to query
152      * @return Pair of <set of ANQP ID's, set of HS20 subtypes>
153      */
buildAnqpIdSet( List<Constants.ANQPElementType> querySet)154     private static Pair<Set<Integer>, Set<Integer>> buildAnqpIdSet(
155             List<Constants.ANQPElementType> querySet) {
156         Set<Integer> anqpIds = new HashSet<>();
157         Set<Integer> hs20Subtypes = new HashSet<>();
158         for (Constants.ANQPElementType elementType : querySet) {
159             Integer id = Constants.getANQPElementID(elementType);
160             if (id != null) {
161                 anqpIds.add(id);
162             } else {
163                 id = Constants.getHS20ElementID(elementType);
164                 hs20Subtypes.add(id);
165             }
166         }
167         return Pair.create(anqpIds, hs20Subtypes);
168     }
169 
170 }
171