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