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 17 package com.android.internal.telephony.gsm; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.ContentResolver; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.database.ContentObserver; 27 import android.database.Cursor; 28 import android.net.ConnectivityManager; 29 import android.net.LinkAddress; 30 import android.net.LinkCapabilities; 31 import android.net.LinkProperties; 32 import android.net.LinkProperties.CompareResult; 33 import android.net.NetworkConfig; 34 import android.net.NetworkUtils; 35 import android.net.ProxyProperties; 36 import android.net.TrafficStats; 37 import android.net.Uri; 38 import android.os.AsyncResult; 39 import android.os.Message; 40 import android.os.SystemClock; 41 import android.os.SystemProperties; 42 import android.provider.Settings; 43 import android.provider.Telephony; 44 import android.telephony.CellLocation; 45 import android.telephony.ServiceState; 46 import android.telephony.TelephonyManager; 47 import android.telephony.cdma.CdmaCellLocation; 48 import android.telephony.gsm.GsmCellLocation; 49 import android.text.TextUtils; 50 import android.util.EventLog; 51 import android.util.Log; 52 53 import com.android.internal.telephony.ApnContext; 54 import com.android.internal.telephony.ApnSetting; 55 import com.android.internal.telephony.DataCallState; 56 import com.android.internal.telephony.DataConnection; 57 import com.android.internal.telephony.DataConnection.FailCause; 58 import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult; 59 import com.android.internal.telephony.DataConnectionAc; 60 import com.android.internal.telephony.DataConnectionTracker; 61 import com.android.internal.telephony.EventLogTags; 62 import com.android.internal.telephony.Phone; 63 import com.android.internal.telephony.PhoneBase; 64 import com.android.internal.telephony.RILConstants; 65 import com.android.internal.telephony.RetryManager; 66 import com.android.internal.util.AsyncChannel; 67 68 import java.util.ArrayList; 69 import java.util.Collection; 70 import java.util.HashMap; 71 import java.util.List; 72 import java.util.concurrent.ConcurrentHashMap; 73 74 /** 75 * {@hide} 76 */ 77 public final class GsmDataConnectionTracker extends DataConnectionTracker { 78 protected final String LOG_TAG = "GSM"; 79 80 /** 81 * Handles changes to the APN db. 82 */ 83 private class ApnChangeObserver extends ContentObserver { 84 public ApnChangeObserver () { 85 super(mDataConnectionTracker); 86 } 87 88 @Override 89 public void onChange(boolean selfChange) { 90 sendMessage(obtainMessage(EVENT_APN_CHANGED)); 91 } 92 } 93 94 //***** Instance Variables 95 96 private boolean mReregisterOnReconnectFailure = false; 97 private ContentResolver mResolver; 98 99 // Count of PDP reset attempts; reset when we see incoming, 100 // call reRegisterNetwork, or pingTest succeeds. 101 private int mPdpResetCount = 0; 102 103 // Recovery action taken in case of data stall 104 enum RecoveryAction {REREGISTER, RADIO_RESTART, RADIO_RESET}; 105 private RecoveryAction mRecoveryAction = RecoveryAction.REREGISTER; 106 107 108 //***** Constants 109 110 private static final int POLL_PDP_MILLIS = 5 * 1000; 111 112 private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; 113 private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type"; 114 115 static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn"); 116 static final String APN_ID = "apn_id"; 117 private boolean canSetPreferApn = false; 118 119 @Override 120 protected void onActionIntentReconnectAlarm(Intent intent) { 121 if (DBG) log("GPRS reconnect alarm. Previous state was " + mState); 122 123 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); 124 int connectionId = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, -1); 125 126 DataConnectionAc dcac= mDataConnectionAsyncChannels.get(connectionId); 127 128 if (dcac != null) { 129 for (ApnContext apnContext : dcac.getApnListSync()) { 130 apnContext.setReason(reason); 131 if (apnContext.getState() == State.FAILED) { 132 apnContext.setState(State.IDLE); 133 } 134 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext)); 135 } 136 // Alram had expired. Clear pending intent recorded on the DataConnection. 137 dcac.setReconnectIntentSync(null); 138 } 139 } 140 141 /** Watches for changes to the APN db. */ 142 private ApnChangeObserver mApnObserver; 143 144 //***** Constructor 145 146 public GsmDataConnectionTracker(PhoneBase p) { 147 super(p); 148 149 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); 150 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); 151 p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); 152 p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null); 153 p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); 154 p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); 155 p.getServiceStateTracker().registerForDataConnectionAttached(this, 156 EVENT_DATA_CONNECTION_ATTACHED, null); 157 p.getServiceStateTracker().registerForDataConnectionDetached(this, 158 EVENT_DATA_CONNECTION_DETACHED, null); 159 p.getServiceStateTracker().registerForRoamingOn(this, EVENT_ROAMING_ON, null); 160 p.getServiceStateTracker().registerForRoamingOff(this, EVENT_ROAMING_OFF, null); 161 p.getServiceStateTracker().registerForPsRestrictedEnabled(this, 162 EVENT_PS_RESTRICT_ENABLED, null); 163 p.getServiceStateTracker().registerForPsRestrictedDisabled(this, 164 EVENT_PS_RESTRICT_DISABLED, null); 165 166 mDataConnectionTracker = this; 167 mResolver = mPhone.getContext().getContentResolver(); 168 169 mApnObserver = new ApnChangeObserver(); 170 p.getContext().getContentResolver().registerContentObserver( 171 Telephony.Carriers.CONTENT_URI, true, mApnObserver); 172 173 mApnContexts = new ConcurrentHashMap<String, ApnContext>(); 174 initApnContextsAndDataConnection(); 175 broadcastMessenger(); 176 } 177 178 @Override 179 public void dispose() { 180 cleanUpAllConnections(false, null); 181 182 super.dispose(); 183 184 //Unregister for all events 185 mPhone.mCM.unregisterForAvailable(this); 186 mPhone.mCM.unregisterForOffOrNotAvailable(this); 187 mPhone.mIccRecords.unregisterForRecordsLoaded(this); 188 mPhone.mCM.unregisterForDataNetworkStateChanged(this); 189 mPhone.getCallTracker().unregisterForVoiceCallEnded(this); 190 mPhone.getCallTracker().unregisterForVoiceCallStarted(this); 191 mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this); 192 mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this); 193 mPhone.getServiceStateTracker().unregisterForRoamingOn(this); 194 mPhone.getServiceStateTracker().unregisterForRoamingOff(this); 195 mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this); 196 mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this); 197 198 mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver); 199 mApnContexts.clear(); 200 201 destroyDataConnections(); 202 } 203 204 @Override 205 public boolean isApnTypeActive(String type) { 206 ApnContext apnContext = mApnContexts.get(type); 207 if (apnContext == null) return false; 208 209 return (apnContext.getDataConnection() != null); 210 } 211 212 @Override 213 protected boolean isDataPossible(String apnType) { 214 ApnContext apnContext = mApnContexts.get(apnType); 215 if (apnContext == null) { 216 return false; 217 } 218 boolean apnContextIsEnabled = apnContext.isEnabled(); 219 State apnContextState = apnContext.getState(); 220 boolean apnTypePossible = !(apnContextIsEnabled && 221 (apnContextState == State.FAILED)); 222 boolean dataAllowed = isDataAllowed(); 223 boolean possible = dataAllowed && apnTypePossible; 224 225 if (DBG) { 226 log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " + 227 "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s", 228 apnType, possible, dataAllowed, apnTypePossible, 229 apnContextIsEnabled, apnContextState)); 230 } 231 return possible; 232 } 233 234 @Override 235 protected void finalize() { 236 if(DBG) log("finalize"); 237 } 238 239 @Override 240 protected String getActionIntentReconnectAlarm() { 241 return INTENT_RECONNECT_ALARM; 242 } 243 244 private ApnContext addApnContext(String type) { 245 ApnContext apnContext = new ApnContext(type, LOG_TAG); 246 apnContext.setDependencyMet(false); 247 mApnContexts.put(type, apnContext); 248 return apnContext; 249 } 250 251 protected void initApnContextsAndDataConnection() { 252 boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true); 253 // Load device network attributes from resources 254 String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray( 255 com.android.internal.R.array.networkAttributes); 256 for (String networkConfigString : networkConfigStrings) { 257 NetworkConfig networkConfig = new NetworkConfig(networkConfigString); 258 ApnContext apnContext = null; 259 260 switch (networkConfig.type) { 261 case ConnectivityManager.TYPE_MOBILE: 262 apnContext = addApnContext(Phone.APN_TYPE_DEFAULT); 263 apnContext.setEnabled(defaultEnabled); 264 break; 265 case ConnectivityManager.TYPE_MOBILE_MMS: 266 apnContext = addApnContext(Phone.APN_TYPE_MMS); 267 break; 268 case ConnectivityManager.TYPE_MOBILE_SUPL: 269 apnContext = addApnContext(Phone.APN_TYPE_SUPL); 270 break; 271 case ConnectivityManager.TYPE_MOBILE_DUN: 272 apnContext = addApnContext(Phone.APN_TYPE_DUN); 273 break; 274 case ConnectivityManager.TYPE_MOBILE_HIPRI: 275 apnContext = addApnContext(Phone.APN_TYPE_HIPRI); 276 ApnContext defaultContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT); 277 if (defaultContext != null) { 278 applyNewState(apnContext, apnContext.isEnabled(), 279 defaultContext.getDependencyMet()); 280 } else { 281 // the default will set the hipri dep-met when it is created 282 } 283 continue; 284 case ConnectivityManager.TYPE_MOBILE_FOTA: 285 apnContext = addApnContext(Phone.APN_TYPE_FOTA); 286 break; 287 case ConnectivityManager.TYPE_MOBILE_IMS: 288 apnContext = addApnContext(Phone.APN_TYPE_IMS); 289 break; 290 case ConnectivityManager.TYPE_MOBILE_CBS: 291 apnContext = addApnContext(Phone.APN_TYPE_CBS); 292 break; 293 default: 294 // skip unknown types 295 continue; 296 } 297 if (apnContext != null) { 298 // set the prop, but also apply the newly set enabled and dependency values 299 onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet); 300 } 301 } 302 } 303 304 @Override 305 protected LinkProperties getLinkProperties(String apnType) { 306 ApnContext apnContext = mApnContexts.get(apnType); 307 if (apnContext != null) { 308 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 309 if (dcac != null) { 310 if (DBG) log("return link properites for " + apnType); 311 return dcac.getLinkPropertiesSync(); 312 } 313 } 314 if (DBG) log("return new LinkProperties"); 315 return new LinkProperties(); 316 } 317 318 @Override 319 protected LinkCapabilities getLinkCapabilities(String apnType) { 320 ApnContext apnContext = mApnContexts.get(apnType); 321 if (apnContext!=null) { 322 DataConnectionAc dataConnectionAc = apnContext.getDataConnectionAc(); 323 if (dataConnectionAc != null) { 324 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType); 325 return dataConnectionAc.getLinkCapabilitiesSync(); 326 } 327 } 328 if (DBG) log("return new LinkCapabilities"); 329 return new LinkCapabilities(); 330 } 331 332 @Override 333 // Return all active apn types 334 public String[] getActiveApnTypes() { 335 if (DBG) log("get all active apn types"); 336 ArrayList<String> result = new ArrayList<String>(); 337 338 for (ApnContext apnContext : mApnContexts.values()) { 339 if (apnContext.isReady()) { 340 result.add(apnContext.getApnType()); 341 } 342 } 343 344 return (String[])result.toArray(new String[0]); 345 } 346 347 @Override 348 // Return active apn of specific apn type 349 public String getActiveApnString(String apnType) { 350 if (DBG) log( "get active apn string for type:" + apnType); 351 ApnContext apnContext = mApnContexts.get(apnType); 352 if (apnContext != null) { 353 ApnSetting apnSetting = apnContext.getApnSetting(); 354 if (apnSetting != null) { 355 return apnSetting.apn; 356 } 357 } 358 return null; 359 } 360 361 @Override 362 public boolean isApnTypeEnabled(String apnType) { 363 ApnContext apnContext = mApnContexts.get(apnType); 364 if (apnContext == null) { 365 return false; 366 } 367 return apnContext.isEnabled(); 368 } 369 370 @Override 371 protected void setState(State s) { 372 if (DBG) log("setState should not be used in GSM" + s); 373 } 374 375 // Return state of specific apn type 376 @Override 377 public State getState(String apnType) { 378 ApnContext apnContext = mApnContexts.get(apnType); 379 if (apnContext != null) { 380 return apnContext.getState(); 381 } 382 return State.FAILED; 383 } 384 385 // Return state of overall 386 public State getOverallState() { 387 boolean isConnecting = false; 388 boolean isFailed = true; // All enabled Apns should be FAILED. 389 boolean isAnyEnabled = false; 390 391 for (ApnContext apnContext : mApnContexts.values()) { 392 if (apnContext.isEnabled()) { 393 isAnyEnabled = true; 394 switch (apnContext.getState()) { 395 case CONNECTED: 396 case DISCONNECTING: 397 if (DBG) log("overall state is CONNECTED"); 398 return State.CONNECTED; 399 case CONNECTING: 400 case INITING: 401 isConnecting = true; 402 isFailed = false; 403 break; 404 case IDLE: 405 case SCANNING: 406 isFailed = false; 407 break; 408 } 409 } 410 } 411 412 if (!isAnyEnabled) { // Nothing enabled. return IDLE. 413 if (DBG) log( "overall state is IDLE"); 414 return State.IDLE; 415 } 416 417 if (isConnecting) { 418 if (DBG) log( "overall state is CONNECTING"); 419 return State.CONNECTING; 420 } else if (!isFailed) { 421 if (DBG) log( "overall state is IDLE"); 422 return State.IDLE; 423 } else { 424 if (DBG) log( "overall state is FAILED"); 425 return State.FAILED; 426 } 427 } 428 429 /** 430 * Ensure that we are connected to an APN of the specified type. 431 * 432 * @param type the APN type 433 * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or 434 * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a 435 * broadcast will be sent by the ConnectivityManager when a 436 * connection to the APN has been established. 437 */ 438 @Override 439 public synchronized int enableApnType(String apnType) { 440 ApnContext apnContext = mApnContexts.get(apnType); 441 if (apnContext == null || !isApnTypeAvailable(apnType)) { 442 if (DBG) log("enableApnType: " + apnType + " is type not available"); 443 return Phone.APN_TYPE_NOT_AVAILABLE; 444 } 445 446 // If already active, return 447 if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")"); 448 449 if (apnContext.getState() == State.CONNECTED) { 450 if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE"); 451 return Phone.APN_ALREADY_ACTIVE; 452 } 453 setEnabled(apnTypeToId(apnType), true); 454 if (DBG) { 455 log("enableApnType: new apn request for type " + apnType + 456 " return APN_REQUEST_STARTED"); 457 } 458 return Phone.APN_REQUEST_STARTED; 459 } 460 461 // A new APN has gone active and needs to send events to catch up with the 462 // current condition 463 private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) { 464 switch (apnContext.getState()) { 465 case IDLE: 466 case INITING: 467 break; 468 case CONNECTING: 469 case SCANNING: 470 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING); 471 break; 472 case CONNECTED: 473 case DISCONNECTING: 474 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING); 475 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTED); 476 break; 477 } 478 } 479 480 @Override 481 public synchronized int disableApnType(String type) { 482 if (DBG) log("disableApnType:" + type); 483 ApnContext apnContext = mApnContexts.get(type); 484 485 if (apnContext != null) { 486 setEnabled(apnTypeToId(type), false); 487 if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) { 488 if (DBG) log("diableApnType: return APN_REQUEST_STARTED"); 489 return Phone.APN_REQUEST_STARTED; 490 } else { 491 if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE"); 492 return Phone.APN_ALREADY_INACTIVE; 493 } 494 495 } else { 496 if (DBG) { 497 log("disableApnType: no apn context was found, return APN_REQUEST_FAILED"); 498 } 499 return Phone.APN_REQUEST_FAILED; 500 } 501 } 502 503 @Override 504 protected boolean isApnTypeAvailable(String type) { 505 if (type.equals(Phone.APN_TYPE_DUN) && fetchDunApn() != null) { 506 return true; 507 } 508 509 if (mAllApns != null) { 510 for (ApnSetting apn : mAllApns) { 511 if (apn.canHandleType(type)) { 512 return true; 513 } 514 } 515 } 516 return false; 517 } 518 519 /** 520 * Report on whether data connectivity is enabled for any APN. 521 * @return {@code false} if data connectivity has been explicitly disabled, 522 * {@code true} otherwise. 523 */ 524 @Override 525 public boolean getAnyDataEnabled() { 526 synchronized (mDataEnabledLock) { 527 if (!(mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled)) return false; 528 for (ApnContext apnContext : mApnContexts.values()) { 529 // Make sure we dont have a context that going down 530 // and is explicitly disabled. 531 if (isDataAllowed(apnContext)) { 532 return true; 533 } 534 } 535 return false; 536 } 537 } 538 539 private boolean isDataAllowed(ApnContext apnContext) { 540 return apnContext.isReady() && isDataAllowed(); 541 } 542 543 //****** Called from ServiceStateTracker 544 /** 545 * Invoked when ServiceStateTracker observes a transition from GPRS 546 * attach to detach. 547 */ 548 protected void onDataConnectionDetached() { 549 /* 550 * We presently believe it is unnecessary to tear down the PDP context 551 * when GPRS detaches, but we should stop the network polling. 552 */ 553 if (DBG) log ("onDataConnectionDetached: stop polling and notify detached"); 554 stopNetStatPoll(); 555 notifyDataConnection(Phone.REASON_DATA_DETACHED); 556 } 557 558 private void onDataConnectionAttached() { 559 if (DBG) log("onDataConnectionAttached"); 560 if (getOverallState() == State.CONNECTED) { 561 if (DBG) log("onDataConnectionAttached: start polling notify attached"); 562 startNetStatPoll(); 563 notifyDataConnection(Phone.REASON_DATA_ATTACHED); 564 } else { 565 // update APN availability so that APN can be enabled. 566 notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED); 567 } 568 569 setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED); 570 } 571 572 @Override 573 protected boolean isDataAllowed() { 574 final boolean internalDataEnabled; 575 synchronized (mDataEnabledLock) { 576 internalDataEnabled = mInternalDataEnabled; 577 } 578 579 int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState(); 580 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState(); 581 582 boolean allowed = 583 (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) && 584 mPhone.mIccRecords.getRecordsLoaded() && 585 (mPhone.getState() == Phone.State.IDLE || 586 mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) && 587 internalDataEnabled && 588 (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) && 589 !mIsPsRestricted && 590 desiredPowerState; 591 if (!allowed && DBG) { 592 String reason = ""; 593 if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) { 594 reason += " - gprs= " + gprsState; 595 } 596 if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded"; 597 if (mPhone.getState() != Phone.State.IDLE && 598 !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { 599 reason += " - PhoneState= " + mPhone.getState(); 600 reason += " - Concurrent voice and data not allowed"; 601 } 602 if (!internalDataEnabled) reason += " - mInternalDataEnabled= false"; 603 if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) { 604 reason += " - Roaming and data roaming not enabled"; 605 } 606 if (mIsPsRestricted) reason += " - mIsPsRestricted= true"; 607 if (!desiredPowerState) reason += " - desiredPowerState= false"; 608 if (DBG) log("isDataAllowed: not allowed due to" + reason); 609 } 610 return allowed; 611 } 612 613 private void setupDataOnReadyApns(String reason) { 614 // Stop reconnect alarms on all data connections pending 615 // retry. Reset ApnContext state to IDLE. 616 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 617 if (dcac.getReconnectIntentSync() != null) { 618 cancelReconnectAlarm(dcac); 619 } 620 // update retry config for existing calls to match up 621 // ones for the new RAT. 622 if (dcac.dataConnection != null) { 623 Collection<ApnContext> apns = dcac.getApnListSync(); 624 625 boolean hasDefault = false; 626 for (ApnContext apnContext : apns) { 627 if (apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) { 628 hasDefault = true; 629 break; 630 } 631 } 632 configureRetry(dcac.dataConnection, hasDefault); 633 } 634 } 635 636 // Only check for default APN state 637 for (ApnContext apnContext : mApnContexts.values()) { 638 if (apnContext.getState() == State.FAILED) { 639 // By this time, alarms for all failed Apns 640 // should be stopped if any. 641 // Make sure to set the state back to IDLE 642 // so that setup data can happen. 643 apnContext.setState(State.IDLE); 644 } 645 if (apnContext.isReady()) { 646 if (apnContext.getState() == State.IDLE) { 647 apnContext.setReason(reason); 648 trySetupData(apnContext); 649 } 650 } 651 } 652 } 653 654 private boolean trySetupData(String reason, String type) { 655 if (DBG) { 656 log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason) 657 + " isPsRestricted=" + mIsPsRestricted); 658 } 659 660 if (type == null) { 661 type = Phone.APN_TYPE_DEFAULT; 662 } 663 664 ApnContext apnContext = mApnContexts.get(type); 665 666 if (apnContext == null ){ 667 if (DBG) log("trySetupData new apn context for type:" + type); 668 apnContext = new ApnContext(type, LOG_TAG); 669 mApnContexts.put(type, apnContext); 670 } 671 apnContext.setReason(reason); 672 673 return trySetupData(apnContext); 674 } 675 676 private boolean trySetupData(ApnContext apnContext) { 677 if (DBG) { 678 log("trySetupData for type:" + apnContext.getApnType() + 679 " due to " + apnContext.getReason()); 680 log("trySetupData with mIsPsRestricted=" + mIsPsRestricted); 681 } 682 683 if (mPhone.getSimulatedRadioControl() != null) { 684 // Assume data is connected on the simulator 685 // FIXME this can be improved 686 apnContext.setState(State.CONNECTED); 687 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 688 689 log("trySetupData: (fix?) We're on the simulator; assuming data is connected"); 690 return true; 691 } 692 693 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState(); 694 695 if ((apnContext.getState() == State.IDLE || apnContext.getState() == State.SCANNING) && 696 isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) { 697 698 if (apnContext.getState() == State.IDLE) { 699 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType()); 700 if (waitingApns.isEmpty()) { 701 if (DBG) log("trySetupData: No APN found"); 702 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext); 703 notifyOffApnsOfAvailability(apnContext.getReason()); 704 return false; 705 } else { 706 apnContext.setWaitingApns(waitingApns); 707 if (DBG) { 708 log ("trySetupData: Create from mAllApns : " + apnListToString(mAllApns)); 709 } 710 } 711 } 712 713 if (DBG) { 714 log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns())); 715 } 716 // apnContext.setReason(apnContext.getReason()); 717 boolean retValue = setupData(apnContext); 718 notifyOffApnsOfAvailability(apnContext.getReason()); 719 return retValue; 720 } else { 721 // TODO: check the condition. 722 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT) 723 && (apnContext.getState() == State.IDLE 724 || apnContext.getState() == State.SCANNING)) 725 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType()); 726 notifyOffApnsOfAvailability(apnContext.getReason()); 727 return false; 728 } 729 } 730 731 @Override 732 // Disabled apn's still need avail/unavail notificiations - send them out 733 protected void notifyOffApnsOfAvailability(String reason) { 734 for (ApnContext apnContext : mApnContexts.values()) { 735 if (!apnContext.isReady()) { 736 if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType()); 737 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(), 738 apnContext.getApnType(), 739 Phone.DataState.DISCONNECTED); 740 } else { 741 if (DBG) { 742 log("notifyOffApnsOfAvailability skipped apn due to isReady==false: " + 743 apnContext.toString()); 744 } 745 } 746 } 747 } 748 749 /** 750 * If tearDown is true, this only tears down a CONNECTED session. Presently, 751 * there is no mechanism for abandoning an INITING/CONNECTING session, 752 * but would likely involve cancelling pending async requests or 753 * setting a flag or new state to ignore them when they came in 754 * @param tearDown true if the underlying GsmDataConnection should be 755 * disconnected. 756 * @param reason reason for the clean up. 757 */ 758 protected void cleanUpAllConnections(boolean tearDown, String reason) { 759 if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason); 760 761 for (ApnContext apnContext : mApnContexts.values()) { 762 apnContext.setReason(reason); 763 cleanUpConnection(tearDown, apnContext); 764 } 765 766 stopNetStatPoll(); 767 // TODO: Do we need mRequestedApnType? 768 mRequestedApnType = Phone.APN_TYPE_DEFAULT; 769 } 770 771 /** 772 * Cleanup all connections. 773 * 774 * TODO: Cleanup only a specified connection passed as a parameter. 775 * Also, make sure when you clean up a conn, if it is last apply 776 * logic as though it is cleanupAllConnections 777 * 778 * @param tearDown true if the underlying DataConnection should be disconnected. 779 * @param reason for the clean up. 780 */ 781 782 @Override 783 protected void onCleanUpAllConnections(String cause) { 784 cleanUpAllConnections(true, cause); 785 } 786 787 private void cleanUpConnection(boolean tearDown, ApnContext apnContext) { 788 789 if (apnContext == null) { 790 if (DBG) log("cleanUpConnection: apn context is null"); 791 return; 792 } 793 794 if (DBG) { 795 log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason()); 796 } 797 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 798 if (tearDown) { 799 if (apnContext.isDisconnected()) { 800 // The request is tearDown and but ApnContext is not connected. 801 // If apnContext is not enabled anymore, break the linkage to the DCAC/DC. 802 apnContext.setState(State.IDLE); 803 if (!apnContext.isReady()) { 804 apnContext.setDataConnection(null); 805 apnContext.setDataConnectionAc(null); 806 } 807 } else { 808 // Connection is still there. Try to clean up. 809 if (dcac != null) { 810 if (apnContext.getState() != State.DISCONNECTING) { 811 if (DBG) log("cleanUpConnection: tearing down"); 812 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext); 813 apnContext.getDataConnection().tearDown(apnContext.getReason(), msg); 814 apnContext.setState(State.DISCONNECTING); 815 } 816 } else { 817 // apn is connected but no reference to dcac. 818 // Should not be happen, but reset the state in case. 819 apnContext.setState(State.IDLE); 820 mPhone.notifyDataConnection(apnContext.getReason(), 821 apnContext.getApnType()); 822 } 823 } 824 } else { 825 // force clean up the data connection. 826 if (dcac != null) dcac.resetSync(); 827 apnContext.setState(State.IDLE); 828 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 829 apnContext.setDataConnection(null); 830 apnContext.setDataConnectionAc(null); 831 } 832 833 // make sure reconnection alarm is cleaned up if there is no ApnContext 834 // associated to the connection. 835 if (dcac != null) { 836 Collection<ApnContext> apnList = dcac.getApnListSync(); 837 if (apnList.isEmpty()) { 838 cancelReconnectAlarm(dcac); 839 } 840 } 841 } 842 843 /** 844 * Cancels the alarm associated with DCAC. 845 * 846 * @param DataConnectionAc on which the alarm should be stopped. 847 */ 848 private void cancelReconnectAlarm(DataConnectionAc dcac) { 849 if (dcac == null) return; 850 851 PendingIntent intent = dcac.getReconnectIntentSync(); 852 853 if (intent != null) { 854 AlarmManager am = 855 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 856 am.cancel(intent); 857 dcac.setReconnectIntentSync(null); 858 } 859 } 860 861 /** 862 * @param types comma delimited list of APN types 863 * @return array of APN types 864 */ 865 private String[] parseTypes(String types) { 866 String[] result; 867 // If unset, set to DEFAULT. 868 if (types == null || types.equals("")) { 869 result = new String[1]; 870 result[0] = Phone.APN_TYPE_ALL; 871 } else { 872 result = types.split(","); 873 } 874 return result; 875 } 876 877 private ArrayList<ApnSetting> createApnList(Cursor cursor) { 878 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); 879 if (cursor.moveToFirst()) { 880 do { 881 String[] types = parseTypes( 882 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); 883 ApnSetting apn = new ApnSetting( 884 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), 885 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), 886 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), 887 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), 888 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), 889 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), 890 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), 891 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), 892 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), 893 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), 894 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), 895 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), 896 types, 897 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)), 898 cursor.getString(cursor.getColumnIndexOrThrow( 899 Telephony.Carriers.ROAMING_PROTOCOL)), 900 cursor.getInt(cursor.getColumnIndexOrThrow( 901 Telephony.Carriers.CARRIER_ENABLED)) == 1, 902 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER))); 903 result.add(apn); 904 } while (cursor.moveToNext()); 905 } 906 if (DBG) log("createApnList: X result=" + result); 907 return result; 908 } 909 910 private boolean dataConnectionNotInUse(DataConnectionAc dcac) { 911 for (ApnContext apnContext : mApnContexts.values()) { 912 if (apnContext.getDataConnectionAc() == dcac) return false; 913 } 914 return true; 915 } 916 917 private GsmDataConnection findFreeDataConnection() { 918 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 919 if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) { 920 log("findFreeDataConnection: found free GsmDataConnection"); 921 return (GsmDataConnection) dcac.dataConnection; 922 } 923 } 924 log("findFreeDataConnection: NO free GsmDataConnection"); 925 return null; 926 } 927 928 protected GsmDataConnection findReadyDataConnection(ApnSetting apn) { 929 if (DBG) 930 log("findReadyDataConnection: apn string <" + 931 (apn!=null?(apn.toString()):"null") +">"); 932 if (apn == null) { 933 return null; 934 } 935 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 936 ApnSetting apnSetting = dcac.getApnSettingSync(); 937 if (DBG) { 938 log("findReadyDataConnection: dc apn string <" + 939 (apnSetting != null ? (apnSetting.toString()) : "null") + ">"); 940 } 941 if ((apnSetting != null) && TextUtils.equals(apnSetting.toString(), apn.toString())) { 942 return (GsmDataConnection) dcac.dataConnection; 943 } 944 } 945 return null; 946 } 947 948 949 private boolean setupData(ApnContext apnContext) { 950 if (DBG) log("setupData: apnContext=" + apnContext); 951 ApnSetting apn; 952 GsmDataConnection dc; 953 954 int profileId = getApnProfileID(apnContext.getApnType()); 955 apn = apnContext.getNextWaitingApn(); 956 if (apn == null) { 957 if (DBG) log("setupData: return for no apn found!"); 958 return false; 959 } 960 961 // First, check to see if ApnContext already has DC. 962 // This could happen if the retries are currently engaged. 963 dc = (GsmDataConnection)apnContext.getDataConnection(); 964 965 if (dc == null) { 966 967 dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext); 968 969 if (dc == null) { 970 dc = findReadyDataConnection(apn); 971 } 972 973 if (dc == null) { 974 if (DBG) log("setupData: No ready GsmDataConnection found!"); 975 // TODO: When allocating you are mapping type to id. If more than 1 free, 976 // then could findFreeDataConnection get the wrong one?? 977 dc = findFreeDataConnection(); 978 } 979 980 if (dc == null) { 981 dc = createDataConnection(); 982 } 983 984 if (dc == null) { 985 if (DBG) log("setupData: No free GsmDataConnection found!"); 986 return false; 987 } 988 989 DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()); 990 dc.setProfileId( profileId ); 991 dc.setActiveApnType(apnContext.getApnType()); 992 int refCount = dcac.getRefCountSync(); 993 if (DBG) log("setupData: init dc and apnContext refCount=" + refCount); 994 995 // configure retry count if no other Apn is using the same connection. 996 if (refCount == 0) { 997 configureRetry(dc, apn.canHandleType(Phone.APN_TYPE_DEFAULT)); 998 } 999 apnContext.setDataConnectionAc(dcac); 1000 apnContext.setDataConnection(dc); 1001 } 1002 1003 apnContext.setApnSetting(apn); 1004 apnContext.setState(State.INITING); 1005 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1006 // If reconnect alarm is active on this DataConnection, wait for the alarm being 1007 // fired so that we don't disruppt data retry pattern engaged. 1008 if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) { 1009 if (DBG) log("setupData: data reconnection pending"); 1010 apnContext.setState(State.FAILED); 1011 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1012 return true; 1013 } 1014 1015 Message msg = obtainMessage(); 1016 msg.what = EVENT_DATA_SETUP_COMPLETE; 1017 msg.obj = apnContext; 1018 dc.bringUp(msg, apn); 1019 1020 if (DBG) log("setupData: initing!"); 1021 return true; 1022 } 1023 1024 /** 1025 * Handles changes to the APN database. 1026 */ 1027 private void onApnChanged() { 1028 // TODO: How to handle when multiple APNs are active? 1029 1030 ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT); 1031 boolean defaultApnIsDisconnected = defaultApnContext.isDisconnected(); 1032 1033 if (mPhone instanceof GSMPhone) { 1034 // The "current" may no longer be valid. MMS depends on this to send properly. TBD 1035 ((GSMPhone)mPhone).updateCurrentCarrierInProvider(); 1036 } 1037 1038 // TODO: It'd be nice to only do this if the changed entrie(s) 1039 // match the current operator. 1040 if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections"); 1041 createAllApnList(); 1042 cleanUpAllConnections(!defaultApnIsDisconnected, Phone.REASON_APN_CHANGED); 1043 if (defaultApnIsDisconnected) { 1044 setupDataOnReadyApns(Phone.REASON_APN_CHANGED); 1045 } 1046 } 1047 1048 /** 1049 * @param cid Connection id provided from RIL. 1050 * @return DataConnectionAc associated with specified cid. 1051 */ 1052 private DataConnectionAc findDataConnectionAcByCid(int cid) { 1053 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 1054 if (dcac.getCidSync() == cid) { 1055 return dcac; 1056 } 1057 } 1058 return null; 1059 } 1060 1061 /** 1062 * @param dcacs Collection of DataConnectionAc reported from RIL. 1063 * @return List of ApnContext which is connected, but is not present in 1064 * data connection list reported from RIL. 1065 */ 1066 private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) { 1067 if (dcacs == null) return null; 1068 1069 ArrayList<ApnContext> list = new ArrayList<ApnContext>(); 1070 for (ApnContext apnContext : mApnContexts.values()) { 1071 if (apnContext.getState() == State.CONNECTED) { 1072 boolean found = false; 1073 for (DataConnectionAc dcac : dcacs) { 1074 if (dcac == apnContext.getDataConnectionAc()) { 1075 // ApnContext holds the ref to dcac present in data call list. 1076 found = true; 1077 break; 1078 } 1079 } 1080 if (!found) { 1081 // ApnContext does not have dcac reported in data call list. 1082 // Fetch all the ApnContexts that map to this dcac which are in 1083 // INITING state too. 1084 if (DBG) log("onDataStateChanged(ar): Connected apn not found in the list (" + 1085 apnContext.toString() + ")"); 1086 if (apnContext.getDataConnectionAc() != null) { 1087 list.addAll(apnContext.getDataConnectionAc().getApnListSync()); 1088 } else { 1089 list.add(apnContext); 1090 } 1091 } 1092 } 1093 } 1094 return list; 1095 } 1096 1097 /** 1098 * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST 1099 * or RIL_UNSOL_DATA_CALL_LIST_CHANGED 1100 */ 1101 private void onDataStateChanged (AsyncResult ar) { 1102 ArrayList<DataCallState> dataCallStates; 1103 1104 if (DBG) log("onDataStateChanged(ar): E"); 1105 dataCallStates = (ArrayList<DataCallState>)(ar.result); 1106 1107 if (ar.exception != null) { 1108 // This is probably "radio not available" or something 1109 // of that sort. If so, the whole connection is going 1110 // to come down soon anyway 1111 if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore"); 1112 return; 1113 } 1114 if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size()); 1115 1116 // Create a hash map to store the dataCallState of each DataConnectionAc 1117 HashMap<DataCallState, DataConnectionAc> dataCallStateToDcac; 1118 dataCallStateToDcac = new HashMap<DataCallState, DataConnectionAc>(); 1119 for (DataCallState dataCallState : dataCallStates) { 1120 DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid); 1121 1122 if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac); 1123 } 1124 1125 // A list of apns to cleanup, those that aren't in the list we know we have to cleanup 1126 List<ApnContext> apnsToCleanup = findApnContextToClean(dataCallStateToDcac.values()); 1127 1128 // Find which connections have changed state and send a notification or cleanup 1129 for (DataCallState newState : dataCallStates) { 1130 DataConnectionAc dcac = dataCallStateToDcac.get(newState); 1131 1132 if (dcac == null) { 1133 loge("onDataStateChanged(ar): No associated DataConnection ignore"); 1134 continue; 1135 } 1136 1137 // The list of apn's associated with this DataConnection 1138 Collection<ApnContext> apns = dcac.getApnListSync(); 1139 1140 // Find which ApnContexts of this DC are in the "Connected/Connecting" state. 1141 ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>(); 1142 for (ApnContext apnContext : apns) { 1143 if (apnContext.getState() == State.CONNECTED || 1144 apnContext.getState() == State.CONNECTING || 1145 apnContext.getState() == State.INITING) { 1146 connectedApns.add(apnContext); 1147 } 1148 } 1149 if (connectedApns.size() == 0) { 1150 if (DBG) log("onDataStateChanged(ar): no connected apns"); 1151 } else { 1152 // Determine if the connection/apnContext should be cleaned up 1153 // or just a notification should be sent out. 1154 if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid 1155 + " newState=" + newState.toString()); 1156 if (newState.active == 0) { 1157 if (DBG) { 1158 log("onDataStateChanged(ar): inactive, cleanup apns=" + connectedApns); 1159 } 1160 apnsToCleanup.addAll(connectedApns); 1161 } else { 1162 // Its active so update the DataConnections link properties 1163 UpdateLinkPropertyResult result = 1164 dcac.updateLinkPropertiesDataCallStateSync(newState); 1165 if (result.oldLp.equals(result.newLp)) { 1166 if (DBG) log("onDataStateChanged(ar): no change"); 1167 } else { 1168 if (result.oldLp.isIdenticalInterfaceName(result.newLp)) { 1169 if (! result.oldLp.isIdenticalDnses(result.newLp) || 1170 ! result.oldLp.isIdenticalRoutes(result.newLp) || 1171 ! result.oldLp.isIdenticalHttpProxy(result.newLp) || 1172 ! result.oldLp.isIdenticalAddresses(result.newLp)) { 1173 // If the same address type was removed and added we need to cleanup 1174 CompareResult<LinkAddress> car = 1175 result.oldLp.compareAddresses(result.newLp); 1176 boolean needToClean = false; 1177 for (LinkAddress added : car.added) { 1178 for (LinkAddress removed : car.removed) { 1179 if (NetworkUtils.addressTypeMatches(removed.getAddress(), 1180 added.getAddress())) { 1181 needToClean = true; 1182 break; 1183 } 1184 } 1185 } 1186 if (needToClean) { 1187 if (DBG) { 1188 log("onDataStateChanged(ar): addr change, cleanup apns=" + 1189 connectedApns); 1190 } 1191 apnsToCleanup.addAll(connectedApns); 1192 } else { 1193 if (DBG) log("onDataStateChanged(ar): simple change"); 1194 for (ApnContext apnContext : connectedApns) { 1195 mPhone.notifyDataConnection( 1196 Phone.REASON_LINK_PROPERTIES_CHANGED, 1197 apnContext.getApnType()); 1198 } 1199 } 1200 } else { 1201 if (DBG) { 1202 log("onDataStateChanged(ar): no changes"); 1203 } 1204 } 1205 } else { 1206 if (DBG) { 1207 log("onDataStateChanged(ar): interface change, cleanup apns=" 1208 + connectedApns); 1209 } 1210 apnsToCleanup.addAll(connectedApns); 1211 } 1212 } 1213 } 1214 } 1215 } 1216 1217 if (apnsToCleanup.size() != 0) { 1218 // Add an event log when the network drops PDP 1219 int cid = getCellLocationId(); 1220 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid, 1221 TelephonyManager.getDefault().getNetworkType()); 1222 } 1223 1224 // Cleanup those dropped connections 1225 for (ApnContext apnContext : apnsToCleanup) { 1226 cleanUpConnection(true, apnContext); 1227 } 1228 1229 if (DBG) log("onDataStateChanged(ar): X"); 1230 } 1231 1232 private void notifyDefaultData(ApnContext apnContext) { 1233 if (DBG) { 1234 log("notifyDefaultData: type=" + apnContext.getApnType() 1235 + ", reason:" + apnContext.getReason()); 1236 } 1237 apnContext.setState(State.CONNECTED); 1238 // setState(State.CONNECTED); 1239 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1240 startNetStatPoll(); 1241 // reset reconnect timer 1242 apnContext.getDataConnection().resetRetryCount(); 1243 } 1244 1245 // TODO: For multiple Active APNs not exactly sure how to do this. 1246 protected void gotoIdleAndNotifyDataConnection(String reason) { 1247 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason); 1248 notifyDataConnection(reason); 1249 mActiveApn = null; 1250 } 1251 1252 private void resetPollStats() { 1253 mTxPkts = -1; 1254 mRxPkts = -1; 1255 mSentSinceLastRecv = 0; 1256 mNetStatPollPeriod = POLL_NETSTAT_MILLIS; 1257 mNoRecvPollCount = 0; 1258 } 1259 1260 private void doRecovery() { 1261 if (getOverallState() == State.CONNECTED) { 1262 int maxPdpReset = Settings.Secure.getInt(mResolver, 1263 Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, 1264 DEFAULT_MAX_PDP_RESET_FAIL); 1265 if (mPdpResetCount < maxPdpReset) { 1266 mPdpResetCount++; 1267 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv); 1268 if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max"); 1269 cleanUpAllConnections(true, Phone.REASON_PDP_RESET); 1270 } else { 1271 mPdpResetCount = 0; 1272 switch (mRecoveryAction) { 1273 case REREGISTER: 1274 EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv); 1275 if (DBG) log("doRecovery() re-register getting preferred network type"); 1276 mPhone.getServiceStateTracker().reRegisterNetwork(null); 1277 mRecoveryAction = RecoveryAction.RADIO_RESTART; 1278 break; 1279 case RADIO_RESTART: 1280 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv); 1281 if (DBG) log("restarting radio"); 1282 mRecoveryAction = RecoveryAction.RADIO_RESET; 1283 restartRadio(); 1284 break; 1285 case RADIO_RESET: 1286 // This is in case radio restart has not recovered the data. 1287 // It will set an additional "gsm.radioreset" property to tell 1288 // RIL or system to take further action. 1289 // The implementation of hard reset recovery action is up to OEM product. 1290 // Once gsm.radioreset property is consumed, it is expected to set back 1291 // to false by RIL. 1292 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1); 1293 if (DBG) log("restarting radio with reset indication"); 1294 SystemProperties.set("gsm.radioreset", "true"); 1295 // give 1 sec so property change can be notified. 1296 try { 1297 Thread.sleep(1000); 1298 } catch (InterruptedException e) {} 1299 restartRadio(); 1300 break; 1301 default: 1302 throw new RuntimeException("doRecovery: Invalid mRecoveryAction " + 1303 mRecoveryAction); 1304 } 1305 } 1306 } else { 1307 if (DBG) log("doRecovery(): ignore, we're not connected"); 1308 } 1309 } 1310 1311 @Override 1312 protected void startNetStatPoll() { 1313 if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) { 1314 if (DBG) log("startNetStatPoll"); 1315 resetPollStats(); 1316 mNetStatPollEnabled = true; 1317 mPollNetStat.run(); 1318 } 1319 } 1320 1321 @Override 1322 protected void stopNetStatPoll() { 1323 mNetStatPollEnabled = false; 1324 removeCallbacks(mPollNetStat); 1325 if (DBG) log("stopNetStatPoll"); 1326 } 1327 1328 @Override 1329 protected void restartRadio() { 1330 if (DBG) log("restartRadio: ************TURN OFF RADIO**************"); 1331 cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF); 1332 mPhone.getServiceStateTracker().powerOffRadioSafely(this); 1333 /* Note: no need to call setRadioPower(true). Assuming the desired 1334 * radio power state is still ON (as tracked by ServiceStateTracker), 1335 * ServiceStateTracker will call setRadioPower when it receives the 1336 * RADIO_STATE_CHANGED notification for the power off. And if the 1337 * desired power state has changed in the interim, we don't want to 1338 * override it with an unconditional power on. 1339 */ 1340 1341 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); 1342 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); 1343 } 1344 1345 private Runnable mPollNetStat = new Runnable() 1346 { 1347 1348 public void run() { 1349 long sent, received; 1350 long preTxPkts = -1, preRxPkts = -1; 1351 1352 Activity newActivity; 1353 1354 preTxPkts = mTxPkts; 1355 preRxPkts = mRxPkts; 1356 1357 long txSum = 0, rxSum = 0; 1358 for (ApnContext apnContext : mApnContexts.values()) { 1359 if (apnContext.getState() == State.CONNECTED) { 1360 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 1361 if (dcac == null) continue; 1362 1363 LinkProperties linkProp = dcac.getLinkPropertiesSync(); 1364 if (linkProp == null) continue; 1365 1366 String iface = linkProp.getInterfaceName(); 1367 1368 if (iface != null) { 1369 long stats = TrafficStats.getTxPackets(iface); 1370 if (stats > 0) txSum += stats; 1371 stats = TrafficStats.getRxPackets(iface); 1372 if (stats > 0) rxSum += stats; 1373 } 1374 } 1375 } 1376 1377 mTxPkts = txSum; 1378 mRxPkts = rxSum; 1379 1380 // log("tx " + mTxPkts + " rx " + mRxPkts); 1381 1382 if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { 1383 sent = mTxPkts - preTxPkts; 1384 received = mRxPkts - preRxPkts; 1385 1386 if ( sent > 0 && received > 0 ) { 1387 mSentSinceLastRecv = 0; 1388 newActivity = Activity.DATAINANDOUT; 1389 mPdpResetCount = 0; 1390 mRecoveryAction = RecoveryAction.REREGISTER; 1391 } else if (sent > 0 && received == 0) { 1392 if (mPhone.getState() == Phone.State.IDLE) { 1393 mSentSinceLastRecv += sent; 1394 } else { 1395 mSentSinceLastRecv = 0; 1396 } 1397 newActivity = Activity.DATAOUT; 1398 } else if (sent == 0 && received > 0) { 1399 mSentSinceLastRecv = 0; 1400 newActivity = Activity.DATAIN; 1401 mPdpResetCount = 0; 1402 mRecoveryAction = RecoveryAction.REREGISTER; 1403 } else if (sent == 0 && received == 0) { 1404 newActivity = Activity.NONE; 1405 } else { 1406 mSentSinceLastRecv = 0; 1407 newActivity = Activity.NONE; 1408 } 1409 1410 if (mActivity != newActivity && mIsScreenOn) { 1411 mActivity = newActivity; 1412 mPhone.notifyDataActivity(); 1413 } 1414 } 1415 1416 int watchdogTrigger = Settings.Secure.getInt(mResolver, 1417 Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 1418 NUMBER_SENT_PACKETS_OF_HANG); 1419 1420 if (mSentSinceLastRecv >= watchdogTrigger) { 1421 // we already have NUMBER_SENT_PACKETS sent without ack 1422 if (mNoRecvPollCount == 0) { 1423 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED, 1424 mSentSinceLastRecv); 1425 } 1426 1427 int noRecvPollLimit = Settings.Secure.getInt(mResolver, 1428 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); 1429 1430 if (mNoRecvPollCount < noRecvPollLimit) { 1431 // It's possible the PDP context went down and we weren't notified. 1432 // Start polling the context list in an attempt to recover. 1433 if (DBG) log("Polling: no DATAIN in a while; polling PDP"); 1434 mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED)); 1435 1436 mNoRecvPollCount++; 1437 1438 // Slow down the poll interval to let things happen 1439 mNetStatPollPeriod = Settings.Secure.getInt(mResolver, 1440 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, 1441 POLL_NETSTAT_SLOW_MILLIS); 1442 } else { 1443 if (DBG) log("Polling: Sent " + String.valueOf(mSentSinceLastRecv) + 1444 " pkts since last received start recovery process"); 1445 mNoRecvPollCount = 0; 1446 sendMessage(obtainMessage(EVENT_START_RECOVERY)); 1447 } 1448 } else { 1449 mNoRecvPollCount = 0; 1450 if (mIsScreenOn) { 1451 mNetStatPollPeriod = Settings.Secure.getInt(mResolver, 1452 Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 1453 } else { 1454 mNetStatPollPeriod = Settings.Secure.getInt(mResolver, 1455 Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 1456 POLL_NETSTAT_SCREEN_OFF_MILLIS); 1457 } 1458 } 1459 1460 if (mNetStatPollEnabled) { 1461 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod); 1462 } 1463 } 1464 }; 1465 1466 /** 1467 * Returns true if the last fail cause is something that 1468 * seems like it deserves an error notification. 1469 * Transient errors are ignored 1470 */ 1471 private boolean shouldPostNotification(GsmDataConnection.FailCause cause) { 1472 return (cause != GsmDataConnection.FailCause.UNKNOWN); 1473 } 1474 1475 /** 1476 * Return true if data connection need to be setup after disconnected due to 1477 * reason. 1478 * 1479 * @param reason the reason why data is disconnected 1480 * @return true if try setup data connection is need for this reason 1481 */ 1482 private boolean retryAfterDisconnected(String reason) { 1483 boolean retry = true; 1484 1485 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) { 1486 retry = false; 1487 } 1488 return retry; 1489 } 1490 1491 private void reconnectAfterFail(FailCause lastFailCauseCode, 1492 ApnContext apnContext, int retryOverride) { 1493 if (apnContext == null) { 1494 loge("reconnectAfterFail: apnContext == null, impossible"); 1495 return; 1496 } 1497 if ((apnContext.getState() == State.FAILED) && 1498 (apnContext.getDataConnection() != null)) { 1499 if (!apnContext.getDataConnection().isRetryNeeded()) { 1500 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) { 1501 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); 1502 return; 1503 } 1504 if (mReregisterOnReconnectFailure) { 1505 // We've re-registerd once now just retry forever. 1506 apnContext.getDataConnection().retryForeverUsingLastTimeout(); 1507 } else { 1508 // Try to Re-register to the network. 1509 if (DBG) log("reconnectAfterFail: activate failed, Reregistering to network"); 1510 mReregisterOnReconnectFailure = true; 1511 mPhone.getServiceStateTracker().reRegisterNetwork(null); 1512 apnContext.getDataConnection().resetRetryCount(); 1513 return; 1514 } 1515 } 1516 1517 // If retry needs to be backed off for specific case (determined by RIL/Modem) 1518 // use the specified timer instead of pre-configured retry pattern. 1519 int nextReconnectDelay = retryOverride; 1520 if (nextReconnectDelay < 0) { 1521 nextReconnectDelay = apnContext.getDataConnection().getRetryTimer(); 1522 apnContext.getDataConnection().increaseRetryCount(); 1523 } 1524 startAlarmForReconnect(nextReconnectDelay, apnContext); 1525 1526 if (!shouldPostNotification(lastFailCauseCode)) { 1527 if (DBG) { 1528 log("reconnectAfterFail: NOT Posting GPRS Unavailable notification " 1529 + "-- likely transient error"); 1530 } 1531 } else { 1532 notifyNoData(lastFailCauseCode, apnContext); 1533 } 1534 } 1535 } 1536 1537 private void startAlarmForReconnect(int delay, ApnContext apnContext) { 1538 1539 if (DBG) { 1540 log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for " 1541 + (delay / 1000) + "s"); 1542 } 1543 1544 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 1545 1546 if ((dcac == null) || (dcac.dataConnection == null)) { 1547 // should not happen, but just in case. 1548 loge("null dcac or dc."); 1549 return; 1550 } 1551 1552 AlarmManager am = 1553 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 1554 1555 Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' + 1556 dcac.dataConnection.getDataConnectionId()); 1557 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason()); 1558 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, 1559 dcac.dataConnection.getDataConnectionId()); 1560 1561 PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0, 1562 intent, 0); 1563 dcac.setReconnectIntentSync(alarmIntent); 1564 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1565 SystemClock.elapsedRealtime() + delay, alarmIntent); 1566 1567 } 1568 1569 private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode, 1570 ApnContext apnContext) { 1571 if (DBG) log( "notifyNoData: type=" + apnContext.getApnType()); 1572 apnContext.setState(State.FAILED); 1573 if (lastFailCauseCode.isPermanentFail() 1574 && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) { 1575 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType()); 1576 } 1577 } 1578 1579 private void onRecordsLoaded() { 1580 if (DBG) log("onRecordsLoaded: createAllApnList"); 1581 createAllApnList(); 1582 if (mPhone.mCM.getRadioState().isOn()) { 1583 if (DBG) log("onRecordsLoaded: notifying data availability"); 1584 notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED); 1585 } 1586 setupDataOnReadyApns(Phone.REASON_SIM_LOADED); 1587 } 1588 1589 @Override 1590 protected void onSetDependencyMet(String apnType, boolean met) { 1591 // don't allow users to tweak hipri to work around default dependency not met 1592 if (Phone.APN_TYPE_HIPRI.equals(apnType)) return; 1593 1594 ApnContext apnContext = mApnContexts.get(apnType); 1595 if (apnContext == null) { 1596 loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" + 1597 apnType + ", " + met + ")"); 1598 return; 1599 } 1600 applyNewState(apnContext, apnContext.isEnabled(), met); 1601 if (Phone.APN_TYPE_DEFAULT.equals(apnType)) { 1602 // tie actions on default to similar actions on HIPRI regarding dependencyMet 1603 apnContext = mApnContexts.get(Phone.APN_TYPE_HIPRI); 1604 if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met); 1605 } 1606 } 1607 1608 private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) { 1609 boolean cleanup = false; 1610 boolean trySetup = false; 1611 if (DBG) { 1612 log("applyNewState(" + apnContext.getApnType() + ", " + enabled + 1613 "(" + apnContext.isEnabled() + "), " + met + "(" + 1614 apnContext.getDependencyMet() +"))"); 1615 } 1616 if (apnContext.isReady()) { 1617 if (enabled && met) return; 1618 if (!enabled) { 1619 apnContext.setReason(Phone.REASON_DATA_DISABLED); 1620 } else { 1621 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET); 1622 } 1623 cleanup = true; 1624 } else { 1625 if (enabled && met) { 1626 if (apnContext.isEnabled()) { 1627 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET); 1628 } else { 1629 apnContext.setReason(Phone.REASON_DATA_ENABLED); 1630 } 1631 if (apnContext.getState() == State.FAILED) { 1632 apnContext.setState(State.IDLE); 1633 } 1634 trySetup = true; 1635 } 1636 } 1637 apnContext.setEnabled(enabled); 1638 apnContext.setDependencyMet(met); 1639 if (cleanup) cleanUpConnection(true, apnContext); 1640 if (trySetup) trySetupData(apnContext); 1641 } 1642 1643 private DataConnection checkForConnectionForApnContext(ApnContext apnContext) { 1644 // Loop through all apnContexts looking for one with a conn that satisfies this apnType 1645 String apnType = apnContext.getApnType(); 1646 for (ApnContext c : mApnContexts.values()) { 1647 DataConnection conn = c.getDataConnection(); 1648 if (conn != null) { 1649 ApnSetting apnSetting = c.getApnSetting(); 1650 if (apnSetting != null && apnSetting.canHandleType(apnType)) { 1651 if (DBG) { 1652 log("checkForConnectionForApnContext: apnContext=" + apnContext + 1653 " found conn=" + conn); 1654 } 1655 return conn; 1656 } 1657 } 1658 } 1659 if (DBG) log("checkForConnectionForApnContext: apnContext=" + apnContext + " NO conn"); 1660 return null; 1661 } 1662 1663 @Override 1664 protected void onEnableApn(int apnId, int enabled) { 1665 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId)); 1666 if (apnContext == null) { 1667 loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext"); 1668 return; 1669 } 1670 // TODO change our retry manager to use the appropriate numbers for the new APN 1671 if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState"); 1672 applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet()); 1673 } 1674 1675 @Override 1676 // TODO: We shouldnt need this. 1677 protected boolean onTrySetupData(String reason) { 1678 if (DBG) log("onTrySetupData: reason=" + reason); 1679 setupDataOnReadyApns(reason); 1680 return true; 1681 } 1682 1683 protected boolean onTrySetupData(ApnContext apnContext) { 1684 if (DBG) log("onTrySetupData: apnContext=" + apnContext); 1685 return trySetupData(apnContext); 1686 } 1687 1688 @Override 1689 protected void onRoamingOff() { 1690 if (DBG) log("onRoamingOff"); 1691 1692 if (getDataOnRoamingEnabled() == false) { 1693 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF); 1694 setupDataOnReadyApns(Phone.REASON_ROAMING_OFF); 1695 } else { 1696 notifyDataConnection(Phone.REASON_ROAMING_OFF); 1697 } 1698 } 1699 1700 @Override 1701 protected void onRoamingOn() { 1702 if (getDataOnRoamingEnabled()) { 1703 if (DBG) log("onRoamingOn: setup data on roaming"); 1704 setupDataOnReadyApns(Phone.REASON_ROAMING_ON); 1705 notifyDataConnection(Phone.REASON_ROAMING_ON); 1706 } else { 1707 if (DBG) log("onRoamingOn: Tear down data connection on roaming."); 1708 cleanUpAllConnections(true, Phone.REASON_ROAMING_ON); 1709 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON); 1710 } 1711 } 1712 1713 @Override 1714 protected void onRadioAvailable() { 1715 if (DBG) log("onRadioAvailable"); 1716 if (mPhone.getSimulatedRadioControl() != null) { 1717 // Assume data is connected on the simulator 1718 // FIXME this can be improved 1719 // setState(State.CONNECTED); 1720 notifyDataConnection(null); 1721 1722 log("onRadioAvailable: We're on the simulator; assuming data is connected"); 1723 } 1724 1725 if (mPhone.mIccRecords.getRecordsLoaded()) { 1726 notifyOffApnsOfAvailability(null); 1727 } 1728 1729 if (getOverallState() != State.IDLE) { 1730 cleanUpConnection(true, null); 1731 } 1732 } 1733 1734 @Override 1735 protected void onRadioOffOrNotAvailable() { 1736 // Make sure our reconnect delay starts at the initial value 1737 // next time the radio comes on 1738 1739 for (DataConnection dc : mDataConnections.values()) { 1740 dc.resetRetryCount(); 1741 } 1742 mReregisterOnReconnectFailure = false; 1743 1744 if (mPhone.getSimulatedRadioControl() != null) { 1745 // Assume data is connected on the simulator 1746 // FIXME this can be improved 1747 log("We're on the simulator; assuming radio off is meaningless"); 1748 } else { 1749 if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections"); 1750 cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF); 1751 } 1752 notifyOffApnsOfAvailability(null); 1753 } 1754 1755 @Override 1756 protected void onDataSetupComplete(AsyncResult ar) { 1757 1758 ApnContext apnContext = null; 1759 1760 if(ar.userObj instanceof ApnContext){ 1761 apnContext = (ApnContext)ar.userObj; 1762 } else { 1763 throw new RuntimeException("onDataSetupComplete: No apnContext"); 1764 } 1765 1766 if (isDataSetupCompleteOk(ar)) { 1767 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 1768 if (dcac == null) { 1769 throw new RuntimeException("onDataSetupCompete: No dcac"); 1770 } 1771 DataConnection dc = apnContext.getDataConnection(); 1772 1773 if (DBG) { 1774 log(String.format("onDataSetupComplete: success apn=%s", 1775 apnContext.getWaitingApns().get(0).apn)); 1776 } 1777 ApnSetting apn = apnContext.getApnSetting(); 1778 if (apn.proxy != null && apn.proxy.length() != 0) { 1779 try { 1780 String port = apn.port; 1781 if (TextUtils.isEmpty(port)) port = "8080"; 1782 ProxyProperties proxy = new ProxyProperties(apn.proxy, 1783 Integer.parseInt(port), null); 1784 dcac.setLinkPropertiesHttpProxySync(proxy); 1785 } catch (NumberFormatException e) { 1786 loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" + 1787 apn.port + "): " + e); 1788 } 1789 } 1790 1791 // everything is setup 1792 if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) { 1793 SystemProperties.set("gsm.defaultpdpcontext.active", "true"); 1794 if (canSetPreferApn && mPreferredApn == null) { 1795 if (DBG) log("onDataSetupComplete: PREFERED APN is null"); 1796 mPreferredApn = apnContext.getApnSetting(); 1797 if (mPreferredApn != null) { 1798 setPreferredApn(mPreferredApn.id); 1799 } 1800 } 1801 } else { 1802 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); 1803 } 1804 notifyDefaultData(apnContext); 1805 } else { 1806 String apnString; 1807 DataConnection.FailCause cause; 1808 1809 cause = (DataConnection.FailCause) (ar.result); 1810 if (DBG) { 1811 try { 1812 apnString = apnContext.getWaitingApns().get(0).apn; 1813 } catch (Exception e) { 1814 apnString = "<unknown>"; 1815 } 1816 log(String.format("onDataSetupComplete: error apn=%s cause=%s", apnString, cause)); 1817 } 1818 if (cause.isEventLoggable()) { 1819 // Log this failure to the Event Logs. 1820 int cid = getCellLocationId(); 1821 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL, 1822 cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType()); 1823 } 1824 1825 // Count permanent failures and remove the APN we just tried 1826 if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount(); 1827 1828 apnContext.removeNextWaitingApn(); 1829 if (DBG) { 1830 log(String.format("onDataSetupComplete: WaitingApns.size=%d" + 1831 " WaitingApnsPermFailureCountDown=%d", 1832 apnContext.getWaitingApns().size(), 1833 apnContext.getWaitingApnsPermFailCount())); 1834 } 1835 1836 // See if there are more APN's to try 1837 if (apnContext.getWaitingApns().isEmpty()) { 1838 if (apnContext.getWaitingApnsPermFailCount() == 0) { 1839 if (DBG) { 1840 log("onDataSetupComplete: All APN's had permanent failures, stop retrying"); 1841 } 1842 apnContext.setState(State.FAILED); 1843 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); 1844 1845 apnContext.setDataConnection(null); 1846 apnContext.setDataConnectionAc(null); 1847 if (DBG) { 1848 log("onDataSetupComplete: permanent error apn=%s" + apnString ); 1849 } 1850 } else { 1851 if (DBG) log("onDataSetupComplete: Not all permanent failures, retry"); 1852 // check to see if retry should be overridden for this failure. 1853 int retryOverride = -1; 1854 if (ar.exception instanceof DataConnection.CallSetupException) { 1855 retryOverride = 1856 ((DataConnection.CallSetupException)ar.exception).getRetryOverride(); 1857 } 1858 if (retryOverride == RILConstants.MAX_INT) { 1859 if (DBG) log("No retry is suggested."); 1860 } else { 1861 startDelayedRetry(cause, apnContext, retryOverride); 1862 } 1863 } 1864 } else { 1865 if (DBG) log("onDataSetupComplete: Try next APN"); 1866 apnContext.setState(State.SCANNING); 1867 // Wait a bit before trying the next APN, so that 1868 // we're not tying up the RIL command channel 1869 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); 1870 } 1871 } 1872 } 1873 1874 /** 1875 * Called when EVENT_DISCONNECT_DONE is received. 1876 */ 1877 @Override 1878 protected void onDisconnectDone(int connId, AsyncResult ar) { 1879 ApnContext apnContext = null; 1880 1881 if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE connId=" + connId); 1882 if (ar.userObj instanceof ApnContext) { 1883 apnContext = (ApnContext) ar.userObj; 1884 } else { 1885 loge("Invalid ar in onDisconnectDone"); 1886 return; 1887 } 1888 1889 apnContext.setState(State.IDLE); 1890 1891 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1892 1893 // if all data connection are gone, check whether Airplane mode request was 1894 // pending. 1895 if (isDisconnected()) { 1896 if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) { 1897 // Radio will be turned off. No need to retry data setup 1898 apnContext.setApnSetting(null); 1899 apnContext.setDataConnection(null); 1900 apnContext.setDataConnectionAc(null); 1901 return; 1902 } 1903 } 1904 1905 // If APN is still enabled, try to bring it back up automatically 1906 if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) { 1907 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); // TODO - what the heck? This shoudld go 1908 // Wait a bit before trying the next APN, so that 1909 // we're not tying up the RIL command channel. 1910 // This also helps in any external dependency to turn off the context. 1911 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); 1912 } else { 1913 apnContext.setApnSetting(null); 1914 apnContext.setDataConnection(null); 1915 apnContext.setDataConnectionAc(null); 1916 } 1917 } 1918 1919 protected void onPollPdp() { 1920 if (getOverallState() == State.CONNECTED) { 1921 // only poll when connected 1922 mPhone.mCM.getDataCallList(this.obtainMessage(EVENT_DATA_STATE_CHANGED)); 1923 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 1924 } 1925 } 1926 1927 @Override 1928 protected void onVoiceCallStarted() { 1929 if (DBG) log("onVoiceCallStarted"); 1930 if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { 1931 if (DBG) log("onVoiceCallStarted stop polling"); 1932 stopNetStatPoll(); 1933 notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 1934 } 1935 } 1936 1937 @Override 1938 protected void onVoiceCallEnded() { 1939 if (DBG) log("onVoiceCallEnded"); 1940 if (isConnected()) { 1941 if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { 1942 startNetStatPoll(); 1943 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 1944 } else { 1945 // clean slate after call end. 1946 resetPollStats(); 1947 } 1948 } else { 1949 // reset reconnect timer 1950 setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED); 1951 } 1952 } 1953 1954 @Override 1955 protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) { 1956 if (DBG) log("onCleanUpConnection"); 1957 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId)); 1958 if (apnContext != null) { 1959 apnContext.setReason(reason); 1960 cleanUpConnection(tearDown, apnContext); 1961 } 1962 } 1963 1964 protected boolean isConnected() { 1965 for (ApnContext apnContext : mApnContexts.values()) { 1966 if (apnContext.getState() == State.CONNECTED) { 1967 // At least one context is connected, return true 1968 return true; 1969 } 1970 } 1971 // There are not any contexts connected, return false 1972 return false; 1973 } 1974 1975 @Override 1976 public boolean isDisconnected() { 1977 for (ApnContext apnContext : mApnContexts.values()) { 1978 if (!apnContext.isDisconnected()) { 1979 // At least one context was not disconnected return false 1980 return false; 1981 } 1982 } 1983 // All contexts were disconnected so return true 1984 return true; 1985 } 1986 1987 @Override 1988 protected void notifyDataConnection(String reason) { 1989 if (DBG) log("notifyDataConnection: reason=" + reason); 1990 for (ApnContext apnContext : mApnContexts.values()) { 1991 if (apnContext.isReady()) { 1992 if (DBG) log("notifyDataConnection: type:"+apnContext.getApnType()); 1993 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(), 1994 apnContext.getApnType()); 1995 } 1996 } 1997 notifyOffApnsOfAvailability(reason); 1998 } 1999 2000 /** 2001 * Based on the sim operator numeric, create a list for all possible 2002 * Data Connections and setup the preferredApn. 2003 */ 2004 private void createAllApnList() { 2005 mAllApns = new ArrayList<ApnSetting>(); 2006 String operator = mPhone.mIccRecords.getOperatorNumeric(); 2007 if (operator != null) { 2008 String selection = "numeric = '" + operator + "'"; 2009 // query only enabled apn. 2010 // carrier_enabled : 1 means enabled apn, 0 disabled apn. 2011 selection += " and carrier_enabled = 1"; 2012 if (DBG) log("createAllApnList: selection=" + selection); 2013 2014 Cursor cursor = mPhone.getContext().getContentResolver().query( 2015 Telephony.Carriers.CONTENT_URI, null, selection, null, null); 2016 2017 if (cursor != null) { 2018 if (cursor.getCount() > 0) { 2019 mAllApns = createApnList(cursor); 2020 } 2021 cursor.close(); 2022 } 2023 } 2024 2025 if (mAllApns.isEmpty()) { 2026 if (DBG) log("createAllApnList: No APN found for carrier: " + operator); 2027 mPreferredApn = null; 2028 // TODO: What is the right behaviour? 2029 //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN); 2030 } else { 2031 mPreferredApn = getPreferredApn(); 2032 if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) { 2033 mPreferredApn = null; 2034 setPreferredApn(-1); 2035 } 2036 if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn); 2037 } 2038 if (DBG) log("createAllApnList: X mAllApns=" + mAllApns); 2039 } 2040 2041 /** Return the id for a new data connection */ 2042 private GsmDataConnection createDataConnection() { 2043 if (DBG) log("createDataConnection E"); 2044 2045 RetryManager rm = new RetryManager(); 2046 int id = mUniqueIdGenerator.getAndIncrement(); 2047 GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm); 2048 mDataConnections.put(id, conn); 2049 DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG); 2050 int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler()); 2051 if (status == AsyncChannel.STATUS_SUCCESSFUL) { 2052 mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac); 2053 } else { 2054 loge("createDataConnection: Could not connect to dcac.mDc=" + dcac.dataConnection + 2055 " status=" + status); 2056 } 2057 2058 // install reconnect intent filter for this data connection. 2059 IntentFilter filter = new IntentFilter(); 2060 filter.addAction(INTENT_RECONNECT_ALARM + '.' + id); 2061 mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone); 2062 2063 if (DBG) log("createDataConnection() X id=" + id); 2064 return conn; 2065 } 2066 2067 private void configureRetry(DataConnection dc, boolean forDefault) { 2068 if (dc == null) return; 2069 2070 if (!dc.configureRetry(getReryConfig(forDefault))) { 2071 if (forDefault) { 2072 if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) { 2073 // Should never happen, log an error and default to a simple linear sequence. 2074 loge("configureRetry: Could not configure using " + 2075 "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG); 2076 dc.configureRetry(20, 2000, 1000); 2077 } 2078 } else { 2079 if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) { 2080 // Should never happen, log an error and default to a simple sequence. 2081 loge("configureRetry: Could note configure using " + 2082 "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG); 2083 dc.configureRetry("max_retries=3, 333, 333, 333"); 2084 } 2085 } 2086 } 2087 } 2088 2089 private void destroyDataConnections() { 2090 if(mDataConnections != null) { 2091 if (DBG) log("destroyDataConnections: clear mDataConnectionList"); 2092 mDataConnections.clear(); 2093 } else { 2094 if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore"); 2095 } 2096 } 2097 2098 /** 2099 * Build a list of APNs to be used to create PDP's. 2100 * 2101 * @param requestedApnType 2102 * @return waitingApns list to be used to create PDP 2103 * error when waitingApns.isEmpty() 2104 */ 2105 private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) { 2106 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); 2107 2108 if (requestedApnType.equals(Phone.APN_TYPE_DUN)) { 2109 ApnSetting dun = fetchDunApn(); 2110 if (dun != null) { 2111 apnList.add(dun); 2112 if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList); 2113 return apnList; 2114 } 2115 } 2116 2117 String operator = mPhone.mIccRecords.getOperatorNumeric(); 2118 int radioTech = mPhone.getServiceState().getRadioTechnology(); 2119 2120 if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 2121 if (canSetPreferApn && mPreferredApn != null) { 2122 if (DBG) { 2123 log("buildWaitingApns: Preferred APN:" + operator + ":" 2124 + mPreferredApn.numeric + ":" + mPreferredApn); 2125 } 2126 if (mPreferredApn.numeric.equals(operator)) { 2127 if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) { 2128 apnList.add(mPreferredApn); 2129 if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList); 2130 return apnList; 2131 } else { 2132 if (DBG) log("buildWaitingApns: no preferred APN"); 2133 setPreferredApn(-1); 2134 mPreferredApn = null; 2135 } 2136 } else { 2137 if (DBG) log("buildWaitingApns: no preferred APN"); 2138 setPreferredApn(-1); 2139 mPreferredApn = null; 2140 } 2141 } 2142 } 2143 if (mAllApns != null) { 2144 for (ApnSetting apn : mAllApns) { 2145 if (apn.canHandleType(requestedApnType)) { 2146 if (apn.bearer == 0 || apn.bearer == radioTech) { 2147 if (DBG) log("apn info : " +apn.toString()); 2148 apnList.add(apn); 2149 } 2150 } 2151 } 2152 } else { 2153 loge("mAllApns is empty!"); 2154 } 2155 if (DBG) log("buildWaitingApns: X apnList=" + apnList); 2156 return apnList; 2157 } 2158 2159 private String apnListToString (ArrayList<ApnSetting> apns) { 2160 StringBuilder result = new StringBuilder(); 2161 for (int i = 0, size = apns.size(); i < size; i++) { 2162 result.append('[') 2163 .append(apns.get(i).toString()) 2164 .append(']'); 2165 } 2166 return result.toString(); 2167 } 2168 2169 private void startDelayedRetry(GsmDataConnection.FailCause cause, 2170 ApnContext apnContext, int retryOverride) { 2171 notifyNoData(cause, apnContext); 2172 reconnectAfterFail(cause, apnContext, retryOverride); 2173 } 2174 2175 private void setPreferredApn(int pos) { 2176 if (!canSetPreferApn) { 2177 return; 2178 } 2179 2180 ContentResolver resolver = mPhone.getContext().getContentResolver(); 2181 resolver.delete(PREFERAPN_URI, null, null); 2182 2183 if (pos >= 0) { 2184 ContentValues values = new ContentValues(); 2185 values.put(APN_ID, pos); 2186 resolver.insert(PREFERAPN_URI, values); 2187 } 2188 } 2189 2190 private ApnSetting getPreferredApn() { 2191 if (mAllApns.isEmpty()) { 2192 return null; 2193 } 2194 2195 Cursor cursor = mPhone.getContext().getContentResolver().query( 2196 PREFERAPN_URI, new String[] { "_id", "name", "apn" }, 2197 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 2198 2199 if (cursor != null) { 2200 canSetPreferApn = true; 2201 } else { 2202 canSetPreferApn = false; 2203 } 2204 2205 if (canSetPreferApn && cursor.getCount() > 0) { 2206 int pos; 2207 cursor.moveToFirst(); 2208 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); 2209 for(ApnSetting p:mAllApns) { 2210 if (p.id == pos && p.canHandleType(mRequestedApnType)) { 2211 cursor.close(); 2212 return p; 2213 } 2214 } 2215 } 2216 2217 if (cursor != null) { 2218 cursor.close(); 2219 } 2220 2221 return null; 2222 } 2223 2224 @Override 2225 public void handleMessage (Message msg) { 2226 if (DBG) log("handleMessage msg=" + msg); 2227 2228 if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) { 2229 loge("handleMessage: Ignore GSM msgs since GSM phone is inactive"); 2230 return; 2231 } 2232 2233 switch (msg.what) { 2234 case EVENT_RECORDS_LOADED: 2235 onRecordsLoaded(); 2236 break; 2237 2238 case EVENT_DATA_CONNECTION_DETACHED: 2239 onDataConnectionDetached(); 2240 break; 2241 2242 case EVENT_DATA_CONNECTION_ATTACHED: 2243 onDataConnectionAttached(); 2244 break; 2245 2246 case EVENT_DATA_STATE_CHANGED: 2247 onDataStateChanged((AsyncResult) msg.obj); 2248 break; 2249 2250 case EVENT_POLL_PDP: 2251 onPollPdp(); 2252 break; 2253 2254 case EVENT_START_NETSTAT_POLL: 2255 startNetStatPoll(); 2256 break; 2257 2258 case EVENT_START_RECOVERY: 2259 doRecovery(); 2260 break; 2261 2262 case EVENT_APN_CHANGED: 2263 onApnChanged(); 2264 break; 2265 2266 case EVENT_PS_RESTRICT_ENABLED: 2267 /** 2268 * We don't need to explicitly to tear down the PDP context 2269 * when PS restricted is enabled. The base band will deactive 2270 * PDP context and notify us with PDP_CONTEXT_CHANGED. 2271 * But we should stop the network polling and prevent reset PDP. 2272 */ 2273 if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); 2274 stopNetStatPoll(); 2275 mIsPsRestricted = true; 2276 break; 2277 2278 case EVENT_PS_RESTRICT_DISABLED: 2279 /** 2280 * When PS restrict is removed, we need setup PDP connection if 2281 * PDP connection is down. 2282 */ 2283 if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); 2284 mIsPsRestricted = false; 2285 if (isConnected()) { 2286 startNetStatPoll(); 2287 } else { 2288 // TODO: Should all PDN states be checked to fail? 2289 if (mState == State.FAILED) { 2290 cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED); 2291 resetAllRetryCounts(); 2292 mReregisterOnReconnectFailure = false; 2293 } 2294 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT); 2295 } 2296 break; 2297 case EVENT_TRY_SETUP_DATA: 2298 if (msg.obj instanceof ApnContext) { 2299 onTrySetupData((ApnContext)msg.obj); 2300 } else if (msg.obj instanceof String) { 2301 onTrySetupData((String)msg.obj); 2302 } else { 2303 loge("EVENT_TRY_SETUP request w/o apnContext or String"); 2304 } 2305 break; 2306 2307 case EVENT_CLEAN_UP_CONNECTION: 2308 boolean tearDown = (msg.arg1 == 0) ? false : true; 2309 if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown); 2310 if (msg.obj instanceof ApnContext) { 2311 cleanUpConnection(tearDown, (ApnContext)msg.obj); 2312 } else { 2313 loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context"); 2314 } 2315 break; 2316 default: 2317 // handle the message in the super class DataConnectionTracker 2318 super.handleMessage(msg); 2319 break; 2320 } 2321 } 2322 2323 protected int getApnProfileID(String apnType) { 2324 if (TextUtils.equals(apnType, Phone.APN_TYPE_IMS)) { 2325 return RILConstants.DATA_PROFILE_IMS; 2326 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_FOTA)) { 2327 return RILConstants.DATA_PROFILE_FOTA; 2328 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_CBS)) { 2329 return RILConstants.DATA_PROFILE_CBS; 2330 } else { 2331 return RILConstants.DATA_PROFILE_DEFAULT; 2332 } 2333 } 2334 2335 private int getCellLocationId() { 2336 int cid = -1; 2337 CellLocation loc = mPhone.getCellLocation(); 2338 2339 if (loc != null) { 2340 if (loc instanceof GsmCellLocation) { 2341 cid = ((GsmCellLocation)loc).getCid(); 2342 } else if (loc instanceof CdmaCellLocation) { 2343 cid = ((CdmaCellLocation)loc).getBaseStationId(); 2344 } 2345 } 2346 return cid; 2347 } 2348 2349 @Override 2350 protected void log(String s) { 2351 Log.d(LOG_TAG, "[GsmDCT] "+ s); 2352 } 2353 2354 @Override 2355 protected void loge(String s) { 2356 Log.e(LOG_TAG, "[GsmDCT] " + s); 2357 } 2358 } 2359