1 /*
2  * Copyright (C) 2020 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 static android.system.OsConstants.SOCK_DGRAM;
20 import static android.system.OsConstants.SOCK_STREAM;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.os.Parcel;
26 import android.os.ParcelFileDescriptor;
27 import android.os.Parcelable;
28 
29 import java.io.IOException;
30 import java.net.DatagramSocket;
31 import java.net.InetAddress;
32 import java.net.InetSocketAddress;
33 import java.net.Socket;
34 import java.net.UnknownHostException;
35 import java.util.Objects;
36 
37 /**
38  * Used in conjunction with
39  * {@link ConnectivityManager#registerQosCallback}
40  * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}
41  * and/or remote address and port of a connected {@link Socket}.
42  *
43  * @hide
44  */
45 @SystemApi
46 public final class QosSocketInfo implements Parcelable {
47 
48     @NonNull
49     private final Network mNetwork;
50 
51     @NonNull
52     private final ParcelFileDescriptor mParcelFileDescriptor;
53 
54     @NonNull
55     private final InetSocketAddress mLocalSocketAddress;
56 
57     @Nullable
58     private final InetSocketAddress mRemoteSocketAddress;
59 
60     private final int mSocketType;
61 
62     /**
63      * The {@link Network} the socket is on.
64      *
65      * @return the registered {@link Network}
66      */
67     @NonNull
getNetwork()68     public Network getNetwork() {
69         return mNetwork;
70     }
71 
72     /**
73      * The parcel file descriptor wrapped around the socket's file descriptor.
74      *
75      * @return the parcel file descriptor of the socket
76      * @hide
77      */
78     @NonNull
getParcelFileDescriptor()79     public ParcelFileDescriptor getParcelFileDescriptor() {
80         return mParcelFileDescriptor;
81     }
82 
83     /**
84      * The local address of the socket passed into {@link QosSocketInfo(Network, Socket)}.
85      * The value does not reflect any changes that occur to the socket after it is first set
86      * in the constructor.
87      *
88      * @return the local address of the socket
89      */
90     @NonNull
getLocalSocketAddress()91     public InetSocketAddress getLocalSocketAddress() {
92         return mLocalSocketAddress;
93     }
94 
95     /**
96      * The remote address of the socket passed into {@link QosSocketInfo(Network, Socket)}.
97      * The value does not reflect any changes that occur to the socket after it is first set
98      * in the constructor.
99      *
100      * @return the remote address of the socket if socket is connected, null otherwise
101      */
102     @Nullable
getRemoteSocketAddress()103     public InetSocketAddress getRemoteSocketAddress() {
104         return mRemoteSocketAddress;
105     }
106 
107     /**
108      * The socket type of the socket passed in when this QosSocketInfo object was constructed.
109      *
110      * @return the socket type of the socket.
111      * @hide
112      */
getSocketType()113     public int getSocketType()  {
114         return mSocketType;
115     }
116 
117     /**
118      * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}.  The
119      * {@link Socket} must remain bound in order to receive {@link QosSession}s.
120      *
121      * @param network the network
122      * @param socket the bound {@link Socket}
123      */
QosSocketInfo(@onNull final Network network, @NonNull final Socket socket)124     public QosSocketInfo(@NonNull final Network network, @NonNull final Socket socket)
125             throws IOException {
126         Objects.requireNonNull(socket, "socket cannot be null");
127 
128         mNetwork = Objects.requireNonNull(network, "network cannot be null");
129         mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
130         mLocalSocketAddress =
131                 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
132         mSocketType = SOCK_STREAM;
133 
134         if (socket.isConnected()) {
135             mRemoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
136         } else {
137             mRemoteSocketAddress = null;
138         }
139     }
140 
141     /**
142      * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link DatagramSocket}. The
143      * {@link DatagramSocket} must remain bound in order to receive {@link QosSession}s.
144      *
145      * @param network the network
146      * @param socket the bound {@link DatagramSocket}
147      */
QosSocketInfo(@onNull final Network network, @NonNull final DatagramSocket socket)148     public QosSocketInfo(@NonNull final Network network, @NonNull final DatagramSocket socket)
149             throws IOException {
150         Objects.requireNonNull(socket, "socket cannot be null");
151 
152         mNetwork = Objects.requireNonNull(network, "network cannot be null");
153         mParcelFileDescriptor = ParcelFileDescriptor.fromDatagramSocket(socket);
154         mLocalSocketAddress =
155                 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
156         mSocketType = SOCK_DGRAM;
157 
158         if (socket.isConnected()) {
159             mRemoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
160         } else {
161             mRemoteSocketAddress = null;
162         }
163     }
164 
165     /* Parcelable methods */
QosSocketInfo(final Parcel in)166     private QosSocketInfo(final Parcel in) {
167         mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
168         final boolean withFd = in.readBoolean();
169         if (withFd) {
170             mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
171         } else {
172             mParcelFileDescriptor = null;
173         }
174 
175         mLocalSocketAddress = readSocketAddress(in);
176         mRemoteSocketAddress = readSocketAddress(in);
177 
178         mSocketType = in.readInt();
179     }
180 
readSocketAddress(final Parcel in)181     private InetSocketAddress readSocketAddress(final Parcel in) {
182         final byte[] addrBytes = in.createByteArray();
183         if (addrBytes == null) {
184             return null;
185         }
186         final int port = in.readInt();
187 
188         try {
189             return new InetSocketAddress(InetAddress.getByAddress(addrBytes), port);
190         } catch (final UnknownHostException e) {
191             /* This can never happen. UnknownHostException will never be thrown
192                since the address provided is numeric and non-null. */
193             throw new RuntimeException("UnknownHostException on numeric address", e);
194         }
195     }
196 
197     @Override
describeContents()198     public int describeContents() {
199         return 0;
200     }
201 
202     @Override
writeToParcel(@onNull final Parcel dest, final int flags)203     public void writeToParcel(@NonNull final Parcel dest, final int flags) {
204         writeToParcelInternal(dest, flags, /*includeFd=*/ true);
205     }
206 
207     /**
208      * Used when sending QosSocketInfo to telephony, which does not need access to the socket FD.
209      * @hide
210      */
writeToParcelWithoutFd(@onNull final Parcel dest, final int flags)211     public void writeToParcelWithoutFd(@NonNull final Parcel dest, final int flags) {
212         writeToParcelInternal(dest, flags, /*includeFd=*/ false);
213     }
214 
writeToParcelInternal( @onNull final Parcel dest, final int flags, boolean includeFd)215     private void writeToParcelInternal(
216             @NonNull final Parcel dest, final int flags, boolean includeFd) {
217         mNetwork.writeToParcel(dest, 0);
218 
219         if (includeFd) {
220             dest.writeBoolean(true);
221             mParcelFileDescriptor.writeToParcel(dest, 0);
222         } else {
223             dest.writeBoolean(false);
224         }
225 
226         dest.writeByteArray(mLocalSocketAddress.getAddress().getAddress());
227         dest.writeInt(mLocalSocketAddress.getPort());
228 
229         if (mRemoteSocketAddress == null) {
230             dest.writeByteArray(null);
231         } else {
232             dest.writeByteArray(mRemoteSocketAddress.getAddress().getAddress());
233             dest.writeInt(mRemoteSocketAddress.getPort());
234         }
235         dest.writeInt(mSocketType);
236     }
237 
238     @NonNull
239     public static final Parcelable.Creator<QosSocketInfo> CREATOR =
240             new Parcelable.Creator<QosSocketInfo>() {
241             @NonNull
242             @Override
243             public QosSocketInfo createFromParcel(final Parcel in) {
244                 return new QosSocketInfo(in);
245             }
246 
247             @NonNull
248             @Override
249             public QosSocketInfo[] newArray(final int size) {
250                 return new QosSocketInfo[size];
251             }
252         };
253 }
254