blob: ebd9fc58c7fc40a84c328040107da2be2b33b983 [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
162 public static class Builder {
163 @MulticastForwardingMode
164 private final int mForwardingMode;
165 private int mMinScope;
166 private final ArraySet<Inet6Address> mListeningAddresses;
167
168 private Builder(@MulticastForwardingMode final int mode, int scope) {
169 mForwardingMode = mode;
170 mMinScope = scope;
171 mListeningAddresses = new ArraySet<>();
172 }
173
174 /**
175 * Create a builder that forwards nothing.
176 * No properties can be set on such a builder.
177 */
178 public static Builder newBuilderForwardingNone() {
179 return new Builder(FORWARD_NONE, MULTICAST_SCOPE_NONE);
180 }
181
182 /**
183 * Create a builder that forwards packets above a certain scope
184 *
185 * The scope can be changed on this builder, but not the listening addresses.
186 * @param scope the initial scope
187 */
188 public static Builder newBuilderWithMinScope(final int scope) {
189 return new Builder(FORWARD_WITH_MIN_SCOPE, scope);
190 }
191
192 /**
193 * Create a builder that forwards a specified list of listening addresses.
194 *
195 * Addresses can be added and removed from this builder, but the scope can't be set.
196 */
197 public static Builder newBuilderWithListeningAddresses() {
198 return new Builder(FORWARD_SELECTED, MULTICAST_SCOPE_NONE);
199 }
200
201 /**
202 * Sets the minimum scope for this multicast routing config.
203 * This is only meaningful (indeed, allowed) for configs in FORWARD_WITH_MIN_SCOPE mode.
204 * @return this builder
205 */
206 public Builder setMinimumScope(final int scope) {
207 if (FORWARD_WITH_MIN_SCOPE != mForwardingMode) {
208 throw new IllegalArgumentException("Can't set the scope on a builder in mode "
209 + modeToString(mForwardingMode));
210 }
211 mMinScope = scope;
212 return this;
213 }
214
215 /**
216 * Add an address to the set of listening addresses.
217 *
218 * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode.
219 * If this address was already added, this is a no-op.
220 * @return this builder
221 */
222 public Builder addListeningAddress(@NonNull final Inet6Address address) {
223 if (FORWARD_SELECTED != mForwardingMode) {
224 throw new IllegalArgumentException("Can't add an address on a builder in mode "
225 + modeToString(mForwardingMode));
226 }
227 // TODO : should we check that this is a multicast address ?
228 mListeningAddresses.add(address);
229 return this;
230 }
231
232 /**
233 * Remove an address from the set of listening addresses.
234 *
235 * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode.
236 * If this address was not added, or was already removed, this is a no-op.
237 * @return this builder
238 */
239 public Builder removeListeningAddress(@NonNull final Inet6Address address) {
240 if (FORWARD_SELECTED != mForwardingMode) {
241 throw new IllegalArgumentException("Can't remove an address on a builder in mode "
242 + modeToString(mForwardingMode));
243 }
244 mListeningAddresses.remove(address);
245 return this;
246 }
247
248 /**
249 * Build the config.
250 */
251 public MulticastRoutingConfig build() {
252 return new MulticastRoutingConfig(mForwardingMode, mMinScope, mListeningAddresses);
253 }
254 }
255
256 private static String modeToString(@MulticastForwardingMode final int mode) {
257 switch (mode) {
258 case FORWARD_NONE: return "FORWARD_NONE";
259 case FORWARD_SELECTED: return "FORWARD_SELECTED";
260 case FORWARD_WITH_MIN_SCOPE: return "FORWARD_WITH_MIN_SCOPE";
261 default: return "unknown multicast routing mode " + mode;
262 }
263 }
264}