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 */ 77 @NonNull getParcelFileDescriptor()78 ParcelFileDescriptor getParcelFileDescriptor() { 79 return mParcelFileDescriptor; 80 } 81 82 /** 83 * The local address of the socket passed into {@link QosSocketInfo(Network, Socket)}. 84 * The value does not reflect any changes that occur to the socket after it is first set 85 * in the constructor. 86 * 87 * @return the local address of the socket 88 */ 89 @NonNull getLocalSocketAddress()90 public InetSocketAddress getLocalSocketAddress() { 91 return mLocalSocketAddress; 92 } 93 94 /** 95 * The remote address of the socket passed into {@link QosSocketInfo(Network, Socket)}. 96 * The value does not reflect any changes that occur to the socket after it is first set 97 * in the constructor. 98 * 99 * @return the remote address of the socket if socket is connected, null otherwise 100 */ 101 @Nullable getRemoteSocketAddress()102 public InetSocketAddress getRemoteSocketAddress() { 103 return mRemoteSocketAddress; 104 } 105 106 /** 107 * The socket type of the socket passed in when this QosSocketInfo object was constructed. 108 * 109 * @return the socket type of the socket. 110 * @hide 111 */ getSocketType()112 public int getSocketType() { 113 return mSocketType; 114 } 115 116 /** 117 * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}. The 118 * {@link Socket} must remain bound in order to receive {@link QosSession}s. 119 * 120 * @param network the network 121 * @param socket the bound {@link Socket} 122 */ QosSocketInfo(@onNull final Network network, @NonNull final Socket socket)123 public QosSocketInfo(@NonNull final Network network, @NonNull final Socket socket) 124 throws IOException { 125 Objects.requireNonNull(socket, "socket cannot be null"); 126 127 mNetwork = Objects.requireNonNull(network, "network cannot be null"); 128 mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket); 129 mLocalSocketAddress = 130 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort()); 131 mSocketType = SOCK_STREAM; 132 133 if (socket.isConnected()) { 134 mRemoteSocketAddress = (InetSocketAddress) socket.getRemoteSocketAddress(); 135 } else { 136 mRemoteSocketAddress = null; 137 } 138 } 139 140 /** 141 * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link DatagramSocket}. The 142 * {@link DatagramSocket} must remain bound in order to receive {@link QosSession}s. 143 * 144 * @param network the network 145 * @param socket the bound {@link DatagramSocket} 146 * @hide 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 mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in); 169 170 final int localAddressLength = in.readInt(); 171 mLocalSocketAddress = readSocketAddress(in, localAddressLength); 172 173 final int remoteAddressLength = in.readInt(); 174 mRemoteSocketAddress = remoteAddressLength == 0 ? null 175 : readSocketAddress(in, remoteAddressLength); 176 177 mSocketType = in.readInt(); 178 } 179 readSocketAddress(final Parcel in, final int addressLength)180 private @NonNull InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) { 181 final byte[] address = new byte[addressLength]; 182 in.readByteArray(address); 183 final int port = in.readInt(); 184 185 try { 186 return new InetSocketAddress(InetAddress.getByAddress(address), port); 187 } catch (final UnknownHostException e) { 188 /* This can never happen. UnknownHostException will never be thrown 189 since the address provided is numeric and non-null. */ 190 throw new RuntimeException("UnknownHostException on numeric address", e); 191 } 192 } 193 194 @Override describeContents()195 public int describeContents() { 196 return 0; 197 } 198 199 @Override writeToParcel(@onNull final Parcel dest, final int flags)200 public void writeToParcel(@NonNull final Parcel dest, final int flags) { 201 mNetwork.writeToParcel(dest, 0); 202 mParcelFileDescriptor.writeToParcel(dest, 0); 203 204 final byte[] localAddress = mLocalSocketAddress.getAddress().getAddress(); 205 dest.writeInt(localAddress.length); 206 dest.writeByteArray(localAddress); 207 dest.writeInt(mLocalSocketAddress.getPort()); 208 209 if (mRemoteSocketAddress == null) { 210 dest.writeInt(0); 211 } else { 212 final byte[] remoteAddress = mRemoteSocketAddress.getAddress().getAddress(); 213 dest.writeInt(remoteAddress.length); 214 dest.writeByteArray(remoteAddress); 215 dest.writeInt(mRemoteSocketAddress.getPort()); 216 } 217 dest.writeInt(mSocketType); 218 } 219 220 @NonNull 221 public static final Parcelable.Creator<QosSocketInfo> CREATOR = 222 new Parcelable.Creator<QosSocketInfo>() { 223 @NonNull 224 @Override 225 public QosSocketInfo createFromParcel(final Parcel in) { 226 return new QosSocketInfo(in); 227 } 228 229 @NonNull 230 @Override 231 public QosSocketInfo[] newArray(final int size) { 232 return new QosSocketInfo[size]; 233 } 234 }; 235 } 236