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