Network.java revision 32a58f00d388584f5f47c0d5d4c74ce7c8457d78
1/*
2 * Copyright (C) 2014 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.net.NetworkUtils;
20import android.os.Parcelable;
21import android.os.Parcel;
22
23import java.io.IOException;
24import java.net.InetAddress;
25import java.net.InetSocketAddress;
26import java.net.Socket;
27import java.net.SocketException;
28import java.net.UnknownHostException;
29import javax.net.SocketFactory;
30
31/**
32 * Identifies a {@code Network}.  This is supplied to applications via
33 * {@link ConnectivityManager.NetworkCallback} in response to the active
34 * {@link ConnectivityManager#requestNetwork} or passive
35 * {@link ConnectivityManager#registerNetworkCallback} calls.
36 * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis
37 * through a targeted {@link SocketFactory} or process-wide via
38 * {@link ConnectivityManager#setProcessDefaultNetwork}.
39 */
40public class Network implements Parcelable {
41
42    /**
43     * @hide
44     */
45    public final int netId;
46
47    private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
48
49    /**
50     * @hide
51     */
52    public Network(int netId) {
53        this.netId = netId;
54    }
55
56    /**
57     * @hide
58     */
59    public Network(Network that) {
60        this.netId = that.netId;
61    }
62
63    /**
64     * Operates the same as {@code InetAddress.getAllByName} except that host
65     * resolution is done on this network.
66     *
67     * @param host the hostname or literal IP string to be resolved.
68     * @return the array of addresses associated with the specified host.
69     * @throws UnknownHostException if the address lookup fails.
70     */
71    public InetAddress[] getAllByName(String host) throws UnknownHostException {
72        return InetAddress.getAllByNameOnNet(host, netId);
73    }
74
75    /**
76     * Operates the same as {@code InetAddress.getByName} except that host
77     * resolution is done on this network.
78     *
79     * @param host
80     *            the hostName to be resolved to an address or {@code null}.
81     * @return the {@code InetAddress} instance representing the host.
82     * @throws UnknownHostException
83     *             if the address lookup fails.
84     */
85    public InetAddress getByName(String host) throws UnknownHostException {
86        return InetAddress.getByNameOnNet(host, netId);
87    }
88
89    /**
90     * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
91     */
92    private class NetworkBoundSocketFactory extends SocketFactory {
93        private final int mNetId;
94
95        public NetworkBoundSocketFactory(int netId) {
96            super();
97            mNetId = netId;
98        }
99
100        private void connectToHost(Socket socket, String host, int port) throws IOException {
101            // Lookup addresses only on this Network.
102            InetAddress[] hostAddresses = getAllByName(host);
103            // Try all but last address ignoring exceptions.
104            for (int i = 0; i < hostAddresses.length - 1; i++) {
105                try {
106                    socket.connect(new InetSocketAddress(hostAddresses[i], port));
107                    return;
108                } catch (IOException e) {
109                }
110            }
111            // Try last address.  Do throw exceptions.
112            socket.connect(new InetSocketAddress(hostAddresses[hostAddresses.length - 1], port));
113        }
114
115        @Override
116        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
117            Socket socket = createSocket();
118            socket.bind(new InetSocketAddress(localHost, localPort));
119            connectToHost(socket, host, port);
120            return socket;
121        }
122
123        @Override
124        public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
125                int localPort) throws IOException {
126            Socket socket = createSocket();
127            socket.bind(new InetSocketAddress(localAddress, localPort));
128            socket.connect(new InetSocketAddress(address, port));
129            return socket;
130        }
131
132        @Override
133        public Socket createSocket(InetAddress host, int port) throws IOException {
134            Socket socket = createSocket();
135            socket.connect(new InetSocketAddress(host, port));
136            return socket;
137        }
138
139        @Override
140        public Socket createSocket(String host, int port) throws IOException {
141            Socket socket = createSocket();
142            connectToHost(socket, host, port);
143            return socket;
144        }
145
146        @Override
147        public Socket createSocket() throws IOException {
148            Socket socket = new Socket();
149            // Query a property of the underlying socket to ensure the underlying
150            // socket exists so a file descriptor is available to bind to a network.
151            socket.getReuseAddress();
152            if (!NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId)) {
153                throw new SocketException("Failed to bind socket to network.");
154            }
155            return socket;
156        }
157    }
158
159    /**
160     * Returns a {@link SocketFactory} bound to this network.  Any {@link Socket} created by
161     * this factory will have its traffic sent over this {@code Network}.  Note that if this
162     * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
163     * past or future will cease to work.
164     *
165     * @return a {@link SocketFactory} which produces {@link Socket} instances bound to this
166     *         {@code Network}.
167     */
168    public SocketFactory getSocketFactory() {
169        if (mNetworkBoundSocketFactory == null) {
170            mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
171        }
172        return mNetworkBoundSocketFactory;
173    }
174
175    // implement the Parcelable interface
176    public int describeContents() {
177        return 0;
178    }
179    public void writeToParcel(Parcel dest, int flags) {
180        dest.writeInt(netId);
181    }
182
183    public static final Creator<Network> CREATOR =
184        new Creator<Network>() {
185            public Network createFromParcel(Parcel in) {
186                int netId = in.readInt();
187
188                return new Network(netId);
189            }
190
191            public Network[] newArray(int size) {
192                return new Network[size];
193            }
194    };
195
196    @Override
197    public boolean equals(Object obj) {
198        if (obj instanceof Network == false) return false;
199        Network other = (Network)obj;
200        return this.netId == other.netId;
201    }
202
203    @Override
204    public int hashCode() {
205        return netId * 11;
206    }
207
208    @Override
209    public String toString() {
210        return Integer.toString(netId);
211    }
212}
213