1 /*
2  * Copyright (C) 2008 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;
18 
19 import android.net.wifi.SupplicantState;
20 import android.net.wifi.WifiEnterpriseConfig;
21 import android.net.wifi.WifiManager;
22 import android.net.wifi.WifiSsid;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.util.ArraySet;
26 import android.util.Log;
27 import android.util.SparseArray;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.internal.util.Protocol;
31 import com.android.server.wifi.MboOceController.BtmFrameData;
32 import com.android.server.wifi.WifiCarrierInfoManager.SimAuthRequestData;
33 import com.android.server.wifi.hotspot2.AnqpEvent;
34 import com.android.server.wifi.hotspot2.IconEvent;
35 import com.android.server.wifi.hotspot2.WnmData;
36 
37 import java.util.HashMap;
38 import java.util.Map;
39 import java.util.Set;
40 
41 /**
42  * Listen for events from the wpa_supplicant & wificond and broadcast them on
43  * to the various {@link ClientModeImpl} modules interested in handling these events.
44  * @hide
45  */
46 public class WifiMonitor {
47     private static final String TAG = "WifiMonitor";
48 
49     /* Supplicant events reported to a state machine */
50     private static final int BASE = Protocol.BASE_WIFI_MONITOR;
51 
52     /* Connection to supplicant established */
53     public static final int SUP_CONNECTION_EVENT                 = BASE + 1;
54     /* Connection to supplicant lost */
55     public static final int SUP_DISCONNECTION_EVENT              = BASE + 2;
56    /* Network connection completed */
57     public static final int NETWORK_CONNECTION_EVENT             = BASE + 3;
58     /* Network disconnection completed */
59     public static final int NETWORK_DISCONNECTION_EVENT          = BASE + 4;
60     /* Scan results are available */
61     public static final int SCAN_RESULTS_EVENT                   = BASE + 5;
62     /* Supplicate state changed */
63     public static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 6;
64     /* Password failure and EAP authentication failure */
65     public static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 7;
66     /* WPS success detected */
67     public static final int WPS_SUCCESS_EVENT                    = BASE + 8;
68     /* WPS failure detected */
69     public static final int WPS_FAIL_EVENT                       = BASE + 9;
70      /* WPS overlap detected */
71     public static final int WPS_OVERLAP_EVENT                    = BASE + 10;
72      /* WPS timeout detected */
73     public static final int WPS_TIMEOUT_EVENT                    = BASE + 11;
74 
75     /* Request Identity */
76     public static final int SUP_REQUEST_IDENTITY                 = BASE + 15;
77 
78     /* Request SIM Auth */
79     public static final int SUP_REQUEST_SIM_AUTH                 = BASE + 16;
80 
81     public static final int SCAN_FAILED_EVENT                    = BASE + 17;
82     /* Pno scan results are available */
83     public static final int PNO_SCAN_RESULTS_EVENT               = BASE + 18;
84 
85 
86     /* Indicates assoc reject event */
87     public static final int ASSOCIATION_REJECTION_EVENT          = BASE + 43;
88     public static final int ANQP_DONE_EVENT                      = BASE + 44;
89     public static final int ASSOCIATED_BSSID_EVENT               = BASE + 45;
90     public static final int TARGET_BSSID_EVENT                   = BASE + 46;
91 
92     /* hotspot 2.0 ANQP events */
93     public static final int GAS_QUERY_START_EVENT                = BASE + 51;
94     public static final int GAS_QUERY_DONE_EVENT                 = BASE + 52;
95     public static final int RX_HS20_ANQP_ICON_EVENT              = BASE + 53;
96 
97     /* hotspot 2.0 events */
98     public static final int HS20_REMEDIATION_EVENT               = BASE + 61;
99 
100     /* MBO/OCE events */
101     public static final int MBO_OCE_BSS_TM_HANDLING_DONE         = BASE + 71;
102 
103     /* Fils network connection completed */
104     public static final int FILS_NETWORK_CONNECTION_EVENT        = BASE + 62;
105 
106     /* WPS config errrors */
107     private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
108     private static final int CONFIG_AUTH_FAILURE = 18;
109 
110     /* WPS error indications */
111     private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
112     private static final int REASON_WEP_PROHIBITED = 2;
113 
114     private final WifiInjector mWifiInjector;
115     private boolean mVerboseLoggingEnabled = false;
116     private boolean mConnected = false;
117 
WifiMonitor(WifiInjector wifiInjector)118     public WifiMonitor(WifiInjector wifiInjector) {
119         mWifiInjector = wifiInjector;
120     }
121 
enableVerboseLogging(int verbose)122     void enableVerboseLogging(int verbose) {
123         if (verbose > 0) {
124             mVerboseLoggingEnabled = true;
125         } else {
126             mVerboseLoggingEnabled = false;
127         }
128     }
129 
130     private final Map<String, SparseArray<Set<Handler>>> mHandlerMap = new HashMap<>();
registerHandler(String iface, int what, Handler handler)131     public synchronized void registerHandler(String iface, int what, Handler handler) {
132         SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
133         if (ifaceHandlers == null) {
134             ifaceHandlers = new SparseArray<>();
135             mHandlerMap.put(iface, ifaceHandlers);
136         }
137         Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
138         if (ifaceWhatHandlers == null) {
139             ifaceWhatHandlers = new ArraySet<>();
140             ifaceHandlers.put(what, ifaceWhatHandlers);
141         }
142         ifaceWhatHandlers.add(handler);
143     }
144 
145     /**
146      * Deregister the given |handler|
147      * @param iface
148      * @param what
149      * @param handler
150      */
deregisterHandler(String iface, int what, Handler handler)151     public synchronized void deregisterHandler(String iface, int what, Handler handler) {
152         SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
153         if (ifaceHandlers == null) {
154             return;
155         }
156         Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
157         if (ifaceWhatHandlers == null) {
158             return;
159         }
160         ifaceWhatHandlers.remove(handler);
161     }
162 
163     private final Map<String, Boolean> mMonitoringMap = new HashMap<>();
isMonitoring(String iface)164     private boolean isMonitoring(String iface) {
165         Boolean val = mMonitoringMap.get(iface);
166         if (val == null) {
167             return false;
168         } else {
169             return val.booleanValue();
170         }
171     }
172 
173     /**
174      * Enable/Disable monitoring for the provided iface.
175      *
176      * @param iface Name of the iface.
177      * @param enabled true to enable, false to disable.
178      */
179     @VisibleForTesting
setMonitoring(String iface, boolean enabled)180     public void setMonitoring(String iface, boolean enabled) {
181         mMonitoringMap.put(iface, enabled);
182     }
183 
setMonitoringNone()184     private void setMonitoringNone() {
185         for (String iface : mMonitoringMap.keySet()) {
186             setMonitoring(iface, false);
187         }
188     }
189 
190     /**
191      * Start Monitoring for wpa_supplicant events.
192      *
193      * @param iface Name of iface.
194      */
startMonitoring(String iface)195     public synchronized void startMonitoring(String iface) {
196         if (mVerboseLoggingEnabled) Log.d(TAG, "startMonitoring(" + iface + ")");
197         setMonitoring(iface, true);
198         broadcastSupplicantConnectionEvent(iface);
199     }
200 
201     /**
202      * Stop Monitoring for wpa_supplicant events.
203      *
204      * @param iface Name of iface.
205      */
stopMonitoring(String iface)206     public synchronized void stopMonitoring(String iface) {
207         if (mVerboseLoggingEnabled) Log.d(TAG, "stopMonitoring(" + iface + ")");
208         setMonitoring(iface, true);
209         broadcastSupplicantDisconnectionEvent(iface);
210         setMonitoring(iface, false);
211     }
212 
213     /**
214      * Stop Monitoring for wpa_supplicant events.
215      *
216      * TODO: Add unit tests for these once we remove the legacy code.
217      */
stopAllMonitoring()218     public synchronized void stopAllMonitoring() {
219         mConnected = false;
220         setMonitoringNone();
221     }
222 
223 
224     /**
225      * Similar functions to Handler#sendMessage that send the message to the registered handler
226      * for the given interface and message what.
227      * All of these should be called with the WifiMonitor class lock
228      */
sendMessage(String iface, int what)229     private void sendMessage(String iface, int what) {
230         sendMessage(iface, Message.obtain(null, what));
231     }
232 
sendMessage(String iface, int what, Object obj)233     private void sendMessage(String iface, int what, Object obj) {
234         sendMessage(iface, Message.obtain(null, what, obj));
235     }
236 
sendMessage(String iface, int what, int arg1)237     private void sendMessage(String iface, int what, int arg1) {
238         sendMessage(iface, Message.obtain(null, what, arg1, 0));
239     }
240 
sendMessage(String iface, int what, int arg1, int arg2)241     private void sendMessage(String iface, int what, int arg1, int arg2) {
242         sendMessage(iface, Message.obtain(null, what, arg1, arg2));
243     }
244 
sendMessage(String iface, int what, int arg1, int arg2, Object obj)245     private void sendMessage(String iface, int what, int arg1, int arg2, Object obj) {
246         sendMessage(iface, Message.obtain(null, what, arg1, arg2, obj));
247     }
248 
sendMessage(String iface, Message message)249     private void sendMessage(String iface, Message message) {
250         SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
251         if (iface != null && ifaceHandlers != null) {
252             if (isMonitoring(iface)) {
253                 Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);
254                 if (ifaceWhatHandlers != null) {
255                     for (Handler handler : ifaceWhatHandlers) {
256                         if (handler != null) {
257                             sendMessage(handler, Message.obtain(message));
258                         }
259                     }
260                 }
261             } else {
262                 if (mVerboseLoggingEnabled) {
263                     Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
264                 }
265             }
266         } else {
267             if (mVerboseLoggingEnabled) {
268                 Log.d(TAG, "Sending to all monitors because there's no matching iface");
269             }
270             for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
271                 if (isMonitoring(entry.getKey())) {
272                     Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);
273                     for (Handler handler : ifaceWhatHandlers) {
274                         if (handler != null) {
275                             sendMessage(handler, Message.obtain(message));
276                         }
277                     }
278                 }
279             }
280         }
281 
282         message.recycle();
283     }
284 
sendMessage(Handler handler, Message message)285     private void sendMessage(Handler handler, Message message) {
286         message.setTarget(handler);
287         message.sendToTarget();
288     }
289 
290     /**
291      * Broadcast the WPS fail event to all the handlers registered for this event.
292      *
293      * @param iface Name of iface on which this occurred.
294      * @param cfgError Configuration error code.
295      * @param vendorErrorCode Vendor specific error indication code.
296      */
broadcastWpsFailEvent(String iface, int cfgError, int vendorErrorCode)297     public void broadcastWpsFailEvent(String iface, int cfgError, int vendorErrorCode) {
298         int reason = 0;
299         switch(vendorErrorCode) {
300             case REASON_TKIP_ONLY_PROHIBITED:
301                 sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_TKIP_ONLY_PROHIBITED);
302                 return;
303             case REASON_WEP_PROHIBITED:
304                 sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_WEP_PROHIBITED);
305                 return;
306             default:
307                 reason = vendorErrorCode;
308                 break;
309         }
310         switch(cfgError) {
311             case CONFIG_AUTH_FAILURE:
312                 sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_AUTH_FAILURE);
313                 return;
314             case CONFIG_MULTIPLE_PBC_DETECTED:
315                 sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_OVERLAP_ERROR);
316                 return;
317             default:
318                 if (reason == 0) {
319                     reason = cfgError;
320                 }
321                 break;
322         }
323         //For all other errors, return a generic internal error
324         sendMessage(iface, WPS_FAIL_EVENT, WifiManager.ERROR, reason);
325     }
326 
327    /**
328     * Broadcast the WPS succes event to all the handlers registered for this event.
329     *
330     * @param iface Name of iface on which this occurred.
331     */
broadcastWpsSuccessEvent(String iface)332     public void broadcastWpsSuccessEvent(String iface) {
333         sendMessage(iface, WPS_SUCCESS_EVENT);
334     }
335 
336     /**
337      * Broadcast the WPS overlap event to all the handlers registered for this event.
338      *
339      * @param iface Name of iface on which this occurred.
340      */
broadcastWpsOverlapEvent(String iface)341     public void broadcastWpsOverlapEvent(String iface) {
342         sendMessage(iface, WPS_OVERLAP_EVENT);
343     }
344 
345     /**
346      * Broadcast the WPS timeout event to all the handlers registered for this event.
347      *
348      * @param iface Name of iface on which this occurred.
349      */
broadcastWpsTimeoutEvent(String iface)350     public void broadcastWpsTimeoutEvent(String iface) {
351         sendMessage(iface, WPS_TIMEOUT_EVENT);
352     }
353 
354     /**
355      * Broadcast the ANQP done event to all the handlers registered for this event.
356      *
357      * @param iface Name of iface on which this occurred.
358      * @param anqpEvent ANQP result retrieved.
359      */
broadcastAnqpDoneEvent(String iface, AnqpEvent anqpEvent)360     public void broadcastAnqpDoneEvent(String iface, AnqpEvent anqpEvent) {
361         sendMessage(iface, ANQP_DONE_EVENT, anqpEvent);
362     }
363 
364     /**
365      * Broadcast the Icon done event to all the handlers registered for this event.
366      *
367      * @param iface Name of iface on which this occurred.
368      * @param iconEvent Instance of IconEvent containing the icon data retrieved.
369      */
broadcastIconDoneEvent(String iface, IconEvent iconEvent)370     public void broadcastIconDoneEvent(String iface, IconEvent iconEvent) {
371         sendMessage(iface, RX_HS20_ANQP_ICON_EVENT, iconEvent);
372     }
373 
374     /**
375      * Broadcast the WNM event to all the handlers registered for this event.
376      *
377      * @param iface Name of iface on which this occurred.
378      * @param wnmData Instance of WnmData containing the event data.
379      */
broadcastWnmEvent(String iface, WnmData wnmData)380     public void broadcastWnmEvent(String iface, WnmData wnmData) {
381         sendMessage(iface, HS20_REMEDIATION_EVENT, wnmData);
382     }
383 
384     /**
385      * Broadcast the Network identity request event to all the handlers registered for this event.
386      *
387      * @param iface Name of iface on which this occurred.
388      * @param networkId ID of the network in wpa_supplicant.
389      * @param ssid SSID of the network.
390      */
broadcastNetworkIdentityRequestEvent(String iface, int networkId, String ssid)391     public void broadcastNetworkIdentityRequestEvent(String iface, int networkId, String ssid) {
392         sendMessage(iface, SUP_REQUEST_IDENTITY, 0, networkId, ssid);
393     }
394 
395     /**
396      * Broadcast the Network Gsm Sim auth request event to all the handlers registered for this
397      * event.
398      *
399      * @param iface Name of iface on which this occurred.
400      * @param networkId ID of the network in wpa_supplicant.
401      * @param ssid SSID of the network.
402      * @param data Accompanying event data.
403      */
broadcastNetworkGsmAuthRequestEvent(String iface, int networkId, String ssid, String[] data)404     public void broadcastNetworkGsmAuthRequestEvent(String iface, int networkId, String ssid,
405                                                     String[] data) {
406         sendMessage(iface, SUP_REQUEST_SIM_AUTH,
407                 new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.SIM, ssid, data));
408     }
409 
410     /**
411      * Broadcast the Network Umts Sim auth request event to all the handlers registered for this
412      * event.
413      *
414      * @param iface Name of iface on which this occurred.
415      * @param networkId ID of the network in wpa_supplicant.
416      * @param ssid SSID of the network.
417      * @param data Accompanying event data.
418      */
broadcastNetworkUmtsAuthRequestEvent(String iface, int networkId, String ssid, String[] data)419     public void broadcastNetworkUmtsAuthRequestEvent(String iface, int networkId, String ssid,
420                                                      String[] data) {
421         sendMessage(iface, SUP_REQUEST_SIM_AUTH,
422                 new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.AKA, ssid, data));
423     }
424 
425     /**
426      * Broadcast scan result event to all the handlers registered for this event.
427      * @param iface Name of iface on which this occurred.
428      */
broadcastScanResultEvent(String iface)429     public void broadcastScanResultEvent(String iface) {
430         sendMessage(iface, SCAN_RESULTS_EVENT);
431     }
432 
433     /**
434      * Broadcast pno scan result event to all the handlers registered for this event.
435      * @param iface Name of iface on which this occurred.
436      */
broadcastPnoScanResultEvent(String iface)437     public void broadcastPnoScanResultEvent(String iface) {
438         sendMessage(iface, PNO_SCAN_RESULTS_EVENT);
439     }
440 
441     /**
442      * Broadcast scan failed event to all the handlers registered for this event.
443      * @param iface Name of iface on which this occurred.
444      */
broadcastScanFailedEvent(String iface)445     public void broadcastScanFailedEvent(String iface) {
446         sendMessage(iface, SCAN_FAILED_EVENT);
447     }
448 
449     /**
450      * Broadcast the authentication failure event to all the handlers registered for this event.
451      *
452      * @param iface Name of iface on which this occurred.
453      * @param reason Reason for authentication failure. This has to be one of the
454      *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
455      *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
456      *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
457      *               {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
458      * @param errorCode Error code associated with the authentication failure event.
459      *               A value of -1 is used when no error code is reported.
460      */
broadcastAuthenticationFailureEvent(String iface, int reason, int errorCode)461     public void broadcastAuthenticationFailureEvent(String iface, int reason, int errorCode) {
462         sendMessage(iface, AUTHENTICATION_FAILURE_EVENT, reason, errorCode);
463     }
464 
465     /**
466      * Broadcast the association rejection event to all the handlers registered for this event.
467      *
468      * @param iface Name of iface on which this occurred.
469      * @param status Status code for association rejection.
470      * @param timedOut Indicates if the association timed out.
471      * @param bssid BSSID of the access point from which we received the reject.
472      */
broadcastAssociationRejectionEvent(String iface, int status, boolean timedOut, String bssid)473     public void broadcastAssociationRejectionEvent(String iface, int status, boolean timedOut,
474                                                    String bssid) {
475         sendMessage(iface, ASSOCIATION_REJECTION_EVENT, timedOut ? 1 : 0, status, bssid);
476     }
477 
478     /**
479      * Broadcast the association success event to all the handlers registered for this event.
480      *
481      * @param iface Name of iface on which this occurred.
482      * @param bssid BSSID of the access point.
483      */
broadcastAssociatedBssidEvent(String iface, String bssid)484     public void broadcastAssociatedBssidEvent(String iface, String bssid) {
485         sendMessage(iface, ASSOCIATED_BSSID_EVENT, 0, 0, bssid);
486     }
487 
488     /**
489      * Broadcast the start of association event to all the handlers registered for this event.
490      *
491      * @param iface Name of iface on which this occurred.
492      * @param bssid BSSID of the access point.
493      */
broadcastTargetBssidEvent(String iface, String bssid)494     public void broadcastTargetBssidEvent(String iface, String bssid) {
495         sendMessage(iface, TARGET_BSSID_EVENT, 0, 0, bssid);
496     }
497 
498     /**
499      * Broadcast the network connection event to all the handlers registered for this event.
500      *
501      * @param iface Name of iface on which this occurred.
502      * @param networkId ID of the network in wpa_supplicant.
503      * @param bssid BSSID of the access point.
504      */
broadcastNetworkConnectionEvent(String iface, int networkId, String bssid)505     public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
506         sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
507     }
508 
509     /**
510      * Broadcast the fils network connection event to all the handlers registered for this event.
511      *
512      * @param iface Name of iface on which this occurred.
513      * @param networkId ID of the network in wpa_supplicant.
514      * @param bssid BSSID of the access point.
515      */
broadcastFilsNetworkConnectionEvent(String iface, int networkId, String bssid)516     public void broadcastFilsNetworkConnectionEvent(String iface, int networkId, String bssid) {
517         sendMessage(iface, FILS_NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
518     }
519 
520     /**
521      * Broadcast the network disconnection event to all the handlers registered for this event.
522      *
523      * @param iface Name of iface on which this occurred.
524      * @param local Whether the disconnect was locally triggered.
525      * @param reason Disconnect reason code.
526      * @param bssid BSSID of the access point.
527      */
broadcastNetworkDisconnectionEvent(String iface, int local, int reason, String bssid)528     public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
529                                                    String bssid) {
530         sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
531     }
532 
533     /**
534      * Broadcast the supplicant state change event to all the handlers registered for this event.
535      *
536      * @param iface Name of iface on which this occurred.
537      * @param networkId ID of the network in wpa_supplicant.
538      * @param bssid BSSID of the access point.
539      * @param newSupplicantState New supplicant state.
540      */
broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid, String bssid, SupplicantState newSupplicantState)541     public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
542                                                     String bssid,
543                                                     SupplicantState newSupplicantState) {
544         sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
545                 new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
546     }
547 
548     /**
549      * Broadcast the connection to wpa_supplicant event to all the handlers registered for
550      * this event.
551      *
552      * @param iface Name of iface on which this occurred.
553      */
broadcastSupplicantConnectionEvent(String iface)554     public void broadcastSupplicantConnectionEvent(String iface) {
555         sendMessage(iface, SUP_CONNECTION_EVENT);
556     }
557 
558     /**
559      * Broadcast the loss of connection to wpa_supplicant event to all the handlers registered for
560      * this event.
561      *
562      * @param iface Name of iface on which this occurred.
563      */
broadcastSupplicantDisconnectionEvent(String iface)564     public void broadcastSupplicantDisconnectionEvent(String iface) {
565         sendMessage(iface, SUP_DISCONNECTION_EVENT);
566     }
567 
568     /**
569      * Broadcast the bss transition management frame handling event
570      * to all the handlers registered for this event.
571      *
572      * @param iface Name of iface on which this occurred.
573      */
broadcastBssTmHandlingDoneEvent(String iface, BtmFrameData btmFrmData)574     public void broadcastBssTmHandlingDoneEvent(String iface, BtmFrameData btmFrmData) {
575         sendMessage(iface, MBO_OCE_BSS_TM_HANDLING_DONE, btmFrmData);
576     }
577 }
578