diff options
author | Tomasz Wiszkowski <ender@google.com> | 2017-02-08 12:59:31 -0800 |
---|---|---|
committer | Tomasz Wiszkowski <ender@google.com> | 2017-02-28 09:12:38 -0800 |
commit | d3c761e72a5f89c81bd74889a9fec663adcc491e (patch) | |
tree | 75d76bc8409b58379b4c3bad8732842cd6155504 | |
parent | 56a8ff2e077bdbcd1511ad48c05df99c4d8ae300 (diff) |
HIDLization of WifiDirect, stage 3: callbacks.
Callback class crafted to be compatible with legacy code.
Several calls currently generate no action (never used before).
Change-Id: I1e1d94fd23dfb069cd387bc511c979ebeb2e72cf
6 files changed, 1138 insertions, 26 deletions
diff --git a/service/java/com/android/server/wifi/SupplicantP2pIfaceCallback.java b/service/java/com/android/server/wifi/SupplicantP2pIfaceCallback.java new file mode 100644 index 000000000..829d32e1e --- /dev/null +++ b/service/java/com/android/server/wifi/SupplicantP2pIfaceCallback.java @@ -0,0 +1,534 @@ +/* + * 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; + +import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface; +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 libcore.util.HexEncoding; + +import java.util.ArrayList; +import java.util.List; + +public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub { + private static final String TAG = "SupplicantP2pIfaceCallback"; + private static final boolean DBG = true; + + private final String mInterface; + private final WifiMonitor mMonitor; + + public SupplicantP2pIfaceCallback(String iface, WifiMonitor monitor) { + mInterface = iface; + mMonitor = monitor; + } + + + protected static void logd(String s) { + if (DBG) Log.d(TAG, s); + } + + /** + * Used to indicate that a new network has been added. + * + * @param id Network ID allocated to the corresponding network. + */ + public void onNetworkAdded(int networkId) { + } + + + /** + * Used to indicate that a network has been removed. + * + * @param id 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 groupCapabilites 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 = new String(HexEncoding.encode( + primaryDeviceType, 0, primaryDeviceType.length)); + } 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] << 8) + wfdDeviceInfo[1], + (wfdDeviceInfo[2] << 8) + wfdDeviceInfo[3], + (wfdDeviceInfo[4] << 8) + wfdDeviceInfo[5]); + } + + 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 { + group.setNetworkName(NativeUtil.encodeSsid(ssid)); + } catch (Exception e) { + Log.e(TAG, "Could not encode SSID.", e); + return; + } + + group.setIsGroupOwner(isGo); + group.setPassphrase(passphrase); + + if (isPersistent) { + group.setNetworkId(WifiP2pGroup.PERSISTENT_NET_ID); + } else { + group.setNetworkId(WifiP2pGroup.TEMPORARY_NET_ID); + } + + 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 != 0) { + 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 & ISupplicantP2pIface.WpsProvisionMethod.PBC) != 0) { + if (isRequest) { + event.event = WifiP2pProvDiscEvent.PBC_REQ; + mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event); + } else { + event.event = WifiP2pProvDiscEvent.PBC_RSP; + mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event); + } + } else if ((configMethods & ISupplicantP2pIface.WpsProvisionMethod.DISPLAY) != 0) { + event.event = WifiP2pProvDiscEvent.SHOW_PIN; + event.pin = generatedPin; + mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); + } else if ((configMethods & ISupplicantP2pIface.WpsProvisionMethod.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 { + StringBuilder event = new StringBuilder(); + event.append(NativeUtil.macAddressFromByteArray(srcAddress)); + event.append(" "); + event.append(updateIndicator); + event.append(" "); + event.append(NativeUtil.stringFromByteArrayList(tlvs)); + response = WifiP2pServiceResponse.newInstance(event.toString()); + } catch (Exception e) { + Log.e(TAG, "Could not process service discovery response.", e); + return; + } + + mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, response); + } + + + /** + * 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); + } + + + /** + * 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); + } + + + 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; + } +} + diff --git a/service/java/com/android/server/wifi/SupplicantP2pIfaceHal.java b/service/java/com/android/server/wifi/SupplicantP2pIfaceHal.java index cb6e2689b..7b96dc4c4 100644 --- a/service/java/com/android/server/wifi/SupplicantP2pIfaceHal.java +++ b/service/java/com/android/server/wifi/SupplicantP2pIfaceHal.java @@ -75,6 +75,13 @@ public class SupplicantP2pIfaceHal { private Object mLock = new Object(); private boolean mServiceCallbackInstalled = false; + private final WifiMonitor mMonitor; + private SupplicantP2pIfaceCallback mCallback = null; + + public SupplicantP2pIfaceHal(WifiMonitor monitor) { + mMonitor = monitor; + } + /** * Registers a service notification for the ISupplicant service, which triggers intialization of * the ISupplicantP2pIface @@ -118,12 +125,16 @@ public class SupplicantP2pIfaceHal { mIServiceManager = null; // Will need to register a new ServiceNotification return false; } + + // Successful completion by the end of the 'try' block. This will prevent reporting + // proper initialization after exception is caught. + return true; } catch (RemoteException e) { Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: " + e); supplicantServiceDiedHandler(); } - return true; + return false; } } @@ -190,9 +201,20 @@ public class SupplicantP2pIfaceHal { return false; } mISupplicantP2pIface = getP2pIfaceMockable(supplicantIface.getResult()); + } - return true; + if (mISupplicantP2pIface != null && mMonitor != null) { + // TODO(ender): Get rid of hard-coded interface name, which is + // assumed to be the group interface name in several other classes + // ("p2p0" should probably become getName()). + mCallback = new SupplicantP2pIfaceCallback("p2p0", mMonitor); + if (!registerCallback(mCallback)) { + Log.e(TAG, "Callback registration failed. Initialization incomplete."); + return false; + } } + + return true; } private void supplicantServiceDiedHandler() { diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 1e5f6fc49..0b4200de9 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -161,7 +161,7 @@ public class WifiInjector { mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, mWifiStateMachineHandlerThread); mSupplicantStaIfaceHal = new SupplicantStaIfaceHal(mContext, WifiMonitor.getInstance()); mWificondControl = new WificondControl(this, WifiMonitor.getInstance()); - mSupplicantP2pIfaceHal = new SupplicantP2pIfaceHal(); + mSupplicantP2pIfaceHal = new SupplicantP2pIfaceHal(WifiMonitor.getInstance()); mWifiNative = WifiNative.getWlanNativeInterface(); mWifiNative.setSupplicantStaIfaceHal(mSupplicantStaIfaceHal); mWifiNative.setSupplicantP2pIfaceHal(mSupplicantP2pIfaceHal); diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index bc715413c..35098ec68 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -1161,47 +1161,43 @@ public class WifiMonitor { */ private void handleP2pEvents(String dataString, String iface) { if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) { - WifiP2pDevice device = getWifiP2pDevice(dataString); - if (device != null) sendMessage(iface, P2P_DEVICE_FOUND_EVENT, device); + broadcastP2pDeviceFound(iface, getWifiP2pDevice(dataString)); } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) { - WifiP2pDevice device = getWifiP2pDevice(dataString); - if (device != null) sendMessage(iface, P2P_DEVICE_LOST_EVENT, device); + broadcastP2pDeviceLost(iface, getWifiP2pDevice(dataString)); } else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) { - sendMessage(iface, P2P_FIND_STOPPED_EVENT); + broadcastP2pFindStopped(iface); } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) { - sendMessage(iface, P2P_GO_NEGOTIATION_REQUEST_EVENT, new WifiP2pConfig(dataString)); + broadcastP2pGoNegotiationRequest(iface, new WifiP2pConfig(dataString)); } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) { - sendMessage(iface, P2P_GO_NEGOTIATION_SUCCESS_EVENT); + broadcastP2pGoNegotiationSuccess(iface); } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) { - sendMessage(iface, P2P_GO_NEGOTIATION_FAILURE_EVENT, p2pError(dataString)); + broadcastP2pGoNegotiationFailure(iface, p2pError(dataString)); } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) { - sendMessage(iface, P2P_GROUP_FORMATION_SUCCESS_EVENT); + broadcastP2pGroupFormationSuccess(iface); } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) { - sendMessage(iface, P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString)); + broadcastP2pGroupFormationFailure(iface, dataString); } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) { - WifiP2pGroup group = getWifiP2pGroup(dataString); - if (group != null) sendMessage(iface, P2P_GROUP_STARTED_EVENT, group); + broadcastP2pGroupStarted(iface, getWifiP2pGroup(dataString)); } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) { - WifiP2pGroup group = getWifiP2pGroup(dataString); - if (group != null) sendMessage(iface, P2P_GROUP_REMOVED_EVENT, group); + broadcastP2pGroupRemoved(iface, getWifiP2pGroup(dataString)); } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) { - sendMessage(iface, P2P_INVITATION_RECEIVED_EVENT, new WifiP2pGroup(dataString)); + broadcastP2pInvitationReceived(iface, new WifiP2pGroup(dataString)); } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) { - sendMessage(iface, P2P_INVITATION_RESULT_EVENT, p2pError(dataString)); + broadcastP2pInvitationResult(iface, p2pError(dataString)); } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) { - sendMessage(iface, P2P_PROV_DISC_PBC_REQ_EVENT, new WifiP2pProvDiscEvent(dataString)); + broadcastP2pProvisionDiscoveryPbcRequest(iface, new WifiP2pProvDiscEvent(dataString)); } else if (dataString.startsWith(P2P_PROV_DISC_PBC_RSP_STR)) { - sendMessage(iface, P2P_PROV_DISC_PBC_RSP_EVENT, new WifiP2pProvDiscEvent(dataString)); + broadcastP2pProvisionDiscoveryPbcResponse(iface, new WifiP2pProvDiscEvent(dataString)); } else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) { - sendMessage(iface, P2P_PROV_DISC_ENTER_PIN_EVENT, new WifiP2pProvDiscEvent(dataString)); + broadcastP2pProvisionDiscoveryEnterPin(iface, new WifiP2pProvDiscEvent(dataString)); } else if (dataString.startsWith(P2P_PROV_DISC_SHOW_PIN_STR)) { - sendMessage(iface, P2P_PROV_DISC_SHOW_PIN_EVENT, new WifiP2pProvDiscEvent(dataString)); + broadcastP2pProvisionDiscoveryShowPin(iface, new WifiP2pProvDiscEvent(dataString)); } else if (dataString.startsWith(P2P_PROV_DISC_FAILURE_STR)) { - sendMessage(iface, P2P_PROV_DISC_FAILURE_EVENT); + broadcastP2pProvisionDiscoveryFailure(iface); } else if (dataString.startsWith(P2P_SERV_DISC_RESP_STR)) { List<WifiP2pServiceResponse> list = WifiP2pServiceResponse.newInstance(dataString); if (list != null) { - sendMessage(iface, P2P_SERV_DISC_RESP_EVENT, list); + broadcastP2pServiceDiscoveryResponse(iface, list); } else { Log.e(TAG, "Null service resp " + dataString); } @@ -1738,4 +1734,202 @@ public class WifiMonitor { public void broadcastSupplicantDisconnectionEvent(String iface) { sendMessage(iface, SUP_DISCONNECTION_EVENT); } + + /** + * Broadcast new p2p device discovered event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param device Device that has been discovered during recent scan. + */ + public void broadcastP2pDeviceFound(String iface, WifiP2pDevice device) { + if (device != null) { + sendMessage(iface, P2P_DEVICE_FOUND_EVENT, device); + } + } + + /** + * Broadcast p2p device lost event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param device Device that has been lost in recent scan. + */ + public void broadcastP2pDeviceLost(String iface, WifiP2pDevice device) { + if (device != null) { + sendMessage(iface, P2P_DEVICE_LOST_EVENT, device); + } + } + + /** + * Broadcast scan termination event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + */ + public void broadcastP2pFindStopped(String iface) { + sendMessage(iface, P2P_FIND_STOPPED_EVENT); + } + + /** + * Broadcast group owner negotiation request event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param config P2p configuration. + */ + public void broadcastP2pGoNegotiationRequest(String iface, WifiP2pConfig config) { + if (config != null) { + sendMessage(iface, P2P_GO_NEGOTIATION_REQUEST_EVENT, config); + } + } + + /** + * Broadcast group owner negotiation success event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + */ + public void broadcastP2pGoNegotiationSuccess(String iface) { + sendMessage(iface, P2P_GO_NEGOTIATION_SUCCESS_EVENT); + } + + /** + * Broadcast group owner negotiation failure event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param reason Failure reason. + */ + public void broadcastP2pGoNegotiationFailure(String iface, P2pStatus reason) { + sendMessage(iface, P2P_GO_NEGOTIATION_FAILURE_EVENT, reason); + } + + /** + * Broadcast group formation success event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + */ + public void broadcastP2pGroupFormationSuccess(String iface) { + sendMessage(iface, P2P_GROUP_FORMATION_SUCCESS_EVENT); + } + + /** + * Broadcast group formation failure event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param reason Failure reason. + */ + public void broadcastP2pGroupFormationFailure(String iface, String reason) { + sendMessage(iface, P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(reason)); + } + + /** + * Broadcast group started event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param group Started group. + */ + public void broadcastP2pGroupStarted(String iface, WifiP2pGroup group) { + if (group != null) { + sendMessage(iface, P2P_GROUP_STARTED_EVENT, group); + } + } + + /** + * Broadcast group removed event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param group Removed group. + */ + public void broadcastP2pGroupRemoved(String iface, WifiP2pGroup group) { + if (group != null) { + sendMessage(iface, P2P_GROUP_REMOVED_EVENT, group); + } + } + + /** + * Broadcast invitation received event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param group Group to which invitation has been received. + */ + public void broadcastP2pInvitationReceived(String iface, WifiP2pGroup group) { + if (group != null) { + sendMessage(iface, P2P_INVITATION_RECEIVED_EVENT, group); + } + } + + /** + * Broadcast invitation result event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param result Result of invitation. + */ + public void broadcastP2pInvitationResult(String iface, P2pStatus result) { + sendMessage(iface, P2P_INVITATION_RESULT_EVENT, result); + } + + /** + * Broadcast PB discovery request event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery request event. + */ + public void broadcastP2pProvisionDiscoveryPbcRequest(String iface, WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_PBC_REQ_EVENT, event); + } + } + + /** + * Broadcast PB discovery response event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery response event. + */ + public void broadcastP2pProvisionDiscoveryPbcResponse( + String iface, WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_PBC_RSP_EVENT, event); + } + } + + /** + * Broadcast PIN discovery request event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery request event. + */ + public void broadcastP2pProvisionDiscoveryEnterPin(String iface, WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_ENTER_PIN_EVENT, event); + } + } + + /** + * Broadcast PIN discovery response event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery response event. + */ + public void broadcastP2pProvisionDiscoveryShowPin(String iface, WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_SHOW_PIN_EVENT, event); + } + } + + /** + * Broadcast P2P discovery failure event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + */ + public void broadcastP2pProvisionDiscoveryFailure(String iface) { + sendMessage(iface, P2P_PROV_DISC_FAILURE_EVENT); + } + + /** + * Broadcast service discovery response event to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param services List of discovered services. + */ + public void broadcastP2pServiceDiscoveryResponse( + String iface, List<WifiP2pServiceResponse> services) { + sendMessage(iface, P2P_SERV_DISC_RESP_EVENT, services); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceCallbackTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceCallbackTest.java new file mode 100644 index 000000000..bf6299672 --- /dev/null +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceCallbackTest.java @@ -0,0 +1,360 @@ +/* + * 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; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.test.MockAnswerUtil.AnswerWithArguments; +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.WifiP2pWfdInfo; + +import org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.HashSet; + +/** + * Unit tests for SupplicantP2pIfaceCallback + */ +public class SupplicantP2pIfaceCallbackTest { + private static final String TAG = "SupplicantP2pIfaceCallbackTest"; + + private String mIface = "test_p2p0"; + private WifiMonitor mMonitor; + private SupplicantP2pIfaceCallback mDut; + + private byte[] mDeviceAddressInvalid1 = { 0x00 }; + private byte[] mDeviceAddressInvalid2 = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; + private byte[] mDeviceAddress1Bytes = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; + private String mDeviceAddress1String = "00:11:22:33:44:55"; + private byte[] mDeviceAddress2Bytes = { 0x01, 0x12, 0x23, 0x34, 0x45, 0x56 }; + private String mDeviceAddress2String = "01:12:23:34:45:56"; + private byte[] mDeviceInfoBytes = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; + + private class SupplicantP2pIfaceCallbackSpy extends SupplicantP2pIfaceCallback { + SupplicantP2pIfaceCallbackSpy(String iface, WifiMonitor monitor) { + super(iface, monitor); + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mMonitor = mock(WifiMonitor.class); + mDut = new SupplicantP2pIfaceCallbackSpy(mIface, mMonitor); + } + + /** + * Sunny day scenario for onDeviceFound call. + */ + @Test + public void testOnDeviceFound_success() throws Exception { + byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 }; + String fakePrimaryDeviceTypeString = "010203"; + String fakeDeviceName = "test device name"; + short fakeConfigMethods = 0x1234; + byte fakeCapabilities = 123; + int fakeGroupCapabilities = 456; + + doAnswer(new AnswerWithArguments() { + public void answer(String iface, WifiP2pDevice device) { + // NOTE: mDeviceAddress1Bytes seems to be ignored by + // legacy implementation of WifiP2pDevice. + assertEquals(iface, mIface); + assertEquals(device.deviceName, fakeDeviceName); + assertEquals(device.primaryDeviceType, fakePrimaryDeviceTypeString); + assertEquals(device.deviceCapability, fakeCapabilities); + assertEquals(device.groupCapability, fakeGroupCapabilities); + assertEquals(device.wpsConfigMethodsSupported, fakeConfigMethods); + assertEquals(device.deviceAddress, mDeviceAddress2String); + assertEquals(device.status, WifiP2pDevice.AVAILABLE); + } + }) + .when(mMonitor).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + + mDut.onDeviceFound( + mDeviceAddress1Bytes, mDeviceAddress2Bytes, + fakePrimaryDeviceTypeBytes, + fakeDeviceName, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + mDeviceInfoBytes); + + mDut.onDeviceFound( + mDeviceAddress1Bytes, mDeviceAddress2Bytes, + fakePrimaryDeviceTypeBytes, + fakeDeviceName, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + null); + + // Make sure we issued a broadcast each time. + verify(mMonitor, times(2)).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + } + + /** + * Failing scenarios for onDeviceFound call. + */ + @Test + public void testOnDeviceFound_invalidArguments() throws Exception { + byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 }; + String fakePrimaryDeviceTypeString = "010203"; + String fakeDeviceName = "test device name"; + short fakeConfigMethods = 0x1234; + byte fakeCapabilities = 123; + int fakeGroupCapabilities = 456; + + mDut.onDeviceFound( + mDeviceAddress2Bytes, null, + fakePrimaryDeviceTypeBytes, + fakeDeviceName, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + mDeviceInfoBytes); + verify(mMonitor, never()).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + + + mDut.onDeviceFound( + mDeviceAddress1Bytes, mDeviceAddress2Bytes, + null, + fakeDeviceName, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + mDeviceInfoBytes); + verify(mMonitor, never()).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + + + mDut.onDeviceFound( + mDeviceAddress1Bytes, mDeviceAddress2Bytes, + fakePrimaryDeviceTypeBytes, + null, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + mDeviceInfoBytes); + verify(mMonitor, never()).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + + + mDut.onDeviceFound( + mDeviceAddress1Bytes, mDeviceAddressInvalid1, + fakePrimaryDeviceTypeBytes, + null, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + mDeviceInfoBytes); + verify(mMonitor, never()).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + + + mDut.onDeviceFound( + mDeviceAddress1Bytes, mDeviceAddressInvalid2, + fakePrimaryDeviceTypeBytes, + null, fakeConfigMethods, + fakeCapabilities, fakeGroupCapabilities, + mDeviceInfoBytes); + verify(mMonitor, never()).broadcastP2pDeviceFound( + anyString(), any(WifiP2pDevice.class)); + } + + /** + * Sunny day scenario for onDeviceLost call. + */ + @Test + public void testOnDeviceLost_success() throws Exception { + doAnswer(new AnswerWithArguments() { + public void answer(String iface, WifiP2pDevice device) { + assertEquals(iface, mIface); + assertEquals(device.deviceAddress, mDeviceAddress1String); + assertEquals(device.status, WifiP2pDevice.UNAVAILABLE); + } + }) + .when(mMonitor).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + + mDut.onDeviceLost(mDeviceAddress1Bytes); + + // Make sure we issued a broadcast each time. + verify(mMonitor, times(1)).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + } + + /** + * Failing scenarios for onDeviceLost call. + */ + @Test + public void testOnDeviceLost_invalidArguments() throws Exception { + mDut.onDeviceLost(null); + verify(mMonitor, never()).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + + mDut.onDeviceLost(mDeviceAddressInvalid1); + verify(mMonitor, never()).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + + mDut.onDeviceLost(mDeviceAddressInvalid2); + verify(mMonitor, never()).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + } + + /** + * Sunny day scenario for onGoNegotiationRequest call. + */ + @Test + public void testOnGoNegotiationRequest_success() throws Exception { + HashSet<Integer> setups = new HashSet<Integer>(); + + doAnswer(new AnswerWithArguments() { + public void answer(String iface, WifiP2pConfig config) { + assertEquals(iface, mIface); + assertNotNull(config.wps); + setups.add(config.wps.setup); + assertEquals(config.deviceAddress, mDeviceAddress1String); + } + }) + .when(mMonitor).broadcastP2pGoNegotiationRequest( + anyString(), any(WifiP2pConfig.class)); + + mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, + (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.USER_SPECIFIED); + assertTrue(setups.contains(WpsInfo.DISPLAY)); + + mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, + (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.PUSHBUTTON); + assertTrue(setups.contains(WpsInfo.PBC)); + + mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, + (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.REGISTRAR_SPECIFIED); + assertTrue(setups.contains(WpsInfo.KEYPAD)); + + // Invalid should default to PBC + setups.clear(); + mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, (short)0xffff); + assertTrue(setups.contains(WpsInfo.PBC)); + } + + /** + * Failing scenarios for onGoNegotiationRequest call. + */ + @Test + public void testOnGoNegotiationRequest_invalidArguments() throws Exception { + mDut.onGoNegotiationRequest(null, (short)0); + verify(mMonitor, never()).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + + mDut.onGoNegotiationRequest(mDeviceAddressInvalid1, (short)0); + verify(mMonitor, never()).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + + mDut.onGoNegotiationRequest(mDeviceAddressInvalid2, (short)0); + verify(mMonitor, never()).broadcastP2pDeviceLost( + anyString(), any(WifiP2pDevice.class)); + } + + /** + * Sunny day scenario for onGroupStarted call. + */ + @Test + public void testOnGroupStarted_success() throws Exception { + String fakeName = "group name"; + String fakePassphrase = "secret"; + ArrayList<Byte> fakeSsidBytesList = new ArrayList<Byte>() {{ + add((byte)0x30); + add((byte)0x31); + add((byte)0x32); + add((byte)0x33); + }}; + String fakeSsidString = "\"0123\""; + HashSet<String> passwords = new HashSet<String>(); + + doAnswer(new AnswerWithArguments() { + public void answer(String iface, WifiP2pGroup group) { + assertEquals(iface, mIface); + assertNotNull(group.getOwner()); + assertEquals(group.getOwner().deviceAddress, mDeviceAddress1String); + assertEquals(group.getNetworkId(), WifiP2pGroup.PERSISTENT_NET_ID); + passwords.add(group.getPassphrase()); + assertEquals(group.getInterface(), fakeName); + assertEquals(group.getNetworkName(), fakeSsidString); + } + }) + .when(mMonitor).broadcastP2pGroupStarted( + anyString(), any(WifiP2pGroup.class)); + + mDut.onGroupStarted( + fakeName, true, fakeSsidBytesList, 1, null, fakePassphrase, + mDeviceAddress1Bytes, true); + assertTrue(passwords.contains(fakePassphrase)); + + mDut.onGroupStarted( + fakeName, true, fakeSsidBytesList, 1, null, null, + mDeviceAddress1Bytes, true); + assertTrue(passwords.contains(null)); + + verify(mMonitor, times(2)).broadcastP2pGroupStarted( + anyString(), any(WifiP2pGroup.class)); + } + + /** + * Failing scenarios for onGroupStarted call. + */ + @Test + public void testOnGroupStarted_invalidArguments() throws Exception { + String fakeName = "group name"; + String fakePassphrase = "secret"; + ArrayList<Byte> fakeSsidBytesList = new ArrayList<Byte>() {{ + add((byte)0x30); + add((byte)0x31); + add((byte)0x32); + add((byte)0x33); + }}; + String fakeSsidString = "0123"; + + mDut.onGroupStarted( + null, true, fakeSsidBytesList, 1, null, fakePassphrase, + mDeviceAddress1Bytes, true); + verify(mMonitor, never()).broadcastP2pGroupStarted( + anyString(), any(WifiP2pGroup.class)); + + mDut.onGroupStarted( + fakeName, true, null, 1, null, fakePassphrase, + mDeviceAddress1Bytes, true); + verify(mMonitor, never()).broadcastP2pGroupStarted( + anyString(), any(WifiP2pGroup.class)); + + mDut.onGroupStarted( + fakeName, true, fakeSsidBytesList, 1, null, fakePassphrase, + null, true); + verify(mMonitor, never()).broadcastP2pGroupStarted( + anyString(), any(WifiP2pGroup.class)); + } + +} diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceHalTest.java index ca9c29592..b08ebdf97 100644 --- a/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceHalTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SupplicantP2pIfaceHalTest.java @@ -129,7 +129,9 @@ public class SupplicantP2pIfaceHalTest { private InOrder mInOrder; private class SupplicantP2pIfaceHalSpy extends SupplicantP2pIfaceHal { - SupplicantP2pIfaceHalSpy() {} + SupplicantP2pIfaceHalSpy() { + super(null); + } @Override protected IServiceManager getServiceManagerMockable() throws RemoteException { |