blob: 4a3e1be8fa68aef9ed4400e6c35695a7e83bba23 [file] [log] [blame]
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +09001/*
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
17package android.net;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.os.Parcel;
23import android.os.Parcelable;
Yang Sund25444f2023-10-24 18:43:15 +080024import android.text.TextUtils;
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +090025import android.util.ArraySet;
26import android.util.Log;
27
28import java.lang.annotation.Retention;
29import java.lang.annotation.RetentionPolicy;
30import java.net.Inet6Address;
31import java.net.UnknownHostException;
32import java.util.Arrays;
33import java.util.Collections;
Yang Sund25444f2023-10-24 18:43:15 +080034import java.util.Objects;
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +090035import java.util.Set;
Yang Sund25444f2023-10-24 18:43:15 +080036import java.util.StringJoiner;
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +090037
38/**
39 * A class representing a configuration for multicast routing.
40 *
41 * Internal usage to Connectivity
42 * @hide
43 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +090044// @SystemApi(client = MODULE_LIBRARIES)
45public final class MulticastRoutingConfig implements Parcelable {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +090046 private static final String TAG = MulticastRoutingConfig.class.getSimpleName();
47
48 /** Do not forward any multicast packets. */
49 public static final int FORWARD_NONE = 0;
50 /**
51 * Forward only multicast packets with destination in the list of listening addresses.
52 * Ignore the min scope.
53 */
54 public static final int FORWARD_SELECTED = 1;
55 /**
56 * Forward all multicast packets with scope greater or equal than the min scope.
57 * Ignore the list of listening addresses.
58 */
59 public static final int FORWARD_WITH_MIN_SCOPE = 2;
60
Chalard Jeanbb2557a2023-10-09 15:44:56 +090061 /** @hide */
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +090062 @Retention(RetentionPolicy.SOURCE)
63 @IntDef(prefix = { "FORWARD_" }, value = {
64 FORWARD_NONE,
65 FORWARD_SELECTED,
66 FORWARD_WITH_MIN_SCOPE
67 })
68 public @interface MulticastForwardingMode {}
69
70 /**
71 * Not a multicast scope, for configurations that do not use the min scope.
72 */
73 public static final int MULTICAST_SCOPE_NONE = -1;
74
Chalard Jeanbb2557a2023-10-09 15:44:56 +090075 /** @hide */
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +090076 public static final MulticastRoutingConfig CONFIG_FORWARD_NONE =
77 new MulticastRoutingConfig(FORWARD_NONE, MULTICAST_SCOPE_NONE, null);
78
79 @MulticastForwardingMode
80 private final int mForwardingMode;
81
82 private final int mMinScope;
83
84 @NonNull
85 private final Set<Inet6Address> mListeningAddresses;
86
87 private MulticastRoutingConfig(@MulticastForwardingMode final int mode, final int scope,
88 @Nullable final Set<Inet6Address> addresses) {
89 mForwardingMode = mode;
90 mMinScope = scope;
91 if (null != addresses) {
92 mListeningAddresses = Collections.unmodifiableSet(new ArraySet<>(addresses));
93 } else {
94 mListeningAddresses = Collections.emptySet();
95 }
96 }
97
98 /**
99 * Returns the forwarding mode.
100 */
101 @MulticastForwardingMode
102 public int getForwardingMode() {
103 return mForwardingMode;
104 }
105
106 /**
107 * Returns the minimal group address scope that is allowed for forwarding.
108 * If the forwarding mode is not FORWARD_WITH_MIN_SCOPE, will be MULTICAST_SCOPE_NONE.
109 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900110 public int getMinimumScope() {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900111 return mMinScope;
112 }
113
114 /**
115 * Returns the list of group addresses listened by the outgoing interface.
116 * The list will be empty if the forwarding mode is not FORWARD_SELECTED.
117 */
118 @NonNull
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900119 public Set<Inet6Address> getListeningAddresses() {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900120 return mListeningAddresses;
121 }
122
123 private MulticastRoutingConfig(Parcel in) {
124 mForwardingMode = in.readInt();
125 mMinScope = in.readInt();
126 final int count = in.readInt();
127 final ArraySet<Inet6Address> listeningAddresses = new ArraySet<>(count);
128 final byte[] buffer = new byte[16]; // Size of an Inet6Address
129 for (int i = 0; i < count; ++i) {
130 in.readByteArray(buffer);
131 try {
132 listeningAddresses.add((Inet6Address) Inet6Address.getByAddress(buffer));
133 } catch (UnknownHostException e) {
134 Log.wtf(TAG, "Can't read inet6address : " + Arrays.toString(buffer));
135 }
136 }
137 mListeningAddresses = Collections.unmodifiableSet(listeningAddresses);
138 }
139
140 @Override
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900141 public void writeToParcel(@NonNull Parcel dest, int flags) {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900142 dest.writeInt(mForwardingMode);
143 dest.writeInt(mMinScope);
144 dest.writeInt(mListeningAddresses.size());
145 for (final Inet6Address addr : mListeningAddresses) {
146 dest.writeByteArray(addr.getAddress());
147 }
148 }
149
150 @Override
151 public int describeContents() {
152 return 0;
153 }
154
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900155 @NonNull
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900156 public static final Creator<MulticastRoutingConfig> CREATOR = new Creator<>() {
157 @Override
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900158 public MulticastRoutingConfig createFromParcel(@NonNull Parcel in) {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900159 return new MulticastRoutingConfig(in);
160 }
161
162 @Override
163 public MulticastRoutingConfig[] newArray(int size) {
164 return new MulticastRoutingConfig[size];
165 }
166 };
167
Chalard Jean4fe23392023-10-17 23:02:27 +0900168 private static String forwardingModeToString(final int forwardingMode) {
169 switch (forwardingMode) {
170 case FORWARD_NONE: return "NONE";
171 case FORWARD_SELECTED: return "SELECTED";
172 case FORWARD_WITH_MIN_SCOPE: return "WITH_MIN_SCOPE";
173 default: return "UNKNOWN";
174 }
175 }
176
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900177 public static final class Builder {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900178 @MulticastForwardingMode
179 private final int mForwardingMode;
180 private int mMinScope;
181 private final ArraySet<Inet6Address> mListeningAddresses;
182
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900183 // The two constructors with runtime checks for the mode and scope are arguably
184 // less convenient than three static factory methods, but API guidelines mandates
185 // that Builders are built with a constructor and not factory methods.
186 /**
187 * Create a new builder for forwarding mode FORWARD_NONE or FORWARD_SELECTED.
188 *
189 * <p>On a Builder for FORWARD_NONE, no properties can be set.
190 * <p>On a Builder for FORWARD_SELECTED, listening addresses can be added and removed
191 * but the minimum scope can't be set.
192 *
193 * @param mode {@link #FORWARD_NONE} or {@link #FORWARD_SELECTED}. Any other
194 * value will result in IllegalArgumentException.
195 * @see #Builder(int, int)
196 */
197 public Builder(@MulticastForwardingMode final int mode) {
198 if (FORWARD_NONE != mode && FORWARD_SELECTED != mode) {
199 if (FORWARD_WITH_MIN_SCOPE == mode) {
200 throw new IllegalArgumentException("FORWARD_WITH_MIN_SCOPE requires "
201 + "passing the scope as a second argument");
202 } else {
203 throw new IllegalArgumentException("Unknown forwarding mode : " + mode);
204 }
205 }
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900206 mForwardingMode = mode;
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900207 mMinScope = MULTICAST_SCOPE_NONE;
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900208 mListeningAddresses = new ArraySet<>();
209 }
210
211 /**
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900212 * Create a new builder for forwarding mode FORWARD_WITH_MIN_SCOPE.
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900213 *
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900214 * <p>On this Builder the scope can be set with {@link #setMinimumScope}, but
215 * listening addresses can't be added or removed.
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900216 *
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900217 * @param mode Must be {@link #FORWARD_WITH_MIN_SCOPE}.
218 * @param scope the minimum scope for this multicast routing config.
219 * @see Builder#Builder(int)
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900220 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900221 public Builder(@MulticastForwardingMode final int mode, int scope) {
222 if (FORWARD_WITH_MIN_SCOPE != mode) {
223 throw new IllegalArgumentException("Forwarding with a min scope must "
224 + "use forward mode FORWARD_WITH_MIN_SCOPE");
225 }
226 mForwardingMode = mode;
227 mMinScope = scope;
228 mListeningAddresses = new ArraySet<>();
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900229 }
230
231 /**
232 * Sets the minimum scope for this multicast routing config.
233 * This is only meaningful (indeed, allowed) for configs in FORWARD_WITH_MIN_SCOPE mode.
234 * @return this builder
235 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900236 @NonNull
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900237 public Builder setMinimumScope(final int scope) {
238 if (FORWARD_WITH_MIN_SCOPE != mForwardingMode) {
239 throw new IllegalArgumentException("Can't set the scope on a builder in mode "
240 + modeToString(mForwardingMode));
241 }
242 mMinScope = scope;
243 return this;
244 }
245
246 /**
247 * Add an address to the set of listening addresses.
248 *
249 * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode.
250 * If this address was already added, this is a no-op.
251 * @return this builder
252 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900253 @NonNull
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900254 public Builder addListeningAddress(@NonNull final Inet6Address address) {
255 if (FORWARD_SELECTED != mForwardingMode) {
256 throw new IllegalArgumentException("Can't add an address on a builder in mode "
257 + modeToString(mForwardingMode));
258 }
259 // TODO : should we check that this is a multicast address ?
260 mListeningAddresses.add(address);
261 return this;
262 }
263
264 /**
265 * Remove an address from the set of listening addresses.
266 *
267 * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode.
268 * If this address was not added, or was already removed, this is a no-op.
269 * @return this builder
270 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900271 @NonNull
272 public Builder clearListeningAddress(@NonNull final Inet6Address address) {
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900273 if (FORWARD_SELECTED != mForwardingMode) {
274 throw new IllegalArgumentException("Can't remove an address on a builder in mode "
275 + modeToString(mForwardingMode));
276 }
277 mListeningAddresses.remove(address);
278 return this;
279 }
280
281 /**
282 * Build the config.
283 */
Chalard Jeanbb2557a2023-10-09 15:44:56 +0900284 @NonNull
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900285 public MulticastRoutingConfig build() {
286 return new MulticastRoutingConfig(mForwardingMode, mMinScope, mListeningAddresses);
287 }
288 }
289
290 private static String modeToString(@MulticastForwardingMode final int mode) {
291 switch (mode) {
292 case FORWARD_NONE: return "FORWARD_NONE";
293 case FORWARD_SELECTED: return "FORWARD_SELECTED";
294 case FORWARD_WITH_MIN_SCOPE: return "FORWARD_WITH_MIN_SCOPE";
295 default: return "unknown multicast routing mode " + mode;
296 }
297 }
Yang Sund25444f2023-10-24 18:43:15 +0800298
299 public boolean equals(Object other) {
300 if (other == this) {
301 return true;
302 } else if (!(other instanceof MulticastRoutingConfig)) {
303 return false;
304 } else {
305 final MulticastRoutingConfig otherConfig = (MulticastRoutingConfig) other;
306 return mForwardingMode == otherConfig.mForwardingMode
307 && mMinScope == otherConfig.mMinScope
308 && mListeningAddresses.equals(otherConfig.mListeningAddresses);
309 }
310 }
311
312 public int hashCode() {
313 return Objects.hash(mForwardingMode, mMinScope, mListeningAddresses);
314 }
315
316 public String toString() {
317 final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
318
319 resultJoiner.add("ForwardingMode:");
320 resultJoiner.add(modeToString(mForwardingMode));
321
322 if (mForwardingMode == FORWARD_WITH_MIN_SCOPE) {
323 resultJoiner.add("MinScope:");
324 resultJoiner.add(Integer.toString(mMinScope));
325 }
326
327 if (mForwardingMode == FORWARD_SELECTED && !mListeningAddresses.isEmpty()) {
328 resultJoiner.add("ListeningAddresses: [");
329 resultJoiner.add(TextUtils.join(",", mListeningAddresses));
330 resultJoiner.add("]");
331 }
332
333 return resultJoiner.toString();
334 }
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900335}