| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi.p2p; |
| |
| import android.annotation.NonNull; |
| import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback; |
| import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods; |
| import android.net.wifi.WpsInfo; |
| import android.net.wifi.p2p.WifiP2pConfig; |
| import android.net.wifi.p2p.WifiP2pDevice; |
| import android.net.wifi.p2p.WifiP2pGroup; |
| import android.net.wifi.p2p.WifiP2pProvDiscEvent; |
| import android.net.wifi.p2p.WifiP2pWfdInfo; |
| import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; |
| import android.util.Log; |
| |
| import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus; |
| import com.android.server.wifi.util.NativeUtil; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Class used for processing all P2P callbacks. |
| */ |
| public class SupplicantP2pIfaceCallbackImpl extends ISupplicantP2pIfaceCallback.Stub { |
| private static final String TAG = "SupplicantP2pIfaceCallbackImpl"; |
| private static boolean sVerboseLoggingEnabled = true; |
| |
| private final SupplicantP2pIfaceHal mP2pIfaceHal; |
| private final String mInterface; |
| private final WifiP2pMonitor mMonitor; |
| |
| public SupplicantP2pIfaceCallbackImpl( |
| @NonNull SupplicantP2pIfaceHal p2pIfaceHal, |
| @NonNull String iface, @NonNull WifiP2pMonitor monitor) { |
| mP2pIfaceHal = p2pIfaceHal; |
| mInterface = iface; |
| mMonitor = monitor; |
| } |
| |
| /** |
| * Enable verbose logging for all sub modules. |
| */ |
| public static void enableVerboseLogging(int verbose) { |
| sVerboseLoggingEnabled = verbose > 0; |
| } |
| |
| protected static void logd(String s) { |
| if (sVerboseLoggingEnabled) Log.d(TAG, s); |
| } |
| |
| /** |
| * Used to indicate that a new network has been added. |
| * |
| * @param networkId Network ID allocated to the corresponding network. |
| */ |
| public void onNetworkAdded(int networkId) { |
| } |
| |
| |
| /** |
| * Used to indicate that a network has been removed. |
| * |
| * @param networkId Network ID allocated to the corresponding network. |
| */ |
| public void onNetworkRemoved(int networkId) { |
| } |
| |
| |
| /** |
| * Used to indicate that a P2P device has been found. |
| * |
| * @param srcAddress MAC address of the device found. This must either |
| * be the P2P device address or the P2P interface address. |
| * @param p2pDeviceAddress P2P device address. |
| * @param primaryDeviceType Type of device. Refer to section B.1 of Wifi P2P |
| * Technical specification v1.2. |
| * @param deviceName Name of the device. |
| * @param configMethods Mask of WPS configuration methods supported by the |
| * device. |
| * @param deviceCapabilities Refer to section 4.1.4 of Wifi P2P Technical |
| * specification v1.2. |
| * @param groupCapabilities Refer to section 4.1.4 of Wifi P2P Technical |
| * specification v1.2. |
| * @param wfdDeviceInfo WFD device info as described in section 5.1.2 of WFD |
| * technical specification v1.0.0. |
| */ |
| public void onDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress, byte[] primaryDeviceType, |
| String deviceName, short configMethods, byte deviceCapabilities, int groupCapabilities, |
| byte[] wfdDeviceInfo) { |
| WifiP2pDevice device = new WifiP2pDevice(); |
| device.deviceName = deviceName; |
| if (deviceName == null) { |
| Log.e(TAG, "Missing device name."); |
| return; |
| } |
| |
| try { |
| device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode device address.", e); |
| return; |
| } |
| |
| try { |
| device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray(primaryDeviceType); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not encode device primary type.", e); |
| return; |
| } |
| |
| device.deviceCapability = deviceCapabilities; |
| device.groupCapability = groupCapabilities; |
| device.wpsConfigMethodsSupported = configMethods; |
| device.status = WifiP2pDevice.AVAILABLE; |
| |
| if (wfdDeviceInfo != null && wfdDeviceInfo.length >= 6) { |
| device.wfdInfo = new WifiP2pWfdInfo( |
| ((wfdDeviceInfo[0] & 0xFF) << 8) + (wfdDeviceInfo[1] & 0xFF), |
| ((wfdDeviceInfo[2] & 0xFF) << 8) + (wfdDeviceInfo[3] & 0xFF), |
| ((wfdDeviceInfo[4] & 0xFF) << 8) + (wfdDeviceInfo[5] & 0xFF)); |
| } |
| |
| logd("Device discovered on " + mInterface + ": " + device); |
| mMonitor.broadcastP2pDeviceFound(mInterface, device); |
| } |
| |
| /** |
| * Used to indicate that a P2P device has been lost. |
| * |
| * @param p2pDeviceAddress P2P device address. |
| */ |
| public void onDeviceLost(byte[] p2pDeviceAddress) { |
| WifiP2pDevice device = new WifiP2pDevice(); |
| |
| try { |
| device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode device address.", e); |
| return; |
| } |
| |
| device.status = WifiP2pDevice.UNAVAILABLE; |
| |
| logd("Device lost on " + mInterface + ": " + device); |
| mMonitor.broadcastP2pDeviceLost(mInterface, device); |
| } |
| |
| |
| /** |
| * Used to indicate the termination of P2P find operation. |
| */ |
| public void onFindStopped() { |
| logd("Search stopped on " + mInterface); |
| mMonitor.broadcastP2pFindStopped(mInterface); |
| } |
| |
| |
| /** |
| * Used to indicate the reception of a P2P Group Owner negotiation request. |
| * |
| * @param srcAddress MAC address of the device that initiated the GO |
| * negotiation request. |
| * @param passwordId Type of password. |
| */ |
| public void onGoNegotiationRequest(byte[] srcAddress, short passwordId) { |
| WifiP2pConfig config = new WifiP2pConfig(); |
| |
| try { |
| config.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode device address.", e); |
| return; |
| } |
| |
| config.wps = new WpsInfo(); |
| |
| switch (passwordId) { |
| case WpsDevPasswordId.USER_SPECIFIED: |
| config.wps.setup = WpsInfo.DISPLAY; |
| break; |
| |
| case WpsDevPasswordId.PUSHBUTTON: |
| config.wps.setup = WpsInfo.PBC; |
| break; |
| |
| case WpsDevPasswordId.REGISTRAR_SPECIFIED: |
| config.wps.setup = WpsInfo.KEYPAD; |
| break; |
| |
| default: |
| config.wps.setup = WpsInfo.PBC; |
| break; |
| } |
| |
| logd("Group Owner negotiation initiated on " + mInterface + ": " + config); |
| mMonitor.broadcastP2pGoNegotiationRequest(mInterface, config); |
| } |
| |
| |
| /** |
| * Used to indicate the completion of a P2P Group Owner negotiation request. |
| * |
| * @param status Status of the GO negotiation. |
| */ |
| public void onGoNegotiationCompleted(int status) { |
| logd("Group Owner negotiation completed with status: " + status); |
| P2pStatus result = halStatusToP2pStatus(status); |
| |
| if (result == P2pStatus.SUCCESS) { |
| mMonitor.broadcastP2pGoNegotiationSuccess(mInterface); |
| } else { |
| mMonitor.broadcastP2pGoNegotiationFailure(mInterface, result); |
| } |
| } |
| |
| |
| /** |
| * Used to indicate a successful formation of a P2P group. |
| */ |
| public void onGroupFormationSuccess() { |
| logd("Group formation successful on " + mInterface); |
| mMonitor.broadcastP2pGroupFormationSuccess(mInterface); |
| } |
| |
| |
| /** |
| * Used to indicate a failure to form a P2P group. |
| * |
| * @param failureReason Failure reason string for debug purposes. |
| */ |
| public void onGroupFormationFailure(String failureReason) { |
| // TODO(ender): failureReason should probably be an int (P2pStatusCode). |
| logd("Group formation failed on " + mInterface + ": " + failureReason); |
| mMonitor.broadcastP2pGroupFormationFailure(mInterface, failureReason); |
| } |
| |
| |
| /** |
| * Used to indicate the start of a P2P group. |
| * |
| * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1) |
| * @param isGo Whether this device is owner of the group. |
| * @param ssid SSID of the group. |
| * @param frequency Frequency on which this group is created. |
| * @param psk PSK used to secure the group. |
| * @param passphrase PSK passphrase used to secure the group. |
| * @param goDeviceAddress MAC Address of the owner of this group. |
| * @param isPersistent Whether this group is persisted or not. |
| */ |
| public void onGroupStarted(String groupIfName, boolean isGo, ArrayList<Byte> ssid, |
| int frequency, byte[] psk, String passphrase, byte[] goDeviceAddress, |
| boolean isPersistent) { |
| if (groupIfName == null) { |
| Log.e(TAG, "Missing group interface name."); |
| return; |
| } |
| |
| logd("Group " + groupIfName + " started on " + mInterface); |
| |
| WifiP2pGroup group = new WifiP2pGroup(); |
| group.setInterface(groupIfName); |
| |
| try { |
| String quotedSsid = NativeUtil.encodeSsid(ssid); |
| group.setNetworkName(NativeUtil.removeEnclosingQuotes(quotedSsid)); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not encode SSID.", e); |
| return; |
| } |
| |
| group.setFrequency(frequency); |
| group.setIsGroupOwner(isGo); |
| group.setPassphrase(passphrase); |
| |
| if (isPersistent) { |
| group.setNetworkId(WifiP2pGroup.NETWORK_ID_PERSISTENT); |
| } else { |
| group.setNetworkId(WifiP2pGroup.NETWORK_ID_TEMPORARY); |
| } |
| |
| WifiP2pDevice owner = new WifiP2pDevice(); |
| |
| try { |
| owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode Group Owner address.", e); |
| return; |
| } |
| |
| group.setOwner(owner); |
| mMonitor.broadcastP2pGroupStarted(mInterface, group); |
| } |
| |
| |
| /** |
| * Used to indicate the removal of a P2P group. |
| * |
| * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1) |
| * @param isGo Whether this device is owner of the group. |
| */ |
| public void onGroupRemoved(String groupIfName, boolean isGo) { |
| if (groupIfName == null) { |
| Log.e(TAG, "Missing group name."); |
| return; |
| } |
| |
| logd("Group " + groupIfName + " removed from " + mInterface); |
| WifiP2pGroup group = new WifiP2pGroup(); |
| group.setInterface(groupIfName); |
| group.setIsGroupOwner(isGo); |
| mMonitor.broadcastP2pGroupRemoved(mInterface, group); |
| } |
| |
| |
| /** |
| * Used to indicate the reception of a P2P invitation. |
| * |
| * @param srcAddress MAC address of the device that sent the invitation. |
| * @param goDeviceAddress MAC Address of the owner of this group. |
| * @param bssid Bssid of the group. |
| * @param persistentNetworkId Persistent network Id of the group. |
| * @param operatingFrequency Frequency on which the invitation was received. |
| */ |
| public void onInvitationReceived(byte[] srcAddress, byte[] goDeviceAddress, |
| byte[] bssid, int persistentNetworkId, int operatingFrequency) { |
| WifiP2pGroup group = new WifiP2pGroup(); |
| group.setNetworkId(persistentNetworkId); |
| |
| WifiP2pDevice client = new WifiP2pDevice(); |
| |
| try { |
| client.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode MAC address.", e); |
| return; |
| } |
| |
| group.addClient(client); |
| |
| WifiP2pDevice owner = new WifiP2pDevice(); |
| |
| try { |
| owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode Group Owner MAC address.", e); |
| return; |
| } |
| |
| group.setOwner(owner); |
| |
| logd("Invitation received on " + mInterface + ": " + group); |
| mMonitor.broadcastP2pInvitationReceived(mInterface, group); |
| } |
| |
| |
| /** |
| * Used to indicate the result of the P2P invitation request. |
| * |
| * @param bssid Bssid of the group. |
| * @param status Status of the invitation. |
| */ |
| public void onInvitationResult(byte[] bssid, int status) { |
| logd("Invitation completed with status: " + status); |
| mMonitor.broadcastP2pInvitationResult(mInterface, halStatusToP2pStatus(status)); |
| } |
| |
| |
| /** |
| * Used to indicate the completion of a P2P provision discovery request. |
| * |
| * @param p2pDeviceAddress P2P device address. |
| * @param isRequest Whether we received or sent the provision discovery. |
| * @param status Status of the provision discovery (SupplicantStatusCode). |
| * @param configMethods Mask of WPS configuration methods supported. |
| * Only one configMethod bit should be set per call. |
| * @param generatedPin 8 digit pin generated. |
| */ |
| public void onProvisionDiscoveryCompleted(byte[] p2pDeviceAddress, boolean isRequest, |
| byte status, short configMethods, String generatedPin) { |
| if (status != ISupplicantP2pIfaceCallback.P2pProvDiscStatusCode.SUCCESS) { |
| Log.e(TAG, "Provision discovery failed: " + status); |
| mMonitor.broadcastP2pProvisionDiscoveryFailure(mInterface); |
| return; |
| } |
| |
| logd("Provision discovery " + (isRequest ? "request" : "response") |
| + " for WPS Config method: " + configMethods); |
| |
| WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(); |
| event.device = new WifiP2pDevice(); |
| |
| try { |
| event.device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode MAC address.", e); |
| return; |
| } |
| |
| if ((configMethods & WpsConfigMethods.PUSHBUTTON) != 0) { |
| if (isRequest) { |
| event.event = WifiP2pProvDiscEvent.PBC_REQ; |
| mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event); |
| } else { |
| event.event = WifiP2pProvDiscEvent.PBC_RSP; |
| mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event); |
| } |
| } else if (!isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { |
| event.event = WifiP2pProvDiscEvent.SHOW_PIN; |
| event.pin = generatedPin; |
| mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); |
| } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { |
| event.event = WifiP2pProvDiscEvent.ENTER_PIN; |
| mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); |
| } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { |
| event.event = WifiP2pProvDiscEvent.SHOW_PIN; |
| event.pin = generatedPin; |
| mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); |
| } else if (isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { |
| event.event = WifiP2pProvDiscEvent.ENTER_PIN; |
| mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); |
| } else { |
| Log.e(TAG, "Unsupported config methods: " + configMethods); |
| } |
| } |
| |
| |
| /** |
| * Used to indicate the reception of a P2P service discovery response. |
| * |
| * @param srcAddress MAC address of the device that sent the service discovery. |
| * @param updateIndicator Service update indicator. Refer to section 3.1.3 of |
| * Wifi P2P Technical specification v1.2. |
| * @param tlvs Refer to section 3.1.3.1 of Wifi P2P Technical specification v1.2. |
| */ |
| public void onServiceDiscoveryResponse(byte[] srcAddress, short updateIndicator, |
| ArrayList<Byte> tlvs) { |
| List<WifiP2pServiceResponse> response = null; |
| |
| logd("Service discovery response received on " + mInterface); |
| try { |
| String srcAddressStr = NativeUtil.macAddressFromByteArray(srcAddress); |
| // updateIndicator is not used |
| response = WifiP2pServiceResponse.newInstance(srcAddressStr, |
| NativeUtil.byteArrayFromArrayList(tlvs)); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not process service discovery response.", e); |
| return; |
| } |
| mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, response); |
| } |
| |
| private WifiP2pDevice createStaEventDevice(byte[] srcAddress, byte[] p2pDeviceAddress) { |
| WifiP2pDevice device = new WifiP2pDevice(); |
| byte[] deviceAddressBytes; |
| // Legacy STAs may not supply a p2pDeviceAddress (signaled by a zero'd p2pDeviceAddress) |
| // In this case, use srcAddress instead |
| if (!Arrays.equals(NativeUtil.ANY_MAC_BYTES, p2pDeviceAddress)) { |
| deviceAddressBytes = p2pDeviceAddress; |
| } else { |
| deviceAddressBytes = srcAddress; |
| } |
| try { |
| device.deviceAddress = NativeUtil.macAddressFromByteArray(deviceAddressBytes); |
| } catch (Exception e) { |
| Log.e(TAG, "Could not decode MAC address", e); |
| return null; |
| } |
| return device; |
| } |
| |
| /** |
| * Used to indicate when a STA device is connected to this device. |
| * |
| * @param srcAddress MAC address of the device that was authorized. |
| * @param p2pDeviceAddress P2P device address. |
| */ |
| public void onStaAuthorized(byte[] srcAddress, byte[] p2pDeviceAddress) { |
| logd("STA authorized on " + mInterface); |
| WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress); |
| if (device == null) { |
| return; |
| } |
| mMonitor.broadcastP2pApStaConnected(mInterface, device); |
| } |
| |
| |
| /** |
| * Used to indicate when a STA device is disconnected from this device. |
| * |
| * @param srcAddress MAC address of the device that was deauthorized. |
| * @param p2pDeviceAddress P2P device address. |
| */ |
| public void onStaDeauthorized(byte[] srcAddress, byte[] p2pDeviceAddress) { |
| logd("STA deauthorized on " + mInterface); |
| WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress); |
| if (device == null) { |
| return; |
| } |
| mMonitor.broadcastP2pApStaDisconnected(mInterface, device); |
| } |
| |
| |
| private static P2pStatus halStatusToP2pStatus(int status) { |
| P2pStatus result = P2pStatus.UNKNOWN; |
| |
| switch (status) { |
| case P2pStatusCode.SUCCESS: |
| case P2pStatusCode.SUCCESS_DEFERRED: |
| result = P2pStatus.SUCCESS; |
| break; |
| |
| case P2pStatusCode.FAIL_INFO_CURRENTLY_UNAVAILABLE: |
| result = P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE; |
| break; |
| |
| case P2pStatusCode.FAIL_INCOMPATIBLE_PARAMS: |
| result = P2pStatus.INCOMPATIBLE_PARAMETERS; |
| break; |
| |
| case P2pStatusCode.FAIL_LIMIT_REACHED: |
| result = P2pStatus.LIMIT_REACHED; |
| break; |
| |
| case P2pStatusCode.FAIL_INVALID_PARAMS: |
| result = P2pStatus.INVALID_PARAMETER; |
| break; |
| |
| case P2pStatusCode.FAIL_UNABLE_TO_ACCOMMODATE: |
| result = P2pStatus.UNABLE_TO_ACCOMMODATE_REQUEST; |
| break; |
| |
| case P2pStatusCode.FAIL_PREV_PROTOCOL_ERROR: |
| result = P2pStatus.PREVIOUS_PROTOCOL_ERROR; |
| break; |
| |
| case P2pStatusCode.FAIL_NO_COMMON_CHANNELS: |
| result = P2pStatus.NO_COMMON_CHANNEL; |
| break; |
| |
| case P2pStatusCode.FAIL_UNKNOWN_GROUP: |
| result = P2pStatus.UNKNOWN_P2P_GROUP; |
| break; |
| |
| case P2pStatusCode.FAIL_BOTH_GO_INTENT_15: |
| result = P2pStatus.BOTH_GO_INTENT_15; |
| break; |
| |
| case P2pStatusCode.FAIL_INCOMPATIBLE_PROV_METHOD: |
| result = P2pStatus.INCOMPATIBLE_PROVISIONING_METHOD; |
| break; |
| |
| case P2pStatusCode.FAIL_REJECTED_BY_USER: |
| result = P2pStatus.REJECTED_BY_USER; |
| break; |
| } |
| return result; |
| } |
| } |
| |