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