1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.nsd; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.net.Network; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.text.TextUtils; 26 27 import java.util.Objects; 28 29 /** 30 * Encapsulates parameters for {@link NsdManager#discoverServices}. 31 */ 32 @FlaggedApi(NsdManager.Flags.NSD_SUBTYPES_SUPPORT_ENABLED) 33 public final class DiscoveryRequest implements Parcelable { 34 private final int mProtocolType; 35 36 @NonNull 37 private final String mServiceType; 38 39 @Nullable 40 private final String mSubtype; 41 42 @Nullable 43 private final Network mNetwork; 44 45 // TODO: add mDiscoveryConfig for more fine-grained discovery behavior control 46 47 @NonNull 48 public static final Creator<DiscoveryRequest> CREATOR = 49 new Creator<>() { 50 @Override 51 public DiscoveryRequest createFromParcel(Parcel in) { 52 int protocolType = in.readInt(); 53 String serviceType = in.readString(); 54 String subtype = in.readString(); 55 Network network = 56 in.readParcelable(Network.class.getClassLoader(), Network.class); 57 return new DiscoveryRequest(protocolType, serviceType, subtype, network); 58 } 59 60 @Override 61 public DiscoveryRequest[] newArray(int size) { 62 return new DiscoveryRequest[size]; 63 } 64 }; 65 DiscoveryRequest(int protocolType, @NonNull String serviceType, @Nullable String subtype, @Nullable Network network)66 private DiscoveryRequest(int protocolType, @NonNull String serviceType, 67 @Nullable String subtype, @Nullable Network network) { 68 mProtocolType = protocolType; 69 mServiceType = serviceType; 70 mSubtype = subtype; 71 mNetwork = network; 72 } 73 74 /** 75 * Returns the service type in format of dot-joint string of two labels. 76 * 77 * For example, "_ipp._tcp" for internet printer and "_matter._tcp" for <a 78 * href="https://csa-iot.org/all-solutions/matter">Matter</a> operational device. 79 */ 80 @NonNull getServiceType()81 public String getServiceType() { 82 return mServiceType; 83 } 84 85 /** 86 * Returns the subtype without the trailing "._sub" label or {@code null} if no subtype is 87 * specified. 88 * 89 * For example, the return value will be "_printer" for subtype "_printer._sub". 90 */ 91 @Nullable getSubtype()92 public String getSubtype() { 93 return mSubtype; 94 } 95 96 /** 97 * Returns the service discovery protocol. 98 * 99 * @hide 100 */ getProtocolType()101 public int getProtocolType() { 102 return mProtocolType; 103 } 104 105 /** 106 * Returns the {@link Network} on which the query should be sent or {@code null} if no 107 * network is specified. 108 */ 109 @Nullable getNetwork()110 public Network getNetwork() { 111 return mNetwork; 112 } 113 114 @Override toString()115 public String toString() { 116 StringBuilder sb = new StringBuilder(); 117 sb.append(", protocolType: ").append(mProtocolType) 118 .append(", serviceType: ").append(mServiceType) 119 .append(", subtype: ").append(mSubtype) 120 .append(", network: ").append(mNetwork); 121 return sb.toString(); 122 } 123 124 @Override equals(Object other)125 public boolean equals(Object other) { 126 if (this == other) { 127 return true; 128 } else if (!(other instanceof DiscoveryRequest)) { 129 return false; 130 } else { 131 DiscoveryRequest otherRequest = (DiscoveryRequest) other; 132 return mProtocolType == otherRequest.mProtocolType 133 && Objects.equals(mServiceType, otherRequest.mServiceType) 134 && Objects.equals(mSubtype, otherRequest.mSubtype) 135 && Objects.equals(mNetwork, otherRequest.mNetwork); 136 } 137 } 138 139 @Override hashCode()140 public int hashCode() { 141 return Objects.hash(mProtocolType, mServiceType, mSubtype, mNetwork); 142 } 143 144 @Override describeContents()145 public int describeContents() { 146 return 0; 147 } 148 149 @Override writeToParcel(@onNull Parcel dest, int flags)150 public void writeToParcel(@NonNull Parcel dest, int flags) { 151 dest.writeInt(mProtocolType); 152 dest.writeString(mServiceType); 153 dest.writeString(mSubtype); 154 dest.writeParcelable(mNetwork, flags); 155 } 156 157 /** The builder for creating new {@link DiscoveryRequest} objects. */ 158 public static final class Builder { 159 private final int mProtocolType; 160 161 @NonNull 162 private String mServiceType; 163 164 @Nullable 165 private String mSubtype; 166 167 @Nullable 168 private Network mNetwork; 169 170 /** 171 * Creates a new default {@link Builder} object with given service type. 172 * 173 * @throws IllegalArgumentException if {@code serviceType} is {@code null} or an empty 174 * string 175 */ Builder(@onNull String serviceType)176 public Builder(@NonNull String serviceType) { 177 this(NsdManager.PROTOCOL_DNS_SD, serviceType); 178 } 179 180 /** @hide */ Builder(int protocolType, @NonNull String serviceType)181 public Builder(int protocolType, @NonNull String serviceType) { 182 NsdManager.checkProtocol(protocolType); 183 mProtocolType = protocolType; 184 setServiceType(serviceType); 185 } 186 187 /** 188 * Sets the service type to be discovered or {@code null} if no services should be queried. 189 * 190 * The {@code serviceType} must be a dot-joint string of two labels. For example, 191 * "_ipp._tcp" for internet printer. Additionally, the first label must start with 192 * underscore ('_') and the second label must be either "_udp" or "_tcp". Otherwise, {@link 193 * NsdManager#discoverServices} will fail with {@link NsdManager#FAILURE_BAD_PARAMETER}. 194 * 195 * @throws IllegalArgumentException if {@code serviceType} is {@code null} or an empty 196 * string 197 * 198 * @hide 199 */ 200 @NonNull setServiceType(@onNull String serviceType)201 public Builder setServiceType(@NonNull String serviceType) { 202 if (TextUtils.isEmpty(serviceType)) { 203 throw new IllegalArgumentException("Service type cannot be empty"); 204 } 205 mServiceType = serviceType; 206 return this; 207 } 208 209 /** 210 * Sets the optional subtype of the services to be discovered. 211 * 212 * If a non-empty {@code subtype} is specified, it must start with underscore ('_') and 213 * have the trailing "._sub" removed. Otherwise, {@link NsdManager#discoverServices} will 214 * fail with {@link NsdManager#FAILURE_BAD_PARAMETER}. For example, {@code subtype} should 215 * be "_printer" for DNS name "_printer._sub._http._tcp". In this case, only services with 216 * this {@code subtype} will be queried, rather than all services of the base service type. 217 * 218 * Note that a non-empty service type must be specified with {@link #setServiceType} if a 219 * non-empty subtype is specified by this method. 220 */ 221 @NonNull setSubtype(@ullable String subtype)222 public Builder setSubtype(@Nullable String subtype) { 223 mSubtype = subtype; 224 return this; 225 } 226 227 /** 228 * Sets the {@link Network} on which the discovery queries should be sent. 229 * 230 * @param network the discovery network or {@code null} if the query should be sent on 231 * all supported networks 232 */ 233 @NonNull setNetwork(@ullable Network network)234 public Builder setNetwork(@Nullable Network network) { 235 mNetwork = network; 236 return this; 237 } 238 239 /** 240 * Creates a new {@link DiscoveryRequest} object. 241 */ 242 @NonNull build()243 public DiscoveryRequest build() { 244 return new DiscoveryRequest(mProtocolType, mServiceType, mSubtype, mNetwork); 245 } 246 } 247 } 248