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