/* * Copyright (C) 2021 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 com.android.server.connectivity.mdns; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Network; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import com.android.server.connectivity.mdns.util.MdnsUtils; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; /** * API configuration parameters for searching the mDNS service. * *

Use {@link MdnsSearchOptions.Builder} to create {@link MdnsSearchOptions}. * * @hide */ public class MdnsSearchOptions implements Parcelable { // Passive query mode scans less frequently in order to conserve battery and produce less // network traffic. public static final int PASSIVE_QUERY_MODE = 0; // Active query mode scans frequently. public static final int ACTIVE_QUERY_MODE = 1; // Aggressive query mode scans more frequently than the active mode at first, and sends both // unicast and multicast queries simultaneously, but in long sessions it eventually sends as // many queries as the PASSIVE mode. public static final int AGGRESSIVE_QUERY_MODE = 2; /** @hide */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public MdnsSearchOptions createFromParcel(Parcel source) { return new MdnsSearchOptions( source.createStringArrayList(), source.readInt(), source.readInt() == 1, source.readParcelable(null), source.readInt(), source.readString(), source.readInt() == 1, source.readInt()); } @Override public MdnsSearchOptions[] newArray(int size) { return new MdnsSearchOptions[size]; } }; private static MdnsSearchOptions defaultOptions; private final List subtypes; @Nullable private final String resolveInstanceName; private final int queryMode; private final boolean onlyUseIpv6OnIpv6OnlyNetworks; private final int numOfQueriesBeforeBackoff; private final boolean removeExpiredService; // The target network for searching. Null network means search on all possible interfaces. @Nullable private final Network mNetwork; // If the target interface does not have a Network, set to the interface index, otherwise unset. private final int mInterfaceIndex; /** Parcelable constructs for a {@link MdnsSearchOptions}. */ MdnsSearchOptions( List subtypes, int queryMode, boolean removeExpiredService, @Nullable Network network, int interfaceIndex, @Nullable String resolveInstanceName, boolean onlyUseIpv6OnIpv6OnlyNetworks, int numOfQueriesBeforeBackoff) { this.subtypes = new ArrayList<>(); if (subtypes != null) { this.subtypes.addAll(subtypes); } this.queryMode = queryMode; this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff; this.removeExpiredService = removeExpiredService; mNetwork = network; mInterfaceIndex = interfaceIndex; this.resolveInstanceName = resolveInstanceName; } /** Returns a {@link Builder} for {@link MdnsSearchOptions}. */ public static Builder newBuilder() { return new Builder(); } /** Returns a default search options. */ public static synchronized MdnsSearchOptions getDefaultOptions() { if (defaultOptions == null) { defaultOptions = newBuilder().build(); } return defaultOptions; } /** @return the list of subtypes to search. */ public List getSubtypes() { return subtypes; } /** * @return the current query mode. */ public int getQueryMode() { return queryMode; } /** * @return {@code true} if only the IPv4 mDNS host should be queried on network that supports * both IPv6 as well as IPv4. On an IPv6-only network, this is ignored. */ public boolean onlyUseIpv6OnIpv6OnlyNetworks() { return onlyUseIpv6OnIpv6OnlyNetworks; } /** * Returns number of queries should be executed before backoff mode is enabled. * The default number is 3 if it is not set. */ public int numOfQueriesBeforeBackoff() { return numOfQueriesBeforeBackoff; } /** Returns {@code true} if service will be removed after its TTL expires. */ public boolean removeExpiredService() { return removeExpiredService; } /** * Returns the network which the mdns query should target. * * @return the target network or null to search on all possible interfaces. */ @Nullable public Network getNetwork() { return mNetwork; } /** * Returns the interface index which the mdns query should target. * * This is only set when the service is to be searched on an interface that does not have a * Network, in which case {@link #getNetwork()} returns null. * The interface index as per {@link java.net.NetworkInterface#getIndex}, or 0 if unset. */ public int getInterfaceIndex() { return mInterfaceIndex; } /** * If non-null, queries should try to resolve all records of this specific service, rather than * discovering all services. */ @Nullable public String getResolveInstanceName() { return resolveInstanceName; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { out.writeStringList(subtypes); out.writeInt(queryMode); out.writeInt(removeExpiredService ? 1 : 0); out.writeParcelable(mNetwork, 0); out.writeInt(mInterfaceIndex); out.writeString(resolveInstanceName); out.writeInt(onlyUseIpv6OnIpv6OnlyNetworks ? 1 : 0); out.writeInt(numOfQueriesBeforeBackoff); } /** A builder to create {@link MdnsSearchOptions}. */ public static final class Builder { private final Set subtypes; private int queryMode = PASSIVE_QUERY_MODE; private boolean onlyUseIpv6OnIpv6OnlyNetworks = false; private int numOfQueriesBeforeBackoff = 3; private boolean removeExpiredService; private Network mNetwork; private int mInterfaceIndex; private String resolveInstanceName; private Builder() { subtypes = MdnsUtils.newSet(); } /** * Adds a subtype to search. * * @param subtype the subtype to add. */ public Builder addSubtype(@NonNull String subtype) { if (TextUtils.isEmpty(subtype)) { throw new IllegalArgumentException("Empty subtype"); } subtypes.add(subtype); return this; } /** * Adds a set of subtypes to search. * * @param subtypes The list of subtypes to add. */ public Builder addSubtypes(@NonNull Collection subtypes) { this.subtypes.addAll(Objects.requireNonNull(subtypes)); return this; } /** * Sets which query mode should be used. * * @param queryMode the query mode should be used. */ public Builder setQueryMode(int queryMode) { this.queryMode = queryMode; return this; } /** * Sets if only the IPv4 mDNS host should be queried on a network that is both IPv4 & IPv6. * On an IPv6-only network, this is ignored. */ public Builder setOnlyUseIpv6OnIpv6OnlyNetworks(boolean onlyUseIpv6OnIpv6OnlyNetworks) { this.onlyUseIpv6OnIpv6OnlyNetworks = onlyUseIpv6OnIpv6OnlyNetworks; return this; } /** * Sets if the query backoff mode should be turned on. */ public Builder setNumOfQueriesBeforeBackoff(int numOfQueriesBeforeBackoff) { this.numOfQueriesBeforeBackoff = numOfQueriesBeforeBackoff; return this; } /** * Sets if the service should be removed after TTL. * * @param removeExpiredService If set to {@code true}, the service will be removed after TTL */ public Builder setRemoveExpiredService(boolean removeExpiredService) { this.removeExpiredService = removeExpiredService; return this; } /** * Sets if the mdns query should target on specific network. * * @param network the mdns query will target on given network. */ public Builder setNetwork(Network network) { mNetwork = network; return this; } /** * Set the instance name to resolve. * * If non-null, queries should try to resolve all records of this specific service, * rather than discovering all services. * @param name The instance name. */ public Builder setResolveInstanceName(String name) { resolveInstanceName = name; return this; } /** * Set the interface index to use for the query, if not querying on a {@link Network}. * * @see MdnsSearchOptions#getInterfaceIndex() */ public Builder setInterfaceIndex(int index) { mInterfaceIndex = index; return this; } /** Builds a {@link MdnsSearchOptions} with the arguments supplied to this builder. */ public MdnsSearchOptions build() { return new MdnsSearchOptions( new ArrayList<>(subtypes), queryMode, removeExpiredService, mNetwork, mInterfaceIndex, resolveInstanceName, onlyUseIpv6OnIpv6OnlyNetworks, numOfQueriesBeforeBackoff); } } }