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