blob: b9362dabf2ff137b70421cbc6cf67353dc435e47 [file] [log] [blame]
Robert Greenwalt47f69fe2010-06-15 15:43:39 -07001/*
Wink Saville6e809972010-09-21 09:15:35 -07002 * Copyright (C) 2010 The Android Open Source Project
Robert Greenwalt47f69fe2010-06-15 15:43:39 -07003 *
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
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070019import android.net.ProxyProperties;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070020import android.os.Parcelable;
21import android.os.Parcel;
John Wang4e900092011-04-04 12:35:42 -070022import android.text.TextUtils;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070023
24import java.net.InetAddress;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070025import java.net.UnknownHostException;
26import java.util.ArrayList;
27import java.util.Collection;
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070028import java.util.Collections;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070029
30/**
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070031 * Describes the properties of a network link.
Robert Greenwalt992564e2011-02-09 13:56:06 -080032 *
33 * A link represents a connection to a network.
34 * It may have multiple addresses and multiple gateways,
35 * multiple dns servers but only one http proxy.
36 *
37 * Because it's a single network, the dns's
38 * are interchangeable and don't need associating with
39 * particular addresses. The gateways similarly don't
40 * need associating with particular addresses.
41 *
42 * A dual stack interface works fine in this model:
43 * each address has it's own prefix length to describe
44 * the local network. The dns servers all return
45 * both v4 addresses and v6 addresses regardless of the
46 * address family of the server itself (rfc4213) and we
47 * don't care which is used. The gateways will be
48 * selected based on the destination address and the
49 * source address has no relavence.
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070050 * @hide
51 */
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070052public class LinkProperties implements Parcelable {
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070053
Robert Greenwalt4717c262012-10-31 14:32:53 -070054 private String mIfaceName;
Wink Savillee8222252011-07-13 13:44:13 -070055 private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
56 private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
Robert Greenwalt8058f622012-11-09 10:52:27 -080057 private String mDomains;
Wink Savillee8222252011-07-13 13:44:13 -070058 private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070059 private ProxyProperties mHttpProxy;
60
Robert Greenwalt0a46db52011-07-14 14:28:05 -070061 public static class CompareResult<T> {
Robert Greenwaltad55d352011-07-22 11:55:33 -070062 public Collection<T> removed = new ArrayList<T>();
63 public Collection<T> added = new ArrayList<T>();
Wink Savillee8222252011-07-13 13:44:13 -070064
65 @Override
66 public String toString() {
Robert Greenwalt0a46db52011-07-14 14:28:05 -070067 String retVal = "removed=[";
68 for (T addr : removed) retVal += addr.toString() + ",";
69 retVal += "] added=[";
70 for (T addr : added) retVal += addr.toString() + ",";
Wink Savillee8222252011-07-13 13:44:13 -070071 retVal += "]";
72 return retVal;
73 }
74 }
75
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070076 public LinkProperties() {
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070077 clear();
78 }
79
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070080 // copy constructor instead of clone
81 public LinkProperties(LinkProperties source) {
Irfan Sheriffef6c1432010-08-30 20:37:17 -070082 if (source != null) {
Irfan Sheriffed5d7d12010-10-01 16:08:28 -070083 mIfaceName = source.getInterfaceName();
Robert Greenwalt0d8acea2011-07-28 17:21:25 -070084 for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
85 for (InetAddress i : source.getDnses()) mDnses.add(i);
Robert Greenwalt8058f622012-11-09 10:52:27 -080086 mDomains = source.getDomains();
Robert Greenwalt0d8acea2011-07-28 17:21:25 -070087 for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
Wink Savillebe2b0582011-05-18 15:59:04 -070088 mHttpProxy = (source.getHttpProxy() == null) ?
Robert Greenwalt8058f622012-11-09 10:52:27 -080089 null : new ProxyProperties(source.getHttpProxy());
Irfan Sheriffef6c1432010-08-30 20:37:17 -070090 }
Robert Greenwalt37e65eb2010-08-30 10:56:47 -070091 }
92
Irfan Sheriffed5d7d12010-10-01 16:08:28 -070093 public void setInterfaceName(String iface) {
94 mIfaceName = iface;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070095 }
96
Irfan Sheriffed5d7d12010-10-01 16:08:28 -070097 public String getInterfaceName() {
98 return mIfaceName;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -070099 }
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700100
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700101 public Collection<InetAddress> getAddresses() {
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700102 Collection<InetAddress> addresses = new ArrayList<InetAddress>();
103 for (LinkAddress linkAddress : mLinkAddresses) {
104 addresses.add(linkAddress.getAddress());
105 }
106 return Collections.unmodifiableCollection(addresses);
107 }
108
109 public void addLinkAddress(LinkAddress address) {
Robert Greenwalt04cac402011-03-02 17:03:37 -0800110 if (address != null) mLinkAddresses.add(address);
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700111 }
112
113 public Collection<LinkAddress> getLinkAddresses() {
114 return Collections.unmodifiableCollection(mLinkAddresses);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700115 }
116
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700117 public void addDns(InetAddress dns) {
Robert Greenwalt04cac402011-03-02 17:03:37 -0800118 if (dns != null) mDnses.add(dns);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700119 }
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700120
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700121 public Collection<InetAddress> getDnses() {
122 return Collections.unmodifiableCollection(mDnses);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700123 }
124
Robert Greenwalt8058f622012-11-09 10:52:27 -0800125 public String getDomains() {
126 return mDomains;
127 }
128
129 public void setDomains(String domains) {
130 mDomains = domains;
131 }
132
Robert Greenwaltaa70f102011-04-28 14:28:50 -0700133 public void addRoute(RouteInfo route) {
134 if (route != null) mRoutes.add(route);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700135 }
Robert Greenwaltaa70f102011-04-28 14:28:50 -0700136 public Collection<RouteInfo> getRoutes() {
137 return Collections.unmodifiableCollection(mRoutes);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700138 }
139
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700140 public void setHttpProxy(ProxyProperties proxy) {
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700141 mHttpProxy = proxy;
142 }
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700143 public ProxyProperties getHttpProxy() {
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700144 return mHttpProxy;
145 }
146
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700147 public void clear() {
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700148 mIfaceName = null;
Wink Savillee8222252011-07-13 13:44:13 -0700149 mLinkAddresses.clear();
150 mDnses.clear();
Robert Greenwalt8058f622012-11-09 10:52:27 -0800151 mDomains = null;
Wink Savillee8222252011-07-13 13:44:13 -0700152 mRoutes.clear();
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700153 mHttpProxy = null;
154 }
155
156 /**
157 * Implement the Parcelable interface
158 * @hide
159 */
160 public int describeContents() {
161 return 0;
162 }
163
Wink Saville1f6408a2010-08-27 11:15:18 -0700164 @Override
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700165 public String toString() {
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700166 String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700167
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700168 String linkAddresses = "LinkAddresses: [";
John Wang4e900092011-04-04 12:35:42 -0700169 for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ",";
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700170 linkAddresses += "] ";
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700171
172 String dns = "DnsAddresses: [";
Wink Saville1f6408a2010-08-27 11:15:18 -0700173 for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700174 dns += "] ";
175
Robert Greenwalt8058f622012-11-09 10:52:27 -0800176 String domainName = "Domains: " + mDomains;
177
178 String routes = " Routes: [";
Robert Greenwaltaa70f102011-04-28 14:28:50 -0700179 for (RouteInfo route : mRoutes) routes += route.toString() + ",";
180 routes += "] ";
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700181 String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700182
Robert Greenwalt8058f622012-11-09 10:52:27 -0800183 return ifaceName + linkAddresses + routes + dns + domainName + proxy;
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700184 }
185
Wink Savillee8222252011-07-13 13:44:13 -0700186 /**
187 * Compares this {@code LinkProperties} interface name against the target
188 *
189 * @param target LinkProperties to compare.
190 * @return {@code true} if both are identical, {@code false} otherwise.
191 */
192 public boolean isIdenticalInterfaceName(LinkProperties target) {
193 return TextUtils.equals(getInterfaceName(), target.getInterfaceName());
194 }
195
196 /**
Robert Greenwalt4717c262012-10-31 14:32:53 -0700197 * Compares this {@code LinkProperties} interface addresses against the target
Wink Savillee8222252011-07-13 13:44:13 -0700198 *
199 * @param target LinkProperties to compare.
200 * @return {@code true} if both are identical, {@code false} otherwise.
201 */
202 public boolean isIdenticalAddresses(LinkProperties target) {
203 Collection<InetAddress> targetAddresses = target.getAddresses();
204 Collection<InetAddress> sourceAddresses = getAddresses();
205 return (sourceAddresses.size() == targetAddresses.size()) ?
206 sourceAddresses.containsAll(targetAddresses) : false;
207 }
208
209 /**
210 * Compares this {@code LinkProperties} DNS addresses against the target
211 *
212 * @param target LinkProperties to compare.
213 * @return {@code true} if both are identical, {@code false} otherwise.
214 */
215 public boolean isIdenticalDnses(LinkProperties target) {
216 Collection<InetAddress> targetDnses = target.getDnses();
Robert Greenwalt8058f622012-11-09 10:52:27 -0800217 String targetDomains = target.getDomains();
218 if (mDomains == null) {
219 if (targetDomains != null) return false;
220 } else {
221 if (mDomains.equals(targetDomains) == false) return false;
222 }
Wink Savillee8222252011-07-13 13:44:13 -0700223 return (mDnses.size() == targetDnses.size()) ?
224 mDnses.containsAll(targetDnses) : false;
225 }
226
227 /**
228 * Compares this {@code LinkProperties} Routes against the target
229 *
230 * @param target LinkProperties to compare.
231 * @return {@code true} if both are identical, {@code false} otherwise.
232 */
233 public boolean isIdenticalRoutes(LinkProperties target) {
234 Collection<RouteInfo> targetRoutes = target.getRoutes();
235 return (mRoutes.size() == targetRoutes.size()) ?
236 mRoutes.containsAll(targetRoutes) : false;
237 }
238
239 /**
240 * Compares this {@code LinkProperties} HttpProxy against the target
241 *
242 * @param target LinkProperties to compare.
243 * @return {@code true} if both are identical, {@code false} otherwise.
244 */
245 public boolean isIdenticalHttpProxy(LinkProperties target) {
246 return getHttpProxy() == null ? target.getHttpProxy() == null :
247 getHttpProxy().equals(target.getHttpProxy());
248 }
John Wang4e900092011-04-04 12:35:42 -0700249
250 @Override
251 /**
252 * Compares this {@code LinkProperties} instance against the target
253 * LinkProperties in {@code obj}. Two LinkPropertieses are equal if
254 * all their fields are equal in values.
255 *
256 * For collection fields, such as mDnses, containsAll() is used to check
257 * if two collections contains the same elements, independent of order.
258 * There are two thoughts regarding containsAll()
259 * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal.
260 * 2. Worst case performance is O(n^2).
261 *
262 * @param obj the object to be tested for equality.
263 * @return {@code true} if both objects are equal, {@code false} otherwise.
264 */
265 public boolean equals(Object obj) {
266 if (this == obj) return true;
267
268 if (!(obj instanceof LinkProperties)) return false;
269
John Wang4e900092011-04-04 12:35:42 -0700270 LinkProperties target = (LinkProperties) obj;
271
Wink Savillee8222252011-07-13 13:44:13 -0700272 return isIdenticalInterfaceName(target) &&
273 isIdenticalAddresses(target) &&
274 isIdenticalDnses(target) &&
275 isIdenticalRoutes(target) &&
276 isIdenticalHttpProxy(target);
277 }
John Wang4e900092011-04-04 12:35:42 -0700278
Wink Savillee8222252011-07-13 13:44:13 -0700279 /**
280 * Return two lists, a list of addresses that would be removed from
281 * mLinkAddresses and a list of addresses that would be added to
282 * mLinkAddress which would then result in target and mLinkAddresses
283 * being the same list.
284 *
Robert Greenwalt0a46db52011-07-14 14:28:05 -0700285 * @param target is a LinkProperties with the new list of addresses
Wink Savillee8222252011-07-13 13:44:13 -0700286 * @return the removed and added lists.
287 */
Robert Greenwalt0a46db52011-07-14 14:28:05 -0700288 public CompareResult<LinkAddress> compareAddresses(LinkProperties target) {
Wink Savillee8222252011-07-13 13:44:13 -0700289 /*
290 * Duplicate the LinkAddresses into removed, we will be removing
291 * address which are common between mLinkAddresses and target
292 * leaving the addresses that are different. And address which
293 * are in target but not in mLinkAddresses are placed in the
294 * addedAddresses.
295 */
Robert Greenwalt0a46db52011-07-14 14:28:05 -0700296 CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
Wink Savillee8222252011-07-13 13:44:13 -0700297 result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
298 result.added.clear();
Robert Greenwalt0a46db52011-07-14 14:28:05 -0700299 if (target != null) {
300 for (LinkAddress newAddress : target.getLinkAddresses()) {
301 if (! result.removed.remove(newAddress)) {
302 result.added.add(newAddress);
303 }
Wink Savillee8222252011-07-13 13:44:13 -0700304 }
305 }
306 return result;
John Wang4e900092011-04-04 12:35:42 -0700307 }
308
Robert Greenwalt0a46db52011-07-14 14:28:05 -0700309 /**
310 * Return two lists, a list of dns addresses that would be removed from
311 * mDnses and a list of addresses that would be added to
312 * mDnses which would then result in target and mDnses
313 * being the same list.
314 *
315 * @param target is a LinkProperties with the new list of dns addresses
316 * @return the removed and added lists.
317 */
318 public CompareResult<InetAddress> compareDnses(LinkProperties target) {
319 /*
320 * Duplicate the InetAddresses into removed, we will be removing
321 * dns address which are common between mDnses and target
322 * leaving the addresses that are different. And dns address which
323 * are in target but not in mDnses are placed in the
324 * addedAddresses.
325 */
326 CompareResult<InetAddress> result = new CompareResult<InetAddress>();
327
328 result.removed = new ArrayList<InetAddress>(mDnses);
329 result.added.clear();
330 if (target != null) {
331 for (InetAddress newAddress : target.getDnses()) {
332 if (! result.removed.remove(newAddress)) {
333 result.added.add(newAddress);
334 }
335 }
336 }
337 return result;
338 }
339
340 /**
341 * Return two lists, a list of routes that would be removed from
342 * mRoutes and a list of routes that would be added to
343 * mRoutes which would then result in target and mRoutes
344 * being the same list.
345 *
346 * @param target is a LinkProperties with the new list of routes
347 * @return the removed and added lists.
348 */
349 public CompareResult<RouteInfo> compareRoutes(LinkProperties target) {
350 /*
351 * Duplicate the RouteInfos into removed, we will be removing
352 * routes which are common between mDnses and target
353 * leaving the routes that are different. And route address which
354 * are in target but not in mRoutes are placed in added.
355 */
356 CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
357
358 result.removed = new ArrayList<RouteInfo>(mRoutes);
359 result.added.clear();
360 if (target != null) {
361 for (RouteInfo r : target.getRoutes()) {
362 if (! result.removed.remove(r)) {
363 result.added.add(r);
364 }
365 }
366 }
367 return result;
368 }
369
370
John Wang4e900092011-04-04 12:35:42 -0700371 @Override
372 /**
373 * generate hashcode based on significant fields
374 * Equal objects must produce the same hash code, while unequal objects
375 * may have the same hash codes.
376 */
377 public int hashCode() {
378 return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
379 + mLinkAddresses.size() * 31
380 + mDnses.size() * 37
Robert Greenwalt8058f622012-11-09 10:52:27 -0800381 + ((null == mDomains) ? 0 : mDomains.hashCode())
Robert Greenwaltaa70f102011-04-28 14:28:50 -0700382 + mRoutes.size() * 41
John Wang4e900092011-04-04 12:35:42 -0700383 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
384 }
385
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700386 /**
387 * Implement the Parcelable interface.
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700388 */
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700389 public void writeToParcel(Parcel dest, int flags) {
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700390 dest.writeString(getInterfaceName());
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700391 dest.writeInt(mLinkAddresses.size());
392 for(LinkAddress linkAddress : mLinkAddresses) {
393 dest.writeParcelable(linkAddress, flags);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700394 }
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700395
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700396 dest.writeInt(mDnses.size());
397 for(InetAddress d : mDnses) {
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700398 dest.writeByteArray(d.getAddress());
399 }
Robert Greenwalt8058f622012-11-09 10:52:27 -0800400 dest.writeString(mDomains);
Robert Greenwalt992564e2011-02-09 13:56:06 -0800401
Robert Greenwaltaa70f102011-04-28 14:28:50 -0700402 dest.writeInt(mRoutes.size());
403 for(RouteInfo route : mRoutes) {
404 dest.writeParcelable(route, flags);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700405 }
Robert Greenwalt992564e2011-02-09 13:56:06 -0800406
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700407 if (mHttpProxy != null) {
408 dest.writeByte((byte)1);
409 dest.writeParcelable(mHttpProxy, flags);
410 } else {
411 dest.writeByte((byte)0);
412 }
413 }
414
415 /**
416 * Implement the Parcelable interface.
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700417 */
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700418 public static final Creator<LinkProperties> CREATOR =
419 new Creator<LinkProperties>() {
420 public LinkProperties createFromParcel(Parcel in) {
421 LinkProperties netProp = new LinkProperties();
Robert Greenwalt4717c262012-10-31 14:32:53 -0700422
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700423 String iface = in.readString();
424 if (iface != null) {
Robert Greenwalt4717c262012-10-31 14:32:53 -0700425 netProp.setInterfaceName(iface);
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700426 }
427 int addressCount = in.readInt();
428 for (int i=0; i<addressCount; i++) {
Irfan Sheriffed5d7d12010-10-01 16:08:28 -0700429 netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700430 }
431 addressCount = in.readInt();
432 for (int i=0; i<addressCount; i++) {
433 try {
Irfan Sheriff1cf56ab2010-08-04 15:15:49 -0700434 netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700435 } catch (UnknownHostException e) { }
436 }
Robert Greenwalt8058f622012-11-09 10:52:27 -0800437 netProp.setDomains(in.readString());
Robert Greenwalt992564e2011-02-09 13:56:06 -0800438 addressCount = in.readInt();
439 for (int i=0; i<addressCount; i++) {
Robert Greenwaltaa70f102011-04-28 14:28:50 -0700440 netProp.addRoute((RouteInfo)in.readParcelable(null));
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700441 }
442 if (in.readByte() == 1) {
443 netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
444 }
445 return netProp;
446 }
447
Robert Greenwalt37e65eb2010-08-30 10:56:47 -0700448 public LinkProperties[] newArray(int size) {
449 return new LinkProperties[size];
Robert Greenwalt47f69fe2010-06-15 15:43:39 -0700450 }
451 };
452}