content: /*
   * 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.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 SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub {
      private static final String TAG = "SupplicantP2pIfaceCallback";
      private static boolean sVerboseLoggingEnabled = true;
  
      private final String mInterface;
      private final WifiP2pMonitor mMonitor;
  
      public SupplicantP2pIfaceCallback(String iface, WifiP2pMonitor monitor) {
          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.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 != 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;
      }
  }
  
  