1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import android.os.Parcelable;
20import android.os.Parcel;
21
22import com.android.internal.annotations.VisibleForTesting;
23
24import java.util.EnumMap;
25
26/**
27 * Describes the status of a network interface.
28 * <p>Use {@link ConnectivityManager#getActiveNetworkInfo()} to get an instance that represents
29 * the current network connection.
30 */
31public class NetworkInfo implements Parcelable {
32
33    /**
34     * Coarse-grained network state. This is probably what most applications should
35     * use, rather than {@link android.net.NetworkInfo.DetailedState DetailedState}.
36     * The mapping between the two is as follows:
37     * <br/><br/>
38     * <table>
39     * <tr><td><b>Detailed state</b></td><td><b>Coarse-grained state</b></td></tr>
40     * <tr><td><code>IDLE</code></td><td><code>DISCONNECTED</code></td></tr>
41     * <tr><td><code>SCANNING</code></td><td><code>CONNECTING</code></td></tr>
42     * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr>
43     * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr>
44     * <tr><td><code>CONNECTED</code></td><td><code>CONNECTED</code></td></tr>
45     * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr>
46     * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr>
47     * <tr><td><code>UNAVAILABLE</code></td><td><code>DISCONNECTED</code></td></tr>
48     * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
49     * </table>
50     */
51    public enum State {
52        CONNECTING, CONNECTED, SUSPENDED, DISCONNECTING, DISCONNECTED, UNKNOWN
53    }
54
55    /**
56     * The fine-grained state of a network connection. This level of detail
57     * is probably of interest to few applications. Most should use
58     * {@link android.net.NetworkInfo.State State} instead.
59     */
60    public enum DetailedState {
61        /** Ready to start data connection setup. */
62        IDLE,
63        /** Searching for an available access point. */
64        SCANNING,
65        /** Currently setting up data connection. */
66        CONNECTING,
67        /** Network link established, performing authentication. */
68        AUTHENTICATING,
69        /** Awaiting response from DHCP server in order to assign IP address information. */
70        OBTAINING_IPADDR,
71        /** IP traffic should be available. */
72        CONNECTED,
73        /** IP traffic is suspended */
74        SUSPENDED,
75        /** Currently tearing down data connection. */
76        DISCONNECTING,
77        /** IP traffic not available. */
78        DISCONNECTED,
79        /** Attempt to connect failed. */
80        FAILED,
81        /** Access to this network is blocked. */
82        BLOCKED,
83        /** Link has poor connectivity. */
84        VERIFYING_POOR_LINK,
85        /** Checking if network is a captive portal */
86        CAPTIVE_PORTAL_CHECK
87    }
88
89    /**
90     * This is the map described in the Javadoc comment above. The positions
91     * of the elements of the array must correspond to the ordinal values
92     * of <code>DetailedState</code>.
93     */
94    private static final EnumMap<DetailedState, State> stateMap =
95        new EnumMap<DetailedState, State>(DetailedState.class);
96
97    static {
98        stateMap.put(DetailedState.IDLE, State.DISCONNECTED);
99        stateMap.put(DetailedState.SCANNING, State.DISCONNECTED);
100        stateMap.put(DetailedState.CONNECTING, State.CONNECTING);
101        stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
102        stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
103        stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING);
104        stateMap.put(DetailedState.CAPTIVE_PORTAL_CHECK, State.CONNECTING);
105        stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
106        stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
107        stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
108        stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
109        stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
110        stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
111    }
112
113    private int mNetworkType;
114    private int mSubtype;
115    private String mTypeName;
116    private String mSubtypeName;
117    private State mState;
118    private DetailedState mDetailedState;
119    private String mReason;
120    private String mExtraInfo;
121    private boolean mIsFailover;
122    private boolean mIsAvailable;
123    private boolean mIsRoaming;
124    private boolean mIsMetered;
125
126    /**
127     * @hide
128     */
129    public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
130        if (!ConnectivityManager.isNetworkTypeValid(type)) {
131            throw new IllegalArgumentException("Invalid network type: " + type);
132        }
133        mNetworkType = type;
134        mSubtype = subtype;
135        mTypeName = typeName;
136        mSubtypeName = subtypeName;
137        setDetailedState(DetailedState.IDLE, null, null);
138        mState = State.UNKNOWN;
139    }
140
141    /** {@hide} */
142    public NetworkInfo(NetworkInfo source) {
143        if (source != null) {
144            synchronized (source) {
145                mNetworkType = source.mNetworkType;
146                mSubtype = source.mSubtype;
147                mTypeName = source.mTypeName;
148                mSubtypeName = source.mSubtypeName;
149                mState = source.mState;
150                mDetailedState = source.mDetailedState;
151                mReason = source.mReason;
152                mExtraInfo = source.mExtraInfo;
153                mIsFailover = source.mIsFailover;
154                mIsAvailable = source.mIsAvailable;
155                mIsRoaming = source.mIsRoaming;
156                mIsMetered = source.mIsMetered;
157            }
158        }
159    }
160
161    /**
162     * Reports the type of network to which the
163     * info in this {@code NetworkInfo} pertains.
164     * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
165     * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
166     * ConnectivityManager#TYPE_ETHERNET},  {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
167     * types defined by {@link ConnectivityManager}
168     */
169    public int getType() {
170        synchronized (this) {
171            return mNetworkType;
172        }
173    }
174
175    /**
176     * @hide
177     */
178    public void setType(int type) {
179        synchronized (this) {
180            mNetworkType = type;
181        }
182    }
183
184    /**
185     * Return a network-type-specific integer describing the subtype
186     * of the network.
187     * @return the network subtype
188     */
189    public int getSubtype() {
190        synchronized (this) {
191            return mSubtype;
192        }
193    }
194
195    /**
196     * @hide
197     */
198    public void setSubtype(int subtype, String subtypeName) {
199        synchronized (this) {
200            mSubtype = subtype;
201            mSubtypeName = subtypeName;
202        }
203    }
204
205    /**
206     * Return a human-readable name describe the type of the network,
207     * for example "WIFI" or "MOBILE".
208     * @return the name of the network type
209     */
210    public String getTypeName() {
211        synchronized (this) {
212            return mTypeName;
213        }
214    }
215
216    /**
217     * Return a human-readable name describing the subtype of the network.
218     * @return the name of the network subtype
219     */
220    public String getSubtypeName() {
221        synchronized (this) {
222            return mSubtypeName;
223        }
224    }
225
226    /**
227     * Indicates whether network connectivity exists or is in the process
228     * of being established. This is good for applications that need to
229     * do anything related to the network other than read or write data.
230     * For the latter, call {@link #isConnected()} instead, which guarantees
231     * that the network is fully usable.
232     * @return {@code true} if network connectivity exists or is in the process
233     * of being established, {@code false} otherwise.
234     */
235    public boolean isConnectedOrConnecting() {
236        synchronized (this) {
237            return mState == State.CONNECTED || mState == State.CONNECTING;
238        }
239    }
240
241    /**
242     * Indicates whether network connectivity exists and it is possible to establish
243     * connections and pass data.
244     * <p>Always call this before attempting to perform data transactions.
245     * @return {@code true} if network connectivity exists, {@code false} otherwise.
246     */
247    public boolean isConnected() {
248        synchronized (this) {
249            return mState == State.CONNECTED;
250        }
251    }
252
253    /**
254     * Indicates whether network connectivity is possible. A network is unavailable
255     * when a persistent or semi-persistent condition prevents the possibility
256     * of connecting to that network. Examples include
257     * <ul>
258     * <li>The device is out of the coverage area for any network of this type.</li>
259     * <li>The device is on a network other than the home network (i.e., roaming), and
260     * data roaming has been disabled.</li>
261     * <li>The device's radio is turned off, e.g., because airplane mode is enabled.</li>
262     * </ul>
263     * @return {@code true} if the network is available, {@code false} otherwise
264     */
265    public boolean isAvailable() {
266        synchronized (this) {
267            return mIsAvailable;
268        }
269    }
270
271    /**
272     * Sets if the network is available, ie, if the connectivity is possible.
273     * @param isAvailable the new availability value.
274     *
275     * @hide
276     */
277    public void setIsAvailable(boolean isAvailable) {
278        synchronized (this) {
279            mIsAvailable = isAvailable;
280        }
281    }
282
283    /**
284     * Indicates whether the current attempt to connect to the network
285     * resulted from the ConnectivityManager trying to fail over to this
286     * network following a disconnect from another network.
287     * @return {@code true} if this is a failover attempt, {@code false}
288     * otherwise.
289     */
290    public boolean isFailover() {
291        synchronized (this) {
292            return mIsFailover;
293        }
294    }
295
296    /**
297     * Set the failover boolean.
298     * @param isFailover {@code true} to mark the current connection attempt
299     * as a failover.
300     * @hide
301     */
302    public void setFailover(boolean isFailover) {
303        synchronized (this) {
304            mIsFailover = isFailover;
305        }
306    }
307
308    /**
309     * Indicates whether the device is currently roaming on this network.
310     * When {@code true}, it suggests that use of data on this network
311     * may incur extra costs.
312     * @return {@code true} if roaming is in effect, {@code false} otherwise.
313     */
314    public boolean isRoaming() {
315        synchronized (this) {
316            return mIsRoaming;
317        }
318    }
319
320    /** {@hide} */
321    @VisibleForTesting
322    public void setRoaming(boolean isRoaming) {
323        synchronized (this) {
324            mIsRoaming = isRoaming;
325        }
326    }
327
328    /**
329     * Returns if this network is metered. A network is classified as metered
330     * when the user is sensitive to heavy data usage on that connection due to
331     * monetary costs, data limitations or battery/performance issues. You
332     * should check this before doing large data transfers, and warn the user or
333     * delay the operation until another network is available.
334     *
335     * @return {@code true} if large transfers should be avoided, otherwise
336     *         {@code false}.
337     * @hide
338     */
339    public boolean isMetered() {
340        synchronized (this) {
341            return mIsMetered;
342        }
343    }
344
345    /** {@hide} */
346    @VisibleForTesting
347    public void setMetered(boolean isMetered) {
348        synchronized (this) {
349            mIsMetered = isMetered;
350        }
351    }
352
353    /**
354     * Reports the current coarse-grained state of the network.
355     * @return the coarse-grained state
356     */
357    public State getState() {
358        synchronized (this) {
359            return mState;
360        }
361    }
362
363    /**
364     * Reports the current fine-grained state of the network.
365     * @return the fine-grained state
366     */
367    public DetailedState getDetailedState() {
368        synchronized (this) {
369            return mDetailedState;
370        }
371    }
372
373    /**
374     * Sets the fine-grained state of the network.
375     * @param detailedState the {@link DetailedState}.
376     * @param reason a {@code String} indicating the reason for the state change,
377     * if one was supplied. May be {@code null}.
378     * @param extraInfo an optional {@code String} providing addditional network state
379     * information passed up from the lower networking layers.
380     * @hide
381     */
382    public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
383        synchronized (this) {
384            this.mDetailedState = detailedState;
385            this.mState = stateMap.get(detailedState);
386            this.mReason = reason;
387            this.mExtraInfo = extraInfo;
388        }
389    }
390
391    /**
392     * Set the extraInfo field.
393     * @param extraInfo an optional {@code String} providing addditional network state
394     * information passed up from the lower networking layers.
395     * @hide
396     */
397    public void setExtraInfo(String extraInfo) {
398        synchronized (this) {
399            this.mExtraInfo = extraInfo;
400        }
401    }
402
403    /**
404     * Report the reason an attempt to establish connectivity failed,
405     * if one is available.
406     * @return the reason for failure, or null if not available
407     */
408    public String getReason() {
409        synchronized (this) {
410            return mReason;
411        }
412    }
413
414    /**
415     * Report the extra information about the network state, if any was
416     * provided by the lower networking layers.
417     * @return the extra information, or null if not available
418     */
419    public String getExtraInfo() {
420        synchronized (this) {
421            return mExtraInfo;
422        }
423    }
424
425    @Override
426    public String toString() {
427        synchronized (this) {
428            StringBuilder builder = new StringBuilder("[");
429            builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
430            append("], state: ").append(mState).append("/").append(mDetailedState).
431            append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
432            append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
433            append(", failover: ").append(mIsFailover).
434            append(", available: ").append(mIsAvailable).
435            append(", roaming: ").append(mIsRoaming).
436            append(", metered: ").append(mIsMetered).
437            append("]");
438            return builder.toString();
439        }
440    }
441
442    @Override
443    public int describeContents() {
444        return 0;
445    }
446
447    @Override
448    public void writeToParcel(Parcel dest, int flags) {
449        synchronized (this) {
450            dest.writeInt(mNetworkType);
451            dest.writeInt(mSubtype);
452            dest.writeString(mTypeName);
453            dest.writeString(mSubtypeName);
454            dest.writeString(mState.name());
455            dest.writeString(mDetailedState.name());
456            dest.writeInt(mIsFailover ? 1 : 0);
457            dest.writeInt(mIsAvailable ? 1 : 0);
458            dest.writeInt(mIsRoaming ? 1 : 0);
459            dest.writeInt(mIsMetered ? 1 : 0);
460            dest.writeString(mReason);
461            dest.writeString(mExtraInfo);
462        }
463    }
464
465    public static final Creator<NetworkInfo> CREATOR = new Creator<NetworkInfo>() {
466        @Override
467        public NetworkInfo createFromParcel(Parcel in) {
468            int netType = in.readInt();
469            int subtype = in.readInt();
470            String typeName = in.readString();
471            String subtypeName = in.readString();
472            NetworkInfo netInfo = new NetworkInfo(netType, subtype, typeName, subtypeName);
473            netInfo.mState = State.valueOf(in.readString());
474            netInfo.mDetailedState = DetailedState.valueOf(in.readString());
475            netInfo.mIsFailover = in.readInt() != 0;
476            netInfo.mIsAvailable = in.readInt() != 0;
477            netInfo.mIsRoaming = in.readInt() != 0;
478            netInfo.mIsMetered = in.readInt() != 0;
479            netInfo.mReason = in.readString();
480            netInfo.mExtraInfo = in.readString();
481            return netInfo;
482        }
483
484        @Override
485        public NetworkInfo[] newArray(int size) {
486            return new NetworkInfo[size];
487        }
488    };
489}
490