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