GsmDataConnectionTracker.java revision 42acef37339afe6ac608c842f1637870ee9c4f6c
1/* 2 * Copyright (C) 2006 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 com.android.internal.telephony.gsm; 18 19import android.app.AlarmManager; 20import android.app.PendingIntent; 21import android.content.BroadcastReceiver; 22import android.content.ContentResolver; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.SharedPreferences; 28import android.database.ContentObserver; 29import android.database.Cursor; 30import android.net.NetworkInfo; 31import android.net.Uri; 32import android.net.wifi.WifiManager; 33import android.os.AsyncResult; 34import android.os.INetStatService; 35import android.os.Message; 36import android.os.RemoteException; 37import android.os.ServiceManager; 38import android.os.SystemClock; 39import android.os.SystemProperties; 40import android.preference.PreferenceManager; 41import android.provider.Checkin; 42import android.provider.Settings; 43import android.provider.Telephony; 44import android.telephony.ServiceState; 45import android.telephony.TelephonyManager; 46import android.telephony.gsm.GsmCellLocation; 47import android.text.TextUtils; 48import android.util.EventLog; 49import android.util.Log; 50 51import com.android.internal.telephony.DataCallState; 52import com.android.internal.telephony.DataConnection; 53import com.android.internal.telephony.DataConnectionTracker; 54import com.android.internal.telephony.Phone; 55import com.android.internal.telephony.RetryManager; 56import com.android.internal.telephony.TelephonyEventLog; 57import com.android.internal.telephony.DataConnection.FailCause; 58 59import java.io.IOException; 60import java.util.ArrayList; 61 62/** 63 * {@hide} 64 */ 65public final class GsmDataConnectionTracker extends DataConnectionTracker { 66 private static final String LOG_TAG = "GSM"; 67 private static final boolean DBG = true; 68 69 private GSMPhone mGsmPhone; 70 /** 71 * Handles changes to the APN db. 72 */ 73 private class ApnChangeObserver extends ContentObserver { 74 public ApnChangeObserver () { 75 super(mDataConnectionTracker); 76 } 77 78 @Override 79 public void onChange(boolean selfChange) { 80 sendMessage(obtainMessage(EVENT_APN_CHANGED)); 81 } 82 } 83 84 //***** Instance Variables 85 86 INetStatService netstat; 87 // Indicates baseband will not auto-attach 88 private boolean noAutoAttach = false; 89 90 private boolean mReregisterOnReconnectFailure = false; 91 private ContentResolver mResolver; 92 93 private boolean mPingTestActive = false; 94 // Count of PDP reset attempts; reset when we see incoming, 95 // call reRegisterNetwork, or pingTest succeeds. 96 private int mPdpResetCount = 0; 97 private boolean mIsScreenOn = true; 98 99 private final RetryManager mRetryMgr = new RetryManager(); 100 101 /** Delay between APN attempts */ 102 protected static final int APN_DELAY_MILLIS = 5000; 103 104 //useful for debugging 105 boolean failNextConnect = false; 106 107 /** 108 * allApns holds all apns for this sim spn, retrieved from 109 * the Carrier DB. 110 * 111 * Create once after simcard info is loaded 112 */ 113 private ArrayList<ApnSetting> allApns = null; 114 115 /** 116 * waitingApns holds all apns that are waiting to be connected 117 * 118 * It is a subset of allApns and has the same format 119 */ 120 private ArrayList<ApnSetting> waitingApns = null; 121 122 private ApnSetting preferredApn = null; 123 124 /** 125 * pdpList holds all the PDP connection, i.e. IP Link in GPRS 126 */ 127 private ArrayList<DataConnection> pdpList; 128 129 /** Currently requested APN type */ 130 private String mRequestedApnType = Phone.APN_TYPE_DEFAULT; 131 132 /** Currently active APN */ 133 private ApnSetting mActiveApn; 134 135 /** Currently active PdpConnection */ 136 private PdpConnection mActivePdp; 137 138 private static int APN_INVALID_ID = -1; 139 private static int APN_DEFAULT_ID = 0; 140 private static int APN_MMS_ID = 1; 141 private static int APN_SUPL_ID = 2; 142 private static int APN_DUN_ID = 3; 143 private static int APN_HIPRI_ID = 4; 144 private static int APN_NUM_TYPES = 5; 145 146 private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; 147 private int enabledCount = 0; 148 149 /** Is packet service restricted by network */ 150 private boolean mIsPsRestricted = false; 151 152 //***** Constants 153 154 // TODO: Increase this to match the max number of simultaneous 155 // PDP contexts we plan to support. 156 /** 157 * Pool size of PdpConnection objects. 158 */ 159 private static final int PDP_CONNECTION_POOL_SIZE = 1; 160 161 private static final int POLL_PDP_MILLIS = 5 * 1000; 162 163 private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; 164 private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; 165 166 static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn"); 167 static final String APN_ID = "apn_id"; 168 private boolean canSetPreferApn = false; 169 170 BroadcastReceiver mIntentReceiver = new BroadcastReceiver () 171 { 172 @Override 173 public void onReceive(Context context, Intent intent) 174 { 175 String action = intent.getAction(); 176 if (action.equals(Intent.ACTION_SCREEN_ON)) { 177 mIsScreenOn = true; 178 stopNetStatPoll(); 179 startNetStatPoll(); 180 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 181 mIsScreenOn = false; 182 stopNetStatPoll(); 183 startNetStatPoll(); 184 } else if (action.equals((INTENT_RECONNECT_ALARM))) { 185 Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state); 186 187 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); 188 if (state == State.FAILED) { 189 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); 190 msg.arg1 = 0; // tearDown is false 191 msg.obj = (String) reason; 192 sendMessage(msg); 193 } 194 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); 195 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 196 final android.net.NetworkInfo networkInfo = (NetworkInfo) 197 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 198 mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); 199 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 200 final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 201 WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; 202 203 if (!enabled) { 204 // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION 205 // quit and wont report disconnected til next enalbing. 206 mIsWifiConnected = false; 207 } 208 } 209 } 210 }; 211 212 /** Watches for changes to the APN db. */ 213 private ApnChangeObserver apnObserver; 214 215 //***** Constructor 216 217 GsmDataConnectionTracker(GSMPhone p) { 218 super(p); 219 mGsmPhone = p; 220 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); 221 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); 222 p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); 223 p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null); 224 p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); 225 p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); 226 p.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null); 227 p.mSST.registerForGprsDetached(this, EVENT_GPRS_DETACHED, null); 228 p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); 229 p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); 230 p.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null); 231 p.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null); 232 233 this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat")); 234 235 IntentFilter filter = new IntentFilter(); 236 filter.addAction(INTENT_RECONNECT_ALARM); 237 filter.addAction(Intent.ACTION_SCREEN_ON); 238 filter.addAction(Intent.ACTION_SCREEN_OFF); 239 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 240 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 241 242 p.getContext().registerReceiver(mIntentReceiver, filter, null, p.h); 243 244 245 mDataConnectionTracker = this; 246 mResolver = phone.getContext().getContentResolver(); 247 248 apnObserver = new ApnChangeObserver(); 249 p.getContext().getContentResolver().registerContentObserver( 250 Telephony.Carriers.CONTENT_URI, true, apnObserver); 251 252 createAllPdpList(); 253 254 // This preference tells us 1) initial condition for "dataEnabled", 255 // and 2) whether the RIL will setup the baseband to auto-PS attach. 256 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); 257 dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false); 258 if (dataEnabled[APN_DEFAULT_ID]) { 259 enabledCount++; 260 } 261 noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; 262 263 if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) { 264 if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) { 265 // Should never happen, log an error and default to a simple linear sequence. 266 Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG=" 267 + DEFAULT_DATA_RETRY_CONFIG); 268 mRetryMgr.configure(20, 2000, 1000); 269 } 270 } 271 } 272 273 public void dispose() { 274 //Unregister for all events 275 phone.mCM.unregisterForAvailable(this); 276 phone.mCM.unregisterForOffOrNotAvailable(this); 277 mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this); 278 phone.mCM.unregisterForDataStateChanged(this); 279 mGsmPhone.mCT.unregisterForVoiceCallEnded(this); 280 mGsmPhone.mCT.unregisterForVoiceCallStarted(this); 281 mGsmPhone.mSST.unregisterForGprsAttached(this); 282 mGsmPhone.mSST.unregisterForGprsDetached(this); 283 mGsmPhone.mSST.unregisterForRoamingOn(this); 284 mGsmPhone.mSST.unregisterForRoamingOff(this); 285 mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this); 286 mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this); 287 288 phone.getContext().unregisterReceiver(this.mIntentReceiver); 289 phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); 290 291 destroyAllPdpList(); 292 } 293 294 protected void finalize() { 295 if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized"); 296 } 297 298 void setState(State s) { 299 if (DBG) log ("setState: " + s); 300 if (state != s) { 301 if (s == State.INITING) { // request PDP context 302 Checkin.updateStats( 303 phone.getContext().getContentResolver(), 304 Checkin.Stats.Tag.PHONE_GPRS_ATTEMPTED, 1, 0.0); 305 } 306 307 if (s == State.CONNECTED) { // pppd is up 308 Checkin.updateStats( 309 phone.getContext().getContentResolver(), 310 Checkin.Stats.Tag.PHONE_GPRS_CONNECTED, 1, 0.0); 311 } 312 } 313 314 state = s; 315 316 if (state == State.FAILED) { 317 if (waitingApns != null) 318 waitingApns.clear(); // when teardown the connection and set to IDLE 319 } 320 } 321 322 String[] getActiveApnTypes() { 323 String[] result; 324 if (mActiveApn != null) { 325 result = mActiveApn.types; 326 } else { 327 result = new String[1]; 328 result[0] = Phone.APN_TYPE_DEFAULT; 329 } 330 return result; 331 } 332 333 protected String getActiveApnString() { 334 String result = null; 335 if (mActiveApn != null) { 336 result = mActiveApn.apn; 337 } 338 return result; 339 } 340 341 protected int apnTypeToId(String type) { 342 if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) { 343 return APN_DEFAULT_ID; 344 } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { 345 return APN_MMS_ID; 346 } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { 347 return APN_SUPL_ID; 348 } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) { 349 return APN_DUN_ID; 350 } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) { 351 return APN_HIPRI_ID; 352 } else { 353 return APN_INVALID_ID; 354 } 355 } 356 357 /** 358 * Ensure that we are connected to an APN of the specified type. 359 * @param type the APN type (currently the only valid values 360 * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}) 361 * @return the result of the operation. Success is indicated by 362 * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or 363 * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast 364 * will be sent by the ConnectivityManager when a connection to 365 * the APN has been established. 366 */ 367 protected int enableApnType(String type) { 368 int id = apnTypeToId(type); 369 if (id == APN_INVALID_ID) { 370 return Phone.APN_REQUEST_FAILED; 371 } 372 373 // If already active, return 374 if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " 375 + isApnTypeActive(type) + " and state = " + state); 376 if (isApnTypeActive(type)) { 377 if (state == State.INITING) return Phone.APN_REQUEST_STARTED; 378 else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; 379 } 380 381 if (!isApnTypeAvailable(type)) { 382 return Phone.APN_TYPE_NOT_AVAILABLE; 383 } 384 385 setEnabled(id, true); 386 mRequestedApnType = type; 387 sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); 388 return Phone.APN_REQUEST_STARTED; 389 } 390 391 /** 392 * The APN of the specified type is no longer needed. Ensure that if 393 * use of the default APN has not been explicitly disabled, we are connected 394 * to the default APN. 395 * @param type the APN type. The only valid values are currently 396 * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}. 397 * @return 398 */ 399 protected int disableApnType(String type) { 400 if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")"); 401 int id = apnTypeToId(type); 402 if (id == APN_INVALID_ID) { 403 return Phone.APN_REQUEST_FAILED; 404 } 405 if (isEnabled(id)) { 406 setEnabled(id, false); 407 if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { 408 mRequestedApnType = Phone.APN_TYPE_DEFAULT; 409 if (dataEnabled[APN_DEFAULT_ID]) { 410 return Phone.APN_ALREADY_ACTIVE; 411 } else { 412 return Phone.APN_REQUEST_STARTED; 413 } 414 } else { 415 return Phone.APN_REQUEST_STARTED; 416 } 417 } else { 418 return Phone.APN_REQUEST_FAILED; 419 } 420 } 421 422 /** 423 * The data connection is expected to be setup while device 424 * 1. has sim card 425 * 2. registered to gprs service 426 * 3. user doesn't explicitly disable data service 427 * 4. wifi is not on 428 * 429 * @return false while no data connection if all above requirements are met. 430 */ 431 public boolean isDataConnectionAsDesired() { 432 boolean roaming = phone.getServiceState().getRoaming(); 433 434 if (mGsmPhone.mSIMRecords.getRecordsLoaded() && 435 mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && 436 (!roaming || getDataOnRoamingEnabled()) && 437 !mIsWifiConnected && 438 !mIsPsRestricted ) { 439 return (state == State.CONNECTED); 440 } 441 return true; 442 } 443 444 private boolean isApnTypeActive(String type) { 445 // TODO: to support simultaneous, mActiveApn can be a List instead. 446 return mActiveApn != null && mActiveApn.canHandleType(type); 447 } 448 449 private boolean isApnTypeAvailable(String type) { 450 if (allApns != null) { 451 for (ApnSetting apn : allApns) { 452 if (apn.canHandleType(type)) { 453 return true; 454 } 455 } 456 } 457 return false; 458 } 459 460 private boolean isEnabled(int id) { 461 if (id != APN_INVALID_ID) { 462 return dataEnabled[id]; 463 } 464 return false; 465 } 466 467 private void setEnabled(int id, boolean enable) { 468 if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ')'); 469 if (dataEnabled[id] != enable) { 470 dataEnabled[id] = enable; 471 472 if (enable) { 473 enabledCount++; 474 } else { 475 enabledCount--; 476 } 477 478 if (enabledCount == 0) { 479 setPrivateDataEnabled(false); 480 } else if (enabledCount == 1) { 481 setPrivateDataEnabled(true); 482 } 483 } 484 } 485 486 /** 487 * Prevent mobile data connections from being established, 488 * or once again allow mobile data connections. If the state 489 * toggles, then either tear down or set up data, as 490 * appropriate to match the new state. 491 * <p>This operation only affects the default APN, and if the same APN is 492 * currently being used for MMS traffic, the teardown will not happen 493 * even when {@code enable} is {@code false}.</p> 494 * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data 495 * @return {@code true} if the operation succeeded 496 */ 497 public boolean setDataEnabled(boolean enable) { 498 if (DBG) Log.d(LOG_TAG, "setDataEnabled("+enable+")"); 499 setEnabled(APN_DEFAULT_ID, enable); 500 return true; 501 } 502 503 private void setPrivateDataEnabled(boolean enable) { 504 if (DBG) Log.d(LOG_TAG, "setPrivateDataEnabled("+enable+")"); 505 if (enable) { 506 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); 507 } else { 508 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); 509 msg.arg1 = 1; // tearDown is true 510 msg.obj = Phone.REASON_DATA_DISABLED; 511 sendMessage(msg); 512 } 513 } 514 515 /** 516 * Report the current state of data connectivity (enabled or disabled) for 517 * the default APN. 518 * @return {@code false} if data connectivity has been explicitly disabled, 519 * {@code true} otherwise. 520 */ 521 public boolean getDataEnabled() { 522 return dataEnabled[APN_DEFAULT_ID]; 523 } 524 525 /** 526 * Report on whether data connectivity is enabled for any APN. 527 * @return {@code false} if data connectivity has been explicitly disabled, 528 * {@code true} otherwise. 529 */ 530 public boolean getAnyDataEnabled() { 531 return (enabledCount != 0); 532 } 533 534 /** 535 * Formerly this method was ArrayList<PdpConnection> getAllPdps() 536 */ 537 public ArrayList<DataConnection> getAllDataConnections() { 538 ArrayList<DataConnection> pdps = (ArrayList<DataConnection>)pdpList.clone(); 539 return pdps; 540 } 541 542 private boolean isDataAllowed() { 543 boolean roaming = phone.getServiceState().getRoaming(); 544 return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()); 545 } 546 547 //****** Called from ServiceStateTracker 548 /** 549 * Invoked when ServiceStateTracker observes a transition from GPRS 550 * attach to detach. 551 */ 552 protected void onGprsDetached() { 553 /* 554 * We presently believe it is unnecessary to tear down the PDP context 555 * when GPRS detaches, but we should stop the network polling. 556 */ 557 stopNetStatPoll(); 558 phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); 559 } 560 561 private void onGprsAttached() { 562 if (state == State.CONNECTED) { 563 startNetStatPoll(); 564 phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); 565 } else { 566 if (state == State.FAILED) { 567 cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); 568 mRetryMgr.resetRetryCount(); 569 } 570 trySetupData(Phone.REASON_GPRS_ATTACHED); 571 } 572 } 573 574 private boolean trySetupData(String reason) { 575 if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); 576 577 Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); 578 579 if (phone.getSimulatedRadioControl() != null) { 580 // Assume data is connected on the simulator 581 // FIXME this can be improved 582 setState(State.CONNECTED); 583 phone.notifyDataConnection(reason); 584 585 Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); 586 return true; 587 } 588 589 int gprsState = mGsmPhone.mSST.getCurrentGprsState(); 590 boolean roaming = phone.getServiceState().getRoaming(); 591 boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState(); 592 593 if ((state == State.IDLE || state == State.SCANNING) 594 && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) 595 && mGsmPhone.mSIMRecords.getRecordsLoaded() 596 && phone.getState() == Phone.State.IDLE 597 && isDataAllowed() 598 && !mIsPsRestricted 599 && desiredPowerState ) { 600 601 if (state == State.IDLE) { 602 waitingApns = buildWaitingApns(); 603 if (waitingApns.isEmpty()) { 604 if (DBG) log("No APN found"); 605 notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); 606 return false; 607 } else { 608 log ("Create from allApns : " + apnListToString(allApns)); 609 } 610 } 611 612 if (DBG) { 613 log ("Setup waitngApns : " + apnListToString(waitingApns)); 614 } 615 return setupData(reason); 616 } else { 617 if (DBG) 618 log("trySetupData: Not ready for data: " + 619 " dataState=" + state + 620 " gprsState=" + gprsState + 621 " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + 622 " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + 623 " phoneState=" + phone.getState() + 624 " isDataAllowed=" + isDataAllowed() + 625 " dataEnabled=" + getAnyDataEnabled() + 626 " roaming=" + roaming + 627 " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + 628 " ps restricted=" + mIsPsRestricted + 629 " desiredPowerState=" + desiredPowerState); 630 return false; 631 } 632 } 633 634 /** 635 * If tearDown is true, this only tears down a CONNECTED session. Presently, 636 * there is no mechanism for abandoning an INITING/CONNECTING session, 637 * but would likely involve cancelling pending async requests or 638 * setting a flag or new state to ignore them when they came in 639 * @param tearDown true if the underlying PdpConnection should be 640 * disconnected. 641 * @param reason reason for the clean up. 642 */ 643 private void cleanUpConnection(boolean tearDown, String reason) { 644 if (DBG) log("Clean up connection due to " + reason); 645 646 // Clear the reconnect alarm, if set. 647 if (mReconnectIntent != null) { 648 AlarmManager am = 649 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 650 am.cancel(mReconnectIntent); 651 mReconnectIntent = null; 652 } 653 654 setState(State.DISCONNECTING); 655 656 for (DataConnection conn : pdpList) { 657 PdpConnection pdp = (PdpConnection) conn; 658 if (tearDown) { 659 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); 660 pdp.disconnect(msg); 661 } else { 662 pdp.clearSettings(); 663 } 664 } 665 stopNetStatPoll(); 666 667 if (!tearDown) { 668 setState(State.IDLE); 669 phone.notifyDataConnection(reason); 670 mActiveApn = null; 671 } 672 } 673 674 /** 675 * @param types comma delimited list of APN types 676 * @return array of APN types 677 */ 678 private String[] parseTypes(String types) { 679 String[] result; 680 // If unset, set to DEFAULT. 681 if (types == null || types.equals("")) { 682 result = new String[1]; 683 result[0] = Phone.APN_TYPE_ALL; 684 } else { 685 result = types.split(","); 686 } 687 return result; 688 } 689 690 private ArrayList<ApnSetting> createApnList(Cursor cursor) { 691 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); 692 if (cursor.moveToFirst()) { 693 do { 694 String[] types = parseTypes( 695 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); 696 ApnSetting apn = new ApnSetting( 697 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), 698 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), 699 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), 700 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), 701 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), 702 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), 703 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), 704 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), 705 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), 706 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), 707 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), 708 types); 709 result.add(apn); 710 } while (cursor.moveToNext()); 711 } 712 return result; 713 } 714 715 private PdpConnection findFreePdp() { 716 for (DataConnection conn : pdpList) { 717 PdpConnection pdp = (PdpConnection) conn; 718 if (pdp.getState() == DataConnection.State.INACTIVE) { 719 return pdp; 720 } 721 } 722 return null; 723 } 724 725 private boolean setupData(String reason) { 726 ApnSetting apn; 727 PdpConnection pdp; 728 729 apn = getNextApn(); 730 if (apn == null) return false; 731 pdp = findFreePdp(); 732 if (pdp == null) { 733 if (DBG) log("setupData: No free PdpConnection found!"); 734 return false; 735 } 736 mActiveApn = apn; 737 mActivePdp = pdp; 738 739 Message msg = obtainMessage(); 740 msg.what = EVENT_DATA_SETUP_COMPLETE; 741 msg.obj = reason; 742 pdp.connect(apn, msg); 743 744 setState(State.INITING); 745 phone.notifyDataConnection(reason); 746 return true; 747 } 748 749 String getInterfaceName(String apnType) { 750 if (mActivePdp != null 751 && (apnType == null || mActiveApn.canHandleType(apnType))) { 752 return mActivePdp.getInterface(); 753 } 754 return null; 755 } 756 757 protected String getIpAddress(String apnType) { 758 if (mActivePdp != null 759 && (apnType == null || mActiveApn.canHandleType(apnType))) { 760 return mActivePdp.getIpAddress(); 761 } 762 return null; 763 } 764 765 String getGateway(String apnType) { 766 if (mActivePdp != null 767 && (apnType == null || mActiveApn.canHandleType(apnType))) { 768 return mActivePdp.getGatewayAddress(); 769 } 770 return null; 771 } 772 773 protected String[] getDnsServers(String apnType) { 774 if (mActivePdp != null 775 && (apnType == null || mActiveApn.canHandleType(apnType))) { 776 return mActivePdp.getDnsServers(); 777 } 778 return null; 779 } 780 781 private boolean 782 pdpStatesHasCID (ArrayList<DataCallState> states, int cid) { 783 for (int i = 0, s = states.size() ; i < s ; i++) { 784 if (states.get(i).cid == cid) return true; 785 } 786 787 return false; 788 } 789 790 private boolean 791 pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) { 792 for (int i = 0, s = states.size() ; i < s ; i++) { 793 if ((states.get(i).cid == cid) && (states.get(i).active != 0)) { 794 return true; 795 } 796 } 797 798 return false; 799 } 800 801 /** 802 * Handles changes to the APN database. 803 */ 804 private void onApnChanged() { 805 boolean isConnected; 806 807 isConnected = (state != State.IDLE && state != State.FAILED); 808 809 // The "current" may no longer be valid. MMS depends on this to send properly. 810 mGsmPhone.updateCurrentCarrierInProvider(); 811 812 // TODO: It'd be nice to only do this if the changed entrie(s) 813 // match the current operator. 814 createAllApnList(); 815 if (state != State.DISCONNECTING) { 816 cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); 817 if (!isConnected) { 818 // reset reconnect timer 819 mRetryMgr.resetRetryCount(); 820 mReregisterOnReconnectFailure = false; 821 trySetupData(Phone.REASON_APN_CHANGED); 822 } 823 } 824 } 825 826 /** 827 * @param explicitPoll if true, indicates that *we* polled for this 828 * update while state == CONNECTED rather than having it delivered 829 * via an unsolicited response (which could have happened at any 830 * previous state 831 */ 832 protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) { 833 ArrayList<DataCallState> pdpStates; 834 835 pdpStates = (ArrayList<DataCallState>)(ar.result); 836 837 if (ar.exception != null) { 838 // This is probably "radio not available" or something 839 // of that sort. If so, the whole connection is going 840 // to come down soon anyway 841 return; 842 } 843 844 if (state == State.CONNECTED) { 845 // The way things are supposed to work, the PDP list 846 // should not contain the CID after it disconnects. 847 // However, the way things really work, sometimes the PDP 848 // context is still listed with active = false, which 849 // makes it hard to distinguish an activating context from 850 // an activated-and-then deactivated one. 851 if (!pdpStatesHasCID(pdpStates, cidActive)) { 852 // It looks like the PDP context has deactivated. 853 // Tear everything down and try to reconnect. 854 855 Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); 856 857 // Add an event log when the network drops PDP 858 int cid = -1; 859 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 860 if (loc != null) cid = loc.getCid(); 861 EventLog.List val = new EventLog.List(cid, 862 TelephonyManager.getDefault().getNetworkType()); 863 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); 864 865 cleanUpConnection(true, null); 866 return; 867 } else if (!pdpStatesHasActiveCID(pdpStates, cidActive)) { 868 // Here, we only consider this authoritative if we asked for the 869 // PDP list. If it was an unsolicited response, we poll again 870 // to make sure everyone agrees on the initial state. 871 872 if (!explicitPoll) { 873 // We think it disconnected but aren't sure...poll from our side 874 phone.mCM.getPDPContextList( 875 this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 876 } else { 877 Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " 878 + " Reconnecting"); 879 880 // Log the network drop on the event log. 881 int cid = -1; 882 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 883 if (loc != null) cid = loc.getCid(); 884 EventLog.List val = new EventLog.List(cid, 885 TelephonyManager.getDefault().getNetworkType()); 886 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); 887 888 cleanUpConnection(true, null); 889 } 890 } 891 } 892 } 893 894 private void notifyDefaultData(String reason) { 895 setupDnsProperties(); 896 setState(State.CONNECTED); 897 phone.notifyDataConnection(reason); 898 startNetStatPoll(); 899 // reset reconnect timer 900 mRetryMgr.resetRetryCount(); 901 mReregisterOnReconnectFailure = false; 902 } 903 904 private void setupDnsProperties() { 905 int mypid = android.os.Process.myPid(); 906 String[] servers = getDnsServers(null); 907 String propName; 908 String propVal; 909 int count; 910 911 count = 0; 912 for (int i = 0; i < servers.length; i++) { 913 String serverAddr = servers[i]; 914 if (!TextUtils.equals(serverAddr, "0.0.0.0")) { 915 SystemProperties.set("net.dns" + (i+1) + "." + mypid, serverAddr); 916 count++; 917 } 918 } 919 for (int i = count+1; i <= 4; i++) { 920 propName = "net.dns" + i + "." + mypid; 921 propVal = SystemProperties.get(propName); 922 if (propVal.length() != 0) { 923 SystemProperties.set(propName, ""); 924 } 925 } 926 /* 927 * Bump the property that tells the name resolver library 928 * to reread the DNS server list from the properties. 929 */ 930 propVal = SystemProperties.get("net.dnschange"); 931 if (propVal.length() != 0) { 932 try { 933 int n = Integer.parseInt(propVal); 934 SystemProperties.set("net.dnschange", "" + (n+1)); 935 } catch (NumberFormatException e) { 936 } 937 } 938 } 939 940 /** 941 * This is a kludge to deal with the fact that 942 * the PDP state change notification doesn't always work 943 * with certain RIL impl's/basebands 944 * 945 */ 946 private void startPeriodicPdpPoll() { 947 removeMessages(EVENT_POLL_PDP); 948 949 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 950 } 951 952 private void resetPollStats() { 953 txPkts = -1; 954 rxPkts = -1; 955 sentSinceLastRecv = 0; 956 netStatPollPeriod = POLL_NETSTAT_MILLIS; 957 mNoRecvPollCount = 0; 958 } 959 960 private void doRecovery() { 961 if (state == State.CONNECTED) { 962 int maxPdpReset = Settings.Gservices.getInt(mResolver, 963 Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, 964 DEFAULT_MAX_PDP_RESET_FAIL); 965 if (mPdpResetCount < maxPdpReset) { 966 mPdpResetCount++; 967 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv); 968 cleanUpConnection(true, Phone.REASON_PDP_RESET); 969 } else { 970 mPdpResetCount = 0; 971 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); 972 mGsmPhone.mSST.reRegisterNetwork(null); 973 } 974 // TODO: Add increasingly drastic recovery steps, eg, 975 // reset the radio, reset the device. 976 } 977 } 978 979 protected void startNetStatPoll() { 980 if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { 981 Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); 982 resetPollStats(); 983 netStatPollEnabled = true; 984 mPollNetStat.run(); 985 } 986 } 987 988 protected void stopNetStatPoll() { 989 netStatPollEnabled = false; 990 removeCallbacks(mPollNetStat); 991 Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); 992 } 993 994 protected void restartRadio() { 995 Log.d(LOG_TAG, "************TURN OFF RADIO**************"); 996 cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); 997 phone.mCM.setRadioPower(false, null); 998 /* Note: no need to call setRadioPower(true). Assuming the desired 999 * radio power state is still ON (as tracked by ServiceStateTracker), 1000 * ServiceStateTracker will call setRadioPower when it receives the 1001 * RADIO_STATE_CHANGED notification for the power off. And if the 1002 * desired power state has changed in the interim, we don't want to 1003 * override it with an unconditional power on. 1004 */ 1005 1006 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); 1007 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); 1008 } 1009 1010 private Runnable mPollNetStat = new Runnable() 1011 { 1012 1013 public void run() { 1014 long sent, received; 1015 long preTxPkts = -1, preRxPkts = -1; 1016 1017 Activity newActivity; 1018 1019 preTxPkts = txPkts; 1020 preRxPkts = rxPkts; 1021 1022 try { 1023 txPkts = netstat.getMobileTxPackets(); 1024 rxPkts = netstat.getMobileRxPackets(); 1025 } catch (RemoteException e) { 1026 txPkts = 0; 1027 rxPkts = 0; 1028 } 1029 1030 //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); 1031 1032 if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { 1033 sent = txPkts - preTxPkts; 1034 received = rxPkts - preRxPkts; 1035 1036 if ( sent > 0 && received > 0 ) { 1037 sentSinceLastRecv = 0; 1038 newActivity = Activity.DATAINANDOUT; 1039 mPdpResetCount = 0; 1040 } else if (sent > 0 && received == 0) { 1041 if (phone.getState() == Phone.State.IDLE) { 1042 sentSinceLastRecv += sent; 1043 } else { 1044 sentSinceLastRecv = 0; 1045 } 1046 newActivity = Activity.DATAOUT; 1047 } else if (sent == 0 && received > 0) { 1048 sentSinceLastRecv = 0; 1049 newActivity = Activity.DATAIN; 1050 mPdpResetCount = 0; 1051 } else if (sent == 0 && received == 0) { 1052 newActivity = Activity.NONE; 1053 } else { 1054 sentSinceLastRecv = 0; 1055 newActivity = Activity.NONE; 1056 } 1057 1058 if (activity != newActivity && mIsScreenOn) { 1059 activity = newActivity; 1060 phone.notifyDataActivity(); 1061 } 1062 } 1063 1064 int watchdogTrigger = Settings.Gservices.getInt(mResolver, 1065 Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 1066 NUMBER_SENT_PACKETS_OF_HANG); 1067 1068 if (sentSinceLastRecv >= watchdogTrigger) { 1069 // we already have NUMBER_SENT_PACKETS sent without ack 1070 if (mNoRecvPollCount == 0) { 1071 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, 1072 sentSinceLastRecv); 1073 } 1074 1075 int noRecvPollLimit = Settings.Gservices.getInt(mResolver, 1076 Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); 1077 1078 if (mNoRecvPollCount < noRecvPollLimit) { 1079 // It's possible the PDP context went down and we weren't notified. 1080 // Start polling the context list in an attempt to recover. 1081 if (DBG) log("no DATAIN in a while; polling PDP"); 1082 phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 1083 1084 mNoRecvPollCount++; 1085 1086 // Slow down the poll interval to let things happen 1087 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 1088 Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, 1089 POLL_NETSTAT_SLOW_MILLIS); 1090 } else { 1091 if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + 1092 " pkts since last received"); 1093 // We've exceeded the threshold. Run ping test as a final check; 1094 // it will proceed with recovery if ping fails. 1095 stopNetStatPoll(); 1096 Thread pingTest = new Thread() { 1097 public void run() { 1098 runPingTest(); 1099 } 1100 }; 1101 mPingTestActive = true; 1102 pingTest.start(); 1103 } 1104 } else { 1105 mNoRecvPollCount = 0; 1106 if (mIsScreenOn) { 1107 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 1108 Settings.Gservices.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 1109 } else { 1110 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 1111 Settings.Gservices.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 1112 POLL_NETSTAT_SCREEN_OFF_MILLIS); 1113 } 1114 } 1115 1116 if (netStatPollEnabled) { 1117 mDataConnectionTracker.postDelayed(this, netStatPollPeriod); 1118 } 1119 } 1120 }; 1121 1122 private void runPingTest () { 1123 int status = -1; 1124 try { 1125 String address = Settings.Gservices.getString(mResolver, 1126 Settings.Gservices.PDP_WATCHDOG_PING_ADDRESS); 1127 int deadline = Settings.Gservices.getInt(mResolver, 1128 Settings.Gservices.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE); 1129 if (DBG) log("pinging " + address + " for " + deadline + "s"); 1130 if (address != null && !NULL_IP.equals(address)) { 1131 Process p = Runtime.getRuntime() 1132 .exec("ping -c 1 -i 1 -w "+ deadline + " " + address); 1133 status = p.waitFor(); 1134 } 1135 } catch (IOException e) { 1136 Log.w(LOG_TAG, "ping failed: IOException"); 1137 } catch (Exception e) { 1138 Log.w(LOG_TAG, "exception trying to ping"); 1139 } 1140 1141 if (status == 0) { 1142 // ping succeeded. False alarm. Reset netStatPoll. 1143 // ("-1" for this event indicates a false alarm) 1144 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1); 1145 mPdpResetCount = 0; 1146 sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); 1147 } else { 1148 // ping failed. Proceed with recovery. 1149 sendMessage(obtainMessage(EVENT_START_RECOVERY)); 1150 } 1151 } 1152 1153 /** 1154 * Returns true if the last fail cause is something that 1155 * seems like it deserves an error notification. 1156 * Transient errors are ignored 1157 */ 1158 private boolean shouldPostNotification(PdpConnection.FailCause cause) { 1159 boolean shouldPost = true; 1160 // TODO CHECK 1161 // if (dataLink != null) { 1162 // shouldPost = dataLink.getLastLinkExitCode() != DataLink.EXIT_OPEN_FAILED; 1163 //} 1164 return (shouldPost && cause != PdpConnection.FailCause.UNKNOWN); 1165 } 1166 1167 /** 1168 * Return true if data connection need to be setup after disconnected due to 1169 * reason. 1170 * 1171 * @param reason the reason why data is disconnected 1172 * @return true if try setup data connection is need for this reason 1173 */ 1174 private boolean retryAfterDisconnected(String reason) { 1175 boolean retry = true; 1176 1177 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || 1178 Phone.REASON_DATA_DISABLED.equals(reason) ) { 1179 retry = false; 1180 } 1181 return retry; 1182 } 1183 1184 private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { 1185 if (state == State.FAILED) { 1186 if (!mRetryMgr.isRetryNeeded()) { 1187 if (mReregisterOnReconnectFailure) { 1188 // We've re-registerd once now just retry forever. 1189 mRetryMgr.retryForeverUsingLastTimeout(); 1190 } else { 1191 // Try to re-register to the network. 1192 Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); 1193 mReregisterOnReconnectFailure = true; 1194 mGsmPhone.mSST.reRegisterNetwork(null); 1195 mRetryMgr.resetRetryCount(); 1196 return; 1197 } 1198 } 1199 1200 int nextReconnectDelay = mRetryMgr.getRetryTimer(); 1201 Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " 1202 + (nextReconnectDelay / 1000) + "s"); 1203 1204 AlarmManager am = 1205 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 1206 Intent intent = new Intent(INTENT_RECONNECT_ALARM); 1207 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); 1208 mReconnectIntent = PendingIntent.getBroadcast( 1209 phone.getContext(), 0, intent, 0); 1210 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1211 SystemClock.elapsedRealtime() + nextReconnectDelay, 1212 mReconnectIntent); 1213 1214 mRetryMgr.increaseRetryCount(); 1215 1216 if (!shouldPostNotification(lastFailCauseCode)) { 1217 Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " 1218 + "-- likely transient error"); 1219 } else { 1220 notifyNoData(lastFailCauseCode); 1221 } 1222 } 1223 } 1224 1225 private void notifyNoData(PdpConnection.FailCause lastFailCauseCode) { 1226 setState(State.FAILED); 1227 } 1228 1229 protected void onRecordsLoaded() { 1230 createAllApnList(); 1231 if (state == State.FAILED) { 1232 cleanUpConnection(false, null); 1233 } 1234 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); 1235 } 1236 1237 protected void onEnableNewApn() { 1238 // TODO: To support simultaneous PDP contexts, this should really only call 1239 // cleanUpConnection if it needs to free up a PdpConnection. 1240 cleanUpConnection(true, Phone.REASON_APN_SWITCHED); 1241 } 1242 1243 protected void onTrySetupData(String reason) { 1244 trySetupData(reason); 1245 } 1246 1247 protected void onRoamingOff() { 1248 trySetupData(Phone.REASON_ROAMING_OFF); 1249 } 1250 1251 protected void onRoamingOn() { 1252 if (getDataOnRoamingEnabled()) { 1253 trySetupData(Phone.REASON_ROAMING_ON); 1254 } else { 1255 if (DBG) log("Tear down data connection on roaming."); 1256 cleanUpConnection(true, Phone.REASON_ROAMING_ON); 1257 } 1258 } 1259 1260 protected void onRadioAvailable() { 1261 if (phone.getSimulatedRadioControl() != null) { 1262 // Assume data is connected on the simulator 1263 // FIXME this can be improved 1264 setState(State.CONNECTED); 1265 phone.notifyDataConnection(null); 1266 1267 Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); 1268 } 1269 1270 if (state != State.IDLE) { 1271 cleanUpConnection(true, null); 1272 } 1273 } 1274 1275 protected void onRadioOffOrNotAvailable() { 1276 // Make sure our reconnect delay starts at the initial value 1277 // next time the radio comes on 1278 mRetryMgr.resetRetryCount(); 1279 mReregisterOnReconnectFailure = false; 1280 1281 if (phone.getSimulatedRadioControl() != null) { 1282 // Assume data is connected on the simulator 1283 // FIXME this can be improved 1284 Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); 1285 } else { 1286 if (DBG) log("Radio is off and clean up all connection"); 1287 // TODO: Should we reset mRequestedApnType to "default"? 1288 cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); 1289 } 1290 } 1291 1292 protected void onDataSetupComplete(AsyncResult ar) { 1293 String reason = null; 1294 if (ar.userObj instanceof String) { 1295 reason = (String) ar.userObj; 1296 } 1297 1298 if (ar.exception == null) { 1299 // everything is setup 1300 if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { 1301 SystemProperties.set("gsm.defaultpdpcontext.active", "true"); 1302 if (canSetPreferApn && preferredApn == null) { 1303 Log.d(LOG_TAG, "PREFERED APN is null"); 1304 preferredApn = mActiveApn; 1305 setPreferredApn(preferredApn.id); 1306 } 1307 } else { 1308 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); 1309 } 1310 notifyDefaultData(reason); 1311 1312 // TODO: For simultaneous PDP support, we need to build another 1313 // trigger another TRY_SETUP_DATA for the next APN type. (Note 1314 // that the existing connection may service that type, in which 1315 // case we should try the next type, etc. 1316 } else { 1317 PdpConnection.FailCause cause; 1318 cause = (PdpConnection.FailCause) (ar.result); 1319 if(DBG) log("PDP setup failed " + cause); 1320 // Log this failure to the Event Logs. 1321 if (cause.isEventLoggable()) { 1322 int cid = -1; 1323 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 1324 if (loc != null) cid = loc.getCid(); 1325 1326 EventLog.List val = new EventLog.List( 1327 cause.ordinal(), cid, 1328 TelephonyManager.getDefault().getNetworkType()); 1329 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); 1330 } 1331 1332 // No try for permanent failure 1333 if (cause.isPermanentFail()) { 1334 notifyNoData(cause); 1335 return; 1336 } 1337 1338 waitingApns.remove(0); 1339 if (waitingApns.isEmpty()) { 1340 // No more to try, start delayed retry 1341 startDelayedRetry(cause, reason); 1342 } else { 1343 // we still have more apns to try 1344 setState(State.SCANNING); 1345 // Wait a bit before trying the next APN, so that 1346 // we're not tying up the RIL command channel 1347 sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS); 1348 } 1349 } 1350 } 1351 1352 protected void onDisconnectDone(AsyncResult ar) { 1353 String reason = null; 1354 if(DBG) log("EVENT_DISCONNECT_DONE"); 1355 if (ar.userObj instanceof String) { 1356 reason = (String) ar.userObj; 1357 } 1358 setState(State.IDLE); 1359 phone.notifyDataConnection(reason); 1360 mActiveApn = null; 1361 if (retryAfterDisconnected(reason)) { 1362 trySetupData(reason); 1363 } 1364 } 1365 1366 protected void onPollPdp() { 1367 if (state == State.CONNECTED) { 1368 // only poll when connected 1369 phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 1370 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 1371 } 1372 } 1373 1374 protected void onVoiceCallStarted() { 1375 if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1376 stopNetStatPoll(); 1377 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 1378 } 1379 } 1380 1381 protected void onVoiceCallEnded() { 1382 if (state == State.CONNECTED) { 1383 if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1384 startNetStatPoll(); 1385 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 1386 } else { 1387 // clean slate after call end. 1388 resetPollStats(); 1389 } 1390 } else { 1391 // reset reconnect timer 1392 mRetryMgr.resetRetryCount(); 1393 mReregisterOnReconnectFailure = false; 1394 // in case data setup was attempted when we were on a voice call 1395 trySetupData(Phone.REASON_VOICE_CALL_ENDED); 1396 } 1397 } 1398 1399 protected void onCleanUpConnection(boolean tearDown, String reason) { 1400 cleanUpConnection(tearDown, reason); 1401 } 1402 1403 /** 1404 * Based on the sim operator numeric, create a list for all possible pdps 1405 * with all apns associated with that pdp 1406 * 1407 * 1408 */ 1409 private void createAllApnList() { 1410 allApns = new ArrayList<ApnSetting>(); 1411 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1412 1413 if (operator != null) { 1414 String selection = "numeric = '" + operator + "'"; 1415 1416 Cursor cursor = phone.getContext().getContentResolver().query( 1417 Telephony.Carriers.CONTENT_URI, null, selection, null, null); 1418 1419 if (cursor != null) { 1420 if (cursor.getCount() > 0) { 1421 allApns = createApnList(cursor); 1422 // TODO: Figure out where this fits in. This basically just 1423 // writes the pap-secrets file. No longer tied to PdpConnection 1424 // object. Not used on current platform (no ppp). 1425 //PdpConnection pdp = pdpList.get(pdp_name); 1426 //if (pdp != null && pdp.dataLink != null) { 1427 // pdp.dataLink.setPasswordInfo(cursor); 1428 //} 1429 } 1430 cursor.close(); 1431 } 1432 } 1433 1434 if (allApns.isEmpty()) { 1435 if (DBG) log("No APN found for carrier: " + operator); 1436 preferredApn = null; 1437 notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); 1438 } else { 1439 preferredApn = getPreferredApn(); 1440 Log.d(LOG_TAG, "Get PreferredAPN"); 1441 if (preferredApn != null && !preferredApn.numeric.equals(operator)) { 1442 preferredApn = null; 1443 setPreferredApn(-1); 1444 } 1445 } 1446 } 1447 1448 private void createAllPdpList() { 1449 pdpList = new ArrayList<DataConnection>(); 1450 DataConnection pdp; 1451 1452 for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { 1453 pdp = new PdpConnection(mGsmPhone); 1454 pdpList.add(pdp); 1455 } 1456 } 1457 1458 private void destroyAllPdpList() { 1459 if(pdpList != null) { 1460 PdpConnection pdp; 1461 pdpList.removeAll(pdpList); 1462 } 1463 } 1464 1465 /** 1466 * 1467 * @return waitingApns list to be used to create PDP 1468 * error when waitingApns.isEmpty() 1469 */ 1470 private ArrayList<ApnSetting> buildWaitingApns() { 1471 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); 1472 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1473 1474 if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1475 if (canSetPreferApn && preferredApn != null) { 1476 Log.i(LOG_TAG, "Preferred APN:" + operator + ":" 1477 + preferredApn.numeric + ":" + preferredApn); 1478 if (preferredApn.numeric.equals(operator)) { 1479 Log.i(LOG_TAG, "Waiting APN set to preferred APN"); 1480 apnList.add(preferredApn); 1481 return apnList; 1482 } else { 1483 setPreferredApn(-1); 1484 preferredApn = null; 1485 } 1486 } 1487 } 1488 1489 if (allApns != null) { 1490 for (ApnSetting apn : allApns) { 1491 if (apn.canHandleType(mRequestedApnType)) { 1492 apnList.add(apn); 1493 } 1494 } 1495 } 1496 return apnList; 1497 } 1498 1499 /** 1500 * Get next apn in waitingApns 1501 * @return the first apn found in waitingApns, null if none 1502 */ 1503 private ApnSetting getNextApn() { 1504 ArrayList<ApnSetting> list = waitingApns; 1505 ApnSetting apn = null; 1506 1507 if (list != null) { 1508 if (!list.isEmpty()) { 1509 apn = list.get(0); 1510 } 1511 } 1512 return apn; 1513 } 1514 1515 private String apnListToString (ArrayList<ApnSetting> apns) { 1516 StringBuilder result = new StringBuilder(); 1517 for (int i = 0, size = apns.size(); i < size; i++) { 1518 result.append('[') 1519 .append(apns.get(i).toString()) 1520 .append(']'); 1521 } 1522 return result.toString(); 1523 } 1524 1525 private void startDelayedRetry(PdpConnection.FailCause cause, String reason) { 1526 notifyNoData(cause); 1527 if (mRequestedApnType == Phone.APN_TYPE_DEFAULT) { 1528 reconnectAfterFail(cause, reason); 1529 } 1530 } 1531 1532 private void setPreferredApn(int pos) { 1533 if (!canSetPreferApn) { 1534 return; 1535 } 1536 1537 ContentResolver resolver = phone.getContext().getContentResolver(); 1538 resolver.delete(PREFERAPN_URI, null, null); 1539 1540 if (pos >= 0) { 1541 ContentValues values = new ContentValues(); 1542 values.put(APN_ID, pos); 1543 resolver.insert(PREFERAPN_URI, values); 1544 } 1545 } 1546 1547 private ApnSetting getPreferredApn() { 1548 if (allApns.isEmpty()) { 1549 return null; 1550 } 1551 1552 Cursor cursor = phone.getContext().getContentResolver().query( 1553 PREFERAPN_URI, new String[] { "_id", "name", "apn" }, 1554 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 1555 1556 if (cursor != null) { 1557 canSetPreferApn = true; 1558 } else { 1559 canSetPreferApn = false; 1560 } 1561 1562 if (canSetPreferApn && cursor.getCount() > 0) { 1563 int pos; 1564 cursor.moveToFirst(); 1565 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); 1566 for(ApnSetting p:allApns) { 1567 if (p.id == pos && p.canHandleType(mRequestedApnType)) { 1568 cursor.close(); 1569 return p; 1570 } 1571 } 1572 } 1573 1574 if (cursor != null) { 1575 cursor.close(); 1576 } 1577 1578 return null; 1579 } 1580 1581 public void handleMessage (Message msg) { 1582 if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg); 1583 switch (msg.what) { 1584 case EVENT_RECORDS_LOADED: 1585 onRecordsLoaded(); 1586 break; 1587 1588 case EVENT_ENABLE_NEW_APN: 1589 onEnableNewApn(); 1590 break; 1591 1592 case EVENT_GPRS_DETACHED: 1593 onGprsDetached(); 1594 break; 1595 1596 case EVENT_GPRS_ATTACHED: 1597 onGprsAttached(); 1598 break; 1599 1600 case EVENT_DATA_STATE_CHANGED: 1601 onPdpStateChanged((AsyncResult) msg.obj, false); 1602 break; 1603 1604 case EVENT_GET_PDP_LIST_COMPLETE: 1605 onPdpStateChanged((AsyncResult) msg.obj, true); 1606 break; 1607 1608 case EVENT_POLL_PDP: 1609 onPollPdp(); 1610 break; 1611 1612 case EVENT_START_NETSTAT_POLL: 1613 mPingTestActive = false; 1614 startNetStatPoll(); 1615 break; 1616 1617 case EVENT_START_RECOVERY: 1618 mPingTestActive = false; 1619 doRecovery(); 1620 break; 1621 1622 case EVENT_APN_CHANGED: 1623 onApnChanged(); 1624 break; 1625 1626 case EVENT_PS_RESTRICT_ENABLED: 1627 /** 1628 * We don't need to explicitly to tear down the PDP context 1629 * when PS restricted is enabled. The base band will deactive 1630 * PDP context and notify us with PDP_CONTEXT_CHANGED. 1631 * But we should stop the network polling and prevent reset PDP. 1632 */ 1633 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); 1634 stopNetStatPoll(); 1635 mIsPsRestricted = true; 1636 break; 1637 1638 case EVENT_PS_RESTRICT_DISABLED: 1639 /** 1640 * When PS restrict is removed, we need setup PDP connection if 1641 * PDP connection is down. 1642 */ 1643 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); 1644 mIsPsRestricted = false; 1645 if (state == State.CONNECTED) { 1646 startNetStatPoll(); 1647 } else { 1648 if (state == State.FAILED) { 1649 cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); 1650 mRetryMgr.resetRetryCount(); 1651 mReregisterOnReconnectFailure = false; 1652 } 1653 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); 1654 } 1655 break; 1656 1657 default: 1658 // handle the message in the super class DataConnectionTracker 1659 super.handleMessage(msg); 1660 break; 1661 } 1662 } 1663 1664 protected void log(String s) { 1665 Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s); 1666 } 1667 1668} 1669