content: /*
   * Copyright (C) 2012 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 android.net.wifi.p2p.nsd;
  
  import android.annotation.UnsupportedAppUsage;
  import android.net.wifi.p2p.WifiP2pManager;
  import android.os.Build;
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import java.util.Locale;
  
  /**
   * A class for creating a service discovery request for use with
   * {@link WifiP2pManager#addServiceRequest} and {@link WifiP2pManager#removeServiceRequest}
   *
   * <p>This class is used to create service discovery request for custom
   * vendor specific service discovery protocol {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}
   * or to search all service protocols {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}.
   *
   * <p>For the purpose of creating a UPnP or Bonjour service request, use
   * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest} respectively.
   *
   * {@see WifiP2pManager}
   * {@see WifiP2pUpnpServiceRequest}
   * {@see WifiP2pDnsSdServiceRequest}
   */
  public class WifiP2pServiceRequest implements Parcelable {
  
      /**
       * Service discovery protocol. It's defined in table63 in Wi-Fi Direct specification.
       */
      private int mProtocolType;
  
      /**
       * The length of the service request TLV.
       * The value is equal to 2 plus the number of octets in the
       * query data field.
       */
      private int mLength;
  
      /**
       * Service transaction ID.
       * This is a nonzero value used to match the service request/response TLVs.
       */
      private int mTransId;
  
      /**
       * The hex dump string of query data for the requested service information.
       *
       * e.g) DnsSd apple file sharing over tcp (dns name=_afpovertcp._tcp.local.)
       * 0b5f6166706f766572746370c00c000c01
       */
      private String mQuery;
  
      /**
       * This constructor is only used in newInstance().
       *
       * @param protocolType service discovery protocol.
       * @param query The part of service specific query.
       * @hide
       */
      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
      protected WifiP2pServiceRequest(int protocolType, String query) {
          validateQuery(query);
  
          mProtocolType = protocolType;
          mQuery = query;
          if (query != null) {
              mLength = query.length()/2 + 2;
          } else {
              mLength = 2;
          }
      }
  
      /**
       * This constructor is only used in Parcelable.
       *
       * @param serviceType service discovery type.
       * @param length the length of service discovery packet.
       * @param transId the transaction id
       * @param query The part of service specific query.
       */
      private WifiP2pServiceRequest(int serviceType, int length,
              int transId, String query) {
          mProtocolType = serviceType;
          mLength = length;
          mTransId = transId;
          mQuery = query;
      }
  
      /**
       * Return transaction id.
       *
       * @return transaction id
       * @hide
       */
      public int getTransactionId() {
          return mTransId;
      }
  
      /**
       * Set transaction id.
       *
       * @param id
       * @hide
       */
      public void setTransactionId(int id) {
          mTransId = id;
      }
  
      /**
       * Return wpa_supplicant request string.
       *
       * The format is the hex dump of the following frame.
       * <pre>
       * _______________________________________________________________
       * |        Length (2)        |   Type (1)   | Transaction ID (1) |
       * |                  Query Data (variable)                       |
       * </pre>
       *
       * @return wpa_supplicant request string.
       * @hide
       */
      public String getSupplicantQuery() {
          StringBuffer sb = new StringBuffer();
          // length is retained as little endian format.
          sb.append(String.format(Locale.US, "%02x", (mLength) & 0xff));
          sb.append(String.format(Locale.US, "%02x", (mLength >> 8) & 0xff));
          sb.append(String.format(Locale.US, "%02x", mProtocolType));
          sb.append(String.format(Locale.US, "%02x", mTransId));
          if (mQuery != null) {
              sb.append(mQuery);
          }
  
          return sb.toString();
      }
  
      /**
       * Validate query.
       *
       * <p>If invalid, throw IllegalArgumentException.
       * @param query The part of service specific query.
       */
      private void validateQuery(String query) {
          if (query == null) {
              return;
          }
  
          int UNSIGNED_SHORT_MAX = 0xffff;
          if (query.length()%2 == 1) {
              throw new IllegalArgumentException(
                      "query size is invalid. query=" + query);
          }
          if (query.length()/2 > UNSIGNED_SHORT_MAX) {
              throw new IllegalArgumentException(
                      "query size is too large. len=" + query.length());
          }
  
          // check whether query is hex string.
          query = query.toLowerCase(Locale.ROOT);
          char[] chars = query.toCharArray();
          for (char c: chars) {
              if (!((c >= '0' && c <= '9') ||
                      (c >= 'a' && c <= 'f'))){
                  throw new IllegalArgumentException(
                          "query should be hex string. query=" + query);
              }
          }
      }
  
      /**
       * Create a service discovery request.
       *
       * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}
       * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
       * In order to create a UPnP or Bonjour service request, use
       * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest}
       * respectively
       *
       * @param queryData hex string that is vendor specific.  Can be null.
       * @return service discovery request.
       */
      public static WifiP2pServiceRequest newInstance(int protocolType, String queryData) {
          return new WifiP2pServiceRequest(protocolType, queryData);
      }
  
      /**
       * Create a service discovery request.
       *
       * @param protocolType can be {@link WifiP2pServiceInfo#SERVICE_TYPE_ALL}
       * or {@link WifiP2pServiceInfo#SERVICE_TYPE_VENDOR_SPECIFIC}.
       * In order to create a UPnP or Bonjour service request, use
       * {@link WifiP2pUpnpServiceRequest} or {@link WifiP2pDnsSdServiceRequest}
       * respectively
       *
       * @return service discovery request.
       */
      public static WifiP2pServiceRequest newInstance(int protocolType ) {
          return new WifiP2pServiceRequest(protocolType, null);
      }
  
      @Override
      public boolean equals(Object o) {
          if (o == this) {
              return true;
          }
          if (!(o instanceof WifiP2pServiceRequest)) {
              return false;
          }
  
          WifiP2pServiceRequest req = (WifiP2pServiceRequest)o;
  
          /*
           * Not compare transaction id.
           * Transaction id may be changed on each service discovery operation.
           */
          if ((req.mProtocolType != mProtocolType) ||
                  (req.mLength != mLength)) {
              return false;
          }
  
          if (req.mQuery == null && mQuery == null) {
              return true;
          } else if (req.mQuery != null) {
              return req.mQuery.equals(mQuery);
          }
          return false;
     }
  
      @Override
      public int hashCode() {
          int result = 17;
          result = 31 * result + mProtocolType;
          result = 31 * result + mLength;
          result = 31 * result + (mQuery == null ? 0 : mQuery.hashCode());
          return result;
      }
  
      /** Implement the Parcelable interface {@hide} */
      public int describeContents() {
          return 0;
      }
  
      /** Implement the Parcelable interface {@hide} */
      public void writeToParcel(Parcel dest, int flags) {
          dest.writeInt(mProtocolType);
          dest.writeInt(mLength);
          dest.writeInt(mTransId);
          dest.writeString(mQuery);
      }
  
      /** Implement the Parcelable interface {@hide} */
      @UnsupportedAppUsage
      public static final @android.annotation.NonNull Creator<WifiP2pServiceRequest> CREATOR =
          new Creator<WifiP2pServiceRequest>() {
              public WifiP2pServiceRequest createFromParcel(Parcel in) {
                  int servType = in.readInt();
                  int length = in.readInt();
                  int transId = in.readInt();
                  String query = in.readString();
                  return new WifiP2pServiceRequest(servType, length, transId, query);
              }
  
              public WifiP2pServiceRequest[] newArray(int size) {
                  return new WifiP2pServiceRequest[size];
              }
          };
  }
  