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
17package com.android.internal.telephony.cdma;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.Context;
22import android.content.Intent;
23import android.os.AsyncResult;
24import android.os.Message;
25import android.os.SystemClock;
26import android.os.SystemProperties;
27import android.telephony.ServiceState;
28import android.telephony.TelephonyManager;
29import android.telephony.cdma.CdmaCellLocation;
30import android.text.TextUtils;
31import android.util.EventLog;
32import android.util.Log;
33
34import com.android.internal.telephony.ApnSetting;
35import com.android.internal.telephony.CommandsInterface;
36import com.android.internal.telephony.DataCallState;
37import com.android.internal.telephony.DataConnection.FailCause;
38import com.android.internal.telephony.DataConnection;
39import com.android.internal.telephony.DataConnectionAc;
40import com.android.internal.telephony.DataConnectionTracker;
41import com.android.internal.telephony.DctConstants;
42import com.android.internal.telephony.EventLogTags;
43import com.android.internal.telephony.PhoneConstants;
44import com.android.internal.telephony.IccRecords;
45import com.android.internal.telephony.Phone;
46import com.android.internal.telephony.RetryManager;
47import com.android.internal.telephony.RILConstants;
48import com.android.internal.telephony.UiccCard;
49import com.android.internal.telephony.uicc.UiccController;
50import com.android.internal.util.AsyncChannel;
51
52import java.io.FileDescriptor;
53import java.io.PrintWriter;
54import java.util.ArrayList;
55
56/**
57 * {@hide}
58 */
59public final class CdmaDataConnectionTracker extends DataConnectionTracker {
60    protected final String LOG_TAG = "CDMA";
61
62    private CDMAPhone mCdmaPhone;
63    private CdmaSubscriptionSourceManager mCdmaSSM;
64
65    /** The DataConnection being setup */
66    private CdmaDataConnection mPendingDataConnection;
67
68    private boolean mPendingRestartRadio = false;
69    private static final int TIME_DELAYED_TO_RESTART_RADIO =
70            SystemProperties.getInt("ro.cdma.timetoradiorestart", 60000);
71
72    /**
73     * Pool size of CdmaDataConnection objects.
74     */
75    private static final int DATA_CONNECTION_POOL_SIZE = 1;
76
77    private static final String INTENT_RECONNECT_ALARM =
78        "com.android.internal.telephony.cdma-reconnect";
79
80    private static final String INTENT_DATA_STALL_ALARM =
81        "com.android.internal.telephony.cdma-data-stall";
82
83    private static final String[] mSupportedApnTypes = {
84            PhoneConstants.APN_TYPE_DEFAULT,
85            PhoneConstants.APN_TYPE_MMS,
86            PhoneConstants.APN_TYPE_DUN,
87            PhoneConstants.APN_TYPE_HIPRI };
88
89    private static final String[] mDefaultApnTypes = {
90            PhoneConstants.APN_TYPE_DEFAULT,
91            PhoneConstants.APN_TYPE_MMS,
92            PhoneConstants.APN_TYPE_HIPRI };
93
94    private String[] mDunApnTypes = {
95            PhoneConstants.APN_TYPE_DUN };
96
97    private static final int mDefaultApnId = DctConstants.APN_DEFAULT_ID;
98
99    /* Constructor */
100
101    CdmaDataConnectionTracker(CDMAPhone p) {
102        super(p);
103        mCdmaPhone = p;
104
105        p.mCM.registerForAvailable (this, DctConstants.EVENT_RADIO_AVAILABLE, null);
106        p.mCM.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
107        p.mCM.registerForDataNetworkStateChanged (this, DctConstants.EVENT_DATA_STATE_CHANGED, null);
108        p.mCT.registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED, null);
109        p.mCT.registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED, null);
110        p.mSST.registerForDataConnectionAttached(this, DctConstants.EVENT_TRY_SETUP_DATA, null);
111        p.mSST.registerForDataConnectionDetached(this, DctConstants.EVENT_CDMA_DATA_DETACHED, null);
112        p.mSST.registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
113        p.mSST.registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF, null);
114        p.mCM.registerForCdmaOtaProvision(this, DctConstants.EVENT_CDMA_OTA_PROVISION, null);
115        mCdmaSSM = CdmaSubscriptionSourceManager.getInstance (p.getContext(), p.mCM, this,
116                DctConstants.EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
117
118        mDataConnectionTracker = this;
119
120        createAllDataConnectionList();
121        broadcastMessenger();
122
123        Context c = mCdmaPhone.getContext();
124        String[] t = c.getResources().getStringArray(
125                com.android.internal.R.array.config_cdma_dun_supported_types);
126        if (t != null && t.length > 0) {
127            ArrayList<String> temp = new ArrayList<String>();
128            for(int i=0; i< t.length; i++) {
129                if (!PhoneConstants.APN_TYPE_DUN.equalsIgnoreCase(t[i])) {
130                    temp.add(t[i]);
131                }
132            }
133            temp.add(0,PhoneConstants.APN_TYPE_DUN);
134            mDunApnTypes = temp.toArray(t);
135        }
136
137    }
138
139    @Override
140    public void dispose() {
141        cleanUpConnection(true, null, false);
142
143        super.dispose();
144
145        // Unregister from all events
146        mPhone.mCM.unregisterForAvailable(this);
147        mPhone.mCM.unregisterForOffOrNotAvailable(this);
148        IccRecords r = mIccRecords.get();
149        if (r != null) { r.unregisterForRecordsLoaded(this);}
150        mPhone.mCM.unregisterForDataNetworkStateChanged(this);
151        mCdmaPhone.mCT.unregisterForVoiceCallEnded(this);
152        mCdmaPhone.mCT.unregisterForVoiceCallStarted(this);
153        mCdmaPhone.mSST.unregisterForDataConnectionAttached(this);
154        mCdmaPhone.mSST.unregisterForDataConnectionDetached(this);
155        mCdmaPhone.mSST.unregisterForRoamingOn(this);
156        mCdmaPhone.mSST.unregisterForRoamingOff(this);
157        mCdmaSSM.dispose(this);
158        mPhone.mCM.unregisterForCdmaOtaProvision(this);
159
160        destroyAllDataConnectionList();
161    }
162
163    @Override
164    protected void finalize() {
165        if(DBG) log("CdmaDataConnectionTracker finalized");
166    }
167
168    @Override
169    protected String getActionIntentReconnectAlarm() {
170        return INTENT_RECONNECT_ALARM;
171    }
172
173    @Override
174    protected String getActionIntentDataStallAlarm() {
175        return INTENT_DATA_STALL_ALARM;
176    }
177
178    @Override
179    protected void restartDataStallAlarm() {}
180
181    @Override
182    protected void setState(DctConstants.State s) {
183        if (DBG) log ("setState: " + s);
184        if (mState != s) {
185            EventLog.writeEvent(EventLogTags.CDMA_DATA_STATE_CHANGE,
186                    mState.toString(), s.toString());
187            mState = s;
188        }
189    }
190
191    @Override
192    public synchronized DctConstants.State getState(String apnType) {
193        return mState;
194    }
195
196    @Override
197    public DctConstants.State getOverallState() {
198        return mState;
199    }
200
201    @Override
202    protected boolean isApnTypeAvailable(String type) {
203        for (String s : mSupportedApnTypes) {
204            if (TextUtils.equals(type, s)) {
205                return true;
206            }
207        }
208        return false;
209    }
210
211    @Override
212    protected boolean isDataAllowed() {
213        final boolean internalDataEnabled;
214        synchronized (mDataEnabledLock) {
215            internalDataEnabled = mInternalDataEnabled;
216        }
217
218        int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
219        boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
220        boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
221        boolean subscriptionFromNv = (mCdmaSSM.getCdmaSubscriptionSource()
222                                       == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_NV);
223
224        IccRecords r = mIccRecords.get();
225        boolean allowed =
226                    (psState == ServiceState.STATE_IN_SERVICE ||
227                            mAutoAttachOnCreation) &&
228                    (subscriptionFromNv ||
229                            (r != null && r.getRecordsLoaded())) &&
230                    (mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
231                            mPhone.getState() ==PhoneConstants.State.IDLE) &&
232                    !roaming &&
233                    internalDataEnabled &&
234                    desiredPowerState &&
235                    !mPendingRestartRadio &&
236                    ((mPhone.getLteOnCdmaMode() ==PhoneConstants.LTE_ON_CDMA_TRUE) ||
237                            !mCdmaPhone.needsOtaServiceProvisioning());
238        if (!allowed && DBG) {
239            String reason = "";
240            if (!((psState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
241                reason += " - psState= " + psState;
242            }
243            if (!subscriptionFromNv &&
244                    !(r != null && r.getRecordsLoaded())) {
245                reason += " - RUIM not loaded";
246            }
247            if (!(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
248                    mPhone.getState() ==PhoneConstants.State.IDLE)) {
249                reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
250            }
251            if (roaming) reason += " - Roaming";
252            if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
253            if (!desiredPowerState) reason += " - desiredPowerState= false";
254            if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
255            if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
256            log("Data not allowed due to" + reason);
257        }
258        return allowed;
259    }
260
261    @Override
262    protected boolean isDataPossible(String apnType) {
263        boolean possible = isDataAllowed() && !(getAnyDataEnabled() &&
264                mState == DctConstants.State.FAILED);
265        if (!possible && DBG && isDataAllowed()) {
266            log("Data not possible.  No coverage: dataState = " + mState);
267        }
268        return possible;
269    }
270
271    private boolean trySetupData(String reason) {
272        if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
273
274        if (mPhone.getSimulatedRadioControl() != null) {
275            // Assume data is connected on the simulator
276            // FIXME  this can be improved
277            setState(DctConstants.State.CONNECTED);
278            notifyDataConnection(reason);
279            notifyOffApnsOfAvailability(reason);
280
281            log("(fix?) We're on the simulator; assuming data is connected");
282            return true;
283        }
284
285        int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
286        boolean roaming = mPhone.getServiceState().getRoaming();
287        boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
288
289        if ((mState == DctConstants.State.IDLE || mState == DctConstants.State.SCANNING) &&
290                isDataAllowed() && getAnyDataEnabled() && !isEmergency()) {
291            boolean retValue = setupData(reason);
292            notifyOffApnsOfAvailability(reason);
293            return retValue;
294        } else {
295            notifyOffApnsOfAvailability(reason);
296            return false;
297        }
298    }
299
300    /**
301     * Cleanup the CDMA data connection (only one is supported)
302     *
303     * @param tearDown true if the underlying DataConnection should be disconnected.
304     * @param reason for the clean up.
305     * @param doAll Set RefCount to 0 and tear down data call even if
306     *              multiple APN types are associated with it.
307     */
308    private void cleanUpConnection(boolean tearDown, String reason, boolean doAll) {
309        if (DBG) log("cleanUpConnection: reason: " + reason);
310
311        // Clear the reconnect alarm, if set.
312        if (mReconnectIntent != null) {
313            AlarmManager am =
314                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
315            am.cancel(mReconnectIntent);
316            mReconnectIntent = null;
317        }
318
319        setState(DctConstants.State.DISCONNECTING);
320        notifyOffApnsOfAvailability(reason);
321
322        boolean notificationDeferred = false;
323        for (DataConnection conn : mDataConnections.values()) {
324            if(conn != null) {
325                DataConnectionAc dcac =
326                    mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
327                if (tearDown) {
328                    if (doAll) {
329                        if (DBG) log("cleanUpConnection: teardown, conn.tearDownAll");
330                        conn.tearDownAll(reason, obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
331                                conn.getDataConnectionId(), 0, reason));
332                    } else {
333                        if (DBG) log("cleanUpConnection: teardown, conn.tearDown");
334                        conn.tearDown(reason, obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
335                                conn.getDataConnectionId(), 0, reason));
336                    }
337                    notificationDeferred = true;
338                } else {
339                    if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
340                    if (dcac != null) {
341                        dcac.resetSync();
342                    }
343                    notificationDeferred = false;
344                }
345            }
346        }
347
348        stopNetStatPoll();
349        stopDataStallAlarm();
350
351        if (!notificationDeferred) {
352            if (DBG) log("cleanupConnection: !notificationDeferred");
353            gotoIdleAndNotifyDataConnection(reason);
354        }
355    }
356
357    private CdmaDataConnection findFreeDataConnection() {
358        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
359            if (dcac.isInactiveSync()) {
360                log("found free GsmDataConnection");
361                return (CdmaDataConnection) dcac.dataConnection;
362            }
363        }
364        log("NO free CdmaDataConnection");
365        return null;
366    }
367
368    private boolean setupData(String reason) {
369        CdmaDataConnection conn = findFreeDataConnection();
370
371        if (conn == null) {
372            if (DBG) log("setupData: No free CdmaDataConnection found!");
373            return false;
374        }
375
376        /** TODO: We probably want the connection being setup to a parameter passed around */
377        mPendingDataConnection = conn;
378        String[] types;
379        int apnId;
380        if (mRequestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
381            types = mDunApnTypes;
382            apnId = DctConstants.APN_DUN_ID;
383        } else {
384            types = mDefaultApnTypes;
385            apnId = mDefaultApnId;
386        }
387        mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "",
388                                    "", 0, types, "IP", "IP", true, 0);
389        if (DBG) log("call conn.bringUp mActiveApn=" + mActiveApn);
390
391        Message msg = obtainMessage();
392        msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
393        msg.obj = reason;
394        conn.bringUp(msg, mActiveApn);
395
396        setState(DctConstants.State.INITING);
397        notifyDataConnection(reason);
398        return true;
399    }
400
401    private void notifyDefaultData(String reason) {
402        setState(DctConstants.State.CONNECTED);
403        notifyDataConnection(reason);
404        startNetStatPoll();
405        startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
406        mDataConnections.get(0).resetRetryCount();
407    }
408
409    @Override
410    protected void restartRadio() {
411        if (DBG) log("Cleanup connection and wait " +
412                (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio");
413        cleanUpAllConnections(null);
414        sendEmptyMessageDelayed(DctConstants.EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO);
415        mPendingRestartRadio = true;
416    }
417
418    /**
419     * Returns true if the last fail cause is something that
420     * seems like it deserves an error notification.
421     * Transient errors are ignored
422     */
423    private boolean
424    shouldPostNotification(FailCause cause) {
425        return (cause != FailCause.UNKNOWN);
426    }
427
428    /**
429     * Return true if data connection need to be setup after disconnected due to
430     * reason.
431     *
432     * @param reason the reason why data is disconnected
433     * @return true if try setup data connection is need for this reason
434     */
435    private boolean retryAfterDisconnected(String reason) {
436        boolean retry = true;
437
438        if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
439            retry = false;
440        }
441        return retry;
442    }
443
444    private void reconnectAfterFail(FailCause lastFailCauseCode, String reason, int retryOverride) {
445        if (mState == DctConstants.State.FAILED) {
446            /**
447             * For now With CDMA we never try to reconnect on
448             * error and instead just continue to retry
449             * at the last time until the state is changed.
450             * TODO: Make this configurable?
451             */
452            int nextReconnectDelay = retryOverride;
453            if (nextReconnectDelay < 0) {
454                nextReconnectDelay = mDataConnections.get(0).getRetryTimer();
455                mDataConnections.get(0).increaseRetryCount();
456            }
457            startAlarmForReconnect(nextReconnectDelay, reason);
458
459            if (!shouldPostNotification(lastFailCauseCode)) {
460                log("NOT Posting Data Connection Unavailable notification "
461                                + "-- likely transient error");
462            } else {
463                notifyNoData(lastFailCauseCode);
464            }
465        }
466    }
467
468    private void startAlarmForReconnect(int delay, String reason) {
469
470        log("Data Connection activate failed. Scheduling next attempt for "
471                + (delay / 1000) + "s");
472
473        AlarmManager am =
474            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
475        Intent intent = new Intent(INTENT_RECONNECT_ALARM);
476        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
477        mReconnectIntent = PendingIntent.getBroadcast(
478                mPhone.getContext(), 0, intent, 0);
479        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
480                SystemClock.elapsedRealtime() + delay, mReconnectIntent);
481
482    }
483
484    private void notifyNoData(FailCause lastFailCauseCode) {
485        setState(DctConstants.State.FAILED);
486        notifyOffApnsOfAvailability(null);
487    }
488
489    protected void gotoIdleAndNotifyDataConnection(String reason) {
490        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
491        setState(DctConstants.State.IDLE);
492        notifyDataConnection(reason);
493        mActiveApn = null;
494    }
495
496    protected void onRecordsLoaded() {
497        if (mState == DctConstants.State.FAILED) {
498            cleanUpAllConnections(null);
499        }
500        sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
501    }
502
503    protected void onNVReady() {
504        if (mState == DctConstants.State.FAILED) {
505            cleanUpAllConnections(null);
506        }
507        sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA));
508    }
509
510    /**
511     * @override com.android.internal.telephony.DataConnectionTracker
512     */
513    @Override
514    protected void onEnableNewApn() {
515        // No mRequestedApnType check; only one connection is supported
516        cleanUpConnection(true, Phone.REASON_APN_SWITCHED, false);
517    }
518
519    /**
520     * @override com.android.internal.telephony.DataConnectionTracker
521     */
522    @Override
523    protected boolean onTrySetupData(String reason) {
524        return trySetupData(reason);
525    }
526
527    /**
528     * @override com.android.internal.telephony.DataConnectionTracker
529     */
530    @Override
531    protected void onRoamingOff() {
532        if (mUserDataEnabled == false) return;
533
534        if (getDataOnRoamingEnabled() == false) {
535            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
536            trySetupData(Phone.REASON_ROAMING_OFF);
537        } else {
538            notifyDataConnection(Phone.REASON_ROAMING_OFF);
539        }
540    }
541
542    /**
543     * @override com.android.internal.telephony.DataConnectionTracker
544     */
545    @Override
546    protected void onRoamingOn() {
547        if (mUserDataEnabled == false) return;
548
549        if (getDataOnRoamingEnabled()) {
550            trySetupData(Phone.REASON_ROAMING_ON);
551            notifyDataConnection(Phone.REASON_ROAMING_ON);
552        } else {
553            if (DBG) log("Tear down data connection on roaming.");
554            cleanUpAllConnections(null);
555            notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
556        }
557    }
558
559    /**
560     * @override com.android.internal.telephony.DataConnectionTracker
561     */
562    @Override
563    protected void onRadioAvailable() {
564        if (mPhone.getSimulatedRadioControl() != null) {
565            // Assume data is connected on the simulator
566            // FIXME  this can be improved
567            setState(DctConstants.State.CONNECTED);
568            notifyDataConnection(null);
569
570            log("We're on the simulator; assuming data is connected");
571        }
572
573        notifyOffApnsOfAvailability(null);
574
575        if (mState != DctConstants.State.IDLE) {
576            cleanUpAllConnections(null);
577        }
578    }
579
580    /**
581     * @override com.android.internal.telephony.DataConnectionTracker
582     */
583    @Override
584    protected void onRadioOffOrNotAvailable() {
585        mDataConnections.get(0).resetRetryCount();
586
587        if (mPhone.getSimulatedRadioControl() != null) {
588            // Assume data is connected on the simulator
589            // FIXME  this can be improved
590            log("We're on the simulator; assuming radio off is meaningless");
591        } else {
592            if (DBG) log("Radio is off and clean up all connection");
593            cleanUpAllConnections(null);
594        }
595    }
596
597    /**
598     * @override com.android.internal.telephony.DataConnectionTracker
599     */
600    @Override
601    protected void onDataSetupComplete(AsyncResult ar) {
602        String reason = null;
603        if (ar.userObj instanceof String) {
604            reason = (String) ar.userObj;
605        }
606
607        if (isDataSetupCompleteOk(ar)) {
608            // Everything is setup
609            notifyDefaultData(reason);
610        } else {
611            FailCause cause = (FailCause) (ar.result);
612            if(DBG) log("Data Connection setup failed " + cause);
613
614            // No try for permanent failure
615            if (cause.isPermanentFail()) {
616                notifyNoData(cause);
617                return;
618            }
619
620            int retryOverride = -1;
621            if (ar.exception instanceof DataConnection.CallSetupException) {
622                retryOverride =
623                    ((DataConnection.CallSetupException)ar.exception).getRetryOverride();
624            }
625            if (retryOverride == RILConstants.MAX_INT) {
626                if (DBG) log("No retry is suggested.");
627            } else {
628                startDelayedRetry(cause, reason, retryOverride);
629            }
630        }
631    }
632
633    /**
634     * Called when DctConstants.EVENT_DISCONNECT_DONE is received.
635     */
636    @Override
637    protected void onDisconnectDone(int connId, AsyncResult ar) {
638        if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
639        String reason = null;
640        if (ar.userObj instanceof String) {
641            reason = (String) ar.userObj;
642        }
643        setState(DctConstants.State.IDLE);
644
645        // Since the pending request to turn off or restart radio will be processed here,
646        // remove the pending event to restart radio from the message queue.
647        if (mPendingRestartRadio) removeMessages(DctConstants.EVENT_RESTART_RADIO);
648
649        // Process the pending request to turn off radio in ServiceStateTracker first.
650        // If radio is turned off in ServiceStateTracker, ignore the pending event to restart radio.
651        CdmaServiceStateTracker ssTracker = mCdmaPhone.mSST;
652        if (ssTracker.processPendingRadioPowerOffAfterDataOff()) {
653            mPendingRestartRadio = false;
654        } else {
655            onRestartRadio();
656        }
657
658        notifyDataConnection(reason);
659        mActiveApn = null;
660        if (retryAfterDisconnected(reason)) {
661          // Wait a bit before trying, so we're not tying up RIL command channel.
662          startAlarmForReconnect(APN_DELAY_MILLIS, reason);
663      }
664    }
665
666    /**
667     * @override com.android.internal.telephony.DataConnectionTracker
668     */
669    @Override
670    protected void onVoiceCallStarted() {
671        if (mState == DctConstants.State.CONNECTED &&
672                !mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
673            stopNetStatPoll();
674            stopDataStallAlarm();
675            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
676            notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_STARTED);
677        }
678    }
679
680    /**
681     * @override com.android.internal.telephony.DataConnectionTracker
682     */
683    @Override
684    protected void onVoiceCallEnded() {
685        if (mState == DctConstants.State.CONNECTED) {
686            if (!mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
687                startNetStatPoll();
688                startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
689                notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
690            } else {
691                // clean slate after call end.
692                resetPollStats();
693            }
694            notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_ENDED);
695        } else {
696            mDataConnections.get(0).resetRetryCount();
697            // in case data setup was attempted when we were on a voice call
698            trySetupData(Phone.REASON_VOICE_CALL_ENDED);
699        }
700    }
701
702    @Override
703    protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
704        // No apnId check; only one connection is supported
705        cleanUpConnection(tearDown, reason, (apnId == DctConstants.APN_DUN_ID));
706    }
707
708    @Override
709    protected void onCleanUpAllConnections(String cause) {
710        // Only one CDMA connection is supported
711        cleanUpConnection(true, cause, false);
712    }
713
714    private void createAllDataConnectionList() {
715        CdmaDataConnection dataConn;
716
717        String retryConfig = SystemProperties.get("ro.cdma.data_retry_config");
718        for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) {
719            RetryManager rm = new RetryManager();
720            if (!rm.configure(retryConfig)) {
721                if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
722                    // Should never happen, log an error and default to a simple linear sequence.
723                    log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
724                            + DEFAULT_DATA_RETRY_CONFIG);
725                    rm.configure(20, 2000, 1000);
726                }
727            }
728
729            int id = mUniqueIdGenerator.getAndIncrement();
730            dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm, this);
731            mDataConnections.put(id, dataConn);
732            DataConnectionAc dcac = new DataConnectionAc(dataConn, LOG_TAG);
733            int status = dcac.fullyConnectSync(mPhone.getContext(), this, dataConn.getHandler());
734            if (status == AsyncChannel.STATUS_SUCCESSFUL) {
735                log("Fully connected");
736                mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
737            } else {
738                log("Could not connect to dcac.dataConnection=" + dcac.dataConnection +
739                        " status=" + status);
740            }
741
742        }
743    }
744
745    private void destroyAllDataConnectionList() {
746        if(mDataConnections != null) {
747            mDataConnections.clear();
748        }
749    }
750
751    private void onCdmaDataDetached() {
752        if (mState == DctConstants.State.CONNECTED) {
753            startNetStatPoll();
754            startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
755            notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED);
756        } else {
757            if (mState == DctConstants.State.FAILED) {
758                cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED, false);
759                mDataConnections.get(0).resetRetryCount();
760
761                CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
762                EventLog.writeEvent(EventLogTags.CDMA_DATA_SETUP_FAILED,
763                        loc != null ? loc.getBaseStationId() : -1,
764                        TelephonyManager.getDefault().getNetworkType());
765            }
766            trySetupData(Phone.REASON_CDMA_DATA_DETACHED);
767        }
768    }
769
770    private void onCdmaOtaProvision(AsyncResult ar) {
771        if (ar.exception != null) {
772            int [] otaPrivision = (int [])ar.result;
773            if ((otaPrivision != null) && (otaPrivision.length > 1)) {
774                switch (otaPrivision[0]) {
775                case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
776                case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
777                    mDataConnections.get(0).resetRetryCount();
778                    break;
779                default:
780                    break;
781                }
782            }
783        }
784    }
785
786    private void onRestartRadio() {
787        if (mPendingRestartRadio) {
788            log("************TURN OFF RADIO**************");
789            mPhone.mCM.setRadioPower(false, null);
790            /* Note: no need to call setRadioPower(true).  Assuming the desired
791             * radio power state is still ON (as tracked by ServiceStateTracker),
792             * ServiceStateTracker will call setRadioPower when it receives the
793             * RADIO_STATE_CHANGED notification for the power off.  And if the
794             * desired power state has changed in the interim, we don't want to
795             * override it with an unconditional power on.
796             */
797            mPendingRestartRadio = false;
798        }
799    }
800
801    private void writeEventLogCdmaDataDrop() {
802        CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
803        EventLog.writeEvent(EventLogTags.CDMA_DATA_DROP,
804                loc != null ? loc.getBaseStationId() : -1,
805                TelephonyManager.getDefault().getNetworkType());
806    }
807
808    protected void onDataStateChanged(AsyncResult ar) {
809        ArrayList<DataCallState> dataCallStates = (ArrayList<DataCallState>)(ar.result);
810
811        if (ar.exception != null) {
812            // This is probably "radio not available" or something
813            // of that sort. If so, the whole connection is going
814            // to come down soon anyway
815            return;
816        }
817
818        if (mState == DctConstants.State.CONNECTED) {
819            boolean isActiveOrDormantConnectionPresent = false;
820            int connectionState = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE;
821
822            // Check for an active or dormant connection element in
823            // the DATA_CALL_LIST array
824            for (int index = 0; index < dataCallStates.size(); index++) {
825                connectionState = dataCallStates.get(index).active;
826                if (connectionState != DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
827                    isActiveOrDormantConnectionPresent = true;
828                    break;
829                }
830            }
831
832            if (!isActiveOrDormantConnectionPresent) {
833                // No active or dormant connection
834                log("onDataStateChanged: No active connection"
835                        + "state is CONNECTED, disconnecting/cleanup");
836                writeEventLogCdmaDataDrop();
837                cleanUpConnection(true, null, false);
838                return;
839            }
840
841            switch (connectionState) {
842                case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
843                    log("onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore");
844                    mActivity = DctConstants.Activity.NONE;
845                    mPhone.notifyDataActivity();
846                    startNetStatPoll();
847                    startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
848                    break;
849
850                case DATA_CONNECTION_ACTIVE_PH_LINK_DOWN:
851                    log("onDataStateChanged active=LINK_DOWN && CONNECTED, dormant");
852                    mActivity = DctConstants.Activity.DORMANT;
853                    mPhone.notifyDataActivity();
854                    stopNetStatPoll();
855                    stopDataStallAlarm();
856                    break;
857
858                default:
859                    log("onDataStateChanged: IGNORE unexpected DataCallState.active="
860                            + connectionState);
861            }
862        } else {
863            // TODO: Do we need to do anything?
864            log("onDataStateChanged: not connected, state=" + mState + " ignoring");
865        }
866    }
867
868    private void startDelayedRetry(FailCause cause, String reason, int retryOverride) {
869        notifyNoData(cause);
870        reconnectAfterFail(cause, reason, retryOverride);
871    }
872
873    @Override
874    public void handleMessage (Message msg) {
875        if (DBG) log("CdmaDCT handleMessage msg=" + msg);
876
877        if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
878            log("Ignore CDMA msgs since CDMA phone is inactive");
879            return;
880        }
881
882        switch (msg.what) {
883            case DctConstants.EVENT_RECORDS_LOADED:
884                onRecordsLoaded();
885                break;
886
887            case DctConstants.EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
888                if(mCdmaSSM.getCdmaSubscriptionSource() ==
889                       CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_NV) {
890                    onNVReady();
891                }
892                break;
893
894            case DctConstants.EVENT_CDMA_DATA_DETACHED:
895                onCdmaDataDetached();
896                break;
897
898            case DctConstants.EVENT_DATA_STATE_CHANGED:
899                onDataStateChanged((AsyncResult) msg.obj);
900                break;
901
902            case DctConstants.EVENT_CDMA_OTA_PROVISION:
903                onCdmaOtaProvision((AsyncResult) msg.obj);
904                break;
905
906            case DctConstants.EVENT_RESTART_RADIO:
907                if (DBG) log("EVENT_RESTART_RADIO");
908                onRestartRadio();
909                break;
910
911            default:
912                // handle the message in the super class DataConnectionTracker
913                super.handleMessage(msg);
914                break;
915        }
916    }
917
918    @Override
919    protected void onUpdateIcc() {
920        if (mUiccController == null ) {
921            return;
922        }
923
924        IccRecords newIccRecords = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP2);
925
926        IccRecords r = mIccRecords.get();
927        if (r != newIccRecords) {
928            if (r != null) {
929                log("Removing stale icc objects.");
930                r.unregisterForRecordsLoaded(this);
931                mIccRecords.set(null);
932            }
933            if (newIccRecords != null) {
934                log("New records found");
935                mIccRecords.set(newIccRecords);
936                newIccRecords.registerForRecordsLoaded(
937                        this, DctConstants.EVENT_RECORDS_LOADED, null);
938            }
939        }
940    }
941
942    @Override
943    public boolean isDisconnected() {
944        return ((mState == DctConstants.State.IDLE) || (mState == DctConstants.State.FAILED));
945    }
946
947    @Override
948    protected boolean isConnected() {
949        return (mState == DctConstants.State.CONNECTED);
950    }
951
952    @Override
953    protected void log(String s) {
954        Log.d(LOG_TAG, "[CdmaDCT] " + s);
955    }
956
957    @Override
958    protected void loge(String s) {
959        Log.e(LOG_TAG, "[CdmaDCT] " + s);
960    }
961
962    @Override
963    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
964        pw.println("CdmaDataConnectionTracker extends:");
965        super.dump(fd, pw, args);
966        pw.println(" mCdmaPhone=" + mCdmaPhone);
967        pw.println(" mCdmaSSM=" + mCdmaSSM);
968        pw.println(" mPendingDataConnection=" + mPendingDataConnection);
969        pw.println(" mPendingRestartRadio=" + mPendingRestartRadio);
970        pw.println(" mSupportedApnTypes=" + mSupportedApnTypes);
971        pw.println(" mDefaultApnTypes=" + mDefaultApnTypes);
972        pw.println(" mDunApnTypes=" + mDunApnTypes);
973        pw.println(" mDefaultApnId=" + mDefaultApnId);
974    }
975}
976