1 /* 2 * Copyright (C) 2017 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.p2p; 18 19 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback; 20 import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods; 21 import android.net.wifi.WpsInfo; 22 import android.net.wifi.p2p.WifiP2pConfig; 23 import android.net.wifi.p2p.WifiP2pDevice; 24 import android.net.wifi.p2p.WifiP2pGroup; 25 import android.net.wifi.p2p.WifiP2pProvDiscEvent; 26 import android.net.wifi.p2p.WifiP2pWfdInfo; 27 import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; 28 import android.util.Log; 29 30 import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus; 31 import com.android.server.wifi.util.NativeUtil; 32 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 37 /** 38 * Class used for processing all P2P callbacks. 39 */ 40 public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub { 41 private static final String TAG = "SupplicantP2pIfaceCallback"; 42 private static final boolean DBG = true; 43 44 private final String mInterface; 45 private final WifiP2pMonitor mMonitor; 46 47 public SupplicantP2pIfaceCallback(String iface, WifiP2pMonitor monitor) { 48 mInterface = iface; 49 mMonitor = monitor; 50 } 51 52 53 protected static void logd(String s) { 54 if (DBG) Log.d(TAG, s); 55 } 56 57 /** 58 * Used to indicate that a new network has been added. 59 * 60 * @param networkId Network ID allocated to the corresponding network. 61 */ 62 public void onNetworkAdded(int networkId) { 63 } 64 65 66 /** 67 * Used to indicate that a network has been removed. 68 * 69 * @param networkId Network ID allocated to the corresponding network. 70 */ 71 public void onNetworkRemoved(int networkId) { 72 } 73 74 75 /** 76 * Used to indicate that a P2P device has been found. 77 * 78 * @param srcAddress MAC address of the device found. This must either 79 * be the P2P device address or the P2P interface address. 80 * @param p2pDeviceAddress P2P device address. 81 * @param primaryDeviceType Type of device. Refer to section B.1 of Wifi P2P 82 * Technical specification v1.2. 83 * @param deviceName Name of the device. 84 * @param configMethods Mask of WPS configuration methods supported by the 85 * device. 86 * @param deviceCapabilities Refer to section 4.1.4 of Wifi P2P Technical 87 * specification v1.2. 88 * @param groupCapabilities Refer to section 4.1.4 of Wifi P2P Technical 89 * specification v1.2. 90 * @param wfdDeviceInfo WFD device info as described in section 5.1.2 of WFD 91 * technical specification v1.0.0. 92 */ 93 public void onDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress, byte[] primaryDeviceType, 94 String deviceName, short configMethods, byte deviceCapabilities, int groupCapabilities, 95 byte[] wfdDeviceInfo) { 96 WifiP2pDevice device = new WifiP2pDevice(); 97 device.deviceName = deviceName; 98 if (deviceName == null) { 99 Log.e(TAG, "Missing device name."); 100 return; 101 } 102 103 try { 104 device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); 105 } catch (Exception e) { 106 Log.e(TAG, "Could not decode device address.", e); 107 return; 108 } 109 110 try { 111 device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray(primaryDeviceType); 112 } catch (Exception e) { 113 Log.e(TAG, "Could not encode device primary type.", e); 114 return; 115 } 116 117 device.deviceCapability = deviceCapabilities; 118 device.groupCapability = groupCapabilities; 119 device.wpsConfigMethodsSupported = configMethods; 120 device.status = WifiP2pDevice.AVAILABLE; 121 122 if (wfdDeviceInfo != null && wfdDeviceInfo.length >= 6) { 123 device.wfdInfo = new WifiP2pWfdInfo( 124 (wfdDeviceInfo[0] << 8) + wfdDeviceInfo[1], 125 (wfdDeviceInfo[2] << 8) + wfdDeviceInfo[3], 126 (wfdDeviceInfo[4] << 8) + wfdDeviceInfo[5]); 127 } 128 129 logd("Device discovered on " + mInterface + ": " + device); 130 mMonitor.broadcastP2pDeviceFound(mInterface, device); 131 } 132 133 /** 134 * Used to indicate that a P2P device has been lost. 135 * 136 * @param p2pDeviceAddress P2P device address. 137 */ 138 public void onDeviceLost(byte[] p2pDeviceAddress) { 139 WifiP2pDevice device = new WifiP2pDevice(); 140 141 try { 142 device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); 143 } catch (Exception e) { 144 Log.e(TAG, "Could not decode device address.", e); 145 return; 146 } 147 148 device.status = WifiP2pDevice.UNAVAILABLE; 149 150 logd("Device lost on " + mInterface + ": " + device); 151 mMonitor.broadcastP2pDeviceLost(mInterface, device); 152 } 153 154 155 /** 156 * Used to indicate the termination of P2P find operation. 157 */ 158 public void onFindStopped() { 159 logd("Search stopped on " + mInterface); 160 mMonitor.broadcastP2pFindStopped(mInterface); 161 } 162 163 164 /** 165 * Used to indicate the reception of a P2P Group Owner negotiation request. 166 * 167 * @param srcAddress MAC address of the device that initiated the GO 168 * negotiation request. 169 * @param passwordId Type of password. 170 */ 171 public void onGoNegotiationRequest(byte[] srcAddress, short passwordId) { 172 WifiP2pConfig config = new WifiP2pConfig(); 173 174 try { 175 config.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress); 176 } catch (Exception e) { 177 Log.e(TAG, "Could not decode device address.", e); 178 return; 179 } 180 181 config.wps = new WpsInfo(); 182 183 switch (passwordId) { 184 case WpsDevPasswordId.USER_SPECIFIED: 185 config.wps.setup = WpsInfo.DISPLAY; 186 break; 187 188 case WpsDevPasswordId.PUSHBUTTON: 189 config.wps.setup = WpsInfo.PBC; 190 break; 191 192 case WpsDevPasswordId.REGISTRAR_SPECIFIED: 193 config.wps.setup = WpsInfo.KEYPAD; 194 break; 195 196 default: 197 config.wps.setup = WpsInfo.PBC; 198 break; 199 } 200 201 logd("Group Owner negotiation initiated on " + mInterface + ": " + config); 202 mMonitor.broadcastP2pGoNegotiationRequest(mInterface, config); 203 } 204 205 206 /** 207 * Used to indicate the completion of a P2P Group Owner negotiation request. 208 * 209 * @param status Status of the GO negotiation. 210 */ 211 public void onGoNegotiationCompleted(int status) { 212 logd("Group Owner negotiation completed with status: " + status); 213 P2pStatus result = halStatusToP2pStatus(status); 214 215 if (result == P2pStatus.SUCCESS) { 216 mMonitor.broadcastP2pGoNegotiationSuccess(mInterface); 217 } else { 218 mMonitor.broadcastP2pGoNegotiationFailure(mInterface, result); 219 } 220 } 221 222 223 /** 224 * Used to indicate a successful formation of a P2P group. 225 */ 226 public void onGroupFormationSuccess() { 227 logd("Group formation successful on " + mInterface); 228 mMonitor.broadcastP2pGroupFormationSuccess(mInterface); 229 } 230 231 232 /** 233 * Used to indicate a failure to form a P2P group. 234 * 235 * @param failureReason Failure reason string for debug purposes. 236 */ 237 public void onGroupFormationFailure(String failureReason) { 238 // TODO(ender): failureReason should probably be an int (P2pStatusCode). 239 logd("Group formation failed on " + mInterface + ": " + failureReason); 240 mMonitor.broadcastP2pGroupFormationFailure(mInterface, failureReason); 241 } 242 243 244 /** 245 * Used to indicate the start of a P2P group. 246 * 247 * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1) 248 * @param isGo Whether this device is owner of the group. 249 * @param ssid SSID of the group. 250 * @param frequency Frequency on which this group is created. 251 * @param psk PSK used to secure the group. 252 * @param passphrase PSK passphrase used to secure the group. 253 * @param goDeviceAddress MAC Address of the owner of this group. 254 * @param isPersistent Whether this group is persisted or not. 255 */ 256 public void onGroupStarted(String groupIfName, boolean isGo, ArrayList<Byte> ssid, 257 int frequency, byte[] psk, String passphrase, byte[] goDeviceAddress, 258 boolean isPersistent) { 259 if (groupIfName == null) { 260 Log.e(TAG, "Missing group interface name."); 261 return; 262 } 263 264 logd("Group " + groupIfName + " started on " + mInterface); 265 266 WifiP2pGroup group = new WifiP2pGroup(); 267 group.setInterface(groupIfName); 268 269 try { 270 String quotedSsid = NativeUtil.encodeSsid(ssid); 271 group.setNetworkName(NativeUtil.removeEnclosingQuotes(quotedSsid)); 272 } catch (Exception e) { 273 Log.e(TAG, "Could not encode SSID.", e); 274 return; 275 } 276 277 group.setIsGroupOwner(isGo); 278 group.setPassphrase(passphrase); 279 280 if (isPersistent) { 281 group.setNetworkId(WifiP2pGroup.PERSISTENT_NET_ID); 282 } else { 283 group.setNetworkId(WifiP2pGroup.TEMPORARY_NET_ID); 284 } 285 286 WifiP2pDevice owner = new WifiP2pDevice(); 287 288 try { 289 owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress); 290 } catch (Exception e) { 291 Log.e(TAG, "Could not decode Group Owner address.", e); 292 return; 293 } 294 295 group.setOwner(owner); 296 mMonitor.broadcastP2pGroupStarted(mInterface, group); 297 } 298 299 300 /** 301 * Used to indicate the removal of a P2P group. 302 * 303 * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1) 304 * @param isGo Whether this device is owner of the group. 305 */ 306 public void onGroupRemoved(String groupIfName, boolean isGo) { 307 if (groupIfName == null) { 308 Log.e(TAG, "Missing group name."); 309 return; 310 } 311 312 logd("Group " + groupIfName + " removed from " + mInterface); 313 WifiP2pGroup group = new WifiP2pGroup(); 314 group.setInterface(groupIfName); 315 group.setIsGroupOwner(isGo); 316 mMonitor.broadcastP2pGroupRemoved(mInterface, group); 317 } 318 319 320 /** 321 * Used to indicate the reception of a P2P invitation. 322 * 323 * @param srcAddress MAC address of the device that sent the invitation. 324 * @param goDeviceAddress MAC Address of the owner of this group. 325 * @param bssid Bssid of the group. 326 * @param persistentNetworkId Persistent network Id of the group. 327 * @param operatingFrequency Frequency on which the invitation was received. 328 */ 329 public void onInvitationReceived(byte[] srcAddress, byte[] goDeviceAddress, 330 byte[] bssid, int persistentNetworkId, int operatingFrequency) { 331 WifiP2pGroup group = new WifiP2pGroup(); 332 group.setNetworkId(persistentNetworkId); 333 334 WifiP2pDevice client = new WifiP2pDevice(); 335 336 try { 337 client.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress); 338 } catch (Exception e) { 339 Log.e(TAG, "Could not decode MAC address.", e); 340 return; 341 } 342 343 group.addClient(client); 344 345 WifiP2pDevice owner = new WifiP2pDevice(); 346 347 try { 348 owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress); 349 } catch (Exception e) { 350 Log.e(TAG, "Could not decode Group Owner MAC address.", e); 351 return; 352 } 353 354 group.setOwner(owner); 355 356 logd("Invitation received on " + mInterface + ": " + group); 357 mMonitor.broadcastP2pInvitationReceived(mInterface, group); 358 } 359 360 361 /** 362 * Used to indicate the result of the P2P invitation request. 363 * 364 * @param bssid Bssid of the group. 365 * @param status Status of the invitation. 366 */ 367 public void onInvitationResult(byte[] bssid, int status) { 368 logd("Invitation completed with status: " + status); 369 mMonitor.broadcastP2pInvitationResult(mInterface, halStatusToP2pStatus(status)); 370 } 371 372 373 /** 374 * Used to indicate the completion of a P2P provision discovery request. 375 * 376 * @param p2pDeviceAddress P2P device address. 377 * @param isRequest Whether we received or sent the provision discovery. 378 * @param status Status of the provision discovery (SupplicantStatusCode). 379 * @param configMethods Mask of WPS configuration methods supported. 380 * Only one configMethod bit should be set per call. 381 * @param generatedPin 8 digit pin generated. 382 */ 383 public void onProvisionDiscoveryCompleted(byte[] p2pDeviceAddress, boolean isRequest, 384 byte status, short configMethods, String generatedPin) { 385 if (status != ISupplicantP2pIfaceCallback.P2pProvDiscStatusCode.SUCCESS) { 386 Log.e(TAG, "Provision discovery failed: " + status); 387 mMonitor.broadcastP2pProvisionDiscoveryFailure(mInterface); 388 return; 389 } 390 391 logd("Provision discovery " + (isRequest ? "request" : "response") 392 + " for WPS Config method: " + configMethods); 393 394 WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(); 395 event.device = new WifiP2pDevice(); 396 397 try { 398 event.device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); 399 } catch (Exception e) { 400 Log.e(TAG, "Could not decode MAC address.", e); 401 return; 402 } 403 404 if ((configMethods & WpsConfigMethods.PUSHBUTTON) != 0) { 405 if (isRequest) { 406 event.event = WifiP2pProvDiscEvent.PBC_REQ; 407 mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event); 408 } else { 409 event.event = WifiP2pProvDiscEvent.PBC_RSP; 410 mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event); 411 } 412 } else if (!isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { 413 event.event = WifiP2pProvDiscEvent.SHOW_PIN; 414 event.pin = generatedPin; 415 mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); 416 } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { 417 event.event = WifiP2pProvDiscEvent.ENTER_PIN; 418 mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); 419 } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { 420 event.event = WifiP2pProvDiscEvent.SHOW_PIN; 421 event.pin = generatedPin; 422 mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); 423 } else if (isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { 424 event.event = WifiP2pProvDiscEvent.ENTER_PIN; 425 mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); 426 } else { 427 Log.e(TAG, "Unsupported config methods: " + configMethods); 428 } 429 } 430 431 432 /** 433 * Used to indicate the reception of a P2P service discovery response. 434 * 435 * @param srcAddress MAC address of the device that sent the service discovery. 436 * @param updateIndicator Service update indicator. Refer to section 3.1.3 of 437 * Wifi P2P Technical specification v1.2. 438 * @param tlvs Refer to section 3.1.3.1 of Wifi P2P Technical specification v1.2. 439 */ 440 public void onServiceDiscoveryResponse(byte[] srcAddress, short updateIndicator, 441 ArrayList<Byte> tlvs) { 442 List<WifiP2pServiceResponse> response = null; 443 444 logd("Service discovery response received on " + mInterface); 445 try { 446 String srcAddressStr = NativeUtil.macAddressFromByteArray(srcAddress); 447 // updateIndicator is not used 448 response = WifiP2pServiceResponse.newInstance(srcAddressStr, 449 NativeUtil.byteArrayFromArrayList(tlvs)); 450 } catch (Exception e) { 451 Log.e(TAG, "Could not process service discovery response.", e); 452 return; 453 } 454 mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, response); 455 } 456 457 private WifiP2pDevice createStaEventDevice(byte[] srcAddress, byte[] p2pDeviceAddress) { 458 WifiP2pDevice device = new WifiP2pDevice(); 459 byte[] deviceAddressBytes; 460 // Legacy STAs may not supply a p2pDeviceAddress (signaled by a zero'd p2pDeviceAddress) 461 // In this case, use srcAddress instead 462 if (!Arrays.equals(NativeUtil.ANY_MAC_BYTES, p2pDeviceAddress)) { 463 deviceAddressBytes = p2pDeviceAddress; 464 } else { 465 deviceAddressBytes = srcAddress; 466 } 467 try { 468 device.deviceAddress = NativeUtil.macAddressFromByteArray(deviceAddressBytes); 469 } catch (Exception e) { 470 Log.e(TAG, "Could not decode MAC address", e); 471 return null; 472 } 473 return device; 474 } 475 476 /** 477 * Used to indicate when a STA device is connected to this device. 478 * 479 * @param srcAddress MAC address of the device that was authorized. 480 * @param p2pDeviceAddress P2P device address. 481 */ 482 public void onStaAuthorized(byte[] srcAddress, byte[] p2pDeviceAddress) { 483 logd("STA authorized on " + mInterface); 484 WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress); 485 if (device == null) { 486 return; 487 } 488 mMonitor.broadcastP2pApStaConnected(mInterface, device); 489 } 490 491 492 /** 493 * Used to indicate when a STA device is disconnected from this device. 494 * 495 * @param srcAddress MAC address of the device that was deauthorized. 496 * @param p2pDeviceAddress P2P device address. 497 */ 498 public void onStaDeauthorized(byte[] srcAddress, byte[] p2pDeviceAddress) { 499 logd("STA deauthorized on " + mInterface); 500 WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress); 501 if (device == null) { 502 return; 503 } 504 mMonitor.broadcastP2pApStaDisconnected(mInterface, device); 505 } 506 507 508 private static P2pStatus halStatusToP2pStatus(int status) { 509 P2pStatus result = P2pStatus.UNKNOWN; 510 511 switch (status) { 512 case P2pStatusCode.SUCCESS: 513 case P2pStatusCode.SUCCESS_DEFERRED: 514 result = P2pStatus.SUCCESS; 515 break; 516 517 case P2pStatusCode.FAIL_INFO_CURRENTLY_UNAVAILABLE: 518 result = P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE; 519 break; 520 521 case P2pStatusCode.FAIL_INCOMPATIBLE_PARAMS: 522 result = P2pStatus.INCOMPATIBLE_PARAMETERS; 523 break; 524 525 case P2pStatusCode.FAIL_LIMIT_REACHED: 526 result = P2pStatus.LIMIT_REACHED; 527 break; 528 529 case P2pStatusCode.FAIL_INVALID_PARAMS: 530 result = P2pStatus.INVALID_PARAMETER; 531 break; 532 533 case P2pStatusCode.FAIL_UNABLE_TO_ACCOMMODATE: 534 result = P2pStatus.UNABLE_TO_ACCOMMODATE_REQUEST; 535 break; 536 537 case P2pStatusCode.FAIL_PREV_PROTOCOL_ERROR: 538 result = P2pStatus.PREVIOUS_PROTOCOL_ERROR; 539 break; 540 541 case P2pStatusCode.FAIL_NO_COMMON_CHANNELS: 542 result = P2pStatus.NO_COMMON_CHANNEL; 543 break; 544 545 case P2pStatusCode.FAIL_UNKNOWN_GROUP: 546 result = P2pStatus.UNKNOWN_P2P_GROUP; 547 break; 548 549 case P2pStatusCode.FAIL_BOTH_GO_INTENT_15: 550 result = P2pStatus.BOTH_GO_INTENT_15; 551 break; 552 553 case P2pStatusCode.FAIL_INCOMPATIBLE_PROV_METHOD: 554 result = P2pStatus.INCOMPATIBLE_PROVISIONING_METHOD; 555 break; 556 557 case P2pStatusCode.FAIL_REJECTED_BY_USER: 558 result = P2pStatus.REJECTED_BY_USER; 559 break; 560 } 561 return result; 562 } 563 } 564 565