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 570 public void setInternalDataEnable(boolean enabled) { 571 if (DBG) log("setInternalDataEnable: E enabled=" + enabled); 572 final AsyncChannel channel = mDataConnectionTrackerAc; 573 if (channel != null) { 574 channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE, 575 enabled ? DctConstants.ENABLED : DctConstants.DISABLED); 576 } 577 if (VDBG) log("setInternalDataEnable: X enabled=" + enabled); 578 } 579 580 @Override 581 public void setUserDataEnable(boolean enabled) { 582 if (DBG) log("setUserDataEnable: E enabled=" + enabled); 583 final AsyncChannel channel = mDataConnectionTrackerAc; 584 if (channel != null) { 585 channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE, 586 enabled ? DctConstants.ENABLED : DctConstants.DISABLED); 587 mUserDataEnabled = enabled; 588 } 589 if (VDBG) log("setUserDataEnable: X enabled=" + enabled); 590 } 591 592 @Override 593 public void setPolicyDataEnable(boolean enabled) { 594 if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")"); 595 final AsyncChannel channel = mDataConnectionTrackerAc; 596 if (channel != null) { 597 channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE, 598 enabled ? DctConstants.ENABLED : DctConstants.DISABLED); 599 mPolicyDataEnabled = enabled; 600 } 601 } 602 603 /** 604 * Eanble/disable FailFast 605 * 606 * @param enabled is DctConstants.ENABLED/DISABLED 607 */ 608 public void setEnableFailFastMobileData(int enabled) { 609 if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")"); 610 final AsyncChannel channel = mDataConnectionTrackerAc; 611 if (channel != null) { 612 channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled); 613 } 614 } 615 616 /** 617 * carrier dependency is met/unmet 618 * @param met 619 */ 620 public void setDependencyMet(boolean met) { 621 Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType); 622 try { 623 if (DBG) log("setDependencyMet: E met=" + met); 624 Message msg = Message.obtain(); 625 msg.what = DctConstants.CMD_SET_DEPENDENCY_MET; 626 msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED); 627 msg.setData(bundle); 628 mDataConnectionTrackerAc.sendMessage(msg); 629 if (VDBG) log("setDependencyMet: X met=" + met); 630 } catch (NullPointerException e) { 631 loge("setDependencyMet: X mAc was null" + e); 632 } 633 } 634 635 /** 636 * Inform DCT mobile provisioning has started, it ends when provisioning completes. 637 */ 638 public void enableMobileProvisioning(String url) { 639 if (DBG) log("enableMobileProvisioning(url=" + url + ")"); 640 final AsyncChannel channel = mDataConnectionTrackerAc; 641 if (channel != null) { 642 Message msg = Message.obtain(); 643 msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING; 644 msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url)); 645 channel.sendMessage(msg); 646 } 647 } 648 649 /** 650 * Return if this network is the provisioning network. Valid only if connected. 651 * @param met 652 */ 653 public boolean isProvisioningNetwork() { 654 boolean retVal; 655 try { 656 Message msg = Message.obtain(); 657 msg.what = DctConstants.CMD_IS_PROVISIONING_APN; 658 msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType)); 659 Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg); 660 retVal = result.arg1 == DctConstants.ENABLED; 661 } catch (NullPointerException e) { 662 loge("isProvisioningNetwork: X " + e); 663 retVal = false; 664 } 665 if (DBG) log("isProvisioningNetwork: retVal=" + retVal); 666 return retVal; 667 } 668 669 @Override 670 public void addStackedLink(LinkProperties link) { 671 mLinkProperties.addStackedLink(link); 672 } 673 674 @Override 675 public void removeStackedLink(LinkProperties link) { 676 mLinkProperties.removeStackedLink(link); 677 } 678 679 @Override 680 public String toString() { 681 final CharArrayWriter writer = new CharArrayWriter(); 682 final PrintWriter pw = new PrintWriter(writer); 683 pw.print("Mobile data state: "); pw.println(mMobileDataState); 684 pw.print("Data enabled: user="); pw.print(mUserDataEnabled); 685 pw.print(", policy="); pw.println(mPolicyDataEnabled); 686 return writer.toString(); 687 } 688 689 /** 690 * Internal method supporting the ENABLE_MMS feature. 691 * @param apnType the type of APN to be enabled or disabled (e.g., mms) 692 * @param enable {@code true} to enable the specified APN type, 693 * {@code false} to disable it. 694 * @return an integer value representing the outcome of the request. 695 */ 696 private int setEnableApn(String apnType, boolean enable) { 697 getPhoneService(false); 698 /* 699 * If the phone process has crashed in the past, we'll get a 700 * RemoteException and need to re-reference the service. 701 */ 702 for (int retry = 0; retry < 2; retry++) { 703 if (mPhoneService == null) { 704 loge("Ignoring feature request because could not acquire PhoneService"); 705 break; 706 } 707 708 try { 709 if (enable) { 710 return mPhoneService.enableApnType(apnType); 711 } else { 712 return mPhoneService.disableApnType(apnType); 713 } 714 } catch (RemoteException e) { 715 if (retry == 0) getPhoneService(true); 716 } 717 } 718 719 loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\""); 720 return PhoneConstants.APN_REQUEST_FAILED; 721 } 722 723 public static String networkTypeToApnType(int netType) { 724 switch(netType) { 725 case ConnectivityManager.TYPE_MOBILE: 726 return PhoneConstants.APN_TYPE_DEFAULT; // TODO - use just one of these 727 case ConnectivityManager.TYPE_MOBILE_MMS: 728 return PhoneConstants.APN_TYPE_MMS; 729 case ConnectivityManager.TYPE_MOBILE_SUPL: 730 return PhoneConstants.APN_TYPE_SUPL; 731 case ConnectivityManager.TYPE_MOBILE_DUN: 732 return PhoneConstants.APN_TYPE_DUN; 733 case ConnectivityManager.TYPE_MOBILE_HIPRI: 734 return PhoneConstants.APN_TYPE_HIPRI; 735 case ConnectivityManager.TYPE_MOBILE_FOTA: 736 return PhoneConstants.APN_TYPE_FOTA; 737 case ConnectivityManager.TYPE_MOBILE_IMS: 738 return PhoneConstants.APN_TYPE_IMS; 739 case ConnectivityManager.TYPE_MOBILE_CBS: 740 return PhoneConstants.APN_TYPE_CBS; 741 case ConnectivityManager.TYPE_MOBILE_IA: 742 return PhoneConstants.APN_TYPE_IA; 743 default: 744 sloge("Error mapping networkType " + netType + " to apnType."); 745 return null; 746 } 747 } 748 749 750 /** 751 * @see android.net.NetworkStateTracker#getLinkProperties() 752 */ 753 @Override 754 public LinkProperties getLinkProperties() { 755 return new LinkProperties(mLinkProperties); 756 } 757 758 /** 759 * @see android.net.NetworkStateTracker#getLinkCapabilities() 760 */ 761 @Override 762 public LinkCapabilities getLinkCapabilities() { 763 return new LinkCapabilities(mLinkCapabilities); 764 } 765 766 public void supplyMessenger(Messenger messenger) { 767 if (VDBG) log(mApnType + " got supplyMessenger"); 768 AsyncChannel ac = new AsyncChannel(); 769 ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger); 770 } 771 772 private void log(String s) { 773 Slog.d(TAG, mApnType + ": " + s); 774 } 775 776 private void loge(String s) { 777 Slog.e(TAG, mApnType + ": " + s); 778 } 779 780 static private void sloge(String s) { 781 Slog.e(TAG, s); 782 } 783 784 @Override 785 public LinkQualityInfo getLinkQualityInfo() { 786 if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) { 787 // no data available yet; just return 788 return null; 789 } 790 791 MobileLinkQualityInfo li = new MobileLinkQualityInfo(); 792 793 li.setNetworkType(mNetworkInfo.getType()); 794 795 mSamplingDataTracker.setCommonLinkQualityInfoFields(li); 796 797 if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) { 798 li.setMobileNetworkType(mNetworkInfo.getSubtype()); 799 800 NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype()); 801 if (entry != null) { 802 li.setTheoreticalRxBandwidth(entry.downloadBandwidth); 803 li.setTheoreticalRxBandwidth(entry.uploadBandwidth); 804 li.setTheoreticalLatency(entry.latency); 805 } 806 807 if (mSignalStrength != null) { 808 li.setNormalizedSignalStrength(getNormalizedSignalStrength( 809 li.getMobileNetworkType(), mSignalStrength)); 810 } 811 } 812 813 SignalStrength ss = mSignalStrength; 814 if (ss != null) { 815 816 li.setRssi(ss.getGsmSignalStrength()); 817 li.setGsmErrorRate(ss.getGsmBitErrorRate()); 818 li.setCdmaDbm(ss.getCdmaDbm()); 819 li.setCdmaEcio(ss.getCdmaEcio()); 820 li.setEvdoDbm(ss.getEvdoDbm()); 821 li.setEvdoEcio(ss.getEvdoEcio()); 822 li.setEvdoSnr(ss.getEvdoSnr()); 823 li.setLteSignalStrength(ss.getLteSignalStrength()); 824 li.setLteRsrp(ss.getLteRsrp()); 825 li.setLteRsrq(ss.getLteRsrq()); 826 li.setLteRssnr(ss.getLteRssnr()); 827 li.setLteCqi(ss.getLteCqi()); 828 } 829 830 if (VDBG) { 831 Slog.d(TAG, "Returning LinkQualityInfo with" 832 + " MobileNetworkType = " + String.valueOf(li.getMobileNetworkType()) 833 + " Theoretical Rx BW = " + String.valueOf(li.getTheoreticalRxBandwidth()) 834 + " gsm Signal Strength = " + String.valueOf(li.getRssi()) 835 + " cdma Signal Strength = " + String.valueOf(li.getCdmaDbm()) 836 + " evdo Signal Strength = " + String.valueOf(li.getEvdoDbm()) 837 + " Lte Signal Strength = " + String.valueOf(li.getLteSignalStrength())); 838 } 839 840 return li; 841 } 842 843 static class NetworkDataEntry { 844 public int networkType; 845 public int downloadBandwidth; // in kbps 846 public int uploadBandwidth; // in kbps 847 public int latency; // in millisecond 848 849 NetworkDataEntry(int i1, int i2, int i3, int i4) { 850 networkType = i1; 851 downloadBandwidth = i2; 852 uploadBandwidth = i3; 853 latency = i4; 854 } 855 } 856 857 private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] { 858 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE, 237, 118, UNKNOWN), 859 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS, 48, 40, UNKNOWN), 860 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS, 384, 64, UNKNOWN), 861 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA, 14400, UNKNOWN, UNKNOWN), 862 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA, 14400, 5760, UNKNOWN), 863 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA, 14400, 5760, UNKNOWN), 864 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP, 21000, 5760, UNKNOWN), 865 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA, UNKNOWN, UNKNOWN, UNKNOWN), 866 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT, UNKNOWN, UNKNOWN, UNKNOWN), 867 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0, 2468, 153, UNKNOWN), 868 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A, 3072, 1800, UNKNOWN), 869 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B, 14700, 1800, UNKNOWN), 870 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN, UNKNOWN, UNKNOWN, UNKNOWN), 871 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE, 100000, 50000, UNKNOWN), 872 new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD, UNKNOWN, UNKNOWN, UNKNOWN), 873 }; 874 875 private static NetworkDataEntry getNetworkDataEntry(int networkType) { 876 for (NetworkDataEntry entry : mTheoreticalBWTable) { 877 if (entry.networkType == networkType) { 878 return entry; 879 } 880 } 881 882 Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType)); 883 return null; 884 } 885 886 private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) { 887 888 int level; 889 890 switch(networkType) { 891 case TelephonyManager.NETWORK_TYPE_EDGE: 892 case TelephonyManager.NETWORK_TYPE_GPRS: 893 case TelephonyManager.NETWORK_TYPE_UMTS: 894 case TelephonyManager.NETWORK_TYPE_HSDPA: 895 case TelephonyManager.NETWORK_TYPE_HSUPA: 896 case TelephonyManager.NETWORK_TYPE_HSPA: 897 case TelephonyManager.NETWORK_TYPE_HSPAP: 898 level = ss.getGsmLevel(); 899 break; 900 case TelephonyManager.NETWORK_TYPE_CDMA: 901 case TelephonyManager.NETWORK_TYPE_1xRTT: 902 level = ss.getCdmaLevel(); 903 break; 904 case TelephonyManager.NETWORK_TYPE_EVDO_0: 905 case TelephonyManager.NETWORK_TYPE_EVDO_A: 906 case TelephonyManager.NETWORK_TYPE_EVDO_B: 907 level = ss.getEvdoLevel(); 908 break; 909 case TelephonyManager.NETWORK_TYPE_LTE: 910 level = ss.getLteLevel(); 911 break; 912 case TelephonyManager.NETWORK_TYPE_IDEN: 913 case TelephonyManager.NETWORK_TYPE_EHRPD: 914 default: 915 return UNKNOWN; 916 } 917 918 return (level * LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) / 919 SignalStrength.NUM_SIGNAL_STRENGTH_BINS; 920 } 921 922 @Override 923 public void startSampling(SamplingDataTracker.SamplingSnapshot s) { 924 mSamplingDataTracker.startSampling(s); 925 } 926 927 @Override 928 public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { 929 mSamplingDataTracker.stopSampling(s); 930 } 931 } 932