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