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     final boolean mDefaultNetwork;
62 
NetworkIdentity( int type, int subType, String subscriberId, String networkId, boolean roaming, boolean metered, boolean defaultNetwork)63     public NetworkIdentity(
64             int type, int subType, String subscriberId, String networkId, boolean roaming,
65             boolean metered, boolean defaultNetwork) {
66         mType = type;
67         mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
68         mSubscriberId = subscriberId;
69         mNetworkId = networkId;
70         mRoaming = roaming;
71         mMetered = metered;
72         mDefaultNetwork = defaultNetwork;
73     }
74 
75     @Override
hashCode()76     public int hashCode() {
77         return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
78                 mDefaultNetwork);
79     }
80 
81     @Override
equals(Object obj)82     public boolean equals(Object obj) {
83         if (obj instanceof NetworkIdentity) {
84             final NetworkIdentity ident = (NetworkIdentity) obj;
85             return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
86                     && Objects.equals(mSubscriberId, ident.mSubscriberId)
87                     && Objects.equals(mNetworkId, ident.mNetworkId)
88                     && mMetered == ident.mMetered
89                     && mDefaultNetwork == ident.mDefaultNetwork;
90         }
91         return false;
92     }
93 
94     @Override
toString()95     public String toString() {
96         final StringBuilder builder = new StringBuilder("{");
97         builder.append("type=").append(getNetworkTypeName(mType));
98         builder.append(", subType=");
99         if (COMBINE_SUBTYPE_ENABLED) {
100             builder.append("COMBINED");
101         } else if (ConnectivityManager.isNetworkTypeMobile(mType)) {
102             builder.append(TelephonyManager.getNetworkTypeName(mSubType));
103         } else {
104             builder.append(mSubType);
105         }
106         if (mSubscriberId != null) {
107             builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
108         }
109         if (mNetworkId != null) {
110             builder.append(", networkId=").append(mNetworkId);
111         }
112         if (mRoaming) {
113             builder.append(", ROAMING");
114         }
115         builder.append(", metered=").append(mMetered);
116         builder.append(", defaultNetwork=").append(mDefaultNetwork);
117         return builder.append("}").toString();
118     }
119 
writeToProto(ProtoOutputStream proto, long tag)120     public void writeToProto(ProtoOutputStream proto, long tag) {
121         final long start = proto.start(tag);
122 
123         proto.write(NetworkIdentityProto.TYPE, mType);
124 
125         // Not dumping mSubType, subtypes are no longer supported.
126 
127         if (mSubscriberId != null) {
128             proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
129         }
130         proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
131         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
132         proto.write(NetworkIdentityProto.METERED, mMetered);
133         proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
134 
135         proto.end(start);
136     }
137 
getType()138     public int getType() {
139         return mType;
140     }
141 
getSubType()142     public int getSubType() {
143         return mSubType;
144     }
145 
getSubscriberId()146     public String getSubscriberId() {
147         return mSubscriberId;
148     }
149 
getNetworkId()150     public String getNetworkId() {
151         return mNetworkId;
152     }
153 
getRoaming()154     public boolean getRoaming() {
155         return mRoaming;
156     }
157 
getMetered()158     public boolean getMetered() {
159         return mMetered;
160     }
161 
getDefaultNetwork()162     public boolean getDefaultNetwork() {
163         return mDefaultNetwork;
164     }
165 
166     /**
167      * Scrub given IMSI on production builds.
168      */
scrubSubscriberId(String subscriberId)169     public static String scrubSubscriberId(String subscriberId) {
170         if (Build.IS_ENG) {
171             return subscriberId;
172         } else if (subscriberId != null) {
173             // TODO: parse this as MCC+MNC instead of hard-coding
174             return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
175         } else {
176             return "null";
177         }
178     }
179 
180     /**
181      * Scrub given IMSI on production builds.
182      */
scrubSubscriberId(String[] subscriberId)183     public static String[] scrubSubscriberId(String[] subscriberId) {
184         if (subscriberId == null) return null;
185         final String[] res = new String[subscriberId.length];
186         for (int i = 0; i < res.length; i++) {
187             res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
188         }
189         return res;
190     }
191 
192     /**
193      * Build a {@link NetworkIdentity} from the given {@link NetworkState},
194      * assuming that any mobile networks are using the current IMSI.
195      */
buildNetworkIdentity(Context context, NetworkState state, boolean defaultNetwork)196     public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
197             boolean defaultNetwork) {
198         final int type = state.networkInfo.getType();
199         final int subType = state.networkInfo.getSubtype();
200 
201         String subscriberId = null;
202         String networkId = null;
203         boolean roaming = !state.networkCapabilities.hasCapability(
204                 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
205         boolean metered = !state.networkCapabilities.hasCapability(
206                 NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
207 
208         if (isNetworkTypeMobile(type)) {
209             if (state.subscriberId == null) {
210                 if (state.networkInfo.getState() != NetworkInfo.State.DISCONNECTED &&
211                         state.networkInfo.getState() != NetworkInfo.State.UNKNOWN) {
212                     Slog.w(TAG, "Active mobile network without subscriber! ni = "
213                             + state.networkInfo);
214                 }
215             }
216 
217             subscriberId = state.subscriberId;
218 
219         } else if (type == TYPE_WIFI) {
220             if (state.networkId != null) {
221                 networkId = state.networkId;
222             } else {
223                 final WifiManager wifi = (WifiManager) context.getSystemService(
224                         Context.WIFI_SERVICE);
225                 final WifiInfo info = wifi.getConnectionInfo();
226                 networkId = info != null ? info.getSSID() : null;
227             }
228         }
229 
230         return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
231                 defaultNetwork);
232     }
233 
234     @Override
compareTo(NetworkIdentity another)235     public int compareTo(NetworkIdentity another) {
236         int res = Integer.compare(mType, another.mType);
237         if (res == 0) {
238             res = Integer.compare(mSubType, another.mSubType);
239         }
240         if (res == 0 && mSubscriberId != null && another.mSubscriberId != null) {
241             res = mSubscriberId.compareTo(another.mSubscriberId);
242         }
243         if (res == 0 && mNetworkId != null && another.mNetworkId != null) {
244             res = mNetworkId.compareTo(another.mNetworkId);
245         }
246         if (res == 0) {
247             res = Boolean.compare(mRoaming, another.mRoaming);
248         }
249         if (res == 0) {
250             res = Boolean.compare(mMetered, another.mMetered);
251         }
252         if (res == 0) {
253             res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
254         }
255         return res;
256     }
257 }
258