blob: 6f4ab11dbb77b4fb44d3f15b03984b2a03a5fc94 [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;
24import android.util.ArraySet;
25import android.util.Log;
26
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.net.Inet6Address;
30import java.net.UnknownHostException;
31import java.util.Arrays;
32import java.util.Collections;
33import java.util.Set;
34
35/**
36 * A class representing a configuration for multicast routing.
37 *
38 * Internal usage to Connectivity
39 * @hide
40 */
41// TODO : @SystemApi
42public class MulticastRoutingConfig implements Parcelable {
43 private static final String TAG = MulticastRoutingConfig.class.getSimpleName();
44
45 /** Do not forward any multicast packets. */
46 public static final int FORWARD_NONE = 0;
47 /**
48 * Forward only multicast packets with destination in the list of listening addresses.
49 * Ignore the min scope.
50 */
51 public static final int FORWARD_SELECTED = 1;
52 /**
53 * Forward all multicast packets with scope greater or equal than the min scope.
54 * Ignore the list of listening addresses.
55 */
56 public static final int FORWARD_WITH_MIN_SCOPE = 2;
57
58 @Retention(RetentionPolicy.SOURCE)
59 @IntDef(prefix = { "FORWARD_" }, value = {
60 FORWARD_NONE,
61 FORWARD_SELECTED,
62 FORWARD_WITH_MIN_SCOPE
63 })
64 public @interface MulticastForwardingMode {}
65
66 /**
67 * Not a multicast scope, for configurations that do not use the min scope.
68 */
69 public static final int MULTICAST_SCOPE_NONE = -1;
70
71 public static final MulticastRoutingConfig CONFIG_FORWARD_NONE =
72 new MulticastRoutingConfig(FORWARD_NONE, MULTICAST_SCOPE_NONE, null);
73
74 @MulticastForwardingMode
75 private final int mForwardingMode;
76
77 private final int mMinScope;
78
79 @NonNull
80 private final Set<Inet6Address> mListeningAddresses;
81
82 private MulticastRoutingConfig(@MulticastForwardingMode final int mode, final int scope,
83 @Nullable final Set<Inet6Address> addresses) {
84 mForwardingMode = mode;
85 mMinScope = scope;
86 if (null != addresses) {
87 mListeningAddresses = Collections.unmodifiableSet(new ArraySet<>(addresses));
88 } else {
89 mListeningAddresses = Collections.emptySet();
90 }
91 }
92
93 /**
94 * Returns the forwarding mode.
95 */
96 @MulticastForwardingMode
97 public int getForwardingMode() {
98 return mForwardingMode;
99 }
100
101 /**
102 * Returns the minimal group address scope that is allowed for forwarding.
103 * If the forwarding mode is not FORWARD_WITH_MIN_SCOPE, will be MULTICAST_SCOPE_NONE.
104 */
105 public int getMinScope() {
106 return mMinScope;
107 }
108
109 /**
110 * Returns the list of group addresses listened by the outgoing interface.
111 * The list will be empty if the forwarding mode is not FORWARD_SELECTED.
112 */
113 @NonNull
114 public Set<Inet6Address> getMulticastListeningAddresses() {
115 return mListeningAddresses;
116 }
117
118 private MulticastRoutingConfig(Parcel in) {
119 mForwardingMode = in.readInt();
120 mMinScope = in.readInt();
121 final int count = in.readInt();
122 final ArraySet<Inet6Address> listeningAddresses = new ArraySet<>(count);
123 final byte[] buffer = new byte[16]; // Size of an Inet6Address
124 for (int i = 0; i < count; ++i) {
125 in.readByteArray(buffer);
126 try {
127 listeningAddresses.add((Inet6Address) Inet6Address.getByAddress(buffer));
128 } catch (UnknownHostException e) {
129 Log.wtf(TAG, "Can't read inet6address : " + Arrays.toString(buffer));
130 }
131 }
132 mListeningAddresses = Collections.unmodifiableSet(listeningAddresses);
133 }
134
135 @Override
136 public void writeToParcel(Parcel dest, int flags) {
137 dest.writeInt(mForwardingMode);
138 dest.writeInt(mMinScope);
139 dest.writeInt(mListeningAddresses.size());
140 for (final Inet6Address addr : mListeningAddresses) {
141 dest.writeByteArray(addr.getAddress());
142 }
143 }
144
145 @Override
146 public int describeContents() {
147 return 0;
148 }
149
150 public static final Creator<MulticastRoutingConfig> CREATOR = new Creator<>() {
151 @Override
152 public MulticastRoutingConfig createFromParcel(Parcel in) {
153 return new MulticastRoutingConfig(in);
154 }
155
156 @Override
157 public MulticastRoutingConfig[] newArray(int size) {
158 return new MulticastRoutingConfig[size];
159 }
160 };
161
Chalard Jean4fe23392023-10-17 23:02:27 +0900162 @Override
163 public String toString() {
164 return "MulticastRoutingConfig{"
165 + "ForwardingMode=" + forwardingModeToString(mForwardingMode)
166 + ", MinScope=" + mMinScope
167 + ", ListeningAddresses=" + mListeningAddresses
168 + '}';
169 }
170
171 private static String forwardingModeToString(final int forwardingMode) {
172 switch (forwardingMode) {
173 case FORWARD_NONE: return "NONE";
174 case FORWARD_SELECTED: return "SELECTED";
175 case FORWARD_WITH_MIN_SCOPE: return "WITH_MIN_SCOPE";
176 default: return "UNKNOWN";
177 }
178 }
179
Chalard Jeanf9d0e3e2023-10-17 13:23:17 +0900180 public static class Builder {
181 @MulticastForwardingMode
182 private final int mForwardingMode;
183 private int mMinScope;
184 private final ArraySet<Inet6Address> mListeningAddresses;
185
186 private Builder(@MulticastForwardingMode final int mode, int scope) {
187 mForwardingMode = mode;
188 mMinScope = scope;
189 mListeningAddresses = new ArraySet<>();
190 }
191
192 /**
193 * Create a builder that forwards nothing.
194 * No properties can be set on such a builder.
195 */
196 public static Builder newBuilderForwardingNone() {
197 return new Builder(FORWARD_NONE, MULTICAST_SCOPE_NONE);
198 }
199
200 /**
201 * Create a builder that forwards packets above a certain scope
202 *
203 * The scope can be changed on this builder, but not the listening addresses.
204 * @param scope the initial scope
205 */
206 public static Builder newBuilderWithMinScope(final int scope) {
207 return new Builder(FORWARD_WITH_MIN_SCOPE, scope);
208 }
209
210 /**
211 * Create a builder that forwards a specified list of listening addresses.
212 *
213 * Addresses can be added and removed from this builder, but the scope can't be set.
214 */
215 public static Builder newBuilderWithListeningAddresses() {
216 return new Builder(FORWARD_SELECTED, MULTICAST_SCOPE_NONE);
217 }
218
219 /**
220 * Sets the minimum scope for this multicast routing config.
221 * This is only meaningful (indeed, allowed) for configs in FORWARD_WITH_MIN_SCOPE mode.
222 * @return this builder
223 */
224 public Builder setMinimumScope(final int scope) {
225 if (FORWARD_WITH_MIN_SCOPE != mForwardingMode) {
226 throw new IllegalArgumentException("Can't set the scope on a builder in mode "
227 + modeToString(mForwardingMode));
228 }
229 mMinScope = scope;
230 return this;
231 }
232
233 /**
234 * Add an address to the set of listening addresses.
235 *
236 * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode.
237 * If this address was already added, this is a no-op.
238 * @return this builder
239 */
240 public Builder addListeningAddress(@NonNull final Inet6Address address) {
241 if (FORWARD_SELECTED != mForwardingMode) {
242 throw new IllegalArgumentException("Can't add an address on a builder in mode "
243 + modeToString(mForwardingMode));
244 }
245 // TODO : should we check that this is a multicast address ?
246 mListeningAddresses.add(address);
247 return this;
248 }
249
250 /**
251 * Remove an address from the set of listening addresses.
252 *
253 * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode.
254 * If this address was not added, or was already removed, this is a no-op.
255 * @return this builder
256 */
257 public Builder removeListeningAddress(@NonNull final Inet6Address address) {
258 if (FORWARD_SELECTED != mForwardingMode) {
259 throw new IllegalArgumentException("Can't remove an address on a builder in mode "
260 + modeToString(mForwardingMode));
261 }
262 mListeningAddresses.remove(address);
263 return this;
264 }
265
266 /**
267 * Build the config.
268 */
269 public MulticastRoutingConfig build() {
270 return new MulticastRoutingConfig(mForwardingMode, mMinScope, mListeningAddresses);
271 }
272 }
273
274 private static String modeToString(@MulticastForwardingMode final int mode) {
275 switch (mode) {
276 case FORWARD_NONE: return "FORWARD_NONE";
277 case FORWARD_SELECTED: return "FORWARD_SELECTED";
278 case FORWARD_WITH_MIN_SCOPE: return "FORWARD_WITH_MIN_SCOPE";
279 default: return "unknown multicast routing mode " + mode;
280 }
281 }
282}