1 /* 2 * Copyright (C) 2021 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 com.android.server.connectivity.mdns; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.Network; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.text.TextUtils; 25 26 import com.android.server.connectivity.mdns.util.MdnsUtils; 27 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.List; 31 import java.util.Objects; 32 import java.util.Set; 33 34 /** 35 * API configuration parameters for searching the mDNS service. 36 * 37 * <p>Use {@link MdnsSearchOptions.Builder} to create {@link MdnsSearchOptions}. 38 * 39 * @hide 40 */ 41 public class MdnsSearchOptions implements Parcelable { 42 // Passive query mode scans less frequently in order to conserve battery and produce less 43 // network traffic. 44 public static final int PASSIVE_QUERY_MODE = 0; 45 // Active query mode scans frequently. 46 public static final int ACTIVE_QUERY_MODE = 1; 47 // Aggressive query mode scans more frequently than the active mode at first, and sends both 48 // unicast and multicast queries simultaneously, but in long sessions it eventually sends as 49 // many queries as the PASSIVE mode. 50 public static final int AGGRESSIVE_QUERY_MODE = 2; 51 52 /** @hide */ 53 public static final Parcelable.Creator<MdnsSearchOptions> CREATOR = 54 new Parcelable.Creator<MdnsSearchOptions>() { 55 @Override 56 public MdnsSearchOptions createFromParcel(Parcel source) { 57 return new MdnsSearchOptions( 58 source.createStringArrayList(), 59 source.readInt(), 60 source.readInt() == 1, 61 source.readParcelable(null), 62 source.readInt(), 63 source.readString(), 64 source.readInt() == 1, 65 source.readInt()); 66 } 67 68 @Override 69 public MdnsSearchOptions[] newArray(int size) { 70 return new MdnsSearchOptions[size]; 71 } 72 }; 73 private static MdnsSearchOptions defaultOptions; 74 private final List<String> subtypes; 75 @Nullable 76 private final String resolveInstanceName; 77 private final int queryMode; 78 private final boolean onlyUseIpv6OnIpv6OnlyNetworks; 79 private final int numOfQueriesBeforeBackoff; 80 private final boolean removeExpiredService; 81 // The target network for searching. Null network means search on all possible interfaces. 82 @Nullable private final Network mNetwork; 83 // If the target interface does not have a Network, set to the interface index, otherwise unset. 84 private final int mInterfaceIndex; 85 86 /** Parcelable constructs for a {@link MdnsSearchOptions}. */ MdnsSearchOptions( List<String> subtypes, int queryMode, boolean removeExpiredService, @Nullable Network network, int interfaceIndex, @Nullable String resolveInstanceName, boolean onlyUseIpv6OnIpv6OnlyNetworks, int numOfQueriesBeforeBackoff)87 MdnsSearchOptions( 88 List<String> subtypes, 89 int queryMode, 90 boolean removeExpiredService, 91 @Nullable Network network, 92 int interfaceIndex, 93 @Nullable String resolveInstanceName, 94 boolean onlyUseIpv6OnIpv6OnlyNetworks, 95 int numOfQueriesBeforeBackoff) { 96 this.subtypes = new ArrayList<>(); 97 if (subtypes != null) { 98 this.subtypes.addAll(subtypes); 99 } 100 this.queryMode = queryMode; 101 this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; 102 this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff; 103 this.removeExpiredService = removeExpiredService; 104 mNetwork = network; 105 mInterfaceIndex = interfaceIndex; 106 this.resolveInstanceName = resolveInstanceName; 107 } 108 109 /** Returns a {@link Builder} for {@link MdnsSearchOptions}. */ newBuilder()110 public static Builder newBuilder() { 111 return new Builder(); 112 } 113 114 /** Returns a default search options. */ getDefaultOptions()115 public static synchronized MdnsSearchOptions getDefaultOptions() { 116 if (defaultOptions == null) { 117 defaultOptions = newBuilder().build(); 118 } 119 return defaultOptions; 120 } 121 122 /** @return the list of subtypes to search. */ getSubtypes()123 public List<String> getSubtypes() { 124 return subtypes; 125 } 126 127 /** 128 * @return the current query mode. 129 */ getQueryMode()130 public int getQueryMode() { 131 return queryMode; 132 } 133 134 /** 135 * @return {@code true} if only the IPv4 mDNS host should be queried on network that supports 136 * both IPv6 as well as IPv4. On an IPv6-only network, this is ignored. 137 */ onlyUseIpv6OnIpv6OnlyNetworks()138 public boolean onlyUseIpv6OnIpv6OnlyNetworks() { 139 return onlyUseIpv6OnIpv6OnlyNetworks; 140 } 141 142 /** 143 * Returns number of queries should be executed before backoff mode is enabled. 144 * The default number is 3 if it is not set. 145 */ numOfQueriesBeforeBackoff()146 public int numOfQueriesBeforeBackoff() { 147 return numOfQueriesBeforeBackoff; 148 } 149 150 /** Returns {@code true} if service will be removed after its TTL expires. */ removeExpiredService()151 public boolean removeExpiredService() { 152 return removeExpiredService; 153 } 154 155 /** 156 * Returns the network which the mdns query should target. 157 * 158 * @return the target network or null to search on all possible interfaces. 159 */ 160 @Nullable getNetwork()161 public Network getNetwork() { 162 return mNetwork; 163 } 164 165 166 /** 167 * Returns the interface index which the mdns query should target. 168 * 169 * This is only set when the service is to be searched on an interface that does not have a 170 * Network, in which case {@link #getNetwork()} returns null. 171 * The interface index as per {@link java.net.NetworkInterface#getIndex}, or 0 if unset. 172 */ getInterfaceIndex()173 public int getInterfaceIndex() { 174 return mInterfaceIndex; 175 } 176 177 /** 178 * If non-null, queries should try to resolve all records of this specific service, rather than 179 * discovering all services. 180 */ 181 @Nullable getResolveInstanceName()182 public String getResolveInstanceName() { 183 return resolveInstanceName; 184 } 185 186 @Override describeContents()187 public int describeContents() { 188 return 0; 189 } 190 191 @Override writeToParcel(Parcel out, int flags)192 public void writeToParcel(Parcel out, int flags) { 193 out.writeStringList(subtypes); 194 out.writeInt(queryMode); 195 out.writeInt(removeExpiredService ? 1 : 0); 196 out.writeParcelable(mNetwork, 0); 197 out.writeInt(mInterfaceIndex); 198 out.writeString(resolveInstanceName); 199 out.writeInt(onlyUseIpv6OnIpv6OnlyNetworks ? 1 : 0); 200 out.writeInt(numOfQueriesBeforeBackoff); 201 } 202 203 /** A builder to create {@link MdnsSearchOptions}. */ 204 public static final class Builder { 205 private final Set<String> subtypes; 206 private int queryMode = PASSIVE_QUERY_MODE; 207 private boolean onlyUseIpv6OnIpv6OnlyNetworks = false; 208 private int numOfQueriesBeforeBackoff = 3; 209 private boolean removeExpiredService; 210 private Network mNetwork; 211 private int mInterfaceIndex; 212 private String resolveInstanceName; 213 Builder()214 private Builder() { 215 subtypes = MdnsUtils.newSet(); 216 } 217 218 /** 219 * Adds a subtype to search. 220 * 221 * @param subtype the subtype to add. 222 */ addSubtype(@onNull String subtype)223 public Builder addSubtype(@NonNull String subtype) { 224 if (TextUtils.isEmpty(subtype)) { 225 throw new IllegalArgumentException("Empty subtype"); 226 } 227 subtypes.add(subtype); 228 return this; 229 } 230 231 /** 232 * Adds a set of subtypes to search. 233 * 234 * @param subtypes The list of subtypes to add. 235 */ addSubtypes(@onNull Collection<String> subtypes)236 public Builder addSubtypes(@NonNull Collection<String> subtypes) { 237 this.subtypes.addAll(Objects.requireNonNull(subtypes)); 238 return this; 239 } 240 241 /** 242 * Sets which query mode should be used. 243 * 244 * @param queryMode the query mode should be used. 245 */ setQueryMode(int queryMode)246 public Builder setQueryMode(int queryMode) { 247 this.queryMode = queryMode; 248 return this; 249 } 250 251 /** 252 * Sets if only the IPv4 mDNS host should be queried on a network that is both IPv4 & IPv6. 253 * On an IPv6-only network, this is ignored. 254 */ setOnlyUseIpv6OnIpv6OnlyNetworks(boolean onlyUseIpv6OnIpv6OnlyNetworks)255 public Builder setOnlyUseIpv6OnIpv6OnlyNetworks(boolean onlyUseIpv6OnIpv6OnlyNetworks) { 256 this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; 257 return this; 258 } 259 260 /** 261 * Sets if the query backoff mode should be turned on. 262 */ setNumOfQueriesBeforeBackoff(int numOfQueriesBeforeBackoff)263 public Builder setNumOfQueriesBeforeBackoff(int numOfQueriesBeforeBackoff) { 264 this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff; 265 return this; 266 } 267 268 /** 269 * Sets if the service should be removed after TTL. 270 * 271 * @param removeExpiredService If set to {@code true}, the service will be removed after TTL 272 */ setRemoveExpiredService(boolean removeExpiredService)273 public Builder setRemoveExpiredService(boolean removeExpiredService) { 274 this.removeExpiredService = removeExpiredService; 275 return this; 276 } 277 278 /** 279 * Sets if the mdns query should target on specific network. 280 * 281 * @param network the mdns query will target on given network. 282 */ setNetwork(Network network)283 public Builder setNetwork(Network network) { 284 mNetwork = network; 285 return this; 286 } 287 288 /** 289 * Set the instance name to resolve. 290 * 291 * If non-null, queries should try to resolve all records of this specific service, 292 * rather than discovering all services. 293 * @param name The instance name. 294 */ setResolveInstanceName(String name)295 public Builder setResolveInstanceName(String name) { 296 resolveInstanceName = name; 297 return this; 298 } 299 300 /** 301 * Set the interface index to use for the query, if not querying on a {@link Network}. 302 * 303 * @see MdnsSearchOptions#getInterfaceIndex() 304 */ setInterfaceIndex(int index)305 public Builder setInterfaceIndex(int index) { 306 mInterfaceIndex = index; 307 return this; 308 } 309 310 /** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */ build()311 public MdnsSearchOptions build() { 312 return new MdnsSearchOptions( 313 new ArrayList<>(subtypes), 314 queryMode, 315 removeExpiredService, 316 mNetwork, 317 mInterfaceIndex, 318 resolveInstanceName, 319 onlyUseIpv6OnIpv6OnlyNetworks, 320 numOfQueriesBeforeBackoff); 321 } 322 } 323 }