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 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 74 public int hashCode() { 75 return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered); 76 } 77 78 @Override 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 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 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 132 public int getType() { 133 return mType; 134 } 135 136 public int getSubType() { 137 return mSubType; 138 } 139 140 public String getSubscriberId() { 141 return mSubscriberId; 142 } 143 144 public String getNetworkId() { 145 return mNetworkId; 146 } 147 148 public boolean getRoaming() { 149 return mRoaming; 150 } 151 152 public boolean getMetered() { 153 return mMetered; 154 } 155 156 /** 157 * Scrub given IMSI on production builds. 158 */ 159 public static String scrubSubscriberId(String subscriberId) { 160 if (Build.IS_ENG) { 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 */ 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 */ 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 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