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