1 /*
2  * Copyright (C) 2022 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 
17 package android.app.admin;
18 
19 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
20 import static org.xmlpull.v1.XmlPullParser.END_TAG;
21 import static org.xmlpull.v1.XmlPullParser.TEXT;
22 
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.SuppressLint;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.IndentingPrintWriter;
29 import android.util.Log;
30 
31 import com.android.modules.utils.TypedXmlPullParser;
32 import com.android.modules.utils.TypedXmlSerializer;
33 
34 import org.xmlpull.v1.XmlPullParserException;
35 
36 import java.io.IOException;
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.stream.Collectors;
45 
46 /**
47  * Network configuration to be set for the user profile
48  * {@see DevicePolicyManager#setPreferentialNetworkServiceConfigs}.
49  */
50 public final class PreferentialNetworkServiceConfig implements Parcelable {
51     final boolean mIsEnabled;
52     final int mNetworkId;
53     final boolean mAllowFallbackToDefaultConnection;
54     final boolean mShouldBlockNonMatchingNetworks;
55     final int[] mIncludedUids;
56     final int[] mExcludedUids;
57 
58     private static final String LOG_TAG = "PreferentialNetworkServiceConfig";
59     private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIG =
60             "preferential_network_service_config";
61     private static final String TAG_CONFIG_ENABLED =
62             "preferential_network_service_config_enabled";
63     private static final String TAG_UID = "uid";
64     private static final String TAG_NETWORK_ID =
65             "preferential_network_service_network_id";
66     private static final String TAG_ALLOW_FALLBACK_TO_DEFAULT_CONNECTION =
67             "allow_fallback_to_default_connection";
68     private static final String TAG_BLOCK_NON_MATCHING_NETWORKS =
69             "block_non_matching_networks";
70     private static final String TAG_INCLUDED_UIDS = "included_uids";
71     private static final String TAG_EXCLUDED_UIDS = "excluded_uids";
72     private static final String ATTR_VALUE = "value";
73 
74     /** @hide */
75     public static final PreferentialNetworkServiceConfig DEFAULT =
76             (new PreferentialNetworkServiceConfig.Builder()).build();
77 
78     /**
79      * Preferential network identifier 1.
80      */
81     public static final int PREFERENTIAL_NETWORK_ID_1 = 1;
82 
83     /**
84      * Preferential network identifier 2.
85      */
86     public static final int PREFERENTIAL_NETWORK_ID_2 = 2;
87 
88     /**
89      * Preferential network identifier 3.
90      */
91     public static final int PREFERENTIAL_NETWORK_ID_3 = 3;
92 
93     /**
94      * Preferential network identifier 4.
95      */
96     public static final int PREFERENTIAL_NETWORK_ID_4 = 4;
97 
98     /**
99      * Preferential network identifier 5.
100      */
101     public static final int PREFERENTIAL_NETWORK_ID_5 = 5;
102 
103     /** @hide */
104     @Retention(RetentionPolicy.SOURCE)
105     @IntDef(prefix = { "PREFERENTIAL_NETWORK_ID_" }, value = {
106             PREFERENTIAL_NETWORK_ID_1,
107             PREFERENTIAL_NETWORK_ID_2,
108             PREFERENTIAL_NETWORK_ID_3,
109             PREFERENTIAL_NETWORK_ID_4,
110             PREFERENTIAL_NETWORK_ID_5,
111     })
112 
113     public @interface PreferentialNetworkPreferenceId {
114     }
115 
PreferentialNetworkServiceConfig(boolean isEnabled, boolean allowFallbackToDefaultConnection, boolean shouldBlockNonMatchingNetworks, int[] includedUids, int[] excludedUids, @PreferentialNetworkPreferenceId int networkId)116     private PreferentialNetworkServiceConfig(boolean isEnabled,
117             boolean allowFallbackToDefaultConnection, boolean shouldBlockNonMatchingNetworks,
118             int[] includedUids,
119             int[] excludedUids, @PreferentialNetworkPreferenceId int networkId) {
120         mIsEnabled = isEnabled;
121         mAllowFallbackToDefaultConnection = allowFallbackToDefaultConnection;
122         mShouldBlockNonMatchingNetworks = shouldBlockNonMatchingNetworks;
123         mIncludedUids = includedUids;
124         mExcludedUids = excludedUids;
125         mNetworkId = networkId;
126     }
127 
PreferentialNetworkServiceConfig(Parcel in)128     private PreferentialNetworkServiceConfig(Parcel in) {
129         mIsEnabled = in.readBoolean();
130         mAllowFallbackToDefaultConnection = in.readBoolean();
131         mShouldBlockNonMatchingNetworks = in.readBoolean();
132         mNetworkId = in.readInt();
133         mIncludedUids = in.createIntArray();
134         mExcludedUids = in.createIntArray();
135     }
136 
137     /**
138      * Is the preferential network enabled.
139      * @return true if enabled else false
140      */
isEnabled()141     public boolean isEnabled() {
142         return mIsEnabled;
143     }
144 
145     /**
146      * Whether fallback to the device-wide default network is allowed.
147      *
148      * This boolean configures whether the default connection (e.g. general cell network or wifi)
149      * should be used if no preferential network service connection is available. If true, the
150      * default connection will be used when no preferential service is available. If false, the
151      * UIDs subject to this configuration will have no default network.
152      * Note that while this boolean determines whether the UIDs subject to this configuration have
153      * a default network in the absence of a preferential service, apps can still explicitly decide
154      * to use another network than their default network by requesting them from the system. This
155      * boolean does not determine whether the UIDs are blocked from using such other networks.
156      * See {@link #shouldBlockNonMatchingNetworks()} for that configuration.
157      *
158      * @return true if fallback is allowed, else false.
159      */
isFallbackToDefaultConnectionAllowed()160     public boolean isFallbackToDefaultConnectionAllowed() {
161         return mAllowFallbackToDefaultConnection;
162     }
163 
164     /**
165      * Whether to block UIDs from using other networks than the preferential service.
166      *
167      * Apps can inspect the list of available networks on the device and choose to use multiple
168      * of them concurrently for performance, privacy or other reasons.
169      * This boolean configures whether the concerned UIDs should be blocked from using
170      * networks that do not match the configured preferential network service even if these
171      * networks are otherwise open to all apps.
172      *
173      * @return true if UIDs should be blocked from using the other networks, else false.
174      */
shouldBlockNonMatchingNetworks()175     public boolean shouldBlockNonMatchingNetworks() {
176         return mShouldBlockNonMatchingNetworks;
177     }
178 
179     /**
180      * Get the array of uids that are applicable for the profile preference.
181      *
182      * {@see #getExcludedUids()}
183      * Included UIDs and Excluded UIDs can't both be non-empty.
184      * if both are empty, it means this request applies to all uids in the user profile.
185      * if included is not empty, then only included UIDs are applied.
186      * if excluded is not empty, then it is all uids in the user profile except these UIDs.
187      * @return Array of uids applicable for the profile preference.
188      *      Empty array would mean that this request applies to all uids in the profile.
189      */
getIncludedUids()190     public @NonNull int[] getIncludedUids() {
191         return mIncludedUids;
192     }
193 
194     /**
195      * Get the array of uids that are excluded for the profile preference.
196      *
197      * {@see #getIncludedUids()}
198      * Included UIDs and Excluded UIDs can't both be non-empty.
199      * if both are empty, it means this request applies to all uids in the user profile.
200      * if included is not empty, then only included UIDs are applied.
201      * if excluded is not empty, then it is all uids in the user profile except these UIDs.
202      * @return Array of uids that are excluded for the profile preference.
203      *      Empty array would mean that this request applies to all uids in the profile.
204      */
getExcludedUids()205     public @NonNull int[] getExcludedUids() {
206         return mExcludedUids;
207     }
208 
209     /**
210      * @return preference enterprise identifier.
211      * preference identifier is applicable only if preference network service is enabled
212      *
213      */
getNetworkId()214     public @PreferentialNetworkPreferenceId int getNetworkId() {
215         return mNetworkId;
216     }
217 
218     @Override
toString()219     public String toString() {
220         return "PreferentialNetworkServiceConfig{"
221                 + "mIsEnabled=" + isEnabled()
222                 + "mAllowFallbackToDefaultConnection=" + isFallbackToDefaultConnectionAllowed()
223                 + "mBlockNonMatchingNetworks=" + shouldBlockNonMatchingNetworks()
224                 + "mIncludedUids=" + Arrays.toString(mIncludedUids)
225                 + "mExcludedUids=" + Arrays.toString(mExcludedUids)
226                 + "mNetworkId=" + mNetworkId
227                 + '}';
228     }
229 
230     @Override
equals(Object o)231     public boolean equals(Object o) {
232         if (this == o) return true;
233         if (o == null || getClass() != o.getClass()) return false;
234         final PreferentialNetworkServiceConfig that = (PreferentialNetworkServiceConfig) o;
235         return mIsEnabled == that.mIsEnabled
236                 && mAllowFallbackToDefaultConnection == that.mAllowFallbackToDefaultConnection
237                 && mShouldBlockNonMatchingNetworks == that.mShouldBlockNonMatchingNetworks
238                 && mNetworkId == that.mNetworkId
239                 && Arrays.equals(mIncludedUids, that.mIncludedUids)
240                 && Arrays.equals(mExcludedUids, that.mExcludedUids);
241     }
242 
243     @Override
hashCode()244     public int hashCode() {
245         return Objects.hash(mIsEnabled, mAllowFallbackToDefaultConnection,
246                 mShouldBlockNonMatchingNetworks, Arrays.hashCode(mIncludedUids),
247                 Arrays.hashCode(mExcludedUids), mNetworkId);
248     }
249 
250     /**
251      * Builder used to create {@link PreferentialNetworkServiceConfig} objects.
252      * Specify the preferred Network preference
253      */
254     public static final class Builder {
255         boolean mIsEnabled = false;
256         int mNetworkId = 0;
257         boolean mAllowFallbackToDefaultConnection = true;
258         boolean mShouldBlockNonMatchingNetworks = false;
259         int[] mIncludedUids = new int[0];
260         int[] mExcludedUids = new int[0];
261 
262         /**
263          * Constructs an empty Builder with preferential network disabled by default.
264          */
Builder()265         public Builder() {}
266 
267         /**
268          * Set the preferential network service enabled state.
269          * Default value is false.
270          * @param isEnabled  the desired network preference to use, true to enable else false
271          * @return The builder to facilitate chaining.
272          */
273         @NonNull
setEnabled(boolean isEnabled)274         public PreferentialNetworkServiceConfig.Builder setEnabled(boolean isEnabled) {
275             mIsEnabled = isEnabled;
276             return this;
277         }
278 
279         /**
280          * Set whether fallback to the device-wide default network is allowed.
281          *
282          * This boolean configures whether the default connection (e.g. general cell network or
283          * wifi) should be used if no preferential network service connection is available. If true,
284          * the default connection will be used when no preferential service is available. If false,
285          * the UIDs subject to this configuration will have no default network.
286          * Note that while this boolean determines whether the UIDs subject to this configuration
287          * have a default network in the absence of a preferential service, apps can still
288          * explicitly decide to use another network than their default network by requesting them
289          * from the system. This boolean does not determine whether the UIDs are blocked from using
290          * such other networks.
291          * Use {@link #setShouldBlockNonMatchingNetworks(boolean)} to specify this.
292          *
293          * The default value is true.
294          *
295          * @param allowFallbackToDefaultConnection  true if fallback is allowed else false
296          * @return The builder to facilitate chaining.
297          */
298         @NonNull
299         @SuppressLint("MissingGetterMatchingBuilder")
setFallbackToDefaultConnectionAllowed( boolean allowFallbackToDefaultConnection)300         public PreferentialNetworkServiceConfig.Builder setFallbackToDefaultConnectionAllowed(
301                 boolean allowFallbackToDefaultConnection) {
302             mAllowFallbackToDefaultConnection = allowFallbackToDefaultConnection;
303             return this;
304         }
305 
306         /**
307          * Set whether to block UIDs from using other networks than the preferential service.
308          *
309          * Apps can inspect the list of available networks on the device and choose to use multiple
310          * of them concurrently for performance, privacy or other reasons.
311          * This boolean configures whether the concerned UIDs should be blocked from using
312          * networks that do not match the configured preferential network service even if these
313          * networks are otherwise open to all apps.
314          *
315          * The default value is false. This value can only be set to {@code true} if
316          * {@link #setFallbackToDefaultConnectionAllowed(boolean)} is set to {@code false}, because
317          * allowing fallback but blocking it does not make sense. Failure to comply with this
318          * constraint will throw when building the object.
319          *
320          * @param blockNonMatchingNetworks true if UIDs should be blocked from using non-matching
321          *                                 networks.
322          * @return The builder to facilitate chaining.
323          */
324         @NonNull
setShouldBlockNonMatchingNetworks( boolean blockNonMatchingNetworks)325         public PreferentialNetworkServiceConfig.Builder setShouldBlockNonMatchingNetworks(
326                 boolean blockNonMatchingNetworks) {
327             mShouldBlockNonMatchingNetworks = blockNonMatchingNetworks;
328             return this;
329         }
330 
331         /**
332          * Set the array of uids whose network access will go through this preferential
333          * network service.
334          * {@see #setExcludedUids(int[])}
335          * Included UIDs and Excluded UIDs can't both be non-empty.
336          * if both are empty, it means this request applies to all uids in the user profile.
337          * if included is not empty, then only included UIDs are applied.
338          * if excluded is not empty, then it is all uids in the user profile except these UIDs.
339          * @param uids  array of included uids
340          * @return The builder to facilitate chaining.
341          */
342         @NonNull
setIncludedUids( @onNull int[] uids)343         public PreferentialNetworkServiceConfig.Builder setIncludedUids(
344                 @NonNull int[] uids) {
345             Objects.requireNonNull(uids);
346             mIncludedUids = uids;
347             return this;
348         }
349 
350         /**
351          * Set the array of uids who are not allowed through this preferential
352          * network service.
353          * {@see #setIncludedUids(int[])}
354          * Included UIDs and Excluded UIDs can't both be non-empty.
355          * if both are empty, it means this request applies to all uids in the user profile.
356          * if included is not empty, then only included UIDs are applied.
357          * if excluded is not empty, then it is all uids in the user profile except these UIDs.
358          * @param uids  array of excluded uids
359          * @return The builder to facilitate chaining.
360          */
361         @NonNull
setExcludedUids( @onNull int[] uids)362         public PreferentialNetworkServiceConfig.Builder setExcludedUids(
363                 @NonNull int[] uids) {
364             Objects.requireNonNull(uids);
365             mExcludedUids = uids;
366             return this;
367         }
368 
369         /**
370          * Returns an instance of {@link PreferentialNetworkServiceConfig} created from the
371          * fields set on this builder.
372          */
373         @NonNull
build()374         public PreferentialNetworkServiceConfig build() {
375             if (mIncludedUids.length > 0 && mExcludedUids.length > 0) {
376                 throw new IllegalStateException("Both includedUids and excludedUids "
377                         + "cannot be nonempty");
378             }
379             if (mShouldBlockNonMatchingNetworks && mAllowFallbackToDefaultConnection) {
380                 throw new IllegalStateException("A config cannot both allow fallback and "
381                         + "block non-matching networks");
382             }
383             return new PreferentialNetworkServiceConfig(mIsEnabled,
384                     mAllowFallbackToDefaultConnection, mShouldBlockNonMatchingNetworks,
385                     mIncludedUids, mExcludedUids, mNetworkId);
386         }
387 
388         /**
389          * Set the preferential network identifier.
390          * preference identifier is applicable only if preferential network service is enabled.
391          * @param preferenceId  preference Id
392          * @return The builder to facilitate chaining.
393          */
394         @NonNull
setNetworkId( @referentialNetworkPreferenceId int preferenceId)395         public PreferentialNetworkServiceConfig.Builder setNetworkId(
396                 @PreferentialNetworkPreferenceId int preferenceId) {
397             if ((preferenceId < PREFERENTIAL_NETWORK_ID_1)
398                     || (preferenceId > PREFERENTIAL_NETWORK_ID_5)) {
399                 throw new IllegalArgumentException("Invalid preference identifier");
400             }
401             mNetworkId = preferenceId;
402             return this;
403         }
404     }
405 
406     @Override
writeToParcel(@onNull android.os.Parcel dest, int flags)407     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
408         dest.writeBoolean(mIsEnabled);
409         dest.writeBoolean(mAllowFallbackToDefaultConnection);
410         dest.writeBoolean(mShouldBlockNonMatchingNetworks);
411         dest.writeInt(mNetworkId);
412         dest.writeIntArray(mIncludedUids);
413         dest.writeIntArray(mExcludedUids);
414     }
415 
writeAttributeValueToXml(TypedXmlSerializer out, String tag, int value)416     private void writeAttributeValueToXml(TypedXmlSerializer out, String tag, int value)
417             throws IOException {
418         out.startTag(null, tag);
419         out.attributeInt(null, ATTR_VALUE, value);
420         out.endTag(null, tag);
421     }
422 
writeAttributeValueToXml(TypedXmlSerializer out, String tag, boolean value)423     private void writeAttributeValueToXml(TypedXmlSerializer out, String tag, boolean value)
424             throws IOException {
425         out.startTag(null, tag);
426         out.attributeBoolean(null, ATTR_VALUE, value);
427         out.endTag(null, tag);
428     }
429 
writeAttributeValuesToXml(TypedXmlSerializer out, String outerTag, String innerTag, @NonNull Collection<String> values)430     private void writeAttributeValuesToXml(TypedXmlSerializer out, String outerTag, String innerTag,
431             @NonNull Collection<String> values) throws IOException {
432         out.startTag(null, outerTag);
433         for (String value : values) {
434             out.startTag(null, innerTag);
435             out.attribute(null, ATTR_VALUE, value);
436             out.endTag(null, innerTag);
437         }
438         out.endTag(null, outerTag);
439     }
440 
readAttributeValues( TypedXmlPullParser parser, String tag, Collection<String> result)441     private static  void readAttributeValues(
442             TypedXmlPullParser parser, String tag, Collection<String> result)
443             throws XmlPullParserException, IOException {
444         result.clear();
445         int outerDepthDAM = parser.getDepth();
446         int typeDAM;
447         while ((typeDAM = parser.next()) != END_DOCUMENT
448                 && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
449             if (typeDAM == END_TAG || typeDAM == TEXT) {
450                 continue;
451             }
452             String tagDAM = parser.getName();
453             if (tag.equals(tagDAM)) {
454                 result.add(parser.getAttributeValue(null, ATTR_VALUE));
455             } else {
456                 Log.e(LOG_TAG, "Expected tag " + tag + " but found " + tagDAM);
457             }
458         }
459     }
460 
intArrayToStringList(int[] array)461     private List<String> intArrayToStringList(int[] array) {
462         return Arrays.stream(array).mapToObj(String::valueOf).collect(Collectors.toList());
463     }
464 
readStringListToIntArray(TypedXmlPullParser parser, String tag)465     private static int[] readStringListToIntArray(TypedXmlPullParser parser, String tag)
466             throws XmlPullParserException, IOException {
467         List<String> stringList = new ArrayList<>();
468         readAttributeValues(parser, tag, stringList);
469         int[] intArray = stringList.stream()
470                 .map(s -> Integer.parseInt(s))
471                 .mapToInt(Integer::intValue)
472                 .toArray();
473         return intArray;
474     }
475 
476     /**
477      * @hide
478      */
getPreferentialNetworkServiceConfig( TypedXmlPullParser parser, String tag)479     public static PreferentialNetworkServiceConfig getPreferentialNetworkServiceConfig(
480             TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException  {
481         int outerDepthDAM = parser.getDepth();
482         int typeDAM;
483         PreferentialNetworkServiceConfig.Builder resultBuilder =
484                 new PreferentialNetworkServiceConfig.Builder();
485         while ((typeDAM = parser.next()) != END_DOCUMENT
486                 && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
487             if (typeDAM == END_TAG || typeDAM == TEXT) {
488                 continue;
489             }
490             String tagDAM = parser.getName();
491             if (TAG_CONFIG_ENABLED.equals(tagDAM)) {
492                 resultBuilder.setEnabled(parser.getAttributeBoolean(null, ATTR_VALUE,
493                         DevicePolicyManager.PREFERENTIAL_NETWORK_SERVICE_ENABLED_DEFAULT));
494             } else if (TAG_NETWORK_ID.equals(tagDAM)) {
495                 int val = parser.getAttributeInt(null, ATTR_VALUE, 0);
496                 if (val != 0) {
497                     resultBuilder.setNetworkId(val);
498                 }
499             } else if (TAG_ALLOW_FALLBACK_TO_DEFAULT_CONNECTION.equals(tagDAM)) {
500                 resultBuilder.setFallbackToDefaultConnectionAllowed(parser.getAttributeBoolean(
501                         null, ATTR_VALUE, true));
502             } else if (TAG_BLOCK_NON_MATCHING_NETWORKS.equals(tagDAM)) {
503                 resultBuilder.setShouldBlockNonMatchingNetworks(parser.getAttributeBoolean(
504                         null, ATTR_VALUE, false));
505             } else if (TAG_INCLUDED_UIDS.equals(tagDAM)) {
506                 resultBuilder.setIncludedUids(readStringListToIntArray(parser, TAG_UID));
507             } else if (TAG_EXCLUDED_UIDS.equals(tagDAM)) {
508                 resultBuilder.setExcludedUids(readStringListToIntArray(parser, TAG_UID));
509             } else {
510                 Log.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM);
511             }
512         }
513         return resultBuilder.build();
514     }
515 
516     /**
517      * @hide
518      */
writeToXml(@onNull TypedXmlSerializer out)519     public void writeToXml(@NonNull TypedXmlSerializer out) throws IOException {
520         out.startTag(null, TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIG);
521         writeAttributeValueToXml(out, TAG_CONFIG_ENABLED, isEnabled());
522         writeAttributeValueToXml(out, TAG_NETWORK_ID, getNetworkId());
523         writeAttributeValueToXml(out, TAG_ALLOW_FALLBACK_TO_DEFAULT_CONNECTION,
524                 isFallbackToDefaultConnectionAllowed());
525         writeAttributeValueToXml(out, TAG_BLOCK_NON_MATCHING_NETWORKS,
526                 shouldBlockNonMatchingNetworks());
527         writeAttributeValuesToXml(out, TAG_INCLUDED_UIDS, TAG_UID,
528                 intArrayToStringList(getIncludedUids()));
529         writeAttributeValuesToXml(out, TAG_EXCLUDED_UIDS, TAG_UID,
530                 intArrayToStringList(getExcludedUids()));
531         out.endTag(null, TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIG);
532     }
533 
534     /**
535      * @hide
536      */
dump(IndentingPrintWriter pw)537     public void dump(IndentingPrintWriter pw) {
538         pw.print("networkId=");
539         pw.println(mNetworkId);
540         pw.print("isEnabled=");
541         pw.println(mIsEnabled);
542         pw.print("allowFallbackToDefaultConnection=");
543         pw.println(mAllowFallbackToDefaultConnection);
544         pw.print("blockNonMatchingNetworks=");
545         pw.println(mShouldBlockNonMatchingNetworks);
546         pw.print("includedUids=");
547         pw.println(Arrays.toString(mIncludedUids));
548         pw.print("excludedUids=");
549         pw.println(Arrays.toString(mExcludedUids));
550     }
551 
552     @Override
describeContents()553     public int describeContents() {
554         return 0;
555     }
556 
557     @NonNull
558     public static final Creator<PreferentialNetworkServiceConfig> CREATOR =
559             new Creator<PreferentialNetworkServiceConfig>() {
560                 @Override
561                 public PreferentialNetworkServiceConfig[] newArray(int size) {
562                     return new PreferentialNetworkServiceConfig[size];
563                 }
564 
565                 @Override
566                 public PreferentialNetworkServiceConfig createFromParcel(
567                         @NonNull android.os.Parcel in) {
568                     return new PreferentialNetworkServiceConfig(in);
569                 }
570             };
571 }
572