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 17 package android.net; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.net.NetworkInfo.DetailedState; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.Messenger; 29 import android.os.RemoteException; 30 import android.os.ServiceManager; 31 import android.telephony.PhoneStateListener; 32 import android.telephony.SignalStrength; 33 import android.telephony.TelephonyManager; 34 import android.text.TextUtils; 35 import android.util.Slog; 36 37 import com.android.internal.telephony.DctConstants; 38 import com.android.internal.telephony.ITelephony; 39 import com.android.internal.telephony.PhoneConstants; 40 import com.android.internal.telephony.TelephonyIntents; 41 import com.android.internal.util.AsyncChannel; 42 43 import java.io.CharArrayWriter; 44 import java.io.PrintWriter; 45 import java.util.concurrent.atomic.AtomicBoolean; 46 47 /** 48 * Track the state of mobile data connectivity. This is done by 49 * receiving broadcast intents from the Phone process whenever 50 * the state of data connectivity changes. 51 * 52 * {@hide} 53 */ 54 public class MobileDataStateTracker extends BaseNetworkStateTracker { 55 56 private static final String TAG = "MobileDataStateTracker"; 57 private static final boolean DBG = true; 58 private static final boolean VDBG = false; 59 60 private PhoneConstants.DataState mMobileDataState; 61 private ITelephony mPhoneService; 62 63 private String mApnType; 64 private NetworkInfo mNetworkInfo; 65 private boolean mTeardownRequested = false; 66 private Handler mTarget; 67 private Context mContext; 68 private LinkProperties mLinkProperties; 69 private LinkCapabilities mLinkCapabilities; 70 private boolean mPrivateDnsRouteSet = false; 71 private boolean mDefaultRouteSet = false; 72 73 // NOTE: these are only kept for debugging output; actual values are 74 // maintained in DataConnectionTracker. 75 protected boolean mUserDataEnabled = true; 76 protected boolean mPolicyDataEnabled = true; 77 78 private Handler mHandler; 79 private AsyncChannel mDataConnectionTrackerAc; 80 81 private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false); 82 83 private SignalStrength mSignalStrength; 84 85 private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker(); 86 87 private static final int UNKNOWN = LinkQualityInfo.UNKNOWN_INT; 88 89 /** 90 * Create a new MobileDataStateTracker 91 * @param netType the ConnectivityManager network type 92 * @param tag the name of this network 93 */ 94 public MobileDataStateTracker(int netType, String tag) { 95 mNetworkInfo = new NetworkInfo(netType, 96 TelephonyManager.getDefault().getNetworkType(), tag, 97 TelephonyManager.getDefault().getNetworkTypeName()); 98 mApnType = networkTypeToApnType(netType); 99 } 100 101 /** 102 * Begin monitoring data connectivity. 103 * 104 * @param context is the current Android context 105 * @param target is the Hander to which to return the events. 106 */ 107 public void startMonitoring(Context context, Handler target) { 108 mTarget = target; 109 mContext = context; 110 111 mHandler = new MdstHandler(target.getLooper(), this); 112 113 IntentFilter filter = new IntentFilter(); 114 filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 115 filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN); 116 filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); 117 118 mContext.registerReceiver(new MobileDataStateReceiver(), filter); 119 mMobileDataState = PhoneConstants.DataState.DISCONNECTED; 120 121 TelephonyManager tm = (TelephonyManager)mContext.getSystemService( 122 Context.TELEPHONY_SERVICE); 123 tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); 124 } 125 126 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 127 @Override 128 public void onSignalStrengthsChanged(SignalStrength signalStrength) { 129 mSignalStrength = signalStrength; 130 } 131 }; 132 133 static class MdstHandler extends Handler { 134 private MobileDataStateTracker mMdst; 135 136 MdstHandler(Looper looper, MobileDataStateTracker mdst) { 137 super(looper); 138 mMdst = mdst; 139 } 140 141 @Override 142 public void handleMessage(Message msg) { 143 switch (msg.what) { 144 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: 145 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 146 if (VDBG) { 147 mMdst.log("MdstHandler connected"); 148 } 149 mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj; 150 } else { 151 if (VDBG) { 152 mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1); 153 } 154 } 155 break; 156 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: 157 if (VDBG) mMdst.log("Disconnected from DataStateTracker"); 158 mMdst.mDataConnectionTrackerAc = null; 159 break; 160 default: { 161 if (VDBG) mMdst.log("Ignorning unknown message=" + msg); 162 break; 163 } 164 } 165 } 166 } 167 168 public boolean isPrivateDnsRouteSet() { 169 return mPrivateDnsRouteSet; 170 } 171 172 public void privateDnsRouteSet(boolean enabled) { 173 mPrivateDnsRouteSet = enabled; 174 } 175 176 public NetworkInfo getNetworkInfo() { 177 return mNetworkInfo; 178 } 179 180 public boolean isDefaultRouteSet() { 181 return mDefaultRouteSet; 182 } 183 184 public void defaultRouteSet(boolean enabled) { 185 mDefaultRouteSet = enabled; 186 } 187 188 /** 189 * This is not implemented. 190 */ 191 public void releaseWakeLock() { 192 } 193 194 private void updateLinkProperitesAndCapatilities(Intent intent) { 195 mLinkProperties = intent.getParcelableExtra( 196 PhoneConstants.DATA_LINK_PROPERTIES_KEY); 197 if (mLinkProperties == null) { 198 loge("CONNECTED event did not supply link properties."); 199 mLinkProperties = new LinkProperties(); 200 } 201 mLinkProperties.setMtu(mContext.getResources().getInteger( 202 com.android.internal.R.integer.config_mobile_mtu)); 203 mLinkCapabilities = intent.getParcelableExtra( 204 PhoneConstants.DATA_LINK_CAPABILITIES_KEY); 205 if (mLinkCapabilities == null) { 206 loge("CONNECTED event did not supply link capabilities."); 207 mLinkCapabilities = new LinkCapabilities(); 208 } 209 } 210 211 private class MobileDataStateReceiver extends BroadcastReceiver { 212 @Override 213 public void onReceive(Context context, Intent intent) { 214 if (intent.getAction().equals(TelephonyIntents. 215 ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) { 216 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); 217 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY); 218 if (!TextUtils.equals(mApnType, apnType)) { 219 return; 220 } 221 if (DBG) { 222 log("Broadcast received: " + intent.getAction() + " apnType=" + apnType 223 + " apnName=" + apnName); 224 } 225 226 // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING 227 mMobileDataState = PhoneConstants.DataState.CONNECTING; 228 updateLinkProperitesAndCapatilities(intent); 229 mNetworkInfo.setIsConnectedToProvisioningNetwork(true); 230 231 // Change state to SUSPENDED so setDetailedState 232 // sends EVENT_STATE_CHANGED to connectivityService 233 setDetailedState(DetailedState.SUSPENDED, "", apnName); 234 } else if (intent.getAction().equals(TelephonyIntents. 235 ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 236 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY); 237 if (VDBG) { 238 log(String.format("Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED" 239 + "mApnType=%s %s received apnType=%s", mApnType, 240 TextUtils.equals(apnType, mApnType) ? "==" : "!=", apnType)); 241 } 242 if (!TextUtils.equals(apnType, mApnType)) { 243 return; 244 } 245 // Assume this isn't a provisioning network. 246 mNetworkInfo.setIsConnectedToProvisioningNetwork(false); 247 if (DBG) { 248 log("Broadcast received: " + intent.getAction() + " apnType=" + apnType); 249 } 250 251 int oldSubtype = mNetworkInfo.getSubtype(); 252 int newSubType = TelephonyManager.getDefault().getNetworkType(); 253 String subTypeName = TelephonyManager.getDefault().getNetworkTypeName(); 254 mNetworkInfo.setSubtype(newSubType, subTypeName); 255 if (newSubType != oldSubtype && mNetworkInfo.isConnected()) { 256 Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED, 257 oldSubtype, 0, mNetworkInfo); 258 msg.sendToTarget(); 259 } 260 261 PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class, 262 intent.getStringExtra(PhoneConstants.STATE_KEY)); 263 String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY); 264 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); 265 mNetworkInfo.setRoaming(intent.getBooleanExtra( 266 PhoneConstants.DATA_NETWORK_ROAMING_KEY, false)); 267 if (VDBG) { 268 log(mApnType + " setting isAvailable to " + 269 intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false)); 270 } 271 mNetworkInfo.setIsAvailable(!intent.getBooleanExtra( 272 PhoneConstants.NETWORK_UNAVAILABLE_KEY, false)); 273 274 if (DBG) { 275 log("Received state=" + state + ", old=" + mMobileDataState + 276 ", reason=" + (reason == null ? "(unspecified)" : reason)); 277 } 278 if (mMobileDataState != state) { 279 mMobileDataState = state; 280 switch (state) { 281 case DISCONNECTED: 282 if(isTeardownRequested()) { 283 setTeardownRequested(false); 284 } 285 286 setDetailedState(DetailedState.DISCONNECTED, reason, apnName); 287 // can't do this here - ConnectivityService needs it to clear stuff 288 // it's ok though - just leave it to be refreshed next time 289 // we connect. 290 //if (DBG) log("clearing mInterfaceName for "+ mApnType + 291 // " as it DISCONNECTED"); 292 //mInterfaceName = null; 293 break; 294 case CONNECTING: 295 setDetailedState(DetailedState.CONNECTING, reason, apnName); 296 break; 297 case SUSPENDED: 298 setDetailedState(DetailedState.SUSPENDED, reason, apnName); 299 break; 300 case CONNECTED: 301 updateLinkProperitesAndCapatilities(intent); 302 setDetailedState(DetailedState.CONNECTED, reason, apnName); 303 break; 304 } 305 306 if (VDBG) { 307 Slog.d(TAG, "TelephonyMgr.DataConnectionStateChanged"); 308 if (mNetworkInfo != null) { 309 Slog.d(TAG, "NetworkInfo = " + mNetworkInfo.toString()); 310 Slog.d(TAG, "subType = " + String.valueOf(mNetworkInfo.getSubtype())); 311 Slog.d(TAG, "subType = " + mNetworkInfo.getSubtypeName()); 312 } 313 if (mLinkProperties != null) { 314 Slog.d(TAG, "LinkProperties = " + mLinkProperties.toString()); 315 } else { 316 Slog.d(TAG, "LinkProperties = " ); 317 } 318 319 if (mLinkCapabilities != null) { 320 Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities.toString()); 321 } else { 322 Slog.d(TAG, "LinkCapabilities = " ); 323 } 324 } 325 326 327 /* lets not sample traffic data across state changes */ 328 mSamplingDataTracker.resetSamplingData(); 329 } else { 330 // There was no state change. Check if LinkProperties has been updated. 331 if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) { 332 mLinkProperties = intent.getParcelableExtra( 333 PhoneConstants.DATA_LINK_PROPERTIES_KEY); 334 if (mLinkProperties == null) { 335 loge("No link property in LINK_PROPERTIES change event."); 336 mLinkProperties = new LinkProperties(); 337 } 338 // Just update reason field in this NetworkInfo 339 mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason, 340 mNetworkInfo.getExtraInfo()); 341 Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, 342 mNetworkInfo); 343 msg.sendToTarget(); 344 } 345 } 346 } else if (intent.getAction(). 347 equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) { 348 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY); 349 if (!TextUtils.equals(apnType, mApnType)) { 350 if (DBG) { 351 log(String.format( 352 "Broadcast received: ACTION_ANY_DATA_CONNECTION_FAILED ignore, " + 353 "mApnType=%s != received apnType=%s", mApnType, apnType)); 354 } 355 return; 356 } 357 // Assume this isn't a provisioning network. 358 mNetworkInfo.setIsConnectedToProvisioningNetwork(false); 359 String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY); 360 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); 361 if (DBG) { 362 log("Broadcast received: " + intent.getAction() + 363 " reason=" + reason == null ? "null" : reason); 364 } 365 setDetailedState(DetailedState.FAILED, reason, apnName); 366 } else { 367 if (DBG) log("Broadcast received: ignore " + intent.getAction()); 368 } 369 } 370 } 371 372 private void getPhoneService(boolean forceRefresh) { 373 if ((mPhoneService == null) || forceRefresh) { 374 mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone")); 375 } 376 } 377 378 /** 379 * Report whether data connectivity is possible. 380 */ 381 public boolean isAvailable() { 382 return mNetworkInfo.isAvailable(); 383 } 384 385 /** 386 * Return the system properties name associated with the tcp buffer sizes 387 * for this network. 388 */ 389 public String getTcpBufferSizesPropName() { 390 String networkTypeStr = "unknown"; 391 TelephonyManager tm = new TelephonyManager(mContext); 392 //TODO We have to edit the parameter for getNetworkType regarding CDMA 393 switch(tm.getNetworkType()) { 394 case TelephonyManager.NETWORK_TYPE_GPRS: 395 networkTypeStr = "gprs"; 396 break; 397 case TelephonyManager.NETWORK_TYPE_EDGE: 398 networkTypeStr = "edge"; 399 break; 400 case TelephonyManager.NETWORK_TYPE_UMTS: 401 networkTypeStr = "umts"; 402 break; 403 case TelephonyManager.NETWORK_TYPE_HSDPA: 404 networkTypeStr = "hsdpa"; 405 break; 406 case TelephonyManager.NETWORK_TYPE_HSUPA: 407 networkTypeStr = "hsupa"; 408 break; 409 case TelephonyManager.NETWORK_TYPE_HSPA: 410 networkTypeStr = "hspa"; 411 break; 412 case TelephonyManager.NETWORK_TYPE_HSPAP: 413 networkTypeStr = "hspap"; 414 break; 415 case TelephonyManager.NETWORK_TYPE_CDMA: 416 networkTypeStr = "cdma"; 417 break; 418 case TelephonyManager.NETWORK_TYPE_1xRTT: 419 networkTypeStr = "1xrtt"; 420 break; 421 case TelephonyManager.NETWORK_TYPE_EVDO_0: 422 networkTypeStr = "evdo"; 423 break; 424 case TelephonyManager.NETWORK_TYPE_EVDO_A: 425 networkTypeStr = "evdo"; 426 break; 427 case TelephonyManager.NETWORK_TYPE_EVDO_B: 428 networkTypeStr = "evdo"; 429 break; 430 case TelephonyManager.NETWORK_TYPE_IDEN: 431 networkTypeStr = "iden"; 432 break; 433 case TelephonyManager.NETWORK_TYPE_LTE: 434 networkTypeStr = "lte"; 435 break; 436 case TelephonyManager.NETWORK_TYPE_EHRPD: 437 networkTypeStr = "ehrpd"; 438 break; 439 default: 440 loge("unknown network type: " + tm.getNetworkType()); 441 } 442 return "net.tcp.buffersize." + networkTypeStr; 443 } 444 445 /** 446 * Tear down mobile data connectivity, i.e., disable the ability to create 447 * mobile data connections. 448 * TODO - make async and return nothing? 449 */ 450 public boolean teardown() { 451 setTeardownRequested(true); 452 return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED); 453 } 454 455 /** 456 * @return true if this is ready to operate 457 */ 458 public boolean isReady() { 459 return mDataConnectionTrackerAc != null; 460 } 461 462 @Override 463 public void captivePortalCheckComplete() { 464 // not implemented 465 } 466 467 @Override 468 public void captivePortalCheckCompleted(boolean isCaptivePortal) { 469 if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) { 470 // Captive portal change enable/disable failing fast 471 setEnableFailFastMobileData( 472 isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED); 473 } 474 } 475 476 /** 477 * Record the detailed state of a network, and if it is a 478 * change from the previous state, send a notification to 479 * any listeners. 480 * @param state the new {@code DetailedState} 481 * @param reason a {@code String} indicating a reason for the state change, 482 * if one was supplied. May be {@code null}. 483 * @param extraInfo optional {@code String} providing extra information about the state change 484 */ 485 private void setDetailedState(NetworkInfo.DetailedState state, String reason, 486 String extraInfo) { 487 if (DBG) log("setDetailed state, old =" 488 + mNetworkInfo.getDetailedState() + " and new state=" + state); 489 if (state != mNetworkInfo.getDetailedState()) { 490 boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING); 491 String lastReason = mNetworkInfo.getReason(); 492 /* 493 * If a reason was supplied when the CONNECTING state was entered, and no 494 * reason was supplied for entering the CONNECTED state, then retain the 495 * reason that was supplied when going to CONNECTING. 496 */ 497 if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null 498 && lastReason != null) 499 reason = lastReason; 500 mNetworkInfo.setDetailedState(state, reason, extraInfo); 501 Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)); 502 msg.sendToTarget(); 503 } 504 } 505 506 public void setTeardownRequested(boolean isRequested) { 507 mTeardownRequested = isRequested; 508 } 509 510 public boolean isTeardownRequested() { 511 return mTeardownRequested; 512 } 513 514 /** 515 * Re-enable mobile data connectivity after a {@link #teardown()}. 516 * TODO - make async and always get a notification? 517 */ 518 public boolean reconnect() { 519 boolean retValue = false; //connected or expect to be? 520 setTeardownRequested(false); 521 switch (setEnableApn(mApnType, true)) { 522 case PhoneConstants.APN_ALREADY_ACTIVE: 523 // need to set self to CONNECTING so the below message is handled. 524 retValue = true; 525 break; 526 case PhoneConstants.APN_REQUEST_STARTED: 527 // set IDLE here , avoid the following second FAILED not sent out 528 mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null); 529 retValue = true; 530 break; 531 case PhoneConstants.APN_REQUEST_FAILED: 532 case PhoneConstants.APN_TYPE_NOT_AVAILABLE: 533 break; 534 default: 535 loge("Error in reconnect - unexpected response."); 536 break; 537 } 538 return retValue; 539 } 540 541 /** 542 * Turn on or off the mobile radio. No connectivity will be possible while the 543 * radio is off. The operation is a no-op if the radio is already in the desired state. 544 * @param turnOn {@code true} if the radio should be turned on, {@code false} if 545 */ 546 public boolean setRadio(boolean turnOn) { 547 getPhoneService(false); 548 /* 549 * If the phone process has crashed in the past, we'll get a 550 * RemoteException and need to re-reference the service. 551 */ 552 for (int retry = 0; retry < 2; retry++) { 553 if (mPhoneService == null) { 554 loge("Ignoring mobile radio request because could not acquire PhoneService"); 555 break; 556 } 557 558 try { 559 return mPhoneService.setRadio(turnOn); 560 } catch (RemoteException e) { 561 if (retry == 0) getPhoneService(true); 562 } 563 } 564 565 loge("Could not set radio power to " + (turnOn ? "on" : "off")); 566 return false; 567 } 568 569 @Override 570 public void setUserDataEnable(boolean enabled) { 571 if (DBG) log("setUserDataEnable: E enabled=" + enabled); 572 final AsyncChannel channel = mDataConnectionTrackerAc; 573 if (channel != null) { 574 channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE, 575 enabled ? DctConstants.ENABLED : DctConstants.DISABLED); 576 mUserDataEnabled = enabled; 577 } 578 if (VDBG) log("setUserDataEnable: X enabled=" + enabled); 579 } 580 581 @Override 582 public void setPolicyDataEnable(boolean enabled) { 583 if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")"); 584 final AsyncChannel channel = mDataConnectionTrackerAc; 585 if (channel != null) { 586 channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE, 587 enabled ? DctConstants.ENABLED : DctConstants.DISABLED); 588 mPolicyDataEnabled = enabled; 589 } 590 } 591 592 /** 593 * Eanble/disable FailFast 594 * 595 * @param enabled is DctConstants.ENABLED/DISABLED 596 */ 597 public void setEnableFailFastMobileData(int enabled) { 598 if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")"); 599 final AsyncChannel channel = mDataConnectionTrackerAc; 600 if (channel != null) { 601 channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled); 602 } 603 } 604 605 /** 606 * carrier dependency is met/unmet 607 * @param met 608 */ 609 public void setDependencyMet(boolean met) { 610 Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType); 611 try { 612 if (DBG) log("setDependencyMet: E met=" + met); 613 Message msg = Message.obtain(); 614 msg.what = DctConstants.CMD_SET_DEPENDENCY_MET; 615 msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED); 616 msg.setData(bundle); 617 mDataConnectionTrackerAc.sendMessage(msg); 618 if (VDBG) log("setDependencyMet: X met=" + met); 619 } catch (NullPointerException e) { 620 loge("setDependencyMet: X mAc was null" + e); 621 } 622 } 623 624 /** 625 * Inform DCT mobile provisioning has started, it ends when provisioning completes. 626 */ 627 public void enableMobileProvisioning(String url) { 628 if (DBG) log("enableMobileProvisioning(url=" + url + ")"); 629 final AsyncChannel channel = mDataConnectionTrackerAc; 630 if (channel != null) { 631 Message msg = Message.obtain(); 632 msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING; 633 msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url)); 634 channel.sendMessage(msg); 635 } 636 } 637 638 /** 639 * Return if this network is the provisioning network. Valid only if connected. 640 * @param met 641 */ 642 public boolean isProvisioningNetwork() { 643 boolean retVal; 644 try { 645 Message msg = Message.obtain(); 646 msg.what = DctConstants.CMD_IS_PROVISIONING_APN; 647 msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType)); 648 Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg); 649 retVal = result.arg1 == DctConstants.ENABLED; 650 } catch (NullPointerException e) { 651 loge("isProvisioningNetwork: X " + e); 652 retVal = false; 653 } 654 if (DBG) log("isProvisioningNetwork: retVal=" + retVal); 655 return retVal; 656 } 657 658 @Override 659 public void addStackedLink(LinkProperties link) { 660 mLinkProperties.addStackedLink(link); 661 } 662 663 @Override 664 public void removeStackedLink(LinkProperties link) { 665 mLinkProperties.removeStackedLink(link); 666 } 667 668 @Override 669 public String toString() { 670 final CharArrayWriter writer = new CharArrayWriter(); 671 final PrintWriter pw = new PrintWriter(writer); 672 pw.print("Mobile data state: "); pw.println(mMobileDataState); 673 pw.print("Data enabled: user="); pw.print(mUserDataEnabled); 674 pw.print(", policy="); pw.println(mPolicyDataEnabled); 675 return writer.toString(); 676 } 677 678 /** 679 * Internal method supporting the ENABLE_MMS feature. 680 * @param apnType the type of APN to be enabled or disabled (e.g., mms) 681 * @param enable {@code true} to enable the specified APN type, 682 * {@code false} to disable it. 683 * @return an integer value representing the outcome of the request. 684 */ 685 private int setEnableApn(String apnType, boolean enable) { 686 getPhoneService(false); 687 /* 688 * If the phone process has crashed in the past, we'll get a 689 * RemoteException and need to re-reference the service. 690 */ 691 for (int retry = 0; retry < 2; retry++) { 692 if (mPhoneService == null) { 693 loge("Ignoring feature request because could not acquire PhoneService"); 694 break; 695 } 696 697 try { 698 if (enable) { 699 return mPhoneService.enableApnType(apnType); 700 } else { 701 return mPhoneService.disableApnType(apnType); 702 } 703 } catch (RemoteException e) { 704 if (retry == 0) getPhoneService(true); 705 } 706 } 707 708 loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\""); 709 return PhoneConstants.APN_REQUEST_FAILED; 710 } 711 712 public static String networkTypeToApnType(int netType) { 713 switch(netType) { 714 case ConnectivityManager.TYPE_MOBILE: 715 return PhoneConstants.APN_TYPE_DEFAULT; // TODO - use just one of these 716 case ConnectivityManager.TYPE_MOBILE_MMS: 717 return PhoneConstants.APN_TYPE_MMS; 718 case ConnectivityManager.TYPE_MOBILE_SUPL: 719 return PhoneConstants.APN_TYPE_SUPL; 720 case ConnectivityManager.TYPE_MOBILE_DUN: 721 return PhoneConstants.APN_TYPE_DUN; 722 case ConnectivityManager.TYPE_MOBILE_HIPRI: 723 return PhoneConstants.APN_TYPE_HIPRI; 724 case ConnectivityManager.TYPE_MOBILE_FOTA: 725 return PhoneConstants.APN_TYPE_FOTA; 726 case ConnectivityManager.TYPE_MOBILE_IMS: 727 return PhoneConstants.APN_TYPE_IMS; 728 case ConnectivityManager.TYPE_MOBILE_CBS: 729 return PhoneConstants.APN_TYPE_CBS; 730 case ConnectivityManager.TYPE_MOBILE_IA: 731 return PhoneConstants.APN_TYPE_IA; 732 default: 733 sloge("Error mapping networkType " + netType + " to apnType."); 734 return null; 735 } 736 } 737 738 739 /** 740 * @see android.net.NetworkStateTracker#getLinkProperties() 741 */ 742 @Override 743 public LinkProperties getLinkProperties() { 744 return new LinkProperties(mLinkProperties); 745 } 746 747 /** 748 * @see android.net.NetworkStateTracker#getLinkCapabilities() 749 */ 750 @Override 751 public LinkCapabilities getLinkCapabilities() { 752 return new LinkCapabilities(mLinkCapabilities); 753 } 754 755 public void supplyMessenger(Messenger messenger) { 756 if (VDBG) log(mApnType + " got supplyMessenger"); 757 AsyncChannel ac = new AsyncChannel(); 758 ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger); 759 } 760 761 private void log(String s) { 762 Slog.d(TAG, mApnType + ": " + s); 763 } 764 765 private void loge(String s) { 766 Slog.e(TAG, mApnType + ": " + s); 767 } 768 769 static private void sloge(String s) { 770 Slog.e(TAG, s); 771 } 772 773 @Override 774 public LinkQualityInfo getLinkQualityInfo() { 775 if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) { 776 // no data available yet; just return 777 return null; 778 } 779 780 MobileLinkQualityInfo li = new MobileLinkQualityInfo(); 781 782 li.setNetworkType(mNetworkInfo.getType()); 783 784 mSamplingDataTracker.setCommonLinkQualityInfoFields(li); 785 786 if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) { 787 li.setMobileNetworkType(mNetworkInfo.getSubtype()); 788 789 NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype()); 790 if (entry != null) { 791 li.setTheoreticalRxBandwidth(entry.downloadBandwidth); 792 li.setTheoreticalRxBandwidth(entry.uploadBandwidth); 793 li.setTheoreticalLatency(entry.latency); 794 } 795 796 if (mSignalStrength != null) { 797 li.setNormalizedSignalStrength(getNormalizedSignalStrength( 798 li.getMobileNetworkType(), mSignalStrength)); 799 } 800 } 801 802 SignalStrength ss = mSignalStrength; 803 if (ss != null) { 804 805 li.setRssi(ss.getGsmSignalStrength()); 806 li.setGsmErrorRate(ss.getGsmBitErrorRate()); 807 li.setCdmaDbm(ss.getCdmaDbm()); 808 li.setCdmaEcio(ss.getCdmaEcio()); 809 li.setEvdoDbm(ss.getEvdoDbm()); 810 li.setEvdoEcio(ss.getEvdoEcio()); 811 li.setEvdoSnr(ss.getEvdoSnr()); 812 li.setLteSignalStrength(ss.getLteSignalStrength()); 813 li.setLteRsrp(ss.getLteRsrp()); 814 li.setLteRsrq(ss.getLteRsrq()); 815 li.setLteRssnr(ss.getLteRssnr()); 816 li.setLteCqi(ss.getLteCqi()); 817 } 818 819 if (VDBG) { 820 Slog.d(TAG, "Returning LinkQualityInfo with" 821 + " MobileNetworkType = " + String.valueOf(li.getMobileNetworkType()) 822 + " Theoretical Rx BW = " + String.valueOf(li.getTheoreticalRxBandwidth()) 823 + " gsm Signal Strength = " + String.valueOf(li.getRssi()) 824 + " cdma Signal Strength = " + String.valueOf(li.getCdmaDbm()) 825 + " evdo Signal Strength = " + String.valueOf(li.getEvdoDbm()) 826 + " Lte Signal Strength = " + String.valueOf(li.getLteSignalStrength())); 827 } 828 829 return li; 830 } 831 832 static class NetworkDataEntry { 833 public int networkType; 834 public int downloadBandwidth; // in kbps 835 public int uploadBandwidth; // in kbps 836 public int latency; // in millisecond 837 838 NetworkDataEntry(int i1, int i2, int i3, int i4) { 839 networkType = i1; 840 downloadBandwidth = i2; 841 uploadBandwidth = i3; 842 latency = i4; 843 } 844 } 845 846 private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] { 847 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE, 237, 118, UNKNOWN), 848 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS, 48, 40, UNKNOWN), 849 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS, 384, 64, UNKNOWN), 850 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA, 14400, UNKNOWN, UNKNOWN), 851 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA, 14400, 5760, UNKNOWN), 852 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA, 14400, 5760, UNKNOWN), 853 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP, 21000, 5760, UNKNOWN), 854 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA, UNKNOWN, UNKNOWN, UNKNOWN), 855 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT, UNKNOWN, UNKNOWN, UNKNOWN), 856 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0, 2468, 153, UNKNOWN), 857 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A, 3072, 1800, UNKNOWN), 858 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B, 14700, 1800, UNKNOWN), 859 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN, UNKNOWN, UNKNOWN, UNKNOWN), 860 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE, 100000, 50000, UNKNOWN), 861 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD, UNKNOWN, UNKNOWN, UNKNOWN), 862 }; 863 864 private static NetworkDataEntry getNetworkDataEntry(int networkType) { 865 for (NetworkDataEntry entry : mTheoreticalBWTable) { 866 if (entry.networkType == networkType) { 867 return entry; 868 } 869 } 870 871 Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType)); 872 return null; 873 } 874 875 private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) { 876 877 int level; 878 879 switch(networkType) { 880 case TelephonyManager.NETWORK_TYPE_EDGE: 881 case TelephonyManager.NETWORK_TYPE_GPRS: 882 case TelephonyManager.NETWORK_TYPE_UMTS: 883 case TelephonyManager.NETWORK_TYPE_HSDPA: 884 case TelephonyManager.NETWORK_TYPE_HSUPA: 885 case TelephonyManager.NETWORK_TYPE_HSPA: 886 case TelephonyManager.NETWORK_TYPE_HSPAP: 887 level = ss.getGsmLevel(); 888 break; 889 case TelephonyManager.NETWORK_TYPE_CDMA: 890 case TelephonyManager.NETWORK_TYPE_1xRTT: 891 level = ss.getCdmaLevel(); 892 break; 893 case TelephonyManager.NETWORK_TYPE_EVDO_0: 894 case TelephonyManager.NETWORK_TYPE_EVDO_A: 895 case TelephonyManager.NETWORK_TYPE_EVDO_B: 896 level = ss.getEvdoLevel(); 897 break; 898 case TelephonyManager.NETWORK_TYPE_LTE: 899 level = ss.getLteLevel(); 900 break; 901 case TelephonyManager.NETWORK_TYPE_IDEN: 902 case TelephonyManager.NETWORK_TYPE_EHRPD: 903 default: 904 return UNKNOWN; 905 } 906 907 return (level * LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) / 908 SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 909 } 910 911 @Override 912 public void startSampling(SamplingDataTracker.SamplingSnapshot s) { 913 mSamplingDataTracker.startSampling(s); 914 } 915 916 @Override 917 public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { 918 mSamplingDataTracker.stopSampling(s); 919 } 920 } 921