OSDN Git Service

Add support for handling mobile provisioning networks.
[android-x86/frameworks-base.git] / core / java / android / net / MobileDataStateTracker.java
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.TelephonyManager;
32 import android.text.TextUtils;
33 import android.util.Slog;
34
35 import com.android.internal.telephony.DctConstants;
36 import com.android.internal.telephony.ITelephony;
37 import com.android.internal.telephony.PhoneConstants;
38 import com.android.internal.telephony.TelephonyIntents;
39 import com.android.internal.util.AsyncChannel;
40
41 import java.io.CharArrayWriter;
42 import java.io.PrintWriter;
43 import java.util.concurrent.atomic.AtomicBoolean;
44
45 /**
46  * Track the state of mobile data connectivity. This is done by
47  * receiving broadcast intents from the Phone process whenever
48  * the state of data connectivity changes.
49  *
50  * {@hide}
51  */
52 public class MobileDataStateTracker implements NetworkStateTracker {
53
54     private static final String TAG = "MobileDataStateTracker";
55     private static final boolean DBG = true;
56     private static final boolean VDBG = false;
57
58     private PhoneConstants.DataState mMobileDataState;
59     private ITelephony mPhoneService;
60
61     private String mApnType;
62     private NetworkInfo mNetworkInfo;
63     private boolean mTeardownRequested = false;
64     private Handler mTarget;
65     private Context mContext;
66     private LinkProperties mLinkProperties;
67     private LinkCapabilities mLinkCapabilities;
68     private boolean mPrivateDnsRouteSet = false;
69     private boolean mDefaultRouteSet = false;
70
71     // NOTE: these are only kept for debugging output; actual values are
72     // maintained in DataConnectionTracker.
73     protected boolean mUserDataEnabled = true;
74     protected boolean mPolicyDataEnabled = true;
75
76     private Handler mHandler;
77     private AsyncChannel mDataConnectionTrackerAc;
78
79     private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
80
81     /**
82      * Create a new MobileDataStateTracker
83      * @param netType the ConnectivityManager network type
84      * @param tag the name of this network
85      */
86     public MobileDataStateTracker(int netType, String tag) {
87         mNetworkInfo = new NetworkInfo(netType,
88                 TelephonyManager.getDefault().getNetworkType(), tag,
89                 TelephonyManager.getDefault().getNetworkTypeName());
90         mApnType = networkTypeToApnType(netType);
91     }
92
93     /**
94      * Begin monitoring data connectivity.
95      *
96      * @param context is the current Android context
97      * @param target is the Hander to which to return the events.
98      */
99     public void startMonitoring(Context context, Handler target) {
100         mTarget = target;
101         mContext = context;
102
103         mHandler = new MdstHandler(target.getLooper(), this);
104
105         IntentFilter filter = new IntentFilter();
106         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
107         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
108         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
109
110         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
111         mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
112     }
113
114     static class MdstHandler extends Handler {
115         private MobileDataStateTracker mMdst;
116
117         MdstHandler(Looper looper, MobileDataStateTracker mdst) {
118             super(looper);
119             mMdst = mdst;
120         }
121
122         @Override
123         public void handleMessage(Message msg) {
124             switch (msg.what) {
125                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
126                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
127                         if (VDBG) {
128                             mMdst.log("MdstHandler connected");
129                         }
130                         mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
131                     } else {
132                         if (VDBG) {
133                             mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
134                         }
135                     }
136                     break;
137                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
138                     if (VDBG) mMdst.log("Disconnected from DataStateTracker");
139                     mMdst.mDataConnectionTrackerAc = null;
140                     break;
141                 default: {
142                     if (VDBG) mMdst.log("Ignorning unknown message=" + msg);
143                     break;
144                 }
145             }
146         }
147     }
148
149     public boolean isPrivateDnsRouteSet() {
150         return mPrivateDnsRouteSet;
151     }
152
153     public void privateDnsRouteSet(boolean enabled) {
154         mPrivateDnsRouteSet = enabled;
155     }
156
157     public NetworkInfo getNetworkInfo() {
158         return mNetworkInfo;
159     }
160
161     public boolean isDefaultRouteSet() {
162         return mDefaultRouteSet;
163     }
164
165     public void defaultRouteSet(boolean enabled) {
166         mDefaultRouteSet = enabled;
167     }
168
169     /**
170      * This is not implemented.
171      */
172     public void releaseWakeLock() {
173     }
174
175     private void updateLinkProperitesAndCapatilities(Intent intent) {
176         mLinkProperties = intent.getParcelableExtra(
177                 PhoneConstants.DATA_LINK_PROPERTIES_KEY);
178         if (mLinkProperties == null) {
179             loge("CONNECTED event did not supply link properties.");
180             mLinkProperties = new LinkProperties();
181         }
182         mLinkCapabilities = intent.getParcelableExtra(
183                 PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
184         if (mLinkCapabilities == null) {
185             loge("CONNECTED event did not supply link capabilities.");
186             mLinkCapabilities = new LinkCapabilities();
187         }
188     }
189
190     private class MobileDataStateReceiver extends BroadcastReceiver {
191         @Override
192         public void onReceive(Context context, Intent intent) {
193             if (intent.getAction().equals(TelephonyIntents.
194                     ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) {
195                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
196                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
197                 if (!TextUtils.equals(mApnType, apnType)) {
198                     return;
199                 }
200                 if (DBG) {
201                     log("Broadcast received: " + intent.getAction() + " apnType=" + apnType
202                             + " apnName=" + apnName);
203                 }
204
205                 // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING
206                 mMobileDataState = PhoneConstants.DataState.CONNECTING;
207                 updateLinkProperitesAndCapatilities(intent);
208                 setDetailedState(DetailedState.CONNECTED_TO_PROVISIONING_NETWORK, "", apnName);
209             } else if (intent.getAction().equals(TelephonyIntents.
210                     ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
211                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
212                 if (!TextUtils.equals(apnType, mApnType)) {
213                     return;
214                 }
215                 if (DBG) {
216                     log("Broadcast received: " + intent.getAction() + " apnType=" + apnType);
217                 }
218
219                 int oldSubtype = mNetworkInfo.getSubtype();
220                 int newSubType = TelephonyManager.getDefault().getNetworkType();
221                 String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
222                 mNetworkInfo.setSubtype(newSubType, subTypeName);
223                 if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
224                     Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
225                                                         oldSubtype, 0, mNetworkInfo);
226                     msg.sendToTarget();
227                 }
228
229                 PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
230                         intent.getStringExtra(PhoneConstants.STATE_KEY));
231                 String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
232                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
233                 mNetworkInfo.setRoaming(intent.getBooleanExtra(
234                         PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
235                 if (DBG) {
236                     log(mApnType + " setting isAvailable to " +
237                             intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
238                 }
239                 mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(
240                         PhoneConstants.NETWORK_UNAVAILABLE_KEY, false));
241
242                 if (DBG) {
243                     log("Received state=" + state + ", old=" + mMobileDataState +
244                         ", reason=" + (reason == null ? "(unspecified)" : reason));
245                 }
246                 if (mMobileDataState != state) {
247                     mMobileDataState = state;
248                     switch (state) {
249                         case DISCONNECTED:
250                             if(isTeardownRequested()) {
251                                 setTeardownRequested(false);
252                             }
253
254                             setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
255                             // can't do this here - ConnectivityService needs it to clear stuff
256                             // it's ok though - just leave it to be refreshed next time
257                             // we connect.
258                             //if (DBG) log("clearing mInterfaceName for "+ mApnType +
259                             //        " as it DISCONNECTED");
260                             //mInterfaceName = null;
261                             break;
262                         case CONNECTING:
263                             setDetailedState(DetailedState.CONNECTING, reason, apnName);
264                             break;
265                         case SUSPENDED:
266                             setDetailedState(DetailedState.SUSPENDED, reason, apnName);
267                             break;
268                         case CONNECTED:
269                             updateLinkProperitesAndCapatilities(intent);
270                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
271                             break;
272                     }
273                 } else {
274                     // There was no state change. Check if LinkProperties has been updated.
275                     if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
276                         mLinkProperties = intent.getParcelableExtra(
277                                 PhoneConstants.DATA_LINK_PROPERTIES_KEY);
278                         if (mLinkProperties == null) {
279                             loge("No link property in LINK_PROPERTIES change event.");
280                             mLinkProperties = new LinkProperties();
281                         }
282                         // Just update reason field in this NetworkInfo
283                         mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
284                                                       mNetworkInfo.getExtraInfo());
285                         Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
286                                                             mNetworkInfo);
287                         msg.sendToTarget();
288                     }
289                 }
290             } else if (intent.getAction().
291                     equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
292                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
293                 if (!TextUtils.equals(apnType, mApnType)) {
294                     return;
295                 }
296                 String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
297                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
298                 if (DBG) {
299                     log("Broadcast received: " + intent.getAction() +
300                                 " reason=" + reason == null ? "null" : reason);
301                 }
302                 setDetailedState(DetailedState.FAILED, reason, apnName);
303             } else {
304                 if (DBG) log("Broadcast received: ignore " + intent.getAction());
305             }
306         }
307     }
308
309     private void getPhoneService(boolean forceRefresh) {
310         if ((mPhoneService == null) || forceRefresh) {
311             mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
312         }
313     }
314
315     /**
316      * Report whether data connectivity is possible.
317      */
318     public boolean isAvailable() {
319         return mNetworkInfo.isAvailable();
320     }
321
322     /**
323      * Return the system properties name associated with the tcp buffer sizes
324      * for this network.
325      */
326     public String getTcpBufferSizesPropName() {
327         String networkTypeStr = "unknown";
328         TelephonyManager tm = new TelephonyManager(mContext);
329         //TODO We have to edit the parameter for getNetworkType regarding CDMA
330         switch(tm.getNetworkType()) {
331         case TelephonyManager.NETWORK_TYPE_GPRS:
332             networkTypeStr = "gprs";
333             break;
334         case TelephonyManager.NETWORK_TYPE_EDGE:
335             networkTypeStr = "edge";
336             break;
337         case TelephonyManager.NETWORK_TYPE_UMTS:
338             networkTypeStr = "umts";
339             break;
340         case TelephonyManager.NETWORK_TYPE_HSDPA:
341             networkTypeStr = "hsdpa";
342             break;
343         case TelephonyManager.NETWORK_TYPE_HSUPA:
344             networkTypeStr = "hsupa";
345             break;
346         case TelephonyManager.NETWORK_TYPE_HSPA:
347             networkTypeStr = "hspa";
348             break;
349         case TelephonyManager.NETWORK_TYPE_HSPAP:
350             networkTypeStr = "hspap";
351             break;
352         case TelephonyManager.NETWORK_TYPE_CDMA:
353             networkTypeStr = "cdma";
354             break;
355         case TelephonyManager.NETWORK_TYPE_1xRTT:
356             networkTypeStr = "1xrtt";
357             break;
358         case TelephonyManager.NETWORK_TYPE_EVDO_0:
359             networkTypeStr = "evdo";
360             break;
361         case TelephonyManager.NETWORK_TYPE_EVDO_A:
362             networkTypeStr = "evdo";
363             break;
364         case TelephonyManager.NETWORK_TYPE_EVDO_B:
365             networkTypeStr = "evdo";
366             break;
367         case TelephonyManager.NETWORK_TYPE_IDEN:
368             networkTypeStr = "iden";
369             break;
370         case TelephonyManager.NETWORK_TYPE_LTE:
371             networkTypeStr = "lte";
372             break;
373         case TelephonyManager.NETWORK_TYPE_EHRPD:
374             networkTypeStr = "ehrpd";
375             break;
376         default:
377             loge("unknown network type: " + tm.getNetworkType());
378         }
379         return "net.tcp.buffersize." + networkTypeStr;
380     }
381
382     /**
383      * Tear down mobile data connectivity, i.e., disable the ability to create
384      * mobile data connections.
385      * TODO - make async and return nothing?
386      */
387     public boolean teardown() {
388         setTeardownRequested(true);
389         return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
390     }
391
392     /**
393      * @return true if this is ready to operate
394      */
395     public boolean isReady() {
396         return mDataConnectionTrackerAc != null;
397     }
398
399     @Override
400     public void captivePortalCheckComplete() {
401         // not implemented
402     }
403
404     @Override
405     public void captivePortalCheckCompleted(boolean isCaptivePortal) {
406         if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) {
407             // Captive portal change enable/disable failing fast
408             setEnableFailFastMobileData(
409                     isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED);
410         }
411     }
412
413     /**
414      * Record the detailed state of a network, and if it is a
415      * change from the previous state, send a notification to
416      * any listeners.
417      * @param state the new {@code DetailedState}
418      * @param reason a {@code String} indicating a reason for the state change,
419      * if one was supplied. May be {@code null}.
420      * @param extraInfo optional {@code String} providing extra information about the state change
421      */
422     private void setDetailedState(NetworkInfo.DetailedState state, String reason,
423             String extraInfo) {
424         if (DBG) log("setDetailed state, old ="
425                 + mNetworkInfo.getDetailedState() + " and new state=" + state);
426         if (state != mNetworkInfo.getDetailedState()) {
427             boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
428             String lastReason = mNetworkInfo.getReason();
429             /*
430              * If a reason was supplied when the CONNECTING state was entered, and no
431              * reason was supplied for entering the CONNECTED state, then retain the
432              * reason that was supplied when going to CONNECTING.
433              */
434             if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
435                     && lastReason != null)
436                 reason = lastReason;
437             mNetworkInfo.setDetailedState(state, reason, extraInfo);
438             Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
439             msg.sendToTarget();
440         }
441     }
442
443     public void setTeardownRequested(boolean isRequested) {
444         mTeardownRequested = isRequested;
445     }
446
447     public boolean isTeardownRequested() {
448         return mTeardownRequested;
449     }
450
451     /**
452      * Re-enable mobile data connectivity after a {@link #teardown()}.
453      * TODO - make async and always get a notification?
454      */
455     public boolean reconnect() {
456         boolean retValue = false; //connected or expect to be?
457         setTeardownRequested(false);
458         switch (setEnableApn(mApnType, true)) {
459             case PhoneConstants.APN_ALREADY_ACTIVE:
460                 // need to set self to CONNECTING so the below message is handled.
461                 retValue = true;
462                 break;
463             case PhoneConstants.APN_REQUEST_STARTED:
464                 // set IDLE here , avoid the following second FAILED not sent out
465                 mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
466                 retValue = true;
467                 break;
468             case PhoneConstants.APN_REQUEST_FAILED:
469             case PhoneConstants.APN_TYPE_NOT_AVAILABLE:
470                 break;
471             default:
472                 loge("Error in reconnect - unexpected response.");
473                 break;
474         }
475         return retValue;
476     }
477
478     /**
479      * Turn on or off the mobile radio. No connectivity will be possible while the
480      * radio is off. The operation is a no-op if the radio is already in the desired state.
481      * @param turnOn {@code true} if the radio should be turned on, {@code false} if
482      */
483     public boolean setRadio(boolean turnOn) {
484         getPhoneService(false);
485         /*
486          * If the phone process has crashed in the past, we'll get a
487          * RemoteException and need to re-reference the service.
488          */
489         for (int retry = 0; retry < 2; retry++) {
490             if (mPhoneService == null) {
491                 loge("Ignoring mobile radio request because could not acquire PhoneService");
492                 break;
493             }
494
495             try {
496                 return mPhoneService.setRadio(turnOn);
497             } catch (RemoteException e) {
498                 if (retry == 0) getPhoneService(true);
499             }
500         }
501
502         loge("Could not set radio power to " + (turnOn ? "on" : "off"));
503         return false;
504     }
505
506     @Override
507     public void setUserDataEnable(boolean enabled) {
508         if (DBG) log("setUserDataEnable: E enabled=" + enabled);
509         final AsyncChannel channel = mDataConnectionTrackerAc;
510         if (channel != null) {
511             channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
512                     enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
513             mUserDataEnabled = enabled;
514         }
515         if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
516     }
517
518     @Override
519     public void setPolicyDataEnable(boolean enabled) {
520         if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
521         final AsyncChannel channel = mDataConnectionTrackerAc;
522         if (channel != null) {
523             channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE,
524                     enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
525             mPolicyDataEnabled = enabled;
526         }
527     }
528
529     /**
530      * Eanble/disable FailFast
531      *
532      * @param enabled is DctConstants.ENABLED/DISABLED
533      */
534     public void setEnableFailFastMobileData(int enabled) {
535         if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")");
536         final AsyncChannel channel = mDataConnectionTrackerAc;
537         if (channel != null) {
538             channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled);
539         }
540     }
541
542     /**
543      * carrier dependency is met/unmet
544      * @param met
545      */
546     public void setDependencyMet(boolean met) {
547         Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType);
548         try {
549             if (DBG) log("setDependencyMet: E met=" + met);
550             Message msg = Message.obtain();
551             msg.what = DctConstants.CMD_SET_DEPENDENCY_MET;
552             msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED);
553             msg.setData(bundle);
554             mDataConnectionTrackerAc.sendMessage(msg);
555             if (VDBG) log("setDependencyMet: X met=" + met);
556         } catch (NullPointerException e) {
557             loge("setDependencyMet: X mAc was null" + e);
558         }
559     }
560
561     /**
562      *  Inform DCT mobile provisioning has started, it ends when provisioning completes.
563      */
564     public void enableMobileProvisioning(String url) {
565         if (DBG) log("enableMobileProvisioning(url=" + url + ")");
566         final AsyncChannel channel = mDataConnectionTrackerAc;
567         if (channel != null) {
568             Message msg = Message.obtain();
569             msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING;
570             msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url));
571             channel.sendMessage(msg);
572         }
573     }
574
575     /**
576      * Return if this network is the provisioning network. Valid only if connected.
577      * @param met
578      */
579     public boolean isProvisioningNetwork() {
580         boolean retVal;
581         try {
582             Message msg = Message.obtain();
583             msg.what = DctConstants.CMD_IS_PROVISIONING_APN;
584             msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType));
585             Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg);
586             retVal = result.arg1 == DctConstants.ENABLED;
587         } catch (NullPointerException e) {
588             loge("isProvisioningNetwork: X " + e);
589             retVal = false;
590         }
591         if (DBG) log("isProvisioningNetwork: retVal=" + retVal);
592         return retVal;
593     }
594
595     @Override
596     public void addStackedLink(LinkProperties link) {
597         mLinkProperties.addStackedLink(link);
598     }
599
600     @Override
601     public void removeStackedLink(LinkProperties link) {
602         mLinkProperties.removeStackedLink(link);
603     }
604
605     @Override
606     public String toString() {
607         final CharArrayWriter writer = new CharArrayWriter();
608         final PrintWriter pw = new PrintWriter(writer);
609         pw.print("Mobile data state: "); pw.println(mMobileDataState);
610         pw.print("Data enabled: user="); pw.print(mUserDataEnabled);
611         pw.print(", policy="); pw.println(mPolicyDataEnabled);
612         return writer.toString();
613     }
614
615    /**
616      * Internal method supporting the ENABLE_MMS feature.
617      * @param apnType the type of APN to be enabled or disabled (e.g., mms)
618      * @param enable {@code true} to enable the specified APN type,
619      * {@code false} to disable it.
620      * @return an integer value representing the outcome of the request.
621      */
622     private int setEnableApn(String apnType, boolean enable) {
623         getPhoneService(false);
624         /*
625          * If the phone process has crashed in the past, we'll get a
626          * RemoteException and need to re-reference the service.
627          */
628         for (int retry = 0; retry < 2; retry++) {
629             if (mPhoneService == null) {
630                 loge("Ignoring feature request because could not acquire PhoneService");
631                 break;
632             }
633
634             try {
635                 if (enable) {
636                     return mPhoneService.enableApnType(apnType);
637                 } else {
638                     return mPhoneService.disableApnType(apnType);
639                 }
640             } catch (RemoteException e) {
641                 if (retry == 0) getPhoneService(true);
642             }
643         }
644
645         loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
646         return PhoneConstants.APN_REQUEST_FAILED;
647     }
648
649     public static String networkTypeToApnType(int netType) {
650         switch(netType) {
651             case ConnectivityManager.TYPE_MOBILE:
652                 return PhoneConstants.APN_TYPE_DEFAULT;  // TODO - use just one of these
653             case ConnectivityManager.TYPE_MOBILE_MMS:
654                 return PhoneConstants.APN_TYPE_MMS;
655             case ConnectivityManager.TYPE_MOBILE_SUPL:
656                 return PhoneConstants.APN_TYPE_SUPL;
657             case ConnectivityManager.TYPE_MOBILE_DUN:
658                 return PhoneConstants.APN_TYPE_DUN;
659             case ConnectivityManager.TYPE_MOBILE_HIPRI:
660                 return PhoneConstants.APN_TYPE_HIPRI;
661             case ConnectivityManager.TYPE_MOBILE_FOTA:
662                 return PhoneConstants.APN_TYPE_FOTA;
663             case ConnectivityManager.TYPE_MOBILE_IMS:
664                 return PhoneConstants.APN_TYPE_IMS;
665             case ConnectivityManager.TYPE_MOBILE_CBS:
666                 return PhoneConstants.APN_TYPE_CBS;
667             default:
668                 sloge("Error mapping networkType " + netType + " to apnType.");
669                 return null;
670         }
671     }
672
673     /**
674      * @see android.net.NetworkStateTracker#getLinkProperties()
675      */
676     public LinkProperties getLinkProperties() {
677         return new LinkProperties(mLinkProperties);
678     }
679
680     /**
681      * @see android.net.NetworkStateTracker#getLinkCapabilities()
682      */
683     public LinkCapabilities getLinkCapabilities() {
684         return new LinkCapabilities(mLinkCapabilities);
685     }
686
687     public void supplyMessenger(Messenger messenger) {
688         if (VDBG) log(mApnType + " got supplyMessenger");
689         AsyncChannel ac = new AsyncChannel();
690         ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger);
691     }
692
693     private void log(String s) {
694         Slog.d(TAG, mApnType + ": " + s);
695     }
696
697     private void loge(String s) {
698         Slog.e(TAG, mApnType + ": " + s);
699     }
700
701     static private void sloge(String s) {
702         Slog.e(TAG, s);
703     }
704 }