MobileDataStateTracker.java revision 843ef36f7b96cc19ea7d2996b7c8661b41ec3452
1/* 2 * Copyright (C) 2008 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 17package android.net; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.os.RemoteException; 24import android.os.Handler; 25import android.os.ServiceManager; 26import android.os.SystemProperties; 27import com.android.internal.telephony.ITelephony; 28import com.android.internal.telephony.Phone; 29import com.android.internal.telephony.TelephonyIntents; 30import android.net.NetworkInfo.DetailedState; 31import android.telephony.TelephonyManager; 32import android.util.Log; 33import android.text.TextUtils; 34 35import java.util.List; 36import java.util.ArrayList; 37 38/** 39 * Track the state of mobile data connectivity. This is done by 40 * receiving broadcast intents from the Phone process whenever 41 * the state of data connectivity changes. 42 * 43 * {@hide} 44 */ 45public class MobileDataStateTracker extends NetworkStateTracker { 46 47 private static final String TAG = "MobileDataStateTracker"; 48 private static final boolean DBG = false; 49 50 private Phone.DataState mMobileDataState; 51 private ITelephony mPhoneService; 52 private static final String[] sDnsPropNames = { 53 "net.rmnet0.dns1", 54 "net.rmnet0.dns2", 55 "net.eth0.dns1", 56 "net.eth0.dns2", 57 "net.eth0.dns3", 58 "net.eth0.dns4", 59 "net.gprs.dns1", 60 "net.gprs.dns2" 61 }; 62 private List<String> mDnsServers; 63 private String mInterfaceName; 64 private int mDefaultGatewayAddr; 65 private int mLastCallingPid = -1; 66 67 /** 68 * Create a new MobileDataStateTracker 69 * @param context the application context of the caller 70 * @param target a message handler for getting callbacks about state changes 71 */ 72 public MobileDataStateTracker(Context context, Handler target) { 73 super(context, target, ConnectivityManager.TYPE_MOBILE, 74 TelephonyManager.getDefault().getNetworkType(), "MOBILE", 75 TelephonyManager.getDefault().getNetworkTypeName()); 76 mPhoneService = null; 77 mDnsServers = new ArrayList<String>(); 78 } 79 80 /** 81 * Begin monitoring mobile data connectivity. 82 */ 83 public void startMonitoring() { 84 IntentFilter filter = 85 new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 86 filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); 87 filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); 88 89 Intent intent = mContext.registerReceiver(new MobileDataStateReceiver(), filter); 90 if (intent != null) 91 mMobileDataState = getMobileDataState(intent); 92 else 93 mMobileDataState = Phone.DataState.DISCONNECTED; 94 } 95 96 private static Phone.DataState getMobileDataState(Intent intent) { 97 String str = intent.getStringExtra(Phone.STATE_KEY); 98 if (str != null) 99 return Enum.valueOf(Phone.DataState.class, str); 100 else 101 return Phone.DataState.DISCONNECTED; 102 } 103 104 private class MobileDataStateReceiver extends BroadcastReceiver { 105 public void onReceive(Context context, Intent intent) { 106 if (intent.getAction().equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 107 Phone.DataState state = getMobileDataState(intent); 108 String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); 109 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); 110 boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY, false); 111 if (DBG) Log.d(TAG, "Received " + intent.getAction() + 112 " broadcast - state = " + state 113 + ", unavailable = " + unavailable 114 + ", reason = " + (reason == null ? "(unspecified)" : reason)); 115 mNetworkInfo.setIsAvailable(!unavailable); 116 if (mMobileDataState != state) { 117 mMobileDataState = state; 118 119 switch (state) { 120 case DISCONNECTED: 121 setDetailedState(DetailedState.DISCONNECTED, reason, apnName); 122 if (mInterfaceName != null) { 123 NetworkUtils.resetConnections(mInterfaceName); 124 } 125 mInterfaceName = null; 126 mDefaultGatewayAddr = 0; 127 break; 128 case CONNECTING: 129 setDetailedState(DetailedState.CONNECTING, reason, apnName); 130 break; 131 case SUSPENDED: 132 setDetailedState(DetailedState.SUSPENDED, reason, apnName); 133 break; 134 case CONNECTED: 135 mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY); 136 if (mInterfaceName == null) { 137 Log.d(TAG, "CONNECTED event did not supply interface name."); 138 } 139 setupDnsProperties(); 140 setDetailedState(DetailedState.CONNECTED, reason, apnName); 141 break; 142 } 143 } 144 } else if (intent.getAction().equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) { 145 String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY); 146 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); 147 if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" + 148 reason == null ? "" : "(" + reason + ")"); 149 setDetailedState(DetailedState.FAILED, reason, apnName); 150 } 151 TelephonyManager tm = TelephonyManager.getDefault(); 152 setRoamingStatus(tm.isNetworkRoaming()); 153 setSubtype(tm.getNetworkType(), tm.getNetworkTypeName()); 154 } 155 } 156 157 /** 158 * Make sure that route(s) exist to the carrier DNS server(s). 159 */ 160 public void addPrivateRoutes() { 161 if (mInterfaceName != null) { 162 for (String addrString : mDnsServers) { 163 int addr = NetworkUtils.lookupHost(addrString); 164 if (addr != -1) { 165 NetworkUtils.addHostRoute(mInterfaceName, addr); 166 } 167 } 168 } 169 } 170 171 public void removePrivateRoutes() { 172 if(mInterfaceName != null) { 173 NetworkUtils.removeHostRoutes(mInterfaceName); 174 } 175 } 176 177 public void removeDefaultRoute() { 178 if(mInterfaceName != null) { 179 mDefaultGatewayAddr = NetworkUtils.getDefaultRoute(mInterfaceName); 180 NetworkUtils.removeDefaultRoute(mInterfaceName); 181 } 182 } 183 184 public void restoreDefaultRoute() { 185 // 0 is not a valid address for a gateway 186 if (mInterfaceName != null && mDefaultGatewayAddr != 0) { 187 NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr); 188 } 189 } 190 191 private void getPhoneService(boolean forceRefresh) { 192 if ((mPhoneService == null) || forceRefresh) { 193 mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone")); 194 } 195 } 196 197 /** 198 * Report whether data connectivity is possible. 199 */ 200 public boolean isAvailable() { 201 getPhoneService(false); 202 203 /* 204 * If the phone process has crashed in the past, we'll get a 205 * RemoteException and need to re-reference the service. 206 */ 207 for (int retry = 0; retry < 2; retry++) { 208 if (mPhoneService == null) break; 209 210 try { 211 return mPhoneService.isDataConnectivityPossible(); 212 } catch (RemoteException e) { 213 // First-time failed, get the phone service again 214 if (retry == 0) getPhoneService(true); 215 } 216 } 217 218 return false; 219 } 220 221 /** 222 * Return the IP addresses of the DNS servers available for the mobile data 223 * network interface. 224 * @return a list of DNS addresses, with no holes. 225 */ 226 public String[] getNameServers() { 227 return getNameServerList(sDnsPropNames); 228 } 229 230 /** 231 * {@inheritDoc} 232 * The mobile data network subtype indicates what generation network technology is in effect, 233 * e.g., GPRS, EDGE, UMTS, etc. 234 */ 235 public int getNetworkSubtype() { 236 return TelephonyManager.getDefault().getNetworkType(); 237 } 238 239 /** 240 * Return the system properties name associated with the tcp buffer sizes 241 * for this network. 242 */ 243 public String getTcpBufferSizesPropName() { 244 String networkTypeStr = "unknown"; 245 TelephonyManager tm = new TelephonyManager(mContext); 246 //TODO We have to edit the parameter for getNetworkType regarding CDMA 247 switch(tm.getNetworkType()) { 248 case TelephonyManager.NETWORK_TYPE_GPRS: 249 networkTypeStr = "gprs"; 250 break; 251 case TelephonyManager.NETWORK_TYPE_EDGE: 252 networkTypeStr = "edge"; 253 break; 254 case TelephonyManager.NETWORK_TYPE_UMTS: 255 networkTypeStr = "umts"; 256 break; 257 case TelephonyManager.NETWORK_TYPE_CDMA: 258 networkTypeStr = "cdma"; 259 break; 260 case TelephonyManager.NETWORK_TYPE_EVDO_0: 261 networkTypeStr = "evdo"; 262 break; 263 case TelephonyManager.NETWORK_TYPE_EVDO_A: 264 networkTypeStr = "evdo"; 265 break; 266 } 267 return "net.tcp.buffersize." + networkTypeStr; 268 } 269 270 /** 271 * Tear down mobile data connectivity, i.e., disable the ability to create 272 * mobile data connections. 273 */ 274 @Override 275 public boolean teardown() { 276 getPhoneService(false); 277 /* 278 * If the phone process has crashed in the past, we'll get a 279 * RemoteException and need to re-reference the service. 280 */ 281 for (int retry = 0; retry < 2; retry++) { 282 if (mPhoneService == null) { 283 Log.w(TAG, 284 "Ignoring mobile data teardown request because could not acquire PhoneService"); 285 break; 286 } 287 288 try { 289 return mPhoneService.disableDataConnectivity(); 290 } catch (RemoteException e) { 291 if (retry == 0) getPhoneService(true); 292 } 293 } 294 295 Log.w(TAG, "Failed to tear down mobile data connectivity"); 296 return false; 297 } 298 299 /** 300 * Re-enable mobile data connectivity after a {@link #teardown()}. 301 */ 302 public boolean reconnect() { 303 getPhoneService(false); 304 /* 305 * If the phone process has crashed in the past, we'll get a 306 * RemoteException and need to re-reference the service. 307 */ 308 for (int retry = 0; retry < 2; retry++) { 309 if (mPhoneService == null) { 310 Log.w(TAG, 311 "Ignoring mobile data connect request because could not acquire PhoneService"); 312 break; 313 } 314 315 try { 316 return mPhoneService.enableDataConnectivity(); 317 } catch (RemoteException e) { 318 if (retry == 0) getPhoneService(true); 319 } 320 } 321 322 Log.w(TAG, "Failed to set up mobile data connectivity"); 323 return false; 324 } 325 326 /** 327 * Turn on or off the mobile radio. No connectivity will be possible while the 328 * radio is off. The operation is a no-op if the radio is already in the desired state. 329 * @param turnOn {@code true} if the radio should be turned on, {@code false} if 330 */ 331 public boolean setRadio(boolean turnOn) { 332 getPhoneService(false); 333 /* 334 * If the phone process has crashed in the past, we'll get a 335 * RemoteException and need to re-reference the service. 336 */ 337 for (int retry = 0; retry < 2; retry++) { 338 if (mPhoneService == null) { 339 Log.w(TAG, 340 "Ignoring mobile radio request because could not acquire PhoneService"); 341 break; 342 } 343 344 try { 345 return mPhoneService.setRadio(turnOn); 346 } catch (RemoteException e) { 347 if (retry == 0) getPhoneService(true); 348 } 349 } 350 351 Log.w(TAG, "Could not set radio power to " + (turnOn ? "on" : "off")); 352 return false; 353 } 354 355 /** 356 * Tells the phone sub-system that the caller wants to 357 * begin using the named feature. The only supported features at 358 * this time are {@code Phone.FEATURE_ENABLE_MMS}, which allows an application 359 * to specify that it wants to send and/or receive MMS data, and 360 * {@code Phone.FEATURE_ENABLE_SUPL}, which is used for Assisted GPS. 361 * @param feature the name of the feature to be used 362 * @param callingPid the process ID of the process that is issuing this request 363 * @param callingUid the user ID of the process that is issuing this request 364 * @return an integer value representing the outcome of the request. 365 * The interpretation of this value is feature-specific. 366 * specific, except that the value {@code -1} 367 * always indicates failure. For {@code Phone.FEATURE_ENABLE_MMS}, 368 * the other possible return values are 369 * <ul> 370 * <li>{@code Phone.APN_ALREADY_ACTIVE}</li> 371 * <li>{@code Phone.APN_REQUEST_STARTED}</li> 372 * <li>{@code Phone.APN_TYPE_NOT_AVAILABLE}</li> 373 * <li>{@code Phone.APN_REQUEST_FAILED}</li> 374 * </ul> 375 */ 376 public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { 377 if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { 378 mLastCallingPid = callingPid; 379 return setEnableApn(Phone.APN_TYPE_MMS, true); 380 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { 381 return setEnableApn(Phone.APN_TYPE_SUPL, true); 382 } else { 383 return -1; 384 } 385 } 386 387 /** 388 * Tells the phone sub-system that the caller is finished 389 * using the named feature. The only supported feature at 390 * this time is {@code Phone.FEATURE_ENABLE_MMS}, which allows an application 391 * to specify that it wants to send and/or receive MMS data. 392 * @param feature the name of the feature that is no longer needed 393 * @param callingPid the process ID of the process that is issuing this request 394 * @param callingUid the user ID of the process that is issuing this request 395 * @return an integer value representing the outcome of the request. 396 * The interpretation of this value is feature-specific, except that 397 * the value {@code -1} always indicates failure. 398 */ 399 public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { 400 if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { 401 return setEnableApn(Phone.APN_TYPE_MMS, false); 402 } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { 403 return setEnableApn(Phone.APN_TYPE_SUPL, false); 404 } else { 405 return -1; 406 } 407 } 408 409 /** 410 * Ensure that a network route exists to deliver traffic to the specified 411 * host via the mobile data network. 412 * @param hostAddress the IP address of the host to which the route is desired, 413 * in network byte order. 414 * @return {@code true} on success, {@code false} on failure 415 */ 416 @Override 417 public boolean requestRouteToHost(int hostAddress) { 418 if (mInterfaceName != null && hostAddress != -1) { 419 if (DBG) { 420 Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress)); 421 } 422 return NetworkUtils.addHostRoute(mInterfaceName, hostAddress) == 0; 423 } else { 424 return false; 425 } 426 } 427 428 @Override 429 public String toString() { 430 StringBuffer sb = new StringBuffer("Mobile data state: "); 431 432 sb.append(mMobileDataState); 433 return sb.toString(); 434 } 435 436 private void setupDnsProperties() { 437 mDnsServers.clear(); 438 // Set up per-process DNS server list on behalf of the MMS process 439 int i = 1; 440 if (mInterfaceName != null) { 441 for (String propName : sDnsPropNames) { 442 if (propName.indexOf(mInterfaceName) != -1) { 443 String propVal = SystemProperties.get(propName); 444 if (propVal != null && propVal.length() != 0 && !propVal.equals("0.0.0.0")) { 445 mDnsServers.add(propVal); 446 if (mLastCallingPid != -1) { 447 SystemProperties.set("net.dns" + i + "." + mLastCallingPid, propVal); 448 } 449 ++i; 450 } 451 } 452 } 453 } 454 if (i == 1) { 455 Log.d(TAG, "DNS server addresses are not known."); 456 } else if (mLastCallingPid != -1) { 457 /* 458 * Bump the property that tells the name resolver library 459 * to reread the DNS server list from the properties. 460 */ 461 String propVal = SystemProperties.get("net.dnschange"); 462 if (propVal.length() != 0) { 463 try { 464 int n = Integer.parseInt(propVal); 465 SystemProperties.set("net.dnschange", "" + (n+1)); 466 } catch (NumberFormatException e) { 467 } 468 } 469 } 470 mLastCallingPid = -1; 471 } 472 473 /** 474 * Internal method supporting the ENABLE_MMS feature. 475 * @param apnType the type of APN to be enabled or disabled (e.g., mms) 476 * @param enable {@code true} to enable the specified APN type, 477 * {@code false} to disable it. 478 * @return an integer value representing the outcome of the request. 479 */ 480 private int setEnableApn(String apnType, boolean enable) { 481 getPhoneService(false); 482 /* 483 * If the phone process has crashed in the past, we'll get a 484 * RemoteException and need to re-reference the service. 485 */ 486 for (int retry = 0; retry < 2; retry++) { 487 if (mPhoneService == null) { 488 Log.w(TAG, 489 "Ignoring feature request because could not acquire PhoneService"); 490 break; 491 } 492 493 try { 494 if (enable) { 495 return mPhoneService.enableApnType(apnType); 496 } else { 497 return mPhoneService.disableApnType(apnType); 498 } 499 } catch (RemoteException e) { 500 if (retry == 0) getPhoneService(true); 501 } 502 } 503 504 Log.w(TAG, "Could not " + (enable ? "enable" : "disable") 505 + " APN type \"" + apnType + "\""); 506 return Phone.APN_REQUEST_FAILED; 507 } 508} 509