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 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 76 public int hashCode() { 77 return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered, 78 mDefaultNetwork); 79 } 80 81 @Override 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 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 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 138 public int getType() { 139 return mType; 140 } 141 142 public int getSubType() { 143 return mSubType; 144 } 145 146 public String getSubscriberId() { 147 return mSubscriberId; 148 } 149 150 public String getNetworkId() { 151 return mNetworkId; 152 } 153 154 public boolean getRoaming() { 155 return mRoaming; 156 } 157 158 public boolean getMetered() { 159 return mMetered; 160 } 161 162 public boolean getDefaultNetwork() { 163 return mDefaultNetwork; 164 } 165 166 /** 167 * Scrub given IMSI on production builds. 168 */ 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 */ 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 */ 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 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