Change DcTracker to not connect or retry if detached. DO NOT MERGE.
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / dataconnection / DcTracker.java
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.dataconnection;
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.content.res.Resources;
27 import android.database.ContentObserver;
28 import android.database.Cursor;
29 import android.net.ConnectivityManager;
30 import android.net.LinkCapabilities;
31 import android.net.LinkProperties;
32 import android.net.NetworkConfig;
33 import android.net.NetworkUtils;
34 import android.net.ProxyProperties;
35 import android.net.Uri;
36 import android.os.AsyncResult;
37 import android.os.Message;
38 import android.os.Messenger;
39 import android.os.SystemClock;
40 import android.os.SystemProperties;
41 import android.provider.Settings;
42 import android.provider.Telephony;
43 import android.telephony.CellLocation;
44 import android.telephony.ServiceState;
45 import android.telephony.TelephonyManager;
46 import android.telephony.cdma.CdmaCellLocation;
47 import android.telephony.gsm.GsmCellLocation;
48 import android.text.TextUtils;
49 import android.util.EventLog;
50 import android.telephony.Rlog;
51
52 import com.android.internal.telephony.Phone;
53 import com.android.internal.telephony.PhoneBase;
54 import com.android.internal.telephony.DctConstants;
55 import com.android.internal.telephony.EventLogTags;
56 import com.android.internal.telephony.gsm.GSMPhone;
57 import com.android.internal.telephony.PhoneConstants;
58 import com.android.internal.telephony.RILConstants;
59 import com.android.internal.telephony.uicc.IccRecords;
60 import com.android.internal.telephony.uicc.UiccController;
61 import com.android.internal.util.AsyncChannel;
62
63 import java.io.FileDescriptor;
64 import java.io.PrintWriter;
65 import java.util.ArrayList;
66 import java.util.concurrent.atomic.AtomicBoolean;
67 import java.util.HashMap;
68
69 /**
70  * {@hide}
71  */
72 public final class DcTracker extends DcTrackerBase {
73     protected final String LOG_TAG = "DCT";
74
75     /**
76      * Handles changes to the APN db.
77      */
78     private class ApnChangeObserver extends ContentObserver {
79         public ApnChangeObserver () {
80             super(mDataConnectionTracker);
81         }
82
83         @Override
84         public void onChange(boolean selfChange) {
85             sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
86         }
87     }
88
89     //***** Instance Variables
90
91     private boolean mReregisterOnReconnectFailure = false;
92
93
94     //***** Constants
95
96     // Used by puppetmaster/*/radio_stress.py
97     private static final String PUPPET_MASTER_RADIO_STRESS_TEST = "gsm.defaultpdpcontext.active";
98
99     private static final int POLL_PDP_MILLIS = 5 * 1000;
100
101     static final Uri PREFERAPN_NO_UPDATE_URI =
102                         Uri.parse("content://telephony/carriers/preferapn_no_update");
103     static final String APN_ID = "apn_id";
104
105     private boolean mCanSetPreferApn = false;
106
107     private AtomicBoolean mAttached = new AtomicBoolean(false);
108
109     /** Watches for changes to the APN db. */
110     private ApnChangeObserver mApnObserver;
111
112     //***** Constructor
113
114     public DcTracker(PhoneBase p) {
115         super(p);
116         if (DBG) log("GsmDCT.constructor");
117         p.mCi.registerForAvailable (this, DctConstants.EVENT_RADIO_AVAILABLE, null);
118         p.mCi.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE,
119                 null);
120         p.mCi.registerForDataNetworkStateChanged (this, DctConstants.EVENT_DATA_STATE_CHANGED,
121                 null);
122         p.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED,
123                 null);
124         p.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED,
125                 null);
126         p.getServiceStateTracker().registerForDataConnectionAttached(this,
127                 DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
128         p.getServiceStateTracker().registerForDataConnectionDetached(this,
129                 DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
130         p.getServiceStateTracker().registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
131         p.getServiceStateTracker().registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF,
132                 null);
133         p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
134                 DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
135         p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
136                 DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
137
138         mDataConnectionTracker = this;
139
140         mApnObserver = new ApnChangeObserver();
141         p.getContext().getContentResolver().registerContentObserver(
142                 Telephony.Carriers.CONTENT_URI, true, mApnObserver);
143
144         initApnContexts();
145
146         for (ApnContext apnContext : mApnContexts.values()) {
147             // Register the reconnect and restart actions.
148             IntentFilter filter = new IntentFilter();
149             filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
150             filter.addAction(INTENT_RESTART_TRYSETUP_ALARM + '.' + apnContext.getApnType());
151             mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
152         }
153
154         ConnectivityManager cm = (ConnectivityManager)p.getContext().getSystemService(
155                 Context.CONNECTIVITY_SERVICE);
156         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE, new Messenger(this));
157         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_MMS, new Messenger(this));
158         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_SUPL, new Messenger(this));
159         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_DUN, new Messenger(this));
160         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_HIPRI, new Messenger(this));
161         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_FOTA, new Messenger(this));
162         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_IMS, new Messenger(this));
163         cm.supplyMessenger(ConnectivityManager.TYPE_MOBILE_CBS, new Messenger(this));
164     }
165
166     @Override
167     public void dispose() {
168         if (DBG) log("GsmDCT.dispose");
169         cleanUpAllConnections(true, null);
170
171         super.dispose();
172
173         //Unregister for all events
174         mPhone.mCi.unregisterForAvailable(this);
175         mPhone.mCi.unregisterForOffOrNotAvailable(this);
176         IccRecords r = mIccRecords.get();
177         if (r != null) { r.unregisterForRecordsLoaded(this);}
178         mPhone.mCi.unregisterForDataNetworkStateChanged(this);
179         mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
180         mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
181         mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
182         mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
183         mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
184         mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
185         mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
186         mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
187
188         mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
189         mApnContexts.clear();
190
191         destroyDataConnections();
192     }
193
194     @Override
195     public boolean isApnTypeActive(String type) {
196         ApnContext apnContext = mApnContexts.get(type);
197         if (apnContext == null) return false;
198
199         return (apnContext.getDcAc() != null);
200     }
201
202     @Override
203     public boolean isDataPossible(String apnType) {
204         ApnContext apnContext = mApnContexts.get(apnType);
205         if (apnContext == null) {
206             return false;
207         }
208         boolean apnContextIsEnabled = apnContext.isEnabled();
209         DctConstants.State apnContextState = apnContext.getState();
210         boolean apnTypePossible = !(apnContextIsEnabled &&
211                 (apnContextState == DctConstants.State.FAILED));
212         boolean dataAllowed = isDataAllowed();
213         boolean possible = dataAllowed && apnTypePossible;
214
215         if (VDBG) {
216             log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
217                     "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
218                     apnType, possible, dataAllowed, apnTypePossible,
219                     apnContextIsEnabled, apnContextState));
220         }
221         return possible;
222     }
223
224     @Override
225     protected void finalize() {
226         if(DBG) log("finalize");
227     }
228
229     private ApnContext addApnContext(String type, NetworkConfig networkConfig) {
230         ApnContext apnContext = new ApnContext(type, LOG_TAG);
231         apnContext.setDependencyMet(networkConfig.dependencyMet);
232         mApnContexts.put(type, apnContext);
233         return apnContext;
234     }
235
236     protected void initApnContexts() {
237         boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true);
238         // Load device network attributes from resources
239         String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
240                 com.android.internal.R.array.networkAttributes);
241         for (String networkConfigString : networkConfigStrings) {
242             NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
243             ApnContext apnContext = null;
244
245             switch (networkConfig.type) {
246             case ConnectivityManager.TYPE_MOBILE:
247                 apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig);
248                 apnContext.setEnabled(defaultEnabled);
249                 break;
250             case ConnectivityManager.TYPE_MOBILE_MMS:
251                 apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig);
252                 break;
253             case ConnectivityManager.TYPE_MOBILE_SUPL:
254                 apnContext = addApnContext(PhoneConstants.APN_TYPE_SUPL, networkConfig);
255                 break;
256             case ConnectivityManager.TYPE_MOBILE_DUN:
257                 apnContext = addApnContext(PhoneConstants.APN_TYPE_DUN, networkConfig);
258                 break;
259             case ConnectivityManager.TYPE_MOBILE_HIPRI:
260                 // Hipri is configured the same as default
261                 apnContext = addApnContext(PhoneConstants.APN_TYPE_HIPRI, networkConfig);
262                 apnContext.setEnabled(defaultEnabled);
263                 continue;
264             case ConnectivityManager.TYPE_MOBILE_FOTA:
265                 apnContext = addApnContext(PhoneConstants.APN_TYPE_FOTA, networkConfig);
266                 break;
267             case ConnectivityManager.TYPE_MOBILE_IMS:
268                 apnContext = addApnContext(PhoneConstants.APN_TYPE_IMS, networkConfig);
269                 break;
270             case ConnectivityManager.TYPE_MOBILE_CBS:
271                 apnContext = addApnContext(PhoneConstants.APN_TYPE_CBS, networkConfig);
272                 break;
273             default:
274                 log("initApnContexts: skipping unknown type=" + networkConfig.type);
275                 continue;
276             }
277         }
278     }
279
280     @Override
281     public LinkProperties getLinkProperties(String apnType) {
282         ApnContext apnContext = mApnContexts.get(apnType);
283         if (apnContext != null) {
284             DcAsyncChannel dcac = apnContext.getDcAc();
285             if (dcac != null) {
286                 if (DBG) log("return link properites for " + apnType);
287                 return dcac.getLinkPropertiesSync();
288             }
289         }
290         if (DBG) log("return new LinkProperties");
291         return new LinkProperties();
292     }
293
294     @Override
295     public LinkCapabilities getLinkCapabilities(String apnType) {
296         ApnContext apnContext = mApnContexts.get(apnType);
297         if (apnContext!=null) {
298             DcAsyncChannel dataConnectionAc = apnContext.getDcAc();
299             if (dataConnectionAc != null) {
300                 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
301                 return dataConnectionAc.getLinkCapabilitiesSync();
302             }
303         }
304         if (DBG) log("return new LinkCapabilities");
305         return new LinkCapabilities();
306     }
307
308     @Override
309     // Return all active apn types
310     public String[] getActiveApnTypes() {
311         if (DBG) log("get all active apn types");
312         ArrayList<String> result = new ArrayList<String>();
313
314         for (ApnContext apnContext : mApnContexts.values()) {
315             if (mAttached.get() && apnContext.isReady()) {
316                 result.add(apnContext.getApnType());
317             }
318         }
319
320         return result.toArray(new String[0]);
321     }
322
323     @Override
324     // Return active apn of specific apn type
325     public String getActiveApnString(String apnType) {
326         if (VDBG) log( "get active apn string for type:" + apnType);
327         ApnContext apnContext = mApnContexts.get(apnType);
328         if (apnContext != null) {
329             ApnSetting apnSetting = apnContext.getApnSetting();
330             if (apnSetting != null) {
331                 return apnSetting.apn;
332             }
333         }
334         return null;
335     }
336
337     @Override
338     public boolean isApnTypeEnabled(String apnType) {
339         ApnContext apnContext = mApnContexts.get(apnType);
340         if (apnContext == null) {
341             return false;
342         }
343         return apnContext.isEnabled();
344     }
345
346     @Override
347     protected void setState(DctConstants.State s) {
348         if (DBG) log("setState should not be used in GSM" + s);
349     }
350
351     // Return state of specific apn type
352     @Override
353     public DctConstants.State getState(String apnType) {
354         ApnContext apnContext = mApnContexts.get(apnType);
355         if (apnContext != null) {
356             return apnContext.getState();
357         }
358         return DctConstants.State.FAILED;
359     }
360
361     // Return state of overall
362     @Override
363     public DctConstants.State getOverallState() {
364         boolean isConnecting = false;
365         boolean isFailed = true; // All enabled Apns should be FAILED.
366         boolean isAnyEnabled = false;
367
368         for (ApnContext apnContext : mApnContexts.values()) {
369             if (apnContext.isEnabled()) {
370                 isAnyEnabled = true;
371                 switch (apnContext.getState()) {
372                 case CONNECTED:
373                 case DISCONNECTING:
374                     if (DBG) log("overall state is CONNECTED");
375                     return DctConstants.State.CONNECTED;
376                 case RETRYING:
377                 case CONNECTING:
378                     isConnecting = true;
379                     isFailed = false;
380                     break;
381                 case IDLE:
382                 case SCANNING:
383                     isFailed = false;
384                     break;
385                 default:
386                     isAnyEnabled = true;
387                     break;
388                 }
389             }
390         }
391
392         if (!isAnyEnabled) { // Nothing enabled. return IDLE.
393             if (DBG) log( "overall state is IDLE");
394             return DctConstants.State.IDLE;
395         }
396
397         if (isConnecting) {
398             if (DBG) log( "overall state is CONNECTING");
399             return DctConstants.State.CONNECTING;
400         } else if (!isFailed) {
401             if (DBG) log( "overall state is IDLE");
402             return DctConstants.State.IDLE;
403         } else {
404             if (DBG) log( "overall state is FAILED");
405             return DctConstants.State.FAILED;
406         }
407     }
408
409     /**
410      * Ensure that we are connected to an APN of the specified type.
411      *
412      * @param apnType the APN type
413      * @return Success is indicated by {@code PhoneConstants.APN_ALREADY_ACTIVE} or
414      *         {@code PhoneConstants.APN_REQUEST_STARTED}. In the latter case, a
415      *         broadcast will be sent by the ConnectivityManager when a
416      *         connection to the APN has been established.
417      */
418     @Override
419     public synchronized int enableApnType(String apnType) {
420         ApnContext apnContext = mApnContexts.get(apnType);
421         if (apnContext == null || !isApnTypeAvailable(apnType)) {
422             if (DBG) log("enableApnType: " + apnType + " is type not available");
423             return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
424         }
425
426         // If already active, return
427         if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
428
429         if (apnContext.getState() == DctConstants.State.CONNECTED) {
430             if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
431             return PhoneConstants.APN_ALREADY_ACTIVE;
432         }
433         setEnabled(apnTypeToId(apnType), true);
434         if (DBG) {
435             log("enableApnType: new apn request for type " + apnType +
436                     " return APN_REQUEST_STARTED");
437         }
438         return PhoneConstants.APN_REQUEST_STARTED;
439     }
440
441     @Override
442     public synchronized int disableApnType(String type) {
443         if (DBG) log("disableApnType:" + type);
444         ApnContext apnContext = mApnContexts.get(type);
445
446         if (apnContext != null) {
447             setEnabled(apnTypeToId(type), false);
448             if (apnContext.getState() != DctConstants.State.IDLE && apnContext.getState()
449                     != DctConstants.State.FAILED) {
450                 if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
451                 return PhoneConstants.APN_REQUEST_STARTED;
452             } else {
453                 if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
454                 return PhoneConstants.APN_ALREADY_INACTIVE;
455             }
456
457         } else {
458             if (DBG) {
459                 log("disableApnType: no apn context was found, return APN_REQUEST_FAILED");
460             }
461             return PhoneConstants.APN_REQUEST_FAILED;
462         }
463     }
464
465     @Override
466     protected boolean isApnTypeAvailable(String type) {
467         if (type.equals(PhoneConstants.APN_TYPE_DUN) && fetchDunApn() != null) {
468             return true;
469         }
470
471         if (mAllApnSettings != null) {
472             for (ApnSetting apn : mAllApnSettings) {
473                 if (apn.canHandleType(type)) {
474                     return true;
475                 }
476             }
477         }
478         return false;
479     }
480
481     /**
482      * Report on whether data connectivity is enabled for any APN.
483      * @return {@code false} if data connectivity has been explicitly disabled,
484      * {@code true} otherwise.
485      */
486     @Override
487     public boolean getAnyDataEnabled() {
488         synchronized (mDataEnabledLock) {
489             if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false;
490             for (ApnContext apnContext : mApnContexts.values()) {
491                 // Make sure we don't have a context that is going down
492                 // and is explicitly disabled.
493                 if (isDataAllowed(apnContext)) {
494                     return true;
495                 }
496             }
497             return false;
498         }
499     }
500
501     private boolean isDataAllowed(ApnContext apnContext) {
502         return apnContext.isReady() && isDataAllowed();
503     }
504
505     //****** Called from ServiceStateTracker
506     /**
507      * Invoked when ServiceStateTracker observes a transition from GPRS
508      * attach to detach.
509      */
510     protected void onDataConnectionDetached() {
511         /*
512          * We presently believe it is unnecessary to tear down the PDP context
513          * when GPRS detaches, but we should stop the network polling.
514          */
515         if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
516         stopNetStatPoll();
517         stopDataStallAlarm();
518         notifyDataConnection(Phone.REASON_DATA_DETACHED);
519         mAttached.set(false);
520     }
521
522     private void onDataConnectionAttached() {
523         if (DBG) log("onDataConnectionAttached");
524         if (getOverallState() == DctConstants.State.CONNECTED) {
525             if (DBG) log("onDataConnectionAttached: start polling notify attached");
526             startNetStatPoll();
527             startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
528             notifyDataConnection(Phone.REASON_DATA_ATTACHED);
529         } else {
530             // update APN availability so that APN can be enabled.
531             notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
532         }
533         mAutoAttachOnCreation = true;
534         mAttached.set(true);
535         setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
536     }
537
538     @Override
539     protected boolean isDataAllowed() {
540         final boolean internalDataEnabled;
541         synchronized (mDataEnabledLock) {
542             internalDataEnabled = mInternalDataEnabled;
543         }
544
545         int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
546         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
547         IccRecords r = mIccRecords.get();
548         boolean recordsLoaded = (r != null) ? r.getRecordsLoaded() : false;
549
550         boolean allowed =
551                     (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
552                     recordsLoaded &&
553                     (mPhone.getState() == PhoneConstants.State.IDLE ||
554                      mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
555                     internalDataEnabled &&
556                     (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
557                     !mIsPsRestricted &&
558                     desiredPowerState;
559         if (!allowed && DBG) {
560             String reason = "";
561             if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
562                 reason += " - gprs= " + gprsState;
563             }
564             if (!recordsLoaded) reason += " - SIM not loaded";
565             if (mPhone.getState() != PhoneConstants.State.IDLE &&
566                     !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
567                 reason += " - PhoneState= " + mPhone.getState();
568                 reason += " - Concurrent voice and data not allowed";
569             }
570             if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
571             if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
572                 reason += " - Roaming and data roaming not enabled";
573             }
574             if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
575             if (!desiredPowerState) reason += " - desiredPowerState= false";
576             if (DBG) log("isDataAllowed: not allowed due to" + reason);
577         }
578         return allowed;
579     }
580
581     private void setupDataOnConnectableApns(String reason) {
582         for (ApnContext apnContext : mApnContexts.values()) {
583             if (apnContext.getState() == DctConstants.State.FAILED) {
584                 apnContext.setState(DctConstants.State.IDLE);
585             }
586             if (apnContext.isConnectable()) {
587                 log("setupDataOnConnectableApns: isConnectable() call trySetupData");
588                 apnContext.setReason(reason);
589                 trySetupData(apnContext);
590             }
591         }
592     }
593
594     private boolean trySetupData(String reason, String type) {
595         if (DBG) {
596             log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason)
597                     + " isPsRestricted=" + mIsPsRestricted);
598         }
599
600         if (type == null) {
601             type = PhoneConstants.APN_TYPE_DEFAULT;
602         }
603
604         ApnContext apnContext = mApnContexts.get(type);
605
606         if (apnContext == null ){
607             if (DBG) log("trySetupData new apn context for type:" + type);
608             apnContext = new ApnContext(type, LOG_TAG);
609             mApnContexts.put(type, apnContext);
610         }
611         apnContext.setReason(reason);
612
613         return trySetupData(apnContext);
614     }
615
616     private boolean trySetupData(ApnContext apnContext) {
617         if (DBG) {
618             log("trySetupData for type:" + apnContext.getApnType() +
619                     " due to " + apnContext.getReason() + " apnContext=" + apnContext);
620             log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
621         }
622
623         if (mPhone.getSimulatedRadioControl() != null) {
624             // Assume data is connected on the simulator
625             // FIXME  this can be improved
626             apnContext.setState(DctConstants.State.CONNECTED);
627             mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
628
629             log("trySetupData: X We're on the simulator; assuming connected retValue=true");
630             return true;
631         }
632
633         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
634
635         if (mAttached.get() && apnContext.isConnectable() &&
636                 isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) {
637             if (apnContext.getState() == DctConstants.State.FAILED) {
638                 if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable");
639                 apnContext.setState(DctConstants.State.IDLE);
640             }
641             if (apnContext.getState() == DctConstants.State.IDLE) {
642                 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
643                 if (waitingApns.isEmpty()) {
644                     notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
645                     notifyOffApnsOfAvailability(apnContext.getReason());
646                     if (DBG) log("trySetupData: X No APN found retValue=false");
647                     return false;
648                 } else {
649                     apnContext.setWaitingApns(waitingApns);
650                     if (DBG) {
651                         log ("trySetupData: Create from mAllApnSettings : "
652                                     + apnListToString(mAllApnSettings));
653                     }
654                 }
655             }
656
657             if (DBG) {
658                 log("trySetupData: call setupData, waitingApns : "
659                         + apnListToString(apnContext.getWaitingApns()));
660             }
661             boolean retValue = setupData(apnContext);
662             notifyOffApnsOfAvailability(apnContext.getReason());
663
664             if (DBG) log("trySetupData: X retValue=" + retValue);
665             return retValue;
666         } else {
667             if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
668                     && apnContext.isConnectable()) {
669                 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
670             }
671             notifyOffApnsOfAvailability(apnContext.getReason());
672             if (DBG) log ("trySetupData: X apnContext not 'ready' retValue=false");
673             return false;
674         }
675     }
676
677     @Override
678     // Disabled apn's still need avail/unavail notificiations - send them out
679     protected void notifyOffApnsOfAvailability(String reason) {
680         for (ApnContext apnContext : mApnContexts.values()) {
681             if (!mAttached.get() || !apnContext.isReady()) {
682                 if (VDBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
683                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
684                                             apnContext.getApnType(),
685                                             PhoneConstants.DataState.DISCONNECTED);
686             } else {
687                 if (VDBG) {
688                     log("notifyOffApnsOfAvailability skipped apn due to attached && isReady " +
689                             apnContext.toString());
690                 }
691             }
692         }
693     }
694
695     /**
696      * If tearDown is true, this only tears down a CONNECTED session. Presently,
697      * there is no mechanism for abandoning an CONNECTING session,
698      * but would likely involve cancelling pending async requests or
699      * setting a flag or new state to ignore them when they came in
700      * @param tearDown true if the underlying DataConnection should be
701      * disconnected.
702      * @param reason reason for the clean up.
703      */
704     protected void cleanUpAllConnections(boolean tearDown, String reason) {
705         if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
706
707         for (ApnContext apnContext : mApnContexts.values()) {
708             apnContext.setReason(reason);
709             cleanUpConnection(tearDown, apnContext);
710         }
711
712         stopNetStatPoll();
713         stopDataStallAlarm();
714
715         // TODO: Do we need mRequestedApnType?
716         mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
717     }
718
719     /**
720      * Cleanup all connections.
721      *
722      * TODO: Cleanup only a specified connection passed as a parameter.
723      *       Also, make sure when you clean up a conn, if it is last apply
724      *       logic as though it is cleanupAllConnections
725      *
726      * @param cause for the clean up.
727      */
728
729     @Override
730     protected void onCleanUpAllConnections(String cause) {
731         cleanUpAllConnections(true, cause);
732     }
733
734     private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
735
736         if (apnContext == null) {
737             if (DBG) log("cleanUpConnection: apn context is null");
738             return;
739         }
740
741         DcAsyncChannel dcac = apnContext.getDcAc();
742         if (DBG) {
743             log("cleanUpConnection: E tearDown=" + tearDown + " reason=" + apnContext.getReason() +
744                     " apnContext=" + apnContext);
745         }
746         if (tearDown) {
747             if (apnContext.isDisconnected()) {
748                 // The request is tearDown and but ApnContext is not connected.
749                 // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
750                 apnContext.setState(DctConstants.State.IDLE);
751                 if (!apnContext.isReady()) {
752                     if (dcac != null) {
753                         dcac.tearDown(apnContext, "", null);
754                     }
755                     apnContext.setDataConnectionAc(null);
756                 }
757             } else {
758                 // Connection is still there. Try to clean up.
759                 if (dcac != null) {
760                     if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
761                         boolean disconnectAll = false;
762                         if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
763                             ApnSetting dunSetting = fetchDunApn();
764                             if (dunSetting != null &&
765                                     dunSetting.equals(apnContext.getApnSetting())) {
766                                 if (DBG) log("tearing down dedicated DUN connection");
767                                 // we need to tear it down - we brought it up just for dun and
768                                 // other people are camped on it and now dun is done.  We need
769                                 // to stop using it and let the normal apn list get used to find
770                                 // connections for the remaining desired connections
771                                 disconnectAll = true;
772                             }
773                         }
774                         if (DBG) {
775                             log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :""));
776                         }
777                         Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
778                         if (disconnectAll) {
779                             apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
780                         } else {
781                             apnContext.getDcAc()
782                                 .tearDown(apnContext, apnContext.getReason(), msg);
783                         }
784                         apnContext.setState(DctConstants.State.DISCONNECTING);
785                     }
786                 } else {
787                     // apn is connected but no reference to dcac.
788                     // Should not be happen, but reset the state in case.
789                     apnContext.setState(DctConstants.State.IDLE);
790                     mPhone.notifyDataConnection(apnContext.getReason(),
791                                                 apnContext.getApnType());
792                 }
793             }
794         } else {
795             // force clean up the data connection.
796             if (dcac != null) dcac.reqReset();
797             apnContext.setState(DctConstants.State.IDLE);
798             mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
799             apnContext.setDataConnectionAc(null);
800         }
801
802         // Make sure reconnection alarm is cleaned up if there is no ApnContext
803         // associated to the connection.
804         if (dcac != null) {
805             cancelReconnectAlarm(apnContext);
806         }
807         if (DBG) {
808             log("cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason() +
809                     " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
810         }
811     }
812
813     /**
814      * Cancels the alarm associated with apnContext.
815      *
816      * @param apnContext on which the alarm should be stopped.
817      */
818     private void cancelReconnectAlarm(ApnContext apnContext) {
819         if (apnContext == null) return;
820
821         PendingIntent intent = apnContext.getReconnectIntent();
822
823         if (intent != null) {
824                 AlarmManager am =
825                     (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
826                 am.cancel(intent);
827                 apnContext.setReconnectIntent(null);
828         }
829     }
830
831     /**
832      * @param types comma delimited list of APN types
833      * @return array of APN types
834      */
835     private String[] parseTypes(String types) {
836         String[] result;
837         // If unset, set to DEFAULT.
838         if (types == null || types.equals("")) {
839             result = new String[1];
840             result[0] = PhoneConstants.APN_TYPE_ALL;
841         } else {
842             result = types.split(",");
843         }
844         return result;
845     }
846
847    private boolean imsiMatches(String imsiDB, String imsiSIM) {
848         // Note: imsiDB value has digit number or 'x' character for seperating USIM information
849         // for MVNO operator. And then digit number is matched at same order and 'x' character
850         // could replace by any digit number.
851         // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
852         //     that means first 6 digits, 8th and 9th digit
853         //     should be set in USIM for GG Operator.
854         int len = imsiDB.length();
855         int idxCompare = 0;
856
857         if (len <= 0) return false;
858         if (len > imsiSIM.length()) return false;
859
860         for (int idx=0; idx<len; idx++) {
861             char c = imsiDB.charAt(idx);
862             if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
863                 continue;
864             } else {
865                 return false;
866             }
867         }
868         return true;
869     }
870
871     private boolean mvnoMatches(IccRecords r, String mvno_type, String mvno_match_data) {
872         if (mvno_type.equalsIgnoreCase("spn")) {
873             if ((r.getServiceProviderName() != null) &&
874                     r.getServiceProviderName().equalsIgnoreCase(mvno_match_data)) {
875                 return true;
876             }
877         } else if (mvno_type.equalsIgnoreCase("imsi")) {
878             String imsiSIM = r.getIMSI();
879             if ((imsiSIM != null) && imsiMatches(mvno_match_data, imsiSIM)) {
880                 return true;
881             }
882         } else if (mvno_type.equalsIgnoreCase("gid")) {
883             String gid1 = r.getGid1();
884             if ((gid1 != null) && gid1.substring(0,
885                     mvno_match_data.length()).equalsIgnoreCase(mvno_match_data)) {
886                 return true;
887             }
888         }
889         return false;
890     }
891
892     private ApnSetting makeApnSetting(Cursor cursor) {
893         String[] types = parseTypes(
894                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
895         ApnSetting apn = new ApnSetting(
896                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
897                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
898                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
899                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
900                 NetworkUtils.trimV4AddrZeros(
901                         cursor.getString(
902                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
903                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
904                 NetworkUtils.trimV4AddrZeros(
905                         cursor.getString(
906                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
907                 NetworkUtils.trimV4AddrZeros(
908                         cursor.getString(
909                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
910                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
911                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
912                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
913                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
914                 types,
915                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
916                 cursor.getString(cursor.getColumnIndexOrThrow(
917                         Telephony.Carriers.ROAMING_PROTOCOL)),
918                 cursor.getInt(cursor.getColumnIndexOrThrow(
919                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
920                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)));
921         return apn;
922     }
923
924     private ArrayList<ApnSetting> createApnList(Cursor cursor) {
925         ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
926         IccRecords r = mIccRecords.get();
927
928         if (cursor.moveToFirst()) {
929             String mvnoType = null;
930             String mvnoMatchData = null;
931             do {
932                 String cursorMvnoType = cursor.getString(
933                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_TYPE));
934                 String cursorMvnoMatchData = cursor.getString(
935                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MVNO_MATCH_DATA));
936                 if (mvnoType != null) {
937                     if (mvnoType.equals(cursorMvnoType) &&
938                             mvnoMatchData.equals(cursorMvnoMatchData)) {
939                         result.add(makeApnSetting(cursor));
940                     }
941                 } else {
942                     // no mvno match yet
943                     if (mvnoMatches(r, cursorMvnoType, cursorMvnoMatchData)) {
944                         // first match - toss out non-mvno data
945                         result.clear();
946                         mvnoType = cursorMvnoType;
947                         mvnoMatchData = cursorMvnoMatchData;
948                         result.add(makeApnSetting(cursor));
949                     } else {
950                         // add only non-mvno data
951                         if (cursorMvnoType.equals("")) {
952                             result.add(makeApnSetting(cursor));
953                         }
954                     }
955                 }
956             } while (cursor.moveToNext());
957         }
958         if (DBG) log("createApnList: X result=" + result);
959         return result;
960     }
961
962     private boolean dataConnectionNotInUse(DcAsyncChannel dcac) {
963         if (DBG) log("dataConnectionNotInUse: check if dcac is inuse dcac=" + dcac);
964         for (ApnContext apnContext : mApnContexts.values()) {
965             if (apnContext.getDcAc() == dcac) {
966                 if (DBG) log("dataConnectionNotInUse: in use by apnContext=" + apnContext);
967                 return false;
968             }
969         }
970         // TODO: Fix retry handling so free DataConnections have empty apnlists.
971         // Probably move retry handling into DataConnections and reduce complexity
972         // of DCT.
973         if (DBG) log("dataConnectionNotInUse: tearDownAll");
974         dcac.tearDownAll("No connection", null);
975         if (DBG) log("dataConnectionNotInUse: not in use return true");
976         return true;
977     }
978
979     private DcAsyncChannel findFreeDataConnection() {
980         for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
981             if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
982                 if (DBG) {
983                     log("findFreeDataConnection: found free DataConnection=" +
984                         " dcac=" + dcac);
985                 }
986                 return dcac;
987             }
988         }
989         log("findFreeDataConnection: NO free DataConnection");
990         return null;
991     }
992
993     private boolean setupData(ApnContext apnContext) {
994         if (DBG) log("setupData: apnContext=" + apnContext);
995         ApnSetting apnSetting;
996         DcAsyncChannel dcac;
997
998         int profileId = getApnProfileID(apnContext.getApnType());
999         apnSetting = apnContext.getNextWaitingApn();
1000         if (apnSetting == null) {
1001             if (DBG) log("setupData: return for no apn found!");
1002             return false;
1003         }
1004
1005         dcac = checkForCompatibleConnectedApnContext(apnContext);
1006         if (dcac != null) {
1007             // Get the dcacApnSetting for the connection we want to share.
1008             ApnSetting dcacApnSetting = dcac.getApnSettingSync();
1009             if (dcacApnSetting != null) {
1010                 // Setting is good, so use it.
1011                 apnSetting = dcacApnSetting;
1012             }
1013         }
1014         if (dcac == null) {
1015             dcac = findFreeDataConnection();
1016
1017             if (dcac == null) {
1018                 dcac = createDataConnection();
1019             }
1020
1021             if (dcac == null) {
1022                 if (DBG) log("setupData: No free DataConnection and couldn't create one, WEIRD");
1023                 return false;
1024             }
1025         }
1026         if (DBG) log("setupData: dcac=" + dcac + " apnSetting=" + apnSetting);
1027
1028         apnContext.setDataConnectionAc(dcac);
1029         apnContext.setApnSetting(apnSetting);
1030         apnContext.setState(DctConstants.State.CONNECTING);
1031         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1032
1033         Message msg = obtainMessage();
1034         msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
1035         msg.obj = apnContext;
1036         dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, msg);
1037
1038         if (DBG) log("setupData: initing!");
1039         return true;
1040     }
1041
1042     /**
1043      * Handles changes to the APN database.
1044      */
1045     private void onApnChanged() {
1046         DctConstants.State overallState = getOverallState();
1047         boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
1048                 overallState == DctConstants.State.FAILED);
1049
1050         if (mPhone instanceof GSMPhone) {
1051             // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
1052             ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
1053         }
1054
1055         // TODO: It'd be nice to only do this if the changed entrie(s)
1056         // match the current operator.
1057         if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
1058         createAllApnList();
1059         cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);
1060         if (isDisconnected) {
1061             setupDataOnConnectableApns(Phone.REASON_APN_CHANGED);
1062         }
1063     }
1064
1065     /**
1066      * @param cid Connection id provided from RIL.
1067      * @return DataConnectionAc associated with specified cid.
1068      */
1069     private DcAsyncChannel findDataConnectionAcByCid(int cid) {
1070         for (DcAsyncChannel dcac : mDataConnectionAcHashMap.values()) {
1071             if (dcac.getCidSync() == cid) {
1072                 return dcac;
1073             }
1074         }
1075         return null;
1076     }
1077
1078     /**
1079      * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
1080      * or RIL_UNSOL_DATA_CALL_LIST_CHANGED
1081      */
1082     private void onDataStateChanged (AsyncResult ar) {
1083         ArrayList<DataCallResponse> dataCallStates;
1084
1085         if (DBG) log("onDataStateChanged(ar): E");
1086         dataCallStates = (ArrayList<DataCallResponse>)(ar.result);
1087
1088         if (ar.exception != null) {
1089             // This is probably "radio not available" or something
1090             // of that sort. If so, the whole connection is going
1091             // to come down soon anyway
1092             if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
1093             return;
1094         }
1095         if (DBG) log("onDataStateChanged(ar): DataCallResponse size=" + dataCallStates.size());
1096
1097         // Create a hash map to store the dataCallState of each DataConnectionAc
1098         HashMap<DataCallResponse, DcAsyncChannel> dataCallStateToDcac;
1099         dataCallStateToDcac = new HashMap<DataCallResponse, DcAsyncChannel>();
1100         for (DataCallResponse dataCallState : dataCallStates) {
1101             DcAsyncChannel dcac = findDataConnectionAcByCid(dataCallState.cid);
1102
1103             if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac);
1104         }
1105
1106         // Check if we should start or stop polling, by looking
1107         // for dormant and active connections.
1108         boolean isAnyDataCallDormant = false;
1109         boolean isAnyDataCallActive = false;
1110         for (DataCallResponse newState : dataCallStates) {
1111             if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) isAnyDataCallActive = true;
1112             if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DOWN) isAnyDataCallDormant = true;
1113         }
1114
1115         if (isAnyDataCallDormant && !isAnyDataCallActive) {
1116             // There is no way to indicate link activity per APN right now. So
1117             // Link Activity will be considered dormant only when all data calls
1118             // are dormant.
1119             // If a single data call is in dormant state and none of the data
1120             // calls are active broadcast overall link state as dormant.
1121             mActivity = DctConstants.Activity.DORMANT;
1122             if (DBG) {
1123                 log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
1124             }
1125             stopNetStatPoll();
1126         } else {
1127             mActivity = DctConstants.Activity.NONE;
1128             if (DBG) {
1129                 log("onDataStateChanged: Data Activity updated to NONE. " +
1130                          "isAnyDataCallActive = " + isAnyDataCallActive +
1131                          " isAnyDataCallDormant = " + isAnyDataCallDormant);
1132             }
1133             if (isAnyDataCallActive) startNetStatPoll();
1134         }
1135
1136         if (DBG) log("onDataStateChanged(ar): X");
1137     }
1138
1139     private void notifyDefaultData(ApnContext apnContext) {
1140         if (DBG) {
1141             log("notifyDefaultData: type=" + apnContext.getApnType()
1142                 + ", reason:" + apnContext.getReason());
1143         }
1144         apnContext.setState(DctConstants.State.CONNECTED);
1145         // setState(DctConstants.State.CONNECTED);
1146         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1147         startNetStatPoll();
1148         startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1149     }
1150
1151     // TODO: For multiple Active APNs not exactly sure how to do this.
1152     @Override
1153     protected void gotoIdleAndNotifyDataConnection(String reason) {
1154         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1155         notifyDataConnection(reason);
1156         mActiveApn = null;
1157     }
1158
1159     @Override
1160     protected void restartRadio() {
1161         if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
1162         cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1163         mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1164         /* Note: no need to call setRadioPower(true).  Assuming the desired
1165          * radio power state is still ON (as tracked by ServiceStateTracker),
1166          * ServiceStateTracker will call setRadioPower when it receives the
1167          * RADIO_STATE_CHANGED notification for the power off.  And if the
1168          * desired power state has changed in the interim, we don't want to
1169          * override it with an unconditional power on.
1170          */
1171
1172         int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1173         SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1174     }
1175
1176     /**
1177      * Return true if data connection need to be setup after disconnected due to
1178      * reason.
1179      *
1180      * @param reason the reason why data is disconnected
1181      * @return true if try setup data connection is need for this reason
1182      */
1183     private boolean retryAfterDisconnected(String reason) {
1184         boolean retry = true;
1185
1186         if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
1187             retry = false;
1188         }
1189         return retry;
1190     }
1191
1192     private void startAlarmForReconnect(int delay, ApnContext apnContext) {
1193         String apnType = apnContext.getApnType();
1194
1195         Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
1196         intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
1197         intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
1198
1199         if (DBG) {
1200             log("startAlarmForReconnect: delay=" + delay + " action=" + intent.getAction()
1201                     + " apn=" + apnContext);
1202         }
1203
1204         PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1205                                         intent, PendingIntent.FLAG_UPDATE_CURRENT);
1206         apnContext.setReconnectIntent(alarmIntent);
1207         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1208                 SystemClock.elapsedRealtime() + delay, alarmIntent);
1209     }
1210
1211     private void startAlarmForRestartTrySetup(int delay, ApnContext apnContext) {
1212         String apnType = apnContext.getApnType();
1213         Intent intent = new Intent(INTENT_RESTART_TRYSETUP_ALARM + "." + apnType);
1214         intent.putExtra(INTENT_RESTART_TRYSETUP_ALARM_EXTRA_TYPE, apnType);
1215
1216         if (DBG) {
1217             log("startAlarmForRestartTrySetup: delay=" + delay + " action=" + intent.getAction()
1218                     + " apn=" + apnContext);
1219         }
1220         PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1221                                         intent, PendingIntent.FLAG_UPDATE_CURRENT);
1222         apnContext.setReconnectIntent(alarmIntent);
1223         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1224                 SystemClock.elapsedRealtime() + delay, alarmIntent);
1225     }
1226
1227     private void notifyNoData(DcFailCause lastFailCauseCode,
1228                               ApnContext apnContext) {
1229         if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
1230         if (lastFailCauseCode.isPermanentFail()
1231             && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
1232             mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1233         }
1234     }
1235
1236     private void onRecordsLoaded() {
1237         if (DBG) log("onRecordsLoaded: createAllApnList");
1238         createAllApnList();
1239         if (mPhone.mCi.getRadioState().isOn()) {
1240             if (DBG) log("onRecordsLoaded: notifying data availability");
1241             notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
1242         }
1243         setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
1244     }
1245
1246     @Override
1247     protected void onSetDependencyMet(String apnType, boolean met) {
1248         // don't allow users to tweak hipri to work around default dependency not met
1249         if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
1250
1251         ApnContext apnContext = mApnContexts.get(apnType);
1252         if (apnContext == null) {
1253             loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
1254                     apnType + ", " + met + ")");
1255             return;
1256         }
1257         applyNewState(apnContext, apnContext.isEnabled(), met);
1258         if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
1259             // tie actions on default to similar actions on HIPRI regarding dependencyMet
1260             apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
1261             if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
1262         }
1263     }
1264
1265     private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1266         boolean cleanup = false;
1267         boolean trySetup = false;
1268         if (DBG) {
1269             log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
1270                     "(" + apnContext.isEnabled() + "), " + met + "(" +
1271                     apnContext.getDependencyMet() +"))");
1272         }
1273         if (apnContext.isReady()) {
1274             if (enabled && met) {
1275                 DctConstants.State state = apnContext.getState();
1276                 switch(state) {
1277                     case CONNECTING:
1278                     case SCANNING:
1279                     case CONNECTED:
1280                     case DISCONNECTING:
1281                         // We're "READY" and active so just return
1282                         if (DBG) log("applyNewState: 'ready' so return");
1283                         return;
1284                     case IDLE:
1285                         // fall through: this is unexpected but if it happens cleanup and try setup
1286                     case FAILED:
1287                     case RETRYING: {
1288                         // We're "READY" but not active so disconnect (cleanup = true) and
1289                         // connect (trySetup = true) to be sure we retry the connection.
1290                         trySetup = true;
1291                         apnContext.setReason(Phone.REASON_DATA_ENABLED);
1292                         break;
1293                     }
1294                 }
1295             } else if (!enabled) {
1296                 apnContext.setReason(Phone.REASON_DATA_DISABLED);
1297             } else {
1298                 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1299             }
1300             cleanup = true;
1301         } else {
1302             if (enabled && met) {
1303                 if (apnContext.isEnabled()) {
1304                     apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1305                 } else {
1306                     apnContext.setReason(Phone.REASON_DATA_ENABLED);
1307                 }
1308                 if (apnContext.getState() == DctConstants.State.FAILED) {
1309                     apnContext.setState(DctConstants.State.IDLE);
1310                 }
1311                 trySetup = true;
1312             }
1313         }
1314         apnContext.setEnabled(enabled);
1315         apnContext.setDependencyMet(met);
1316         if (cleanup) cleanUpConnection(true, apnContext);
1317         if (trySetup) trySetupData(apnContext);
1318     }
1319
1320     private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
1321         String apnType = apnContext.getApnType();
1322         ApnSetting dunSetting = null;
1323
1324         if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
1325             dunSetting = fetchDunApn();
1326         }
1327         if (DBG) {
1328             log("checkForCompatibleConnectedApnContext: apnContext=" + apnContext );
1329         }
1330
1331         DcAsyncChannel potentialDcac = null;
1332         ApnContext potentialApnCtx = null;
1333         for (ApnContext curApnCtx : mApnContexts.values()) {
1334             DcAsyncChannel curDcac = curApnCtx.getDcAc();
1335             if (curDcac != null) {
1336                 ApnSetting apnSetting = curApnCtx.getApnSetting();
1337                 if (dunSetting != null) {
1338                     if (dunSetting.equals(apnSetting)) {
1339                         switch (curApnCtx.getState()) {
1340                             case CONNECTED:
1341                                 if (DBG) {
1342                                     log("checkForCompatibleConnectedApnContext:"
1343                                             + " found dun conn=" + curDcac
1344                                             + " curApnCtx=" + curApnCtx);
1345                                 }
1346                                 return curDcac;
1347                             case RETRYING:
1348                             case CONNECTING:
1349                                 potentialDcac = curDcac;
1350                                 potentialApnCtx = curApnCtx;
1351                             default:
1352                                 // Not connected, potential unchanged
1353                                 break;
1354                         }
1355                     }
1356                 } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
1357                     switch (curApnCtx.getState()) {
1358                         case CONNECTED:
1359                             if (DBG) {
1360                                 log("checkForCompatibleConnectedApnContext:"
1361                                         + " found canHandle conn=" + curDcac
1362                                         + " curApnCtx=" + curApnCtx);
1363                             }
1364                             return curDcac;
1365                         case RETRYING:
1366                         case CONNECTING:
1367                             potentialDcac = curDcac;
1368                             potentialApnCtx = curApnCtx;
1369                         default:
1370                             // Not connected, potential unchanged
1371                             break;
1372                     }
1373                 }
1374             } else {
1375                 if (VDBG) {
1376                     log("checkForCompatibleConnectedApnContext: not conn curApnCtx=" + curApnCtx);
1377                 }
1378             }
1379         }
1380         if (potentialDcac != null) {
1381             if (DBG) {
1382                 log("checkForCompatibleConnectedApnContext: found potential conn=" + potentialDcac
1383                         + " curApnCtx=" + potentialApnCtx);
1384             }
1385             return potentialDcac;
1386         }
1387
1388         if (DBG) log("checkForCompatibleConnectedApnContext: NO conn apnContext=" + apnContext);
1389         return null;
1390     }
1391
1392     @Override
1393     protected void onEnableApn(int apnId, int enabled) {
1394         ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1395         if (apnContext == null) {
1396             loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
1397             return;
1398         }
1399         // TODO change our retry manager to use the appropriate numbers for the new APN
1400         if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
1401         applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet());
1402     }
1403
1404     @Override
1405     // TODO: We shouldnt need this.
1406     protected boolean onTrySetupData(String reason) {
1407         if (DBG) log("onTrySetupData: reason=" + reason);
1408         setupDataOnConnectableApns(reason);
1409         return true;
1410     }
1411
1412     protected boolean onTrySetupData(ApnContext apnContext) {
1413         if (DBG) log("onTrySetupData: apnContext=" + apnContext);
1414         return trySetupData(apnContext);
1415     }
1416
1417     @Override
1418     protected void onRoamingOff() {
1419         if (DBG) log("onRoamingOff");
1420
1421         if (mUserDataEnabled == false) return;
1422
1423         if (getDataOnRoamingEnabled() == false) {
1424             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
1425             setupDataOnConnectableApns(Phone.REASON_ROAMING_OFF);
1426         } else {
1427             notifyDataConnection(Phone.REASON_ROAMING_OFF);
1428         }
1429     }
1430
1431     @Override
1432     protected void onRoamingOn() {
1433         if (mUserDataEnabled == false) return;
1434
1435         if (getDataOnRoamingEnabled()) {
1436             if (DBG) log("onRoamingOn: setup data on roaming");
1437             setupDataOnConnectableApns(Phone.REASON_ROAMING_ON);
1438             notifyDataConnection(Phone.REASON_ROAMING_ON);
1439         } else {
1440             if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
1441             cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1442             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
1443         }
1444     }
1445
1446     @Override
1447     protected void onRadioAvailable() {
1448         if (DBG) log("onRadioAvailable");
1449         if (mPhone.getSimulatedRadioControl() != null) {
1450             // Assume data is connected on the simulator
1451             // FIXME  this can be improved
1452             // setState(DctConstants.State.CONNECTED);
1453             notifyDataConnection(null);
1454
1455             log("onRadioAvailable: We're on the simulator; assuming data is connected");
1456         }
1457
1458         IccRecords r = mIccRecords.get();
1459         if (r != null && r.getRecordsLoaded()) {
1460             notifyOffApnsOfAvailability(null);
1461         }
1462
1463         if (getOverallState() != DctConstants.State.IDLE) {
1464             cleanUpConnection(true, null);
1465         }
1466     }
1467
1468     @Override
1469     protected void onRadioOffOrNotAvailable() {
1470         // Make sure our reconnect delay starts at the initial value
1471         // next time the radio comes on
1472
1473         mReregisterOnReconnectFailure = false;
1474
1475         if (mPhone.getSimulatedRadioControl() != null) {
1476             // Assume data is connected on the simulator
1477             // FIXME  this can be improved
1478             log("We're on the simulator; assuming radio off is meaningless");
1479         } else {
1480             if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
1481             cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1482         }
1483         notifyOffApnsOfAvailability(null);
1484     }
1485
1486     /**
1487      * A SETUP (aka bringUp) has completed, possibly with an error. If
1488      * there is an error this method will call {@link #onDataSetupCompleteError}.
1489      */
1490     @Override
1491     protected void onDataSetupComplete(AsyncResult ar) {
1492
1493         DcFailCause cause = DcFailCause.UNKNOWN;
1494         boolean handleError = false;
1495         ApnContext apnContext = null;
1496
1497         if(ar.userObj instanceof ApnContext){
1498             apnContext = (ApnContext)ar.userObj;
1499         } else {
1500             throw new RuntimeException("onDataSetupComplete: No apnContext");
1501         }
1502
1503         if (ar.exception == null) {
1504             DcAsyncChannel dcac = apnContext.getDcAc();
1505
1506             if (RADIO_TESTS) {
1507                 // Note: To change radio.test.onDSC.null.dcac from command line you need to
1508                 // adb root and adb remount and from the command line you can only change the
1509                 // value to 1 once. To change it a second time you can reboot or execute
1510                 // adb shell stop and then adb shell start. The command line to set the value is:
1511                 // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
1512                 ContentResolver cr = mPhone.getContext().getContentResolver();
1513                 String radioTestProperty = "radio.test.onDSC.null.dcac";
1514                 if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
1515                     log("onDataSetupComplete: " + radioTestProperty +
1516                             " is true, set dcac to null and reset property to false");
1517                     dcac = null;
1518                     Settings.System.putInt(cr, radioTestProperty, 0);
1519                     log("onDataSetupComplete: " + radioTestProperty + "=" +
1520                             Settings.System.getInt(mPhone.getContext().getContentResolver(),
1521                                     radioTestProperty, -1));
1522                 }
1523             }
1524             if (dcac == null) {
1525                 log("onDataSetupComplete: no connection to DC, handle as error");
1526                 cause = DcFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
1527                 handleError = true;
1528             } else {
1529                 ApnSetting apn = apnContext.getApnSetting();
1530                 if (DBG) {
1531                     log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
1532                 }
1533                 if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
1534                     try {
1535                         String port = apn.port;
1536                         if (TextUtils.isEmpty(port)) port = "8080";
1537                         ProxyProperties proxy = new ProxyProperties(apn.proxy,
1538                                 Integer.parseInt(port), null);
1539                         dcac.setLinkPropertiesHttpProxySync(proxy);
1540                     } catch (NumberFormatException e) {
1541                         loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
1542                                 apn.port + "): " + e);
1543                     }
1544                 }
1545
1546                 // everything is setup
1547                 if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
1548                     SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");
1549                     if (mCanSetPreferApn && mPreferredApn == null) {
1550                         if (DBG) log("onDataSetupComplete: PREFERED APN is null");
1551                         mPreferredApn = apn;
1552                         if (mPreferredApn != null) {
1553                             setPreferredApn(mPreferredApn.id);
1554                         }
1555                     }
1556                 } else {
1557                     SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
1558                 }
1559                 notifyDefaultData(apnContext);
1560             }
1561         } else {
1562             cause = (DcFailCause) (ar.result);
1563             if (DBG) {
1564                 ApnSetting apn = apnContext.getApnSetting();
1565                 log(String.format("onDataSetupComplete: error apn=%s cause=%s",
1566                         (apn == null ? "unknown" : apn.apn), cause));
1567             }
1568             if (cause.isEventLoggable()) {
1569                 // Log this failure to the Event Logs.
1570                 int cid = getCellLocationId();
1571                 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1572                         cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
1573             }
1574
1575             // Count permanent failures and remove the APN we just tried
1576             if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
1577
1578             apnContext.removeWaitingApn(apnContext.getApnSetting());
1579             if (DBG) {
1580                 log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1581                         " WaitingApnsPermFailureCountDown=%d",
1582                         apnContext.getWaitingApns().size(),
1583                         apnContext.getWaitingApnsPermFailCount()));
1584             }
1585             handleError = true;
1586         }
1587
1588         if (handleError) {
1589             onDataSetupCompleteError(ar);
1590         }
1591     }
1592
1593     /**
1594      * @return number of milli-seconds to delay between trying apns'
1595      */
1596     private int getApnDelay() {
1597         if (mFailFast) {
1598             return SystemProperties.getInt("persist.radio.apn_ff_delay",
1599                     APN_FAIL_FAST_DELAY_DEFAULT_MILLIS);
1600         } else {
1601             return SystemProperties.getInt("persist.radio.apn_delay", APN_DELAY_DEFAULT_MILLIS);
1602         }
1603     }
1604
1605     /**
1606      * Error has occurred during the SETUP {aka bringUP} request and the DCT
1607      * should either try the next waiting APN or start over from the
1608      * beginning if the list is empty. Between each SETUP request there will
1609      * be a delay defined by {@link #getApnDelay()}.
1610      */
1611     @Override
1612     protected void onDataSetupCompleteError(AsyncResult ar) {
1613         String reason = "";
1614         ApnContext apnContext = null;
1615
1616         if(ar.userObj instanceof ApnContext){
1617             apnContext = (ApnContext)ar.userObj;
1618         } else {
1619             throw new RuntimeException("onDataSetupCompleteError: No apnContext");
1620         }
1621
1622         // See if there are more APN's to try
1623         if (apnContext.getWaitingApns().isEmpty()) {
1624             apnContext.setState(DctConstants.State.FAILED);
1625             mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
1626
1627             apnContext.setDataConnectionAc(null);
1628
1629             if (apnContext.getWaitingApnsPermFailCount() == 0) {
1630                 if (DBG) {
1631                     log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
1632                 }
1633             } else {
1634                 if (DBG) {
1635                     log("onDataSetupComplete: Not all APN's had permanent failures, short delay");
1636                 }
1637                 startAlarmForRestartTrySetup(getApnDelay(), apnContext);
1638             }
1639         } else {
1640             if (DBG) log("onDataSetupComplete: Try next APN");
1641             apnContext.setState(DctConstants.State.SCANNING);
1642             // Wait a bit before trying the next APN, so that
1643             // we're not tying up the RIL command channel
1644             startAlarmForReconnect(getApnDelay(), apnContext);
1645         }
1646     }
1647
1648     /**
1649      * Called when EVENT_DISCONNECT_DONE is received.
1650      */
1651     @Override
1652     protected void onDisconnectDone(int connId, AsyncResult ar) {
1653         ApnContext apnContext = null;
1654
1655         if (ar.userObj instanceof ApnContext) {
1656             apnContext = (ApnContext) ar.userObj;
1657         } else {
1658             loge("onDisconnectDone: Invalid ar in onDisconnectDone, ignore");
1659             return;
1660         }
1661
1662         if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
1663         apnContext.setState(DctConstants.State.IDLE);
1664
1665         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1666
1667         // if all data connection are gone, check whether Airplane mode request was
1668         // pending.
1669         if (isDisconnected()) {
1670             if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
1671                 // Radio will be turned off. No need to retry data setup
1672                 apnContext.setApnSetting(null);
1673                 apnContext.setDataConnectionAc(null);
1674                 return;
1675             }
1676         }
1677
1678         // If APN is still enabled, try to bring it back up automatically
1679         if (mAttached.get() && apnContext.isReady()
1680                 && retryAfterDisconnected(apnContext.getReason())) {
1681             SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "false");
1682             // Wait a bit before trying the next APN, so that
1683             // we're not tying up the RIL command channel.
1684             // This also helps in any external dependency to turn off the context.
1685             startAlarmForReconnect(getApnDelay(), apnContext);
1686         } else {
1687             apnContext.setApnSetting(null);
1688             apnContext.setDataConnectionAc(null);
1689         }
1690     }
1691
1692     /**
1693      * Called when EVENT_DISCONNECT_DC_RETRYING is received.
1694      */
1695     @Override
1696     protected void onDisconnectDcRetrying(int connId, AsyncResult ar) {
1697         // We could just do this in DC!!!
1698         ApnContext apnContext = null;
1699
1700         if (ar.userObj instanceof ApnContext) {
1701             apnContext = (ApnContext) ar.userObj;
1702         } else {
1703             loge("onDisconnectDcRetrying: Invalid ar in onDisconnectDone, ignore");
1704             return;
1705         }
1706
1707         apnContext.setState(DctConstants.State.RETRYING);
1708         if(DBG) log("onDisconnectDcRetrying: apnContext=" + apnContext);
1709
1710         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1711     }
1712
1713     protected void onPollPdp() {
1714         if (getOverallState() == DctConstants.State.CONNECTED) {
1715             // only poll when connected
1716             mPhone.mCi.getDataCallList(obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
1717             sendMessageDelayed(obtainMessage(DctConstants.EVENT_POLL_PDP), POLL_PDP_MILLIS);
1718         }
1719     }
1720
1721     @Override
1722     protected void onVoiceCallStarted() {
1723         if (DBG) log("onVoiceCallStarted");
1724         mInVoiceCall = true;
1725         if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1726             if (DBG) log("onVoiceCallStarted stop polling");
1727             stopNetStatPoll();
1728             stopDataStallAlarm();
1729             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1730         }
1731     }
1732
1733     @Override
1734     protected void onVoiceCallEnded() {
1735         if (DBG) log("onVoiceCallEnded");
1736         mInVoiceCall = false;
1737         if (isConnected()) {
1738             if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1739                 startNetStatPoll();
1740                 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1741                 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1742             } else {
1743                 // clean slate after call end.
1744                 resetPollStats();
1745             }
1746         }
1747         // reset reconnect timer
1748         setupDataOnConnectableApns(Phone.REASON_VOICE_CALL_ENDED);
1749     }
1750
1751     @Override
1752     protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
1753         if (DBG) log("onCleanUpConnection");
1754         ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1755         if (apnContext != null) {
1756             apnContext.setReason(reason);
1757             cleanUpConnection(tearDown, apnContext);
1758         }
1759     }
1760
1761     @Override
1762     protected boolean isConnected() {
1763         for (ApnContext apnContext : mApnContexts.values()) {
1764             if (apnContext.getState() == DctConstants.State.CONNECTED) {
1765                 // At least one context is connected, return true
1766                 return true;
1767             }
1768         }
1769         // There are not any contexts connected, return false
1770         return false;
1771     }
1772
1773     @Override
1774     public boolean isDisconnected() {
1775         for (ApnContext apnContext : mApnContexts.values()) {
1776             if (!apnContext.isDisconnected()) {
1777                 // At least one context was not disconnected return false
1778                 return false;
1779             }
1780         }
1781         // All contexts were disconnected so return true
1782         return true;
1783     }
1784
1785     @Override
1786     protected void notifyDataConnection(String reason) {
1787         if (DBG) log("notifyDataConnection: reason=" + reason);
1788         for (ApnContext apnContext : mApnContexts.values()) {
1789             if (mAttached.get() && apnContext.isReady()) {
1790                 if (DBG) log("notifyDataConnection: type:" + apnContext.getApnType());
1791                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
1792                         apnContext.getApnType());
1793             }
1794         }
1795         notifyOffApnsOfAvailability(reason);
1796     }
1797
1798     /**
1799      * Based on the sim operator numeric, create a list for all possible
1800      * Data Connections and setup the preferredApn.
1801      */
1802     private void createAllApnList() {
1803         mAllApnSettings = new ArrayList<ApnSetting>();
1804         IccRecords r = mIccRecords.get();
1805         String operator = (r != null) ? r.getOperatorNumeric() : "";
1806         if (operator != null) {
1807             String selection = "numeric = '" + operator + "'";
1808             // query only enabled apn.
1809             // carrier_enabled : 1 means enabled apn, 0 disabled apn.
1810             selection += " and carrier_enabled = 1";
1811             if (DBG) log("createAllApnList: selection=" + selection);
1812
1813             Cursor cursor = mPhone.getContext().getContentResolver().query(
1814                     Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1815
1816             if (cursor != null) {
1817                 if (cursor.getCount() > 0) {
1818                     mAllApnSettings = createApnList(cursor);
1819                 }
1820                 cursor.close();
1821             }
1822         }
1823
1824         if (mAllApnSettings.isEmpty()) {
1825             if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
1826             mPreferredApn = null;
1827             // TODO: What is the right behavior?
1828             //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);
1829         } else {
1830             mPreferredApn = getPreferredApn();
1831             if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
1832                 mPreferredApn = null;
1833                 setPreferredApn(-1);
1834             }
1835             if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
1836         }
1837         if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
1838     }
1839
1840     /** Return the DC AsyncChannel for the new data connection */
1841     private DcAsyncChannel createDataConnection() {
1842         if (DBG) log("createDataConnection E");
1843
1844         int id = mUniqueIdGenerator.getAndIncrement();
1845         DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
1846                                                 this, mDcTesterFailBringUpAll, mDcc);
1847         mDataConnections.put(id, conn);
1848         DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
1849         int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
1850         if (status == AsyncChannel.STATUS_SUCCESSFUL) {
1851             mDataConnectionAcHashMap.put(dcac.getDataConnectionIdSync(), dcac);
1852         } else {
1853             loge("createDataConnection: Could not connect to dcac=" + dcac + " status=" + status);
1854         }
1855
1856         if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
1857         return dcac;
1858     }
1859
1860     private void destroyDataConnections() {
1861         if(mDataConnections != null) {
1862             if (DBG) log("destroyDataConnections: clear mDataConnectionList");
1863             mDataConnections.clear();
1864         } else {
1865             if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
1866         }
1867     }
1868
1869     /**
1870      * Build a list of APNs to be used to create PDP's.
1871      *
1872      * @param requestedApnType
1873      * @return waitingApns list to be used to create PDP
1874      *          error when waitingApns.isEmpty()
1875      */
1876     private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) {
1877         if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
1878         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
1879
1880         if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
1881             ApnSetting dun = fetchDunApn();
1882             if (dun != null) {
1883                 apnList.add(dun);
1884                 if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
1885                 return apnList;
1886             }
1887         }
1888
1889         IccRecords r = mIccRecords.get();
1890         String operator = (r != null) ? r.getOperatorNumeric() : "";
1891         int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
1892
1893         // This is a workaround for a bug (7305641) where we don't failover to other
1894         // suitable APNs if our preferred APN fails.  On prepaid ATT sims we need to
1895         // failover to a provisioning APN, but once we've used their default data
1896         // connection we are locked to it for life.  This change allows ATT devices
1897         // to say they don't want to use preferred at all.
1898         boolean usePreferred = true;
1899         try {
1900             usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
1901                     internal.R.bool.config_dontPreferApn);
1902         } catch (Resources.NotFoundException e) {
1903             if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
1904             usePreferred = true;
1905         }
1906         if (DBG) {
1907             log("buildWaitingApns: usePreferred=" + usePreferred
1908                     + " canSetPreferApn=" + mCanSetPreferApn
1909                     + " mPreferredApn=" + mPreferredApn
1910                     + " operator=" + operator + " radioTech=" + radioTech
1911                     + " IccRecords r=" + r);
1912         }
1913
1914         if (usePreferred && mCanSetPreferApn && mPreferredApn != null &&
1915                 mPreferredApn.canHandleType(requestedApnType)) {
1916             if (DBG) {
1917                 log("buildWaitingApns: Preferred APN:" + operator + ":"
1918                         + mPreferredApn.numeric + ":" + mPreferredApn);
1919             }
1920             if (mPreferredApn.numeric.equals(operator)) {
1921                 if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) {
1922                     apnList.add(mPreferredApn);
1923                     if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
1924                     return apnList;
1925                 } else {
1926                     if (DBG) log("buildWaitingApns: no preferred APN");
1927                     setPreferredApn(-1);
1928                     mPreferredApn = null;
1929                 }
1930             } else {
1931                 if (DBG) log("buildWaitingApns: no preferred APN");
1932                 setPreferredApn(-1);
1933                 mPreferredApn = null;
1934             }
1935         }
1936         if (mAllApnSettings != null) {
1937             if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
1938             for (ApnSetting apn : mAllApnSettings) {
1939                 if (DBG) log("buildWaitingApns: apn=" + apn);
1940                 if (apn.canHandleType(requestedApnType)) {
1941                     if (apn.bearer == 0 || apn.bearer == radioTech) {
1942                         if (DBG) log("buildWaitingApns: adding apn=" + apn.toString());
1943                         apnList.add(apn);
1944                     } else {
1945                         if (DBG) {
1946                             log("buildWaitingApns: bearer:" + apn.bearer + " != "
1947                                     + "radioTech:" + radioTech);
1948                         }
1949                     }
1950                 } else {
1951                 if (DBG) {
1952                     log("buildWaitingApns: couldn't handle requesedApnType="
1953                             + requestedApnType);
1954                 }
1955             }
1956             }
1957         } else {
1958             loge("mAllApnSettings is empty!");
1959         }
1960         if (DBG) log("buildWaitingApns: X apnList=" + apnList);
1961         return apnList;
1962     }
1963
1964     private String apnListToString (ArrayList<ApnSetting> apns) {
1965         StringBuilder result = new StringBuilder();
1966         for (int i = 0, size = apns.size(); i < size; i++) {
1967             result.append('[')
1968                   .append(apns.get(i).toString())
1969                   .append(']');
1970         }
1971         return result.toString();
1972     }
1973
1974     private void setPreferredApn(int pos) {
1975         if (!mCanSetPreferApn) {
1976             log("setPreferredApn: X !canSEtPreferApn");
1977             return;
1978         }
1979
1980         log("setPreferredApn: delete");
1981         ContentResolver resolver = mPhone.getContext().getContentResolver();
1982         resolver.delete(PREFERAPN_NO_UPDATE_URI, null, null);
1983
1984         if (pos >= 0) {
1985             log("setPreferredApn: insert");
1986             ContentValues values = new ContentValues();
1987             values.put(APN_ID, pos);
1988             resolver.insert(PREFERAPN_NO_UPDATE_URI, values);
1989         }
1990     }
1991
1992     private ApnSetting getPreferredApn() {
1993         if (mAllApnSettings.isEmpty()) {
1994             log("getPreferredApn: X not found mAllApnSettings.isEmpty");
1995             return null;
1996         }
1997
1998         Cursor cursor = mPhone.getContext().getContentResolver().query(
1999                 PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" },
2000                 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
2001
2002         if (cursor != null) {
2003             mCanSetPreferApn = true;
2004         } else {
2005             mCanSetPreferApn = false;
2006         }
2007         log("getPreferredApn: mRequestedApnType=" + mRequestedApnType + " cursor=" + cursor
2008                 + " cursor.count=" + ((cursor != null) ? cursor.getCount() : 0));
2009
2010         if (mCanSetPreferApn && cursor.getCount() > 0) {
2011             int pos;
2012             cursor.moveToFirst();
2013             pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
2014             for(ApnSetting p : mAllApnSettings) {
2015                 log("getPreferredApn: apnSetting=" + p);
2016                 if (p.id == pos && p.canHandleType(mRequestedApnType)) {
2017                     log("getPreferredApn: X found apnSetting" + p);
2018                     cursor.close();
2019                     return p;
2020                 }
2021             }
2022         }
2023
2024         if (cursor != null) {
2025             cursor.close();
2026         }
2027
2028         log("getPreferredApn: X not found");
2029         return null;
2030     }
2031
2032     @Override
2033     public void handleMessage (Message msg) {
2034         if (DBG) log("handleMessage msg=" + msg);
2035
2036         if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
2037             loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
2038             return;
2039         }
2040
2041         switch (msg.what) {
2042             case DctConstants.EVENT_RECORDS_LOADED:
2043                 onRecordsLoaded();
2044                 break;
2045
2046             case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
2047                 onDataConnectionDetached();
2048                 break;
2049
2050             case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
2051                 onDataConnectionAttached();
2052                 break;
2053
2054             case DctConstants.EVENT_DATA_STATE_CHANGED:
2055                 onDataStateChanged((AsyncResult) msg.obj);
2056                 break;
2057
2058             case DctConstants.EVENT_POLL_PDP:
2059                 onPollPdp();
2060                 break;
2061
2062             case DctConstants.EVENT_DO_RECOVERY:
2063                 doRecovery();
2064                 break;
2065
2066             case DctConstants.EVENT_APN_CHANGED:
2067                 onApnChanged();
2068                 break;
2069
2070             case DctConstants.EVENT_PS_RESTRICT_ENABLED:
2071                 /**
2072                  * We don't need to explicitly to tear down the PDP context
2073                  * when PS restricted is enabled. The base band will deactive
2074                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
2075                  * But we should stop the network polling and prevent reset PDP.
2076                  */
2077                 if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
2078                 stopNetStatPoll();
2079                 stopDataStallAlarm();
2080                 mIsPsRestricted = true;
2081                 break;
2082
2083             case DctConstants.EVENT_PS_RESTRICT_DISABLED:
2084                 /**
2085                  * When PS restrict is removed, we need setup PDP connection if
2086                  * PDP connection is down.
2087                  */
2088                 if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
2089                 mIsPsRestricted  = false;
2090                 if (isConnected()) {
2091                     startNetStatPoll();
2092                     startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
2093                 } else {
2094                     // TODO: Should all PDN states be checked to fail?
2095                     if (mState == DctConstants.State.FAILED) {
2096                         cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
2097                         mReregisterOnReconnectFailure = false;
2098                     }
2099                     trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, PhoneConstants.APN_TYPE_DEFAULT);
2100                 }
2101                 break;
2102
2103             case DctConstants.EVENT_TRY_SETUP_DATA:
2104                 if (msg.obj instanceof ApnContext) {
2105                     onTrySetupData((ApnContext)msg.obj);
2106                 } else if (msg.obj instanceof String) {
2107                     onTrySetupData((String)msg.obj);
2108                 } else {
2109                     loge("EVENT_TRY_SETUP request w/o apnContext or String");
2110                 }
2111                 break;
2112
2113             case DctConstants.EVENT_CLEAN_UP_CONNECTION:
2114                 boolean tearDown = (msg.arg1 == 0) ? false : true;
2115                 if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
2116                 if (msg.obj instanceof ApnContext) {
2117                     cleanUpConnection(tearDown, (ApnContext)msg.obj);
2118                 } else {
2119                     loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context, call super");
2120                     super.handleMessage(msg);
2121                 }
2122                 break;
2123
2124             default:
2125                 // handle the message in the super class DataConnectionTracker
2126                 super.handleMessage(msg);
2127                 break;
2128         }
2129     }
2130
2131     protected int getApnProfileID(String apnType) {
2132         if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
2133             return RILConstants.DATA_PROFILE_IMS;
2134         } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_FOTA)) {
2135             return RILConstants.DATA_PROFILE_FOTA;
2136         } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_CBS)) {
2137             return RILConstants.DATA_PROFILE_CBS;
2138         } else {
2139             return RILConstants.DATA_PROFILE_DEFAULT;
2140         }
2141     }
2142
2143     private int getCellLocationId() {
2144         int cid = -1;
2145         CellLocation loc = mPhone.getCellLocation();
2146
2147         if (loc != null) {
2148             if (loc instanceof GsmCellLocation) {
2149                 cid = ((GsmCellLocation)loc).getCid();
2150             } else if (loc instanceof CdmaCellLocation) {
2151                 cid = ((CdmaCellLocation)loc).getBaseStationId();
2152             }
2153         }
2154         return cid;
2155     }
2156
2157     @Override
2158     protected void onUpdateIcc() {
2159         if (mUiccController == null ) {
2160             return;
2161         }
2162
2163         IccRecords newIccRecords = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP);
2164
2165         IccRecords r = mIccRecords.get();
2166         if (r != newIccRecords) {
2167             if (r != null) {
2168                 log("Removing stale icc objects.");
2169                 r.unregisterForRecordsLoaded(this);
2170                 mIccRecords.set(null);
2171             }
2172             if (newIccRecords != null) {
2173                 log("New records found");
2174                 mIccRecords.set(newIccRecords);
2175                 newIccRecords.registerForRecordsLoaded(
2176                         this, DctConstants.EVENT_RECORDS_LOADED, null);
2177             }
2178         }
2179     }
2180
2181     @Override
2182     protected void log(String s) {
2183         Rlog.d(LOG_TAG, s);
2184     }
2185
2186     @Override
2187     protected void loge(String s) {
2188         Rlog.e(LOG_TAG, s);
2189     }
2190
2191     @Override
2192     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2193         pw.println("DataConnectionTracker extends:");
2194         super.dump(fd, pw, args);
2195         pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
2196         pw.println(" canSetPreferApn=" + mCanSetPreferApn);
2197         pw.println(" mApnObserver=" + mApnObserver);
2198         pw.println(" getOverallState=" + getOverallState());
2199         pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAcHashMap);
2200         pw.println(" mAttached=" + mAttached.get());
2201     }
2202 }