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