Home | History | Annotate | Download | only in net
      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 
     17 package android.net;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.net.NetworkInfo.DetailedState;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.os.Messenger;
     29 import android.os.RemoteException;
     30 import android.os.ServiceManager;
     31 import android.telephony.PhoneStateListener;
     32 import android.telephony.SignalStrength;
     33 import android.telephony.TelephonyManager;
     34 import android.text.TextUtils;
     35 import android.util.Slog;
     36 
     37 import com.android.internal.telephony.DctConstants;
     38 import com.android.internal.telephony.ITelephony;
     39 import com.android.internal.telephony.PhoneConstants;
     40 import com.android.internal.telephony.TelephonyIntents;
     41 import com.android.internal.util.AsyncChannel;
     42 
     43 import java.io.CharArrayWriter;
     44 import java.io.PrintWriter;
     45 import 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  */
     54 public 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