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
17package com.android.server.wifi.hotspot2;
18
19import android.util.Log;
20import android.util.Pair;
21
22import com.android.server.wifi.WifiNative;
23import com.android.server.wifi.hotspot2.anqp.ANQPElement;
24import com.android.server.wifi.hotspot2.anqp.Constants;
25
26import java.util.HashSet;
27import java.util.List;
28import java.util.Map;
29import 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 */
36public class PasspointEventHandler {
37    private final WifiNative mSupplicantHook;
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         */
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         */
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         */
66        void onWnmFrameReceived(WnmData data);
67    }
68
69    public PasspointEventHandler(WifiNative supplicantHook, Callbacks callbacks) {
70        mSupplicantHook = supplicantHook;
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     */
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 (!mSupplicantHook.requestAnqp(
84                mSupplicantHook.getClientInterfaceName(),
85                Utils.macToString(bssid), querySets.first, querySets.second)) {
86            Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " + Utils.macToString(bssid));
87            return false;
88        }
89        Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + Utils.macToString(bssid));
90        return true;
91    }
92
93    /**
94     * Request a passpoint icon file |filename| from the specified AP |bssid|.
95     * @param bssid BSSID of the AP
96     * @param fileName name of the icon file
97     * @return true if request is sent successfully, false otherwise
98     */
99    public boolean requestIcon(long bssid, String fileName) {
100        if (bssid == 0 || fileName == null) return false;
101        return mSupplicantHook.requestIcon(
102                mSupplicantHook.getClientInterfaceName(), Utils.macToString(bssid), fileName);
103    }
104
105    /**
106     * Invoked when ANQP query is completed.
107     * TODO(zqiu): currently ANQP completion notification is through WifiMonitor,
108     * this shouldn't be needed once we switch over to wificond for ANQP requests.
109     * @param anqpEvent ANQP result data retrieved. ANQP elements could be empty in the event to
110     *                  indicate any failures.
111     */
112    public void notifyANQPDone(AnqpEvent anqpEvent) {
113        if (anqpEvent == null) return;
114        mCallbacks.onANQPResponse(anqpEvent.getBssid(), anqpEvent.getElements());
115    }
116
117    /**
118     * Invoked when icon query is completed.
119     * TODO(zqiu): currently icon completion notification is through WifiMonitor,
120     * this shouldn't be needed once we switch over to wificond for icon requests.
121     * @param iconEvent icon event data
122     */
123    public void notifyIconDone(IconEvent iconEvent) {
124        if (iconEvent == null) return;
125        mCallbacks.onIconResponse(
126                iconEvent.getBSSID(), iconEvent.getFileName(), iconEvent.getData());
127    }
128
129    /**
130     * Invoked when a Wireless Network Management (WNM) frame is received.
131     * TODO(zqiu): currently WNM frame notification is through WifiMonitor,
132     * this shouldn't be needed once we switch over to wificond for WNM frame monitoring.
133     * @param data WNM frame data
134     */
135    public void notifyWnmFrameReceived(WnmData data) {
136        mCallbacks.onWnmFrameReceived(data);
137    }
138
139    /**
140     * Create the set of ANQP ID's to query.
141     *
142     * @param querySet elements to query
143     * @return Pair of <set of ANQP ID's, set of HS20 subtypes>
144     */
145    private static Pair<Set<Integer>, Set<Integer>> buildAnqpIdSet(
146            List<Constants.ANQPElementType> querySet) {
147        Set<Integer> anqpIds = new HashSet<>();
148        Set<Integer> hs20Subtypes = new HashSet<>();
149        for (Constants.ANQPElementType elementType : querySet) {
150            Integer id = Constants.getANQPElementID(elementType);
151            if (id != null) {
152                anqpIds.add(id);
153            } else {
154                id = Constants.getHS20ElementID(elementType);
155                hs20Subtypes.add(id);
156            }
157        }
158        return Pair.create(anqpIds, hs20Subtypes);
159    }
160
161}
162