1 /*
2  * Copyright (C) 2019 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 package com.android.server.wifi;
17 
18 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQP3GPPNetwork;
19 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPDomName;
20 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPIPAddrAvailability;
21 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPNAIRealm;
22 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPRoamingConsortium;
23 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPVenueName;
24 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPVenueUrl;
25 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSConnCapability;
26 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSFriendlyName;
27 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSOSUProviders;
28 import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSWANMetrics;
29 
30 import android.annotation.NonNull;
31 import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
32 import android.net.wifi.SecurityParams;
33 import android.net.wifi.SupplicantState;
34 import android.net.wifi.WifiConfiguration;
35 import android.net.wifi.WifiManager;
36 import android.net.wifi.WifiSsid;
37 import android.util.Log;
38 
39 import com.android.server.wifi.hotspot2.AnqpEvent;
40 import com.android.server.wifi.hotspot2.IconEvent;
41 import com.android.server.wifi.hotspot2.WnmData;
42 import com.android.server.wifi.hotspot2.anqp.ANQPElement;
43 import com.android.server.wifi.hotspot2.anqp.ANQPParser;
44 import com.android.server.wifi.hotspot2.anqp.Constants;
45 import com.android.server.wifi.util.NativeUtil;
46 
47 import java.io.IOException;
48 import java.nio.BufferUnderflowException;
49 import java.nio.ByteBuffer;
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.Map;
53 
54 abstract class SupplicantStaIfaceCallbackImpl extends ISupplicantStaIfaceCallback.Stub {
55     private static final String TAG = SupplicantStaIfaceCallbackImpl.class.getSimpleName();
56     private final SupplicantStaIfaceHal mStaIfaceHal;
57     private final String mIfaceName;
58     private final Object mLock;
59     private final WifiMonitor mWifiMonitor;
60     // Used to help check for PSK password mismatch & EAP connection failure.
61     private int mStateBeforeDisconnect = State.INACTIVE;
62     private String mCurrentSsid = null;
63 
SupplicantStaIfaceCallbackImpl(@onNull SupplicantStaIfaceHal staIfaceHal, @NonNull String ifaceName, @NonNull Object lock, @NonNull WifiMonitor wifiMonitor)64     SupplicantStaIfaceCallbackImpl(@NonNull SupplicantStaIfaceHal staIfaceHal,
65             @NonNull String ifaceName,
66             @NonNull Object lock,
67             @NonNull WifiMonitor wifiMonitor) {
68         mStaIfaceHal = staIfaceHal;
69         mIfaceName = ifaceName;
70         mLock = lock;
71         mWifiMonitor = wifiMonitor;
72     }
73 
74     /**
75      * Converts the supplicant state received from HIDL to the equivalent framework state.
76      */
supplicantHidlStateToFrameworkState(int state)77     protected static SupplicantState supplicantHidlStateToFrameworkState(int state) {
78         switch (state) {
79             case ISupplicantStaIfaceCallback.State.DISCONNECTED:
80                 return SupplicantState.DISCONNECTED;
81             case ISupplicantStaIfaceCallback.State.IFACE_DISABLED:
82                 return SupplicantState.INTERFACE_DISABLED;
83             case ISupplicantStaIfaceCallback.State.INACTIVE:
84                 return SupplicantState.INACTIVE;
85             case ISupplicantStaIfaceCallback.State.SCANNING:
86                 return SupplicantState.SCANNING;
87             case ISupplicantStaIfaceCallback.State.AUTHENTICATING:
88                 return SupplicantState.AUTHENTICATING;
89             case ISupplicantStaIfaceCallback.State.ASSOCIATING:
90                 return SupplicantState.ASSOCIATING;
91             case ISupplicantStaIfaceCallback.State.ASSOCIATED:
92                 return SupplicantState.ASSOCIATED;
93             case ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE:
94                 return SupplicantState.FOUR_WAY_HANDSHAKE;
95             case ISupplicantStaIfaceCallback.State.GROUP_HANDSHAKE:
96                 return SupplicantState.GROUP_HANDSHAKE;
97             case ISupplicantStaIfaceCallback.State.COMPLETED:
98                 return SupplicantState.COMPLETED;
99             default:
100                 throw new IllegalArgumentException("Invalid state: " + state);
101         }
102     }
103 
104 
105     /**
106      * Parses the provided payload into an ANQP element.
107      *
108      * @param infoID  Element type.
109      * @param payload Raw payload bytes.
110      * @return AnqpElement instance on success, null on failure.
111      */
parseAnqpElement(Constants.ANQPElementType infoID, ArrayList<Byte> payload)112     private ANQPElement parseAnqpElement(Constants.ANQPElementType infoID,
113                                          ArrayList<Byte> payload) {
114         synchronized (mLock) {
115             try {
116                 return Constants.getANQPElementID(infoID) != null
117                         ? ANQPParser.parseElement(
118                         infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
119                         : ANQPParser.parseHS20Element(
120                         infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
121             } catch (IOException | BufferUnderflowException e) {
122                 Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
123                 return null;
124             }
125         }
126     }
127 
128     /**
129      * Parse the ANQP element data and add to the provided elements map if successful.
130      *
131      * @param elementsMap Map to add the parsed out element to.
132      * @param infoID  Element type.
133      * @param payload Raw payload bytes.
134      */
addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap, Constants.ANQPElementType infoID, ArrayList<Byte> payload)135     private void addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap,
136                                      Constants.ANQPElementType infoID,
137                                      ArrayList<Byte> payload) {
138         synchronized (mLock) {
139             if (payload == null || payload.isEmpty()) return;
140             ANQPElement element = parseAnqpElement(infoID, payload);
141             if (element != null) {
142                 elementsMap.put(infoID, element);
143             }
144         }
145     }
146 
147     @Override
onNetworkAdded(int id)148     public void onNetworkAdded(int id) {
149         synchronized (mLock) {
150             mStaIfaceHal.logCallback("onNetworkAdded id=" + id);
151         }
152     }
153 
154     @Override
onNetworkRemoved(int id)155     public void onNetworkRemoved(int id) {
156         synchronized (mLock) {
157             mStaIfaceHal.logCallback("onNetworkRemoved id=" + id);
158             // Reset state since network has been removed.
159             mStateBeforeDisconnect = State.INACTIVE;
160         }
161     }
162 
163     /**
164      * Added to plumb the new {@code filsHlpSent} param from the V1.3 callback version.
165      */
onStateChanged(int newState, byte[ ] bssid, int id, ArrayList<Byte> ssid, boolean filsHlpSent)166     public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, ArrayList<Byte> ssid,
167             boolean filsHlpSent) {
168         synchronized (mLock) {
169             mStaIfaceHal.logCallback("onStateChanged");
170             SupplicantState newSupplicantState =
171                     supplicantHidlStateToFrameworkState(newState);
172             WifiSsid wifiSsid =
173                     WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
174             String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
175             if (newState != State.DISCONNECTED) {
176                 // onStateChanged(DISCONNECTED) may come before onDisconnected(), so add this
177                 // cache to track the state before the disconnect.
178                 mStateBeforeDisconnect = newState;
179             }
180 
181             if (newState == State.ASSOCIATING || newState == State.ASSOCIATED
182                     || newState == State.COMPLETED) {
183                 mStaIfaceHal.updateOnLinkedNetworkRoaming(mIfaceName, id);
184             }
185 
186             if (newState == State.COMPLETED) {
187                 mWifiMonitor.broadcastNetworkConnectionEvent(
188                         mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), filsHlpSent,
189                         wifiSsid, bssidStr);
190             } else if (newState == State.ASSOCIATING) {
191                 mCurrentSsid = NativeUtil.encodeSsid(ssid);
192             }
193             mWifiMonitor.broadcastSupplicantStateChangeEvent(
194                     mIfaceName, mStaIfaceHal.getCurrentNetworkId(mIfaceName), wifiSsid,
195                     bssidStr, newSupplicantState);
196         }
197     }
198 
199     @Override
onStateChanged(int newState, byte[ ] bssid, int id, ArrayList<Byte> ssid)200     public void onStateChanged(int newState, byte[/* 6 */] bssid, int id, ArrayList<Byte> ssid) {
201         onStateChanged(newState, bssid, id, ssid, false);
202     }
203 
onAnqpQueryDone(byte[ ] bssid, ISupplicantStaIfaceCallback.AnqpData data, ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data, android.hardware.wifi.supplicant.V1_4.ISupplicantStaIfaceCallback.AnqpData dataV14)204     public void onAnqpQueryDone(byte[/* 6 */] bssid,
205             ISupplicantStaIfaceCallback.AnqpData data,
206             ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data,
207             android.hardware.wifi.supplicant.V1_4.ISupplicantStaIfaceCallback.AnqpData dataV14) {
208         Map<Constants.ANQPElementType, ANQPElement> elementsMap = new HashMap<>();
209         addAnqpElementToMap(elementsMap, ANQPVenueName, data.venueName);
210         addAnqpElementToMap(elementsMap, ANQPRoamingConsortium, data.roamingConsortium);
211         addAnqpElementToMap(
212                 elementsMap, ANQPIPAddrAvailability, data.ipAddrTypeAvailability);
213         addAnqpElementToMap(elementsMap, ANQPNAIRealm, data.naiRealm);
214         addAnqpElementToMap(elementsMap, ANQP3GPPNetwork, data.anqp3gppCellularNetwork);
215         addAnqpElementToMap(elementsMap, ANQPDomName, data.domainName);
216         if (dataV14 != null) {
217             addAnqpElementToMap(elementsMap, ANQPVenueUrl, dataV14.venueUrl);
218         }
219         addAnqpElementToMap(elementsMap, HSFriendlyName, hs20Data.operatorFriendlyName);
220         addAnqpElementToMap(elementsMap, HSWANMetrics, hs20Data.wanMetrics);
221         addAnqpElementToMap(elementsMap, HSConnCapability, hs20Data.connectionCapability);
222         addAnqpElementToMap(elementsMap, HSOSUProviders, hs20Data.osuProvidersList);
223         mWifiMonitor.broadcastAnqpDoneEvent(
224                 mIfaceName, new AnqpEvent(NativeUtil.macAddressToLong(bssid), elementsMap));
225     }
226     @Override
onAnqpQueryDone(byte[ ] bssid, ISupplicantStaIfaceCallback.AnqpData data, ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data)227     public void onAnqpQueryDone(byte[/* 6 */] bssid,
228                                 ISupplicantStaIfaceCallback.AnqpData data,
229                                 ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
230         synchronized (mLock) {
231             mStaIfaceHal.logCallback("onAnqpQueryDone");
232             onAnqpQueryDone(bssid, data, hs20Data, null /* v1.4 element */);
233         }
234     }
235 
236     @Override
onHs20IconQueryDone(byte[ ] bssid, String fileName, ArrayList<Byte> data)237     public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
238                                     ArrayList<Byte> data) {
239         synchronized (mLock) {
240             mStaIfaceHal.logCallback("onHs20IconQueryDone");
241             mWifiMonitor.broadcastIconDoneEvent(
242                     mIfaceName,
243                     new IconEvent(NativeUtil.macAddressToLong(bssid), fileName, data.size(),
244                             NativeUtil.byteArrayFromArrayList(data)));
245         }
246     }
247 
248     @Override
onHs20SubscriptionRemediation(byte[ ] bssid, byte osuMethod, String url)249     public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, byte osuMethod, String url) {
250         synchronized (mLock) {
251             mStaIfaceHal.logCallback("onHs20SubscriptionRemediation");
252             mWifiMonitor.broadcastWnmEvent(
253                     mIfaceName,
254                     WnmData.createRemediationEvent(NativeUtil.macAddressToLong(bssid), url,
255                             osuMethod));
256         }
257     }
258 
259     @Override
onHs20DeauthImminentNotice(byte[ ] bssid, int reasonCode, int reAuthDelayInSec, String url)260     public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
261                                            int reAuthDelayInSec, String url) {
262         synchronized (mLock) {
263             mStaIfaceHal.logCallback("onHs20DeauthImminentNotice");
264             mWifiMonitor.broadcastWnmEvent(
265                     mIfaceName,
266                     WnmData.createDeauthImminentEvent(NativeUtil.macAddressToLong(bssid), url,
267                             reasonCode == WnmData.ESS, reAuthDelayInSec));
268         }
269     }
270 
271     @Override
onDisconnected(byte[ ] bssid, boolean locallyGenerated, int reasonCode)272     public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
273         synchronized (mLock) {
274             mStaIfaceHal.logCallback("onDisconnected");
275             if (mStaIfaceHal.isVerboseLoggingEnabled()) {
276                 Log.e(TAG, "onDisconnected state=" + mStateBeforeDisconnect
277                         + " locallyGenerated=" + locallyGenerated
278                         + " reasonCode=" + reasonCode);
279             }
280             WifiConfiguration curConfiguration =
281                     mStaIfaceHal.getCurrentNetworkLocalConfig(mIfaceName);
282             if (curConfiguration != null) {
283                 if (mStateBeforeDisconnect == State.FOURWAY_HANDSHAKE
284                         && WifiConfigurationUtil.isConfigForPskNetwork(curConfiguration)
285                         && (!locallyGenerated || reasonCode != ReasonCode.IE_IN_4WAY_DIFFERS)) {
286                     mWifiMonitor.broadcastAuthenticationFailureEvent(
287                             mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1);
288                 } else if (mStateBeforeDisconnect == State.ASSOCIATED
289                         && WifiConfigurationUtil.isConfigForEapNetwork(curConfiguration)) {
290                     mWifiMonitor.broadcastAuthenticationFailureEvent(
291                             mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, -1);
292                 }
293             }
294             mWifiMonitor.broadcastNetworkDisconnectionEvent(
295                     mIfaceName, locallyGenerated, reasonCode, mCurrentSsid,
296                     NativeUtil.macAddressFromByteArray(bssid));
297         }
298     }
299 
handleAssocRejectEvent(AssocRejectEventInfo assocRejectInfo)300     private void handleAssocRejectEvent(AssocRejectEventInfo assocRejectInfo) {
301         boolean isWrongPwd = false;
302         WifiConfiguration curConfiguration =
303                 mStaIfaceHal.getCurrentNetworkLocalConfig(mIfaceName);
304         if (curConfiguration != null) {
305             if (!assocRejectInfo.timedOut) {
306                 Log.d(TAG, "flush PMK cache due to association rejection for config id "
307                         + curConfiguration.networkId + ".");
308                 mStaIfaceHal.removePmkCacheEntry(curConfiguration.networkId);
309             }
310             // Special handling for WPA3-Personal networks. If the password is
311             // incorrect, the AP will send association rejection, with status code 1
312             // (unspecified failure). In SAE networks, the password authentication
313             // is not related to the 4-way handshake. In this case, we will send an
314             // authentication failure event up.
315             if (assocRejectInfo.statusCode == StatusCode.UNSPECIFIED_FAILURE) {
316                 // Network Selection status is guaranteed to be initialized
317                 SecurityParams params = curConfiguration.getNetworkSelectionStatus()
318                         .getCandidateSecurityParams();
319                 if (params != null
320                         && params.getSecurityType() == WifiConfiguration.SECURITY_TYPE_SAE) {
321                     mStaIfaceHal.logCallback("SAE incorrect password");
322                     isWrongPwd = true;
323                 }
324             } else if (assocRejectInfo.statusCode == StatusCode.CHALLENGE_FAIL
325                     && WifiConfigurationUtil.isConfigForWepNetwork(curConfiguration)) {
326                 mStaIfaceHal.logCallback("WEP incorrect password");
327                 isWrongPwd = true;
328             }
329         }
330 
331         if (isWrongPwd) {
332             mWifiMonitor.broadcastAuthenticationFailureEvent(
333                     mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD, -1);
334         }
335         mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, assocRejectInfo);
336         mStateBeforeDisconnect = State.INACTIVE;
337     }
338 
onAssociationRejected(android.hardware.wifi.supplicant.V1_4 .ISupplicantStaIfaceCallback.AssociationRejectionData assocRejectData)339     public void onAssociationRejected(android.hardware.wifi.supplicant.V1_4
340             .ISupplicantStaIfaceCallback.AssociationRejectionData assocRejectData) {
341         AssocRejectEventInfo assocRejectInfo = new AssocRejectEventInfo(assocRejectData);
342         handleAssocRejectEvent(assocRejectInfo);
343     }
344 
345     @Override
onAssociationRejected(byte[ ] bssid, int statusCode, boolean timedOut)346     public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) {
347         synchronized (mLock) {
348             AssocRejectEventInfo assocRejectInfo = new AssocRejectEventInfo(
349                     mCurrentSsid,
350                     NativeUtil.macAddressFromByteArray(bssid),
351                     statusCode, timedOut);
352             handleAssocRejectEvent(assocRejectInfo);
353         }
354     }
355 
356     @Override
onAuthenticationTimeout(byte[ ] bssid)357     public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
358         synchronized (mLock) {
359             mStaIfaceHal.logCallback("onAuthenticationTimeout");
360             mWifiMonitor.broadcastAuthenticationFailureEvent(
361                     mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT, -1);
362             mStateBeforeDisconnect = State.INACTIVE;
363         }
364     }
365 
366     @Override
onBssidChanged(byte reason, byte[ ] bssid)367     public void onBssidChanged(byte reason, byte[/* 6 */] bssid) {
368         synchronized (mLock) {
369             mStaIfaceHal.logCallback("onBssidChanged");
370             if (reason == BssidChangeReason.ASSOC_START) {
371                 mWifiMonitor.broadcastTargetBssidEvent(
372                         mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
373             } else if (reason == BssidChangeReason.ASSOC_COMPLETE) {
374                 mWifiMonitor.broadcastAssociatedBssidEvent(
375                         mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
376             }
377         }
378     }
379 
onEapFailure(int errorCode)380     public void onEapFailure(int errorCode) {
381         synchronized (mLock) {
382             mStaIfaceHal.logCallback("onEapFailure");
383             mWifiMonitor.broadcastAuthenticationFailureEvent(
384                     mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE, errorCode);
385             mStateBeforeDisconnect = State.INACTIVE;
386         }
387     }
388 
389 
390     @Override
onEapFailure()391     public void onEapFailure() {
392         onEapFailure(-1);
393     }
394 
395     @Override
onWpsEventSuccess()396     public void onWpsEventSuccess() {
397         mStaIfaceHal.logCallback("onWpsEventSuccess");
398         synchronized (mLock) {
399             mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName);
400         }
401     }
402 
403     @Override
onWpsEventFail(byte[ ] bssid, short configError, short errorInd)404     public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
405         synchronized (mLock) {
406             mStaIfaceHal.logCallback("onWpsEventFail");
407             if (configError == WpsConfigError.MSG_TIMEOUT
408                     && errorInd == WpsErrorIndication.NO_ERROR) {
409                 mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName);
410             } else {
411                 mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd);
412             }
413         }
414     }
415 
416     @Override
onWpsEventPbcOverlap()417     public void onWpsEventPbcOverlap() {
418         synchronized (mLock) {
419             mStaIfaceHal.logCallback("onWpsEventPbcOverlap");
420             mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName);
421         }
422     }
423 
424     @Override
onExtRadioWorkStart(int id)425     public void onExtRadioWorkStart(int id) {
426         synchronized (mLock) {
427             mStaIfaceHal.logCallback("onExtRadioWorkStart");
428         }
429     }
430 
431     @Override
onExtRadioWorkTimeout(int id)432     public void onExtRadioWorkTimeout(int id) {
433         synchronized (mLock) {
434             mStaIfaceHal.logCallback("onExtRadioWorkTimeout");
435         }
436     }
437 }
438