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.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.RequiresApi;
24 import android.annotation.SystemApi;
25 import android.os.Build;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 
29 import com.android.net.module.util.HexDump;
30 
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * The OffloadServiceInfo class contains all the necessary information the OffloadEngine needs to
38  * know about how to offload an mDns service. The OffloadServiceInfo is keyed on
39  * {@link OffloadServiceInfo.Key} which is a (serviceName, serviceType) pair.
40  *
41  * @hide
42  */
43 @FlaggedApi("com.android.net.flags.register_nsd_offload_engine_api")
44 @SystemApi
45 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
46 public final class OffloadServiceInfo implements Parcelable {
47     @NonNull
48     private final Key mKey;
49     @NonNull
50     private final String mHostname;
51     @NonNull final List<String> mSubtypes;
52     @Nullable
53     private final byte[] mOffloadPayload;
54     private final int mPriority;
55     private final long mOffloadType;
56 
57     /**
58      * Creates a new OffloadServiceInfo object with the specified parameters.
59      *
60      * @param key The key of the service.
61      * @param subtypes The list of subTypes of the service.
62      * @param hostname The name of the host offering the service. It is meaningful only when
63      *                 offloadType contains OFFLOAD_REPLY.
64      * @param offloadPayload The raw udp payload for hardware offloading.
65      * @param priority The priority of the service, @see #getPriority.
66      * @param offloadType The type of the service offload, @see #getOffloadType.
67      */
OffloadServiceInfo(@onNull Key key, @NonNull List<String> subtypes, @NonNull String hostname, @Nullable byte[] offloadPayload, @IntRange(from = 0, to = Integer.MAX_VALUE) int priority, @OffloadEngine.OffloadType long offloadType)68     public OffloadServiceInfo(@NonNull Key key,
69             @NonNull List<String> subtypes, @NonNull String hostname,
70             @Nullable byte[] offloadPayload,
71             @IntRange(from = 0, to = Integer.MAX_VALUE) int priority,
72             @OffloadEngine.OffloadType long offloadType) {
73         Objects.requireNonNull(key);
74         Objects.requireNonNull(subtypes);
75         Objects.requireNonNull(hostname);
76         mKey = key;
77         mSubtypes = subtypes;
78         mHostname = hostname;
79         mOffloadPayload = offloadPayload;
80         mPriority = priority;
81         mOffloadType = offloadType;
82     }
83 
84     /**
85      * Creates a new OffloadServiceInfo object from a Parcel.
86      *
87      * @param in The Parcel to read the object from.
88      *
89      * @hide
90      */
OffloadServiceInfo(@onNull Parcel in)91     public OffloadServiceInfo(@NonNull Parcel in) {
92         mKey = in.readParcelable(Key.class.getClassLoader(),
93                 Key.class);
94         mSubtypes = in.createStringArrayList();
95         mHostname = in.readString();
96         mOffloadPayload = in.createByteArray();
97         mPriority = in.readInt();
98         mOffloadType = in.readLong();
99     }
100 
101     @Override
writeToParcel(@onNull Parcel dest, int flags)102     public void writeToParcel(@NonNull Parcel dest, int flags) {
103         dest.writeParcelable(mKey, flags);
104         dest.writeStringList(mSubtypes);
105         dest.writeString(mHostname);
106         dest.writeByteArray(mOffloadPayload);
107         dest.writeInt(mPriority);
108         dest.writeLong(mOffloadType);
109     }
110 
111     @Override
describeContents()112     public int describeContents() {
113         return 0;
114     }
115 
116     @NonNull
117     public static final Creator<OffloadServiceInfo> CREATOR = new Creator<OffloadServiceInfo>() {
118         @Override
119         public OffloadServiceInfo createFromParcel(Parcel in) {
120             return new OffloadServiceInfo(in);
121         }
122 
123         @Override
124         public OffloadServiceInfo[] newArray(int size) {
125             return new OffloadServiceInfo[size];
126         }
127     };
128 
129     /**
130      * Get the {@link Key}.
131      */
132     @NonNull
getKey()133     public Key getKey() {
134         return mKey;
135     }
136 
137     /**
138      * Get the host name. (e.g. "Android.local" )
139      */
140     @NonNull
getHostname()141     public String getHostname() {
142         return mHostname;
143     }
144 
145     /**
146      * Get the service subtypes. (e.g. ["_ann"] )
147      */
148     @NonNull
getSubtypes()149     public List<String> getSubtypes() {
150         return Collections.unmodifiableList(mSubtypes);
151     }
152 
153     /**
154      * Get the raw udp payload that the OffloadEngine can use to directly reply the incoming query.
155      * <p>
156      * It is null if the OffloadEngine can not handle transmit. The packet must be sent as-is when
157      * replying to query.
158      */
159     @Nullable
getOffloadPayload()160     public byte[] getOffloadPayload() {
161         if (mOffloadPayload == null) {
162             return null;
163         } else {
164             return mOffloadPayload.clone();
165         }
166     }
167 
168     /**
169      * Create a new OffloadServiceInfo with payload updated.
170      *
171      * @hide
172      */
173     @NonNull
withOffloadPayload(@onNull byte[] offloadPayload)174     public OffloadServiceInfo withOffloadPayload(@NonNull byte[] offloadPayload) {
175         return new OffloadServiceInfo(
176                 this.getKey(),
177                 this.getSubtypes(),
178                 this.getHostname(),
179                 offloadPayload,
180                 this.getPriority(),
181                 this.getOffloadType()
182         );
183     }
184 
185     /**
186      * Get the offloadType.
187      * <p>
188      * For example, if the {@link com.android.server.NsdService} requests the OffloadEngine to both
189      * filter the mDNS queries and replies, the {@link #mOffloadType} =
190      * ({@link OffloadEngine#OFFLOAD_TYPE_FILTER_QUERIES} |
191      * {@link OffloadEngine#OFFLOAD_TYPE_FILTER_REPLIES}).
192      */
getOffloadType()193     @OffloadEngine.OffloadType public long getOffloadType() {
194         return mOffloadType;
195     }
196 
197     /**
198      * Get the priority for the OffloadServiceInfo.
199      * <p>
200      * When OffloadEngine don't have enough resource
201      * (e.g. not enough memory) to offload all the OffloadServiceInfo. The OffloadServiceInfo
202      * having lower priority values should be handled by the OffloadEngine first.
203      */
getPriority()204     public int getPriority() {
205         return mPriority;
206     }
207 
208     /**
209      * Only for debug purpose, the string can be long as the raw packet is dump in the string.
210      */
211     @Override
toString()212     public String toString() {
213         return String.format(
214                 "OffloadServiceInfo{ mOffloadServiceInfoKey=%s, mHostName=%s, "
215                         + "mOffloadPayload=%s, mPriority=%d, mOffloadType=%d, mSubTypes=%s }",
216                 mKey,
217                 mHostname, HexDump.dumpHexString(mOffloadPayload), mPriority,
218                 mOffloadType, mSubtypes.toString());
219     }
220 
221     @Override
equals(Object o)222     public boolean equals(Object o) {
223         if (this == o) return true;
224         if (!(o instanceof OffloadServiceInfo)) return false;
225         OffloadServiceInfo that = (OffloadServiceInfo) o;
226         return mPriority == that.mPriority && mOffloadType == that.mOffloadType
227                 && mKey.equals(that.mKey)
228                 && mHostname.equals(
229                 that.mHostname) && Arrays.equals(mOffloadPayload,
230                 that.mOffloadPayload)
231                 && mSubtypes.equals(that.mSubtypes);
232     }
233 
234     @Override
hashCode()235     public int hashCode() {
236         int result = Objects.hash(mKey, mHostname, mPriority,
237                 mOffloadType, mSubtypes);
238         result = 31 * result + Arrays.hashCode(mOffloadPayload);
239         return result;
240     }
241 
242     /**
243      * The {@link OffloadServiceInfo.Key} is the (serviceName, serviceType) pair.
244      */
245     public static final class Key implements Parcelable {
246         @NonNull
247         private final String mServiceName;
248         @NonNull
249         private final String mServiceType;
250 
251         /**
252          * Creates a new OffloadServiceInfoKey object with the specified parameters.
253          *
254          * @param serviceName The name of the service.
255          * @param serviceType The type of the service.
256          */
Key(@onNull String serviceName, @NonNull String serviceType)257         public Key(@NonNull String serviceName, @NonNull String serviceType) {
258             Objects.requireNonNull(serviceName);
259             Objects.requireNonNull(serviceType);
260             mServiceName = serviceName;
261             mServiceType = serviceType;
262         }
263 
264         /**
265          * Creates a new OffloadServiceInfoKey object from a Parcel.
266          *
267          * @param in The Parcel to read the object from.
268          *
269          * @hide
270          */
Key(@onNull Parcel in)271         public Key(@NonNull Parcel in) {
272             mServiceName = in.readString();
273             mServiceType = in.readString();
274         }
275         /**
276          * Get the service name. (e.g. "NsdChat")
277          */
278         @NonNull
getServiceName()279         public String getServiceName() {
280             return mServiceName;
281         }
282 
283         /**
284          * Get the service type. (e.g. "_http._tcp.local" )
285          */
286         @NonNull
getServiceType()287         public String getServiceType() {
288             return mServiceType;
289         }
290 
291         @Override
writeToParcel(@onNull Parcel dest, int flags)292         public void writeToParcel(@NonNull Parcel dest, int flags) {
293             dest.writeString(mServiceName);
294             dest.writeString(mServiceType);
295         }
296 
297         @Override
describeContents()298         public int describeContents() {
299             return 0;
300         }
301 
302         @NonNull
303         public static final Creator<Key> CREATOR =
304                 new Creator<Key>() {
305             @Override
306             public Key createFromParcel(Parcel in) {
307                 return new Key(in);
308             }
309 
310             @Override
311             public Key[] newArray(int size) {
312                 return new Key[size];
313             }
314         };
315 
316         @Override
equals(Object o)317         public boolean equals(Object o) {
318             if (this == o) return true;
319             if (!(o instanceof Key)) return false;
320             Key that = (Key) o;
321             return Objects.equals(mServiceName, that.mServiceName) && Objects.equals(
322                     mServiceType, that.mServiceType);
323         }
324 
325         @Override
hashCode()326         public int hashCode() {
327             return Objects.hash(mServiceName, mServiceType);
328         }
329 
330         @Override
toString()331         public String toString() {
332             return String.format("OffloadServiceInfoKey{ mServiceName=%s, mServiceType=%s }",
333                     mServiceName, mServiceType);
334         }
335     }
336 }
337