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