OSDN Git Service

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