1/*
2 * Copyright (C) 2011 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 com.android.server.net;
18
19import android.net.NetworkIdentity;
20import android.service.NetworkIdentitySetProto;
21import android.util.proto.ProtoOutputStream;
22
23import java.io.DataInputStream;
24import java.io.DataOutputStream;
25import java.io.IOException;
26import java.util.HashSet;
27
28import static android.net.ConnectivityManager.TYPE_MOBILE;
29
30/**
31 * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
32 * active on that interface.
33 *
34 * @hide
35 */
36public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
37        Comparable<NetworkIdentitySet> {
38    private static final int VERSION_INIT = 1;
39    private static final int VERSION_ADD_ROAMING = 2;
40    private static final int VERSION_ADD_NETWORK_ID = 3;
41    private static final int VERSION_ADD_METERED = 4;
42    private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
43
44    public NetworkIdentitySet() {
45    }
46
47    public NetworkIdentitySet(DataInputStream in) throws IOException {
48        final int version = in.readInt();
49        final int size = in.readInt();
50        for (int i = 0; i < size; i++) {
51            if (version <= VERSION_INIT) {
52                final int ignored = in.readInt();
53            }
54            final int type = in.readInt();
55            final int subType = in.readInt();
56            final String subscriberId = readOptionalString(in);
57            final String networkId;
58            if (version >= VERSION_ADD_NETWORK_ID) {
59                networkId = readOptionalString(in);
60            } else {
61                networkId = null;
62            }
63            final boolean roaming;
64            if (version >= VERSION_ADD_ROAMING) {
65                roaming = in.readBoolean();
66            } else {
67                roaming = false;
68            }
69
70            final boolean metered;
71            if (version >= VERSION_ADD_METERED) {
72                metered = in.readBoolean();
73            } else {
74                // If this is the old data and the type is mobile, treat it as metered. (Note that
75                // if this is a mobile network, TYPE_MOBILE is the only possible type that could be
76                // used.)
77                metered = (type == TYPE_MOBILE);
78            }
79
80            final boolean defaultNetwork;
81            if (version >= VERSION_ADD_DEFAULT_NETWORK) {
82                defaultNetwork = in.readBoolean();
83            } else {
84                defaultNetwork = true;
85            }
86
87            add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
88                    defaultNetwork));
89        }
90    }
91
92    public void writeToStream(DataOutputStream out) throws IOException {
93        out.writeInt(VERSION_ADD_DEFAULT_NETWORK);
94        out.writeInt(size());
95        for (NetworkIdentity ident : this) {
96            out.writeInt(ident.getType());
97            out.writeInt(ident.getSubType());
98            writeOptionalString(out, ident.getSubscriberId());
99            writeOptionalString(out, ident.getNetworkId());
100            out.writeBoolean(ident.getRoaming());
101            out.writeBoolean(ident.getMetered());
102            out.writeBoolean(ident.getDefaultNetwork());
103        }
104    }
105
106    /** @return whether any {@link NetworkIdentity} in this set is considered metered. */
107    public boolean isAnyMemberMetered() {
108        if (isEmpty()) {
109            return false;
110        }
111        for (NetworkIdentity ident : this) {
112            if (ident.getMetered()) {
113                return true;
114            }
115        }
116        return false;
117    }
118
119    /** @return whether any {@link NetworkIdentity} in this set is considered roaming. */
120    public boolean isAnyMemberRoaming() {
121        if (isEmpty()) {
122            return false;
123        }
124        for (NetworkIdentity ident : this) {
125            if (ident.getRoaming()) {
126                return true;
127            }
128        }
129        return false;
130    }
131
132    /** @return whether any {@link NetworkIdentity} in this set is considered on the default
133            network. */
134    public boolean areAllMembersOnDefaultNetwork() {
135        if (isEmpty()) {
136            return true;
137        }
138        for (NetworkIdentity ident : this) {
139            if (!ident.getDefaultNetwork()) {
140                return false;
141            }
142        }
143        return true;
144    }
145
146    private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
147        if (value != null) {
148            out.writeByte(1);
149            out.writeUTF(value);
150        } else {
151            out.writeByte(0);
152        }
153    }
154
155    private static String readOptionalString(DataInputStream in) throws IOException {
156        if (in.readByte() != 0) {
157            return in.readUTF();
158        } else {
159            return null;
160        }
161    }
162
163    @Override
164    public int compareTo(NetworkIdentitySet another) {
165        if (isEmpty()) return -1;
166        if (another.isEmpty()) return 1;
167
168        final NetworkIdentity ident = iterator().next();
169        final NetworkIdentity anotherIdent = another.iterator().next();
170        return ident.compareTo(anotherIdent);
171    }
172
173    public void writeToProto(ProtoOutputStream proto, long tag) {
174        final long start = proto.start(tag);
175
176        for (NetworkIdentity ident : this) {
177            ident.writeToProto(proto, NetworkIdentitySetProto.IDENTITIES);
178        }
179
180        proto.end(start);
181    }
182}
183