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 
17 package android.net;
18 
19 import static android.net.ConnectivityManager.TYPE_WIFI;
20 import static android.net.ConnectivityManager.getNetworkTypeName;
21 import static android.net.ConnectivityManager.isNetworkTypeMobile;
22 
23 import android.content.Context;
24 import android.net.wifi.WifiInfo;
25 import android.net.wifi.WifiManager;
26 import android.os.Build;
27 import android.service.NetworkIdentityProto;
28 import android.telephony.TelephonyManager;
29 import android.util.Slog;
30 import android.util.proto.ProtoOutputStream;
31 
32 import java.util.Objects;
33 
34 /**
35  * Network definition that includes strong identity. Analogous to combining
36  * {@link NetworkInfo} and an IMSI.
37  *
38  * @hide
39  */
40 public class NetworkIdentity implements Comparable<NetworkIdentity> {
41     private static final String TAG = "NetworkIdentity";
42 
43     /**
44      * When enabled, combine all {@link #mSubType} together under
45      * {@link #SUBTYPE_COMBINED}.
46      *
47      * @deprecated we no longer offer to collect statistics on a per-subtype
48      *             basis; this is always disabled.
49      */
50     @Deprecated
51     public static final boolean COMBINE_SUBTYPE_ENABLED = true;
52 
53     public static final int SUBTYPE_COMBINED = -1;
54 
55     final int mType;
56     final int mSubType;
57     final String mSubscriberId;
58     final String mNetworkId;
59     final boolean mRoaming;
60     final boolean mMetered;
61 
NetworkIdentity( int type, int subType, String subscriberId, String networkId, boolean roaming, boolean metered)62     public NetworkIdentity(
63             int type, int subType, String subscriberId, String networkId, boolean roaming,
64             boolean metered) {
65         mType = type;
66         mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
67         mSubscriberId = subscriberId;
68         mNetworkId = networkId;
69         mRoaming = roaming;
70         mMetered = metered;
71     }
72 
73     @Override
hashCode()74     public int hashCode() {
75         return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered);
76     }
77 
78     @Override
equals(Object obj)79     public boolean equals(Object obj) {
80         if (obj instanceof NetworkIdentity) {
81             final NetworkIdentity ident = (NetworkIdentity) obj;
82             return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
83                     && Objects.equals(mSubscriberId, ident.mSubscriberId)
84                     && Objects.equals(mNetworkId, ident.mNetworkId)
85                     && mMetered == ident.mMetered;
86         }
87         return false;
88     }
89 
90     @Override
toString()91     public String toString() {
92         final StringBuilder builder = new StringBuilder("{");
93         builder.append("type=").append(getNetworkTypeName(mType));
94         builder.append(", subType=");
95         if (COMBINE_SUBTYPE_ENABLED) {
96             builder.append("COMBINED");
97         } else if (ConnectivityManager.isNetworkTypeMobile(mType)) {
98             builder.append(TelephonyManager.getNetworkTypeName(mSubType));
99         } else {
100             builder.append(mSubType);
101         }
102         if (mSubscriberId != null) {
103             builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
104         }
105         if (mNetworkId != null) {
106             builder.append(", networkId=").append(mNetworkId);
107         }
108         if (mRoaming) {
109             builder.append(", ROAMING");
110         }
111         builder.append(", metered=").append(mMetered);
112         return builder.append("}").toString();
113     }
114 
writeToProto(ProtoOutputStream proto, long tag)115     public void writeToProto(ProtoOutputStream proto, long tag) {
116         final long start = proto.start(tag);
117 
118         proto.write(NetworkIdentityProto.TYPE, mType);
119 
120         // Not dumping mSubType, subtypes are no longer supported.
121 
122         if (mSubscriberId != null) {
123             proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
124         }
125         proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
126         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
127         proto.write(NetworkIdentityProto.METERED, mMetered);
128 
129         proto.end(start);
130     }
131 
getType()132     public int getType() {
133         return mType;
134     }
135 
getSubType()136     public int getSubType() {
137         return mSubType;
138     }
139 
getSubscriberId()140     public String getSubscriberId() {
141         return mSubscriberId;
142     }
143 
getNetworkId()144     public String getNetworkId() {
145         return mNetworkId;
146     }
147 
getRoaming()148     public boolean getRoaming() {
149         return mRoaming;
150     }
151 
getMetered()152     public boolean getMetered() {
153         return mMetered;
154     }
155 
156     /**
157      * Scrub given IMSI on production builds.
158      */
scrubSubscriberId(String subscriberId)159     public static String scrubSubscriberId(String subscriberId) {
160         if ("eng".equals(Build.TYPE)) {
161             return subscriberId;
162         } else if (subscriberId != null) {
163             // TODO: parse this as MCC+MNC instead of hard-coding
164             return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
165         } else {
166             return "null";
167         }
168     }
169 
170     /**
171      * Scrub given IMSI on production builds.
172      */
scrubSubscriberId(String[] subscriberId)173     public static String[] scrubSubscriberId(String[] subscriberId) {
174         if (subscriberId == null) return null;
175         final String[] res = new String[subscriberId.length];
176         for (int i = 0; i < res.length; i++) {
177             res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
178         }
179         return res;
180     }
181 
182     /**
183      * Build a {@link NetworkIdentity} from the given {@link NetworkState},
184      * assuming that any mobile networks are using the current IMSI.
185      */
buildNetworkIdentity(Context context, NetworkState state)186     public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
187         final int type = state.networkInfo.getType();
188         final int subType = state.networkInfo.getSubtype();
189 
190         String subscriberId = null;
191         String networkId = null;
192         boolean roaming = false;
193         boolean metered = !state.networkCapabilities.hasCapability(
194                 NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
195 
196         if (isNetworkTypeMobile(type)) {
197             if (state.subscriberId == null) {
198                 if (state.networkInfo.getState() != NetworkInfo.State.DISCONNECTED &&
199                         state.networkInfo.getState() != NetworkInfo.State.UNKNOWN) {
200                     Slog.w(TAG, "Active mobile network without subscriber! ni = "
201                             + state.networkInfo);
202                 }
203             }
204 
205             subscriberId = state.subscriberId;
206             roaming = state.networkInfo.isRoaming();
207 
208         } else if (type == TYPE_WIFI) {
209             if (state.networkId != null) {
210                 networkId = state.networkId;
211             } else {
212                 final WifiManager wifi = (WifiManager) context.getSystemService(
213                         Context.WIFI_SERVICE);
214                 final WifiInfo info = wifi.getConnectionInfo();
215                 networkId = info != null ? info.getSSID() : null;
216             }
217         }
218 
219         return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered);
220     }
221 
222     @Override
compareTo(NetworkIdentity another)223     public int compareTo(NetworkIdentity another) {
224         int res = Integer.compare(mType, another.mType);
225         if (res == 0) {
226             res = Integer.compare(mSubType, another.mSubType);
227         }
228         if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
229             res = mSubscriberId.compareTo(another.mSubscriberId);
230         }
231         if (res == 0 && mNetworkId != null && another.mNetworkId != null) {
232             res = mNetworkId.compareTo(another.mNetworkId);
233         }
234         if (res == 0) {
235             res = Boolean.compare(mRoaming, another.mRoaming);
236         }
237         if (res == 0) {
238             res = Boolean.compare(mMetered, another.mMetered);
239         }
240         return res;
241     }
242 }
243