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