1/*
2 * Copyright (C) 2008 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 android.net;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.NetworkInfo.DetailedState;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Looper;
27import android.os.Message;
28import android.os.Messenger;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.telephony.PhoneStateListener;
32import android.telephony.SignalStrength;
33import android.telephony.TelephonyManager;
34import android.text.TextUtils;
35import android.util.Slog;
36
37import com.android.internal.telephony.DctConstants;
38import com.android.internal.telephony.ITelephony;
39import com.android.internal.telephony.PhoneConstants;
40import com.android.internal.telephony.TelephonyIntents;
41import com.android.internal.util.AsyncChannel;
42
43import java.io.CharArrayWriter;
44import java.io.PrintWriter;
45import java.util.concurrent.atomic.AtomicBoolean;
46
47/**
48 * Track the state of mobile data connectivity. This is done by
49 * receiving broadcast intents from the Phone process whenever
50 * the state of data connectivity changes.
51 *
52 * {@hide}
53 */
54public class MobileDataStateTracker extends BaseNetworkStateTracker {
55
56    private static final String TAG = "MobileDataStateTracker";
57    private static final boolean DBG = true;
58    private static final boolean VDBG = false;
59
60    private PhoneConstants.DataState mMobileDataState;
61    private ITelephony mPhoneService;
62
63    private String mApnType;
64    private NetworkInfo mNetworkInfo;
65    private boolean mTeardownRequested = false;
66    private Handler mTarget;
67    private Context mContext;
68    private LinkProperties mLinkProperties;
69    private LinkCapabilities mLinkCapabilities;
70    private boolean mPrivateDnsRouteSet = false;
71    private boolean mDefaultRouteSet = false;
72
73    // NOTE: these are only kept for debugging output; actual values are
74    // maintained in DataConnectionTracker.
75    protected boolean mUserDataEnabled = true;
76    protected boolean mPolicyDataEnabled = true;
77
78    private Handler mHandler;
79    private AsyncChannel mDataConnectionTrackerAc;
80
81    private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
82
83    private SignalStrength mSignalStrength;
84
85    private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
86
87    private static final int UNKNOWN = LinkQualityInfo.UNKNOWN_INT;
88
89    /**
90     * Create a new MobileDataStateTracker
91     * @param netType the ConnectivityManager network type
92     * @param tag the name of this network
93     */
94    public MobileDataStateTracker(int netType, String tag) {
95        mNetworkInfo = new NetworkInfo(netType,
96                TelephonyManager.getDefault().getNetworkType(), tag,
97                TelephonyManager.getDefault().getNetworkTypeName());
98        mApnType = networkTypeToApnType(netType);
99    }
100
101    /**
102     * Begin monitoring data connectivity.
103     *
104     * @param context is the current Android context
105     * @param target is the Hander to which to return the events.
106     */
107    public void startMonitoring(Context context, Handler target) {
108        mTarget = target;
109        mContext = context;
110
111        mHandler = new MdstHandler(target.getLooper(), this);
112
113        IntentFilter filter = new IntentFilter();
114        filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
115        filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
116        filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
117
118        mContext.registerReceiver(new MobileDataStateReceiver(), filter);
119        mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
120
121        TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
122                Context.TELEPHONY_SERVICE);
123        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
124    }
125
126    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
127        @Override
128        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
129            mSignalStrength = signalStrength;
130        }
131    };
132
133    static class MdstHandler extends Handler {
134        private MobileDataStateTracker mMdst;
135
136        MdstHandler(Looper looper, MobileDataStateTracker mdst) {
137            super(looper);
138            mMdst = mdst;
139        }
140
141        @Override
142        public void handleMessage(Message msg) {
143            switch (msg.what) {
144                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
145                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
146                        if (VDBG) {
147                            mMdst.log("MdstHandler connected");
148                        }
149                        mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
150                    } else {
151                        if (VDBG) {
152                            mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
153                        }
154                    }
155                    break;
156                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
157                    if (VDBG) mMdst.log("Disconnected from DataStateTracker");
158                    mMdst.mDataConnectionTrackerAc = null;
159                    break;
160                default: {
161                    if (VDBG) mMdst.log("Ignorning unknown message=" + msg);
162                    break;
163                }
164            }
165        }
166    }
167
168    public boolean isPrivateDnsRouteSet() {
169        return mPrivateDnsRouteSet;
170    }
171
172    public void privateDnsRouteSet(boolean enabled) {
173        mPrivateDnsRouteSet = enabled;
174    }
175
176    public NetworkInfo getNetworkInfo() {
177        return mNetworkInfo;
178    }
179
180    public boolean isDefaultRouteSet() {
181        return mDefaultRouteSet;
182    }
183
184    public void defaultRouteSet(boolean enabled) {
185        mDefaultRouteSet = enabled;
186    }
187
188    /**
189     * This is not implemented.
190     */
191    public void releaseWakeLock() {
192    }
193
194    private void updateLinkProperitesAndCapatilities(Intent intent) {
195        mLinkProperties = intent.getParcelableExtra(
196                PhoneConstants.DATA_LINK_PROPERTIES_KEY);
197        if (mLinkProperties == null) {
198            loge("CONNECTED event did not supply link properties.");
199            mLinkProperties = new LinkProperties();
200        }
201        mLinkProperties.setMtu(mContext.getResources().getInteger(
202                com.android.internal.R.integer.config_mobile_mtu));
203        mLinkCapabilities = intent.getParcelableExtra(
204                PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
205        if (mLinkCapabilities == null) {
206            loge("CONNECTED event did not supply link capabilities.");
207            mLinkCapabilities = new LinkCapabilities();
208        }
209    }
210
211    private class MobileDataStateReceiver extends BroadcastReceiver {
212        @Override
213        public void onReceive(Context context, Intent intent) {
214            if (intent.getAction().equals(TelephonyIntents.
215                    ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) {
216                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
217                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
218                if (!TextUtils.equals(mApnType, apnType)) {
219                    return;
220                }
221                if (DBG) {
222                    log("Broadcast received: " + intent.getAction() + " apnType=" + apnType
223                            + " apnName=" + apnName);
224                }
225
226                // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING
227                mMobileDataState = PhoneConstants.DataState.CONNECTING;
228                updateLinkProperitesAndCapatilities(intent);
229                mNetworkInfo.setIsConnectedToProvisioningNetwork(true);
230
231                // Change state to SUSPENDED so setDetailedState
232                // sends EVENT_STATE_CHANGED to connectivityService
233                setDetailedState(DetailedState.SUSPENDED, "", apnName);
234            } else if (intent.getAction().equals(TelephonyIntents.
235                    ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
236                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
237                if (VDBG) {
238                    log(String.format("Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"
239                        + "mApnType=%s %s received apnType=%s", mApnType,
240                        TextUtils.equals(apnType, mApnType) ? "==" : "!=", apnType));
241                }
242                if (!TextUtils.equals(apnType, mApnType)) {
243                    return;
244                }
245                // Assume this isn't a provisioning network.
246                mNetworkInfo.setIsConnectedToProvisioningNetwork(false);
247                if (DBG) {
248                    log("Broadcast received: " + intent.getAction() + " apnType=" + apnType);
249                }
250
251                int oldSubtype = mNetworkInfo.getSubtype();
252                int newSubType = TelephonyManager.getDefault().getNetworkType();
253                String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
254                mNetworkInfo.setSubtype(newSubType, subTypeName);
255                if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
256                    Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
257                                                        oldSubtype, 0, mNetworkInfo);
258                    msg.sendToTarget();
259                }
260
261                PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
262                        intent.getStringExtra(PhoneConstants.STATE_KEY));
263                String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
264                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
265                mNetworkInfo.setRoaming(intent.getBooleanExtra(
266                        PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
267                if (VDBG) {
268                    log(mApnType + " setting isAvailable to " +
269                            intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
270                }
271                mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(
272                        PhoneConstants.NETWORK_UNAVAILABLE_KEY, false));
273
274                if (DBG) {
275                    log("Received state=" + state + ", old=" + mMobileDataState +
276                        ", reason=" + (reason == null ? "(unspecified)" : reason));
277                }
278                if (mMobileDataState != state) {
279                    mMobileDataState = state;
280                    switch (state) {
281                        case DISCONNECTED:
282                            if(isTeardownRequested()) {
283                                setTeardownRequested(false);
284                            }
285
286                            setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
287                            // can't do this here - ConnectivityService needs it to clear stuff
288                            // it's ok though - just leave it to be refreshed next time
289                            // we connect.
290                            //if (DBG) log("clearing mInterfaceName for "+ mApnType +
291                            //        " as it DISCONNECTED");
292                            //mInterfaceName = null;
293                            break;
294                        case CONNECTING:
295                            setDetailedState(DetailedState.CONNECTING, reason, apnName);
296                            break;
297                        case SUSPENDED:
298                            setDetailedState(DetailedState.SUSPENDED, reason, apnName);
299                            break;
300                        case CONNECTED:
301                            updateLinkProperitesAndCapatilities(intent);
302                            setDetailedState(DetailedState.CONNECTED, reason, apnName);
303                            break;
304                    }
305
306                    if (VDBG) {
307                        Slog.d(TAG, "TelephonyMgr.DataConnectionStateChanged");
308                        if (mNetworkInfo != null) {
309                            Slog.d(TAG, "NetworkInfo = " + mNetworkInfo.toString());
310                            Slog.d(TAG, "subType = " + String.valueOf(mNetworkInfo.getSubtype()));
311                            Slog.d(TAG, "subType = " + mNetworkInfo.getSubtypeName());
312                        }
313                        if (mLinkProperties != null) {
314                            Slog.d(TAG, "LinkProperties = " + mLinkProperties.toString());
315                        } else {
316                            Slog.d(TAG, "LinkProperties = " );
317                        }
318
319                        if (mLinkCapabilities != null) {
320                            Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities.toString());
321                        } else {
322                            Slog.d(TAG, "LinkCapabilities = " );
323                        }
324                    }
325
326
327                    /* lets not sample traffic data across state changes */
328                    mSamplingDataTracker.resetSamplingData();
329                } else {
330                    // There was no state change. Check if LinkProperties has been updated.
331                    if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
332                        mLinkProperties = intent.getParcelableExtra(
333                                PhoneConstants.DATA_LINK_PROPERTIES_KEY);
334                        if (mLinkProperties == null) {
335                            loge("No link property in LINK_PROPERTIES change event.");
336                            mLinkProperties = new LinkProperties();
337                        }
338                        // Just update reason field in this NetworkInfo
339                        mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
340                                                      mNetworkInfo.getExtraInfo());
341                        Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
342                                                            mNetworkInfo);
343                        msg.sendToTarget();
344                    }
345                }
346            } else if (intent.getAction().
347                    equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
348                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
349                if (!TextUtils.equals(apnType, mApnType)) {
350                    if (DBG) {
351                        log(String.format(
352                                "Broadcast received: ACTION_ANY_DATA_CONNECTION_FAILED ignore, " +
353                                "mApnType=%s != received apnType=%s", mApnType, apnType));
354                    }
355                    return;
356                }
357                // Assume this isn't a provisioning network.
358                mNetworkInfo.setIsConnectedToProvisioningNetwork(false);
359                String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
360                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
361                if (DBG) {
362                    log("Broadcast received: " + intent.getAction() +
363                                " reason=" + reason == null ? "null" : reason);
364                }
365                setDetailedState(DetailedState.FAILED, reason, apnName);
366            } else {
367                if (DBG) log("Broadcast received: ignore " + intent.getAction());
368            }
369        }
370    }
371
372    private void getPhoneService(boolean forceRefresh) {
373        if ((mPhoneService == null) || forceRefresh) {
374            mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
375        }
376    }
377
378    /**
379     * Report whether data connectivity is possible.
380     */
381    public boolean isAvailable() {
382        return mNetworkInfo.isAvailable();
383    }
384
385    /**
386     * Return the system properties name associated with the tcp buffer sizes
387     * for this network.
388     */
389    public String getTcpBufferSizesPropName() {
390        String networkTypeStr = "unknown";
391        TelephonyManager tm = new TelephonyManager(mContext);
392        //TODO We have to edit the parameter for getNetworkType regarding CDMA
393        switch(tm.getNetworkType()) {
394        case TelephonyManager.NETWORK_TYPE_GPRS:
395            networkTypeStr = "gprs";
396            break;
397        case TelephonyManager.NETWORK_TYPE_EDGE:
398            networkTypeStr = "edge";
399            break;
400        case TelephonyManager.NETWORK_TYPE_UMTS:
401            networkTypeStr = "umts";
402            break;
403        case TelephonyManager.NETWORK_TYPE_HSDPA:
404            networkTypeStr = "hsdpa";
405            break;
406        case TelephonyManager.NETWORK_TYPE_HSUPA:
407            networkTypeStr = "hsupa";
408            break;
409        case TelephonyManager.NETWORK_TYPE_HSPA:
410            networkTypeStr = "hspa";
411            break;
412        case TelephonyManager.NETWORK_TYPE_HSPAP:
413            networkTypeStr = "hspap";
414            break;
415        case TelephonyManager.NETWORK_TYPE_CDMA:
416            networkTypeStr = "cdma";
417            break;
418        case TelephonyManager.NETWORK_TYPE_1xRTT:
419            networkTypeStr = "1xrtt";
420            break;
421        case TelephonyManager.NETWORK_TYPE_EVDO_0:
422            networkTypeStr = "evdo";
423            break;
424        case TelephonyManager.NETWORK_TYPE_EVDO_A:
425            networkTypeStr = "evdo";
426            break;
427        case TelephonyManager.NETWORK_TYPE_EVDO_B:
428            networkTypeStr = "evdo";
429            break;
430        case TelephonyManager.NETWORK_TYPE_IDEN:
431            networkTypeStr = "iden";
432            break;
433        case TelephonyManager.NETWORK_TYPE_LTE:
434            networkTypeStr = "lte";
435            break;
436        case TelephonyManager.NETWORK_TYPE_EHRPD:
437            networkTypeStr = "ehrpd";
438            break;
439        default:
440            loge("unknown network type: " + tm.getNetworkType());
441        }
442        return "net.tcp.buffersize." + networkTypeStr;
443    }
444
445    /**
446     * Tear down mobile data connectivity, i.e., disable the ability to create
447     * mobile data connections.
448     * TODO - make async and return nothing?
449     */
450    public boolean teardown() {
451        setTeardownRequested(true);
452        return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
453    }
454
455    /**
456     * @return true if this is ready to operate
457     */
458    public boolean isReady() {
459        return mDataConnectionTrackerAc != null;
460    }
461
462    @Override
463    public void captivePortalCheckComplete() {
464        // not implemented
465    }
466
467    @Override
468    public void captivePortalCheckCompleted(boolean isCaptivePortal) {
469        if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) {
470            // Captive portal change enable/disable failing fast
471            setEnableFailFastMobileData(
472                    isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED);
473        }
474    }
475
476    /**
477     * Record the detailed state of a network, and if it is a
478     * change from the previous state, send a notification to
479     * any listeners.
480     * @param state the new {@code DetailedState}
481     * @param reason a {@code String} indicating a reason for the state change,
482     * if one was supplied. May be {@code null}.
483     * @param extraInfo optional {@code String} providing extra information about the state change
484     */
485    private void setDetailedState(NetworkInfo.DetailedState state, String reason,
486            String extraInfo) {
487        if (DBG) log("setDetailed state, old ="
488                + mNetworkInfo.getDetailedState() + " and new state=" + state);
489        if (state != mNetworkInfo.getDetailedState()) {
490            boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
491            String lastReason = mNetworkInfo.getReason();
492            /*
493             * If a reason was supplied when the CONNECTING state was entered, and no
494             * reason was supplied for entering the CONNECTED state, then retain the
495             * reason that was supplied when going to CONNECTING.
496             */
497            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
498                    && lastReason != null)
499                reason = lastReason;
500            mNetworkInfo.setDetailedState(state, reason, extraInfo);
501            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
502            msg.sendToTarget();
503        }
504    }
505
506    public void setTeardownRequested(boolean isRequested) {
507        mTeardownRequested = isRequested;
508    }
509
510    public boolean isTeardownRequested() {
511        return mTeardownRequested;
512    }
513
514    /**
515     * Re-enable mobile data connectivity after a {@link #teardown()}.
516     * TODO - make async and always get a notification?
517     */
518    public boolean reconnect() {
519        boolean retValue = false; //connected or expect to be?
520        setTeardownRequested(false);
521        switch (setEnableApn(mApnType, true)) {
522            case PhoneConstants.APN_ALREADY_ACTIVE:
523                // need to set self to CONNECTING so the below message is handled.
524                retValue = true;
525                break;
526            case PhoneConstants.APN_REQUEST_STARTED:
527                // set IDLE here , avoid the following second FAILED not sent out
528                mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
529                retValue = true;
530                break;
531            case PhoneConstants.APN_REQUEST_FAILED:
532            case PhoneConstants.APN_TYPE_NOT_AVAILABLE:
533                break;
534            default:
535                loge("Error in reconnect - unexpected response.");
536                break;
537        }
538        return retValue;
539    }
540
541    /**
542     * Turn on or off the mobile radio. No connectivity will be possible while the
543     * radio is off. The operation is a no-op if the radio is already in the desired state.
544     * @param turnOn {@code true} if the radio should be turned on, {@code false} if
545     */
546    public boolean setRadio(boolean turnOn) {
547        getPhoneService(false);
548        /*
549         * If the phone process has crashed in the past, we'll get a
550         * RemoteException and need to re-reference the service.
551         */
552        for (int retry = 0; retry < 2; retry++) {
553            if (mPhoneService == null) {
554                loge("Ignoring mobile radio request because could not acquire PhoneService");
555                break;
556            }
557
558            try {
559                return mPhoneService.setRadio(turnOn);
560            } catch (RemoteException e) {
561                if (retry == 0) getPhoneService(true);
562            }
563        }
564
565        loge("Could not set radio power to " + (turnOn ? "on" : "off"));
566        return false;
567    }
568
569
570    public void setInternalDataEnable(boolean enabled) {
571        if (DBG) log("setInternalDataEnable: E enabled=" + enabled);
572        final AsyncChannel channel = mDataConnectionTrackerAc;
573        if (channel != null) {
574            channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE,
575                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
576        }
577        if (VDBG) log("setInternalDataEnable: X enabled=" + enabled);
578    }
579
580    @Override
581    public void setUserDataEnable(boolean enabled) {
582        if (DBG) log("setUserDataEnable: E enabled=" + enabled);
583        final AsyncChannel channel = mDataConnectionTrackerAc;
584        if (channel != null) {
585            channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
586                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
587            mUserDataEnabled = enabled;
588        }
589        if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
590    }
591
592    @Override
593    public void setPolicyDataEnable(boolean enabled) {
594        if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
595        final AsyncChannel channel = mDataConnectionTrackerAc;
596        if (channel != null) {
597            channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE,
598                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
599            mPolicyDataEnabled = enabled;
600        }
601    }
602
603    /**
604     * Eanble/disable FailFast
605     *
606     * @param enabled is DctConstants.ENABLED/DISABLED
607     */
608    public void setEnableFailFastMobileData(int enabled) {
609        if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")");
610        final AsyncChannel channel = mDataConnectionTrackerAc;
611        if (channel != null) {
612            channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled);
613        }
614    }
615
616    /**
617     * carrier dependency is met/unmet
618     * @param met
619     */
620    public void setDependencyMet(boolean met) {
621        Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType);
622        try {
623            if (DBG) log("setDependencyMet: E met=" + met);
624            Message msg = Message.obtain();
625            msg.what = DctConstants.CMD_SET_DEPENDENCY_MET;
626            msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED);
627            msg.setData(bundle);
628            mDataConnectionTrackerAc.sendMessage(msg);
629            if (VDBG) log("setDependencyMet: X met=" + met);
630        } catch (NullPointerException e) {
631            loge("setDependencyMet: X mAc was null" + e);
632        }
633    }
634
635    /**
636     *  Inform DCT mobile provisioning has started, it ends when provisioning completes.
637     */
638    public void enableMobileProvisioning(String url) {
639        if (DBG) log("enableMobileProvisioning(url=" + url + ")");
640        final AsyncChannel channel = mDataConnectionTrackerAc;
641        if (channel != null) {
642            Message msg = Message.obtain();
643            msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING;
644            msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url));
645            channel.sendMessage(msg);
646        }
647    }
648
649    /**
650     * Return if this network is the provisioning network. Valid only if connected.
651     * @param met
652     */
653    public boolean isProvisioningNetwork() {
654        boolean retVal;
655        try {
656            Message msg = Message.obtain();
657            msg.what = DctConstants.CMD_IS_PROVISIONING_APN;
658            msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType));
659            Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg);
660            retVal = result.arg1 == DctConstants.ENABLED;
661        } catch (NullPointerException e) {
662            loge("isProvisioningNetwork: X " + e);
663            retVal = false;
664        }
665        if (DBG) log("isProvisioningNetwork: retVal=" + retVal);
666        return retVal;
667    }
668
669    @Override
670    public void addStackedLink(LinkProperties link) {
671        mLinkProperties.addStackedLink(link);
672    }
673
674    @Override
675    public void removeStackedLink(LinkProperties link) {
676        mLinkProperties.removeStackedLink(link);
677    }
678
679    @Override
680    public String toString() {
681        final CharArrayWriter writer = new CharArrayWriter();
682        final PrintWriter pw = new PrintWriter(writer);
683        pw.print("Mobile data state: "); pw.println(mMobileDataState);
684        pw.print("Data enabled: user="); pw.print(mUserDataEnabled);
685        pw.print(", policy="); pw.println(mPolicyDataEnabled);
686        return writer.toString();
687    }
688
689    /**
690     * Internal method supporting the ENABLE_MMS feature.
691     * @param apnType the type of APN to be enabled or disabled (e.g., mms)
692     * @param enable {@code true} to enable the specified APN type,
693     * {@code false} to disable it.
694     * @return an integer value representing the outcome of the request.
695     */
696    private int setEnableApn(String apnType, boolean enable) {
697        getPhoneService(false);
698        /*
699         * If the phone process has crashed in the past, we'll get a
700         * RemoteException and need to re-reference the service.
701         */
702        for (int retry = 0; retry < 2; retry++) {
703            if (mPhoneService == null) {
704                loge("Ignoring feature request because could not acquire PhoneService");
705                break;
706            }
707
708            try {
709                if (enable) {
710                    return mPhoneService.enableApnType(apnType);
711                } else {
712                    return mPhoneService.disableApnType(apnType);
713                }
714            } catch (RemoteException e) {
715                if (retry == 0) getPhoneService(true);
716            }
717        }
718
719        loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
720        return PhoneConstants.APN_REQUEST_FAILED;
721    }
722
723    public static String networkTypeToApnType(int netType) {
724        switch(netType) {
725            case ConnectivityManager.TYPE_MOBILE:
726                return PhoneConstants.APN_TYPE_DEFAULT;  // TODO - use just one of these
727            case ConnectivityManager.TYPE_MOBILE_MMS:
728                return PhoneConstants.APN_TYPE_MMS;
729            case ConnectivityManager.TYPE_MOBILE_SUPL:
730                return PhoneConstants.APN_TYPE_SUPL;
731            case ConnectivityManager.TYPE_MOBILE_DUN:
732                return PhoneConstants.APN_TYPE_DUN;
733            case ConnectivityManager.TYPE_MOBILE_HIPRI:
734                return PhoneConstants.APN_TYPE_HIPRI;
735            case ConnectivityManager.TYPE_MOBILE_FOTA:
736                return PhoneConstants.APN_TYPE_FOTA;
737            case ConnectivityManager.TYPE_MOBILE_IMS:
738                return PhoneConstants.APN_TYPE_IMS;
739            case ConnectivityManager.TYPE_MOBILE_CBS:
740                return PhoneConstants.APN_TYPE_CBS;
741            case ConnectivityManager.TYPE_MOBILE_IA:
742                return PhoneConstants.APN_TYPE_IA;
743            default:
744                sloge("Error mapping networkType " + netType + " to apnType.");
745                return null;
746        }
747    }
748
749
750    /**
751     * @see android.net.NetworkStateTracker#getLinkProperties()
752     */
753    @Override
754    public LinkProperties getLinkProperties() {
755        return new LinkProperties(mLinkProperties);
756    }
757
758    /**
759     * @see android.net.NetworkStateTracker#getLinkCapabilities()
760     */
761    @Override
762    public LinkCapabilities getLinkCapabilities() {
763        return new LinkCapabilities(mLinkCapabilities);
764    }
765
766    public void supplyMessenger(Messenger messenger) {
767        if (VDBG) log(mApnType + " got supplyMessenger");
768        AsyncChannel ac = new AsyncChannel();
769        ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger);
770    }
771
772    private void log(String s) {
773        Slog.d(TAG, mApnType + ": " + s);
774    }
775
776    private void loge(String s) {
777        Slog.e(TAG, mApnType + ": " + s);
778    }
779
780    static private void sloge(String s) {
781        Slog.e(TAG, s);
782    }
783
784    @Override
785    public LinkQualityInfo getLinkQualityInfo() {
786        if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) {
787            // no data available yet; just return
788            return null;
789        }
790
791        MobileLinkQualityInfo li = new MobileLinkQualityInfo();
792
793        li.setNetworkType(mNetworkInfo.getType());
794
795        mSamplingDataTracker.setCommonLinkQualityInfoFields(li);
796
797        if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
798            li.setMobileNetworkType(mNetworkInfo.getSubtype());
799
800            NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype());
801            if (entry != null) {
802                li.setTheoreticalRxBandwidth(entry.downloadBandwidth);
803                li.setTheoreticalRxBandwidth(entry.uploadBandwidth);
804                li.setTheoreticalLatency(entry.latency);
805            }
806
807            if (mSignalStrength != null) {
808                li.setNormalizedSignalStrength(getNormalizedSignalStrength(
809                        li.getMobileNetworkType(), mSignalStrength));
810            }
811        }
812
813        SignalStrength ss = mSignalStrength;
814        if (ss != null) {
815
816            li.setRssi(ss.getGsmSignalStrength());
817            li.setGsmErrorRate(ss.getGsmBitErrorRate());
818            li.setCdmaDbm(ss.getCdmaDbm());
819            li.setCdmaEcio(ss.getCdmaEcio());
820            li.setEvdoDbm(ss.getEvdoDbm());
821            li.setEvdoEcio(ss.getEvdoEcio());
822            li.setEvdoSnr(ss.getEvdoSnr());
823            li.setLteSignalStrength(ss.getLteSignalStrength());
824            li.setLteRsrp(ss.getLteRsrp());
825            li.setLteRsrq(ss.getLteRsrq());
826            li.setLteRssnr(ss.getLteRssnr());
827            li.setLteCqi(ss.getLteCqi());
828        }
829
830        if (VDBG) {
831            Slog.d(TAG, "Returning LinkQualityInfo with"
832                    + " MobileNetworkType = " + String.valueOf(li.getMobileNetworkType())
833                    + " Theoretical Rx BW = " + String.valueOf(li.getTheoreticalRxBandwidth())
834                    + " gsm Signal Strength = " + String.valueOf(li.getRssi())
835                    + " cdma Signal Strength = " + String.valueOf(li.getCdmaDbm())
836                    + " evdo Signal Strength = " + String.valueOf(li.getEvdoDbm())
837                    + " Lte Signal Strength = " + String.valueOf(li.getLteSignalStrength()));
838        }
839
840        return li;
841    }
842
843    static class NetworkDataEntry {
844        public int networkType;
845        public int downloadBandwidth;               // in kbps
846        public int uploadBandwidth;                 // in kbps
847        public int latency;                         // in millisecond
848
849        NetworkDataEntry(int i1, int i2, int i3, int i4) {
850            networkType = i1;
851            downloadBandwidth = i2;
852            uploadBandwidth = i3;
853            latency = i4;
854        }
855    }
856
857    private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] {
858            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE,      237,     118, UNKNOWN),
859            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS,       48,      40, UNKNOWN),
860            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS,      384,      64, UNKNOWN),
861            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA,   14400, UNKNOWN, UNKNOWN),
862            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA,   14400,    5760, UNKNOWN),
863            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA,    14400,    5760, UNKNOWN),
864            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP,   21000,    5760, UNKNOWN),
865            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA,  UNKNOWN, UNKNOWN, UNKNOWN),
866            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT, UNKNOWN, UNKNOWN, UNKNOWN),
867            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0,   2468,     153, UNKNOWN),
868            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A,   3072,    1800, UNKNOWN),
869            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B,  14700,    1800, UNKNOWN),
870            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN,  UNKNOWN, UNKNOWN, UNKNOWN),
871            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE,    100000,   50000, UNKNOWN),
872            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD, UNKNOWN, UNKNOWN, UNKNOWN),
873    };
874
875    private static NetworkDataEntry getNetworkDataEntry(int networkType) {
876        for (NetworkDataEntry entry : mTheoreticalBWTable) {
877            if (entry.networkType == networkType) {
878                return entry;
879            }
880        }
881
882        Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType));
883        return null;
884    }
885
886    private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) {
887
888        int level;
889
890        switch(networkType) {
891            case TelephonyManager.NETWORK_TYPE_EDGE:
892            case TelephonyManager.NETWORK_TYPE_GPRS:
893            case TelephonyManager.NETWORK_TYPE_UMTS:
894            case TelephonyManager.NETWORK_TYPE_HSDPA:
895            case TelephonyManager.NETWORK_TYPE_HSUPA:
896            case TelephonyManager.NETWORK_TYPE_HSPA:
897            case TelephonyManager.NETWORK_TYPE_HSPAP:
898                level = ss.getGsmLevel();
899                break;
900            case TelephonyManager.NETWORK_TYPE_CDMA:
901            case TelephonyManager.NETWORK_TYPE_1xRTT:
902                level = ss.getCdmaLevel();
903                break;
904            case TelephonyManager.NETWORK_TYPE_EVDO_0:
905            case TelephonyManager.NETWORK_TYPE_EVDO_A:
906            case TelephonyManager.NETWORK_TYPE_EVDO_B:
907                level = ss.getEvdoLevel();
908                break;
909            case TelephonyManager.NETWORK_TYPE_LTE:
910                level = ss.getLteLevel();
911                break;
912            case TelephonyManager.NETWORK_TYPE_IDEN:
913            case TelephonyManager.NETWORK_TYPE_EHRPD:
914            default:
915                return UNKNOWN;
916        }
917
918        return (level * LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) /
919                SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
920    }
921
922    @Override
923    public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
924        mSamplingDataTracker.startSampling(s);
925    }
926
927    @Override
928    public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
929        mSamplingDataTracker.stopSampling(s);
930    }
931}
932