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