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