Home | History | Annotate | Download | only in gsm
      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