Merge "Add Qos Callback support"
This commit is contained in:
@@ -19,6 +19,7 @@ import static android.net.IpSecManager.INVALID_RESOURCE_ID;
|
||||
import static android.net.NetworkRequest.Type.LISTEN;
|
||||
import static android.net.NetworkRequest.Type.REQUEST;
|
||||
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
|
||||
import static android.net.QosCallback.QosCallbackRegistrationException;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
@@ -4849,4 +4850,118 @@ public class ConnectivityManager {
|
||||
Log.d(TAG, "setOemNetworkPreference called with preference: "
|
||||
+ preference.toString());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Registers a {@link QosSocketInfo} with an associated {@link QosCallback}. The callback will
|
||||
* receive available QoS events related to the {@link Network} and local ip + port
|
||||
* specified within socketInfo.
|
||||
* <p/>
|
||||
* The same {@link QosCallback} must be unregistered before being registered a second time,
|
||||
* otherwise {@link QosCallbackRegistrationException} is thrown.
|
||||
* <p/>
|
||||
* This API does not, in itself, require any permission if called with a network that is not
|
||||
* restricted. However, the underlying implementation currently only supports the IMS network,
|
||||
* which is always restricted. That means non-preinstalled callers can't possibly find this API
|
||||
* useful, because they'd never be called back on networks that they would have access to.
|
||||
*
|
||||
* @throws SecurityException if {@link QosSocketInfo#getNetwork()} is restricted and the app is
|
||||
* missing CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
|
||||
* @throws QosCallback.QosCallbackRegistrationException if qosCallback is already registered.
|
||||
* @throws RuntimeException if the app already has too many callbacks registered.
|
||||
*
|
||||
* Exceptions after the time of registration is passed through
|
||||
* {@link QosCallback#onError(QosCallbackException)}. see: {@link QosCallbackException}.
|
||||
*
|
||||
* @param socketInfo the socket information used to match QoS events
|
||||
* @param callback receives qos events that satisfy socketInfo
|
||||
* @param executor The executor on which the callback will be invoked. The provided
|
||||
* {@link Executor} must run callback sequentially, otherwise the order of
|
||||
* callbacks cannot be guaranteed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void registerQosCallback(@NonNull final QosSocketInfo socketInfo,
|
||||
@NonNull final QosCallback callback,
|
||||
@CallbackExecutor @NonNull final Executor executor) {
|
||||
Objects.requireNonNull(socketInfo, "socketInfo must be non-null");
|
||||
Objects.requireNonNull(callback, "callback must be non-null");
|
||||
Objects.requireNonNull(executor, "executor must be non-null");
|
||||
|
||||
try {
|
||||
synchronized (mQosCallbackConnections) {
|
||||
if (getQosCallbackConnection(callback) == null) {
|
||||
final QosCallbackConnection connection =
|
||||
new QosCallbackConnection(this, callback, executor);
|
||||
mQosCallbackConnections.add(connection);
|
||||
mService.registerQosSocketCallback(socketInfo, connection);
|
||||
} else {
|
||||
Log.e(TAG, "registerQosCallback: Callback already registered");
|
||||
throw new QosCallbackRegistrationException();
|
||||
}
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
Log.e(TAG, "registerQosCallback: Error while registering ", e);
|
||||
|
||||
// The same unregister method method is called for consistency even though nothing
|
||||
// will be sent to the ConnectivityService since the callback was never successfully
|
||||
// registered.
|
||||
unregisterQosCallback(callback);
|
||||
e.rethrowFromSystemServer();
|
||||
} catch (final ServiceSpecificException e) {
|
||||
Log.e(TAG, "registerQosCallback: Error while registering ", e);
|
||||
unregisterQosCallback(callback);
|
||||
throw convertServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the given {@link QosCallback}. The {@link QosCallback} will no longer receive
|
||||
* events once unregistered and can be registered a second time.
|
||||
* <p/>
|
||||
* If the {@link QosCallback} does not have an active registration, it is a no-op.
|
||||
*
|
||||
* @param callback the callback being unregistered
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void unregisterQosCallback(@NonNull final QosCallback callback) {
|
||||
Objects.requireNonNull(callback, "The callback must be non-null");
|
||||
try {
|
||||
synchronized (mQosCallbackConnections) {
|
||||
final QosCallbackConnection connection = getQosCallbackConnection(callback);
|
||||
if (connection != null) {
|
||||
connection.stopReceivingMessages();
|
||||
mService.unregisterQosCallback(connection);
|
||||
mQosCallbackConnections.remove(connection);
|
||||
} else {
|
||||
Log.d(TAG, "unregisterQosCallback: Callback not registered");
|
||||
}
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
Log.e(TAG, "unregisterQosCallback: Error while unregistering ", e);
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connection related to the callback.
|
||||
*
|
||||
* @param callback the callback to look up
|
||||
* @return the related connection
|
||||
*/
|
||||
@Nullable
|
||||
private QosCallbackConnection getQosCallbackConnection(final QosCallback callback) {
|
||||
for (final QosCallbackConnection connection : mQosCallbackConnections) {
|
||||
// Checking by reference here is intentional
|
||||
if (connection.getCallback() == callback) {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.app.PendingIntent;
|
||||
import android.net.ConnectionInfo;
|
||||
import android.net.ConnectivityDiagnosticsManager;
|
||||
import android.net.IConnectivityDiagnosticsCallback;
|
||||
import android.net.IQosCallback;
|
||||
import android.net.ISocketKeepaliveCallback;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkAgentConfig;
|
||||
@@ -27,9 +29,9 @@ import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.NetworkState;
|
||||
import android.net.ISocketKeepaliveCallback;
|
||||
import android.net.ProxyInfo;
|
||||
import android.net.UidRange;
|
||||
import android.net.QosSocketInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.INetworkActivityListener;
|
||||
@@ -239,4 +241,7 @@ interface IConnectivityManager
|
||||
void unregisterNetworkActivityListener(in INetworkActivityListener l);
|
||||
|
||||
boolean isDefaultNetworkActive();
|
||||
|
||||
void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
|
||||
void unregisterQosCallback(in IQosCallback callback);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.telephony.data.EpsBearerQosSessionAttributes;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.connectivity.aidl.INetworkAgent;
|
||||
@@ -227,7 +228,7 @@ public abstract class NetworkAgent {
|
||||
*/
|
||||
public static final String REDIRECT_URL_KEY = "redirect URL";
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sent by the NetworkAgent to ConnectivityService to indicate this network was
|
||||
* explicitly selected. This should be sent before the NetworkInfo is marked
|
||||
* CONNECTED so it can be given special treatment at that time.
|
||||
@@ -341,6 +342,24 @@ public abstract class NetworkAgent {
|
||||
*/
|
||||
private static final int EVENT_AGENT_DISCONNECTED = BASE + 19;
|
||||
|
||||
/**
|
||||
* Sent by QosCallbackTracker to {@link NetworkAgent} to register a new filter with
|
||||
* callback.
|
||||
*
|
||||
* arg1 = QoS agent callback ID
|
||||
* obj = {@link QosFilter}
|
||||
* @hide
|
||||
*/
|
||||
public static final int CMD_REGISTER_QOS_CALLBACK = BASE + 20;
|
||||
|
||||
/**
|
||||
* Sent by QosCallbackTracker to {@link NetworkAgent} to unregister a callback.
|
||||
*
|
||||
* arg1 = QoS agent callback ID
|
||||
* @hide
|
||||
*/
|
||||
public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
|
||||
|
||||
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
|
||||
// The subtype can be changed with (TODO) setLegacySubtype, but it starts
|
||||
// with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
|
||||
@@ -520,6 +539,17 @@ public abstract class NetworkAgent {
|
||||
onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
|
||||
break;
|
||||
}
|
||||
case CMD_REGISTER_QOS_CALLBACK: {
|
||||
onQosCallbackRegistered(
|
||||
msg.arg1 /* QoS callback id */,
|
||||
(QosFilter) msg.obj /* QoS filter */);
|
||||
break;
|
||||
}
|
||||
case CMD_UNREGISTER_QOS_CALLBACK: {
|
||||
onQosCallbackUnregistered(
|
||||
msg.arg1 /* QoS callback id */);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -553,6 +583,8 @@ public abstract class NetworkAgent {
|
||||
}
|
||||
|
||||
private static class NetworkAgentBinder extends INetworkAgent.Stub {
|
||||
private static final String LOG_TAG = NetworkAgentBinder.class.getSimpleName();
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
private NetworkAgentBinder(Handler handler) {
|
||||
@@ -639,6 +671,25 @@ public abstract class NetworkAgent {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
|
||||
slot, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQosFilterCallbackRegistered(final int qosCallbackId,
|
||||
final QosFilterParcelable qosFilterParcelable) {
|
||||
if (qosFilterParcelable.getQosFilter() != null) {
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(CMD_REGISTER_QOS_CALLBACK, qosCallbackId, 0,
|
||||
qosFilterParcelable.getQosFilter()));
|
||||
return;
|
||||
}
|
||||
|
||||
Log.wtf(LOG_TAG, "onQosFilterCallbackRegistered: qos filter is null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQosCallbackUnregistered(final int qosCallbackId) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(
|
||||
CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1067,8 +1118,68 @@ public abstract class NetworkAgent {
|
||||
protected void preventAutomaticReconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a qos callback is registered with a filter.
|
||||
* @param qosCallbackId the id for the callback registered
|
||||
* @param filter the filter being registered
|
||||
*/
|
||||
public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a qos callback is registered with a filter.
|
||||
* <p/>
|
||||
* Any QoS events that are sent with the same callback id after this method is called
|
||||
* are a no-op.
|
||||
*
|
||||
* @param qosCallbackId the id for the callback being unregistered
|
||||
*/
|
||||
public void onQosCallbackUnregistered(final int qosCallbackId) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends the attributes of Eps Bearer Qos Session back to the Application
|
||||
*
|
||||
* @param qosCallbackId the callback id that the session belongs to
|
||||
* @param sessionId the unique session id across all Eps Bearer Qos Sessions
|
||||
* @param attributes the attributes of the Eps Qos Session
|
||||
*/
|
||||
public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId,
|
||||
@NonNull final EpsBearerQosSessionAttributes attributes) {
|
||||
Objects.requireNonNull(attributes, "The attributes must be non-null");
|
||||
queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
|
||||
new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
|
||||
attributes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends event that the Eps Qos Session was lost.
|
||||
*
|
||||
* @param qosCallbackId the callback id that the session belongs to
|
||||
* @param sessionId the unique session id across all Eps Bearer Qos Sessions
|
||||
*/
|
||||
public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) {
|
||||
queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
|
||||
new QosSession(sessionId, QosSession.TYPE_EPS_BEARER)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the exception type back to the application.
|
||||
*
|
||||
* The NetworkAgent should not send anymore messages with this id.
|
||||
*
|
||||
* @param qosCallbackId the callback id this exception belongs to
|
||||
* @param exceptionType the type of exception
|
||||
*/
|
||||
public final void sendQosCallbackError(final int qosCallbackId,
|
||||
@QosCallbackException.ExceptionType final int exceptionType) {
|
||||
queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
|
||||
}
|
||||
|
||||
|
||||
/** @hide */
|
||||
protected void log(String s) {
|
||||
protected void log(final String s) {
|
||||
Log.d(LOG_TAG, "NetworkAgent: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
91
core/java/android/net/QosCallback.java
Normal file
91
core/java/android/net/QosCallback.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Receives Qos information given a {@link Network}. The callback is registered with
|
||||
* {@link ConnectivityManager#registerQosCallback}.
|
||||
*
|
||||
* <p>
|
||||
* <br/>
|
||||
* The callback will no longer receive calls if any of the following takes place:
|
||||
* <ol>
|
||||
* <li>{@link ConnectivityManager#unregisterQosCallback(QosCallback)} is called with the same
|
||||
* callback instance.</li>
|
||||
* <li>{@link QosCallback#onError(QosCallbackException)} is called.</li>
|
||||
* <li>A network specific issue occurs. eg. Congestion on a carrier network.</li>
|
||||
* <li>The network registered with the callback has no associated QoS providers</li>
|
||||
* </ul>
|
||||
* {@hide}
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class QosCallback {
|
||||
/**
|
||||
* Invoked after an error occurs on a registered callback. Once called, the callback is
|
||||
* automatically unregistered and the callback will no longer receive calls.
|
||||
*
|
||||
* <p>The underlying exception can either be a runtime exception or a custom exception made for
|
||||
* {@link QosCallback}. see: {@link QosCallbackException}.
|
||||
*
|
||||
* @param exception wraps the underlying cause
|
||||
*/
|
||||
public void onError(@NonNull final QosCallbackException exception) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a Qos Session first becomes available to the callback or if its attributes have
|
||||
* changed.
|
||||
* <p>
|
||||
* Note: The callback may be called multiple times with the same attributes.
|
||||
*
|
||||
* @param session the available session
|
||||
* @param sessionAttributes the attributes of the session
|
||||
*/
|
||||
public void onQosSessionAvailable(@NonNull final QosSession session,
|
||||
@NonNull final QosSessionAttributes sessionAttributes) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a Qos Session is lost.
|
||||
* <p>
|
||||
* At least one call to
|
||||
* {@link QosCallback#onQosSessionAvailable(QosSession, QosSessionAttributes)}
|
||||
* with the same {@link QosSession} will precede a call to lost.
|
||||
*
|
||||
* @param session the lost session
|
||||
*/
|
||||
public void onQosSessionLost(@NonNull final QosSession session) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when there is a problem registering {@link QosCallback} with
|
||||
* {@link ConnectivityManager#registerQosCallback(QosSocketInfo, QosCallback, Executor)}.
|
||||
*/
|
||||
public static class QosCallbackRegistrationException extends RuntimeException {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public QosCallbackRegistrationException() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
128
core/java/android/net/QosCallbackConnection.java
Normal file
128
core/java/android/net/QosCallbackConnection.java
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.telephony.data.EpsBearerQosSessionAttributes;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Sends messages from {@link com.android.server.ConnectivityService} to the registered
|
||||
* {@link QosCallback}.
|
||||
* <p/>
|
||||
* This is a satellite class of {@link ConnectivityManager} and not meant
|
||||
* to be used in other contexts.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class QosCallbackConnection extends android.net.IQosCallback.Stub {
|
||||
|
||||
@NonNull private final ConnectivityManager mConnectivityManager;
|
||||
@Nullable private volatile QosCallback mCallback;
|
||||
@NonNull private final Executor mExecutor;
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public QosCallback getCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* The constructor for the connection
|
||||
*
|
||||
* @param connectivityManager the mgr that created this connection
|
||||
* @param callback the callback to send messages back to
|
||||
* @param executor The executor on which the callback will be invoked. The provided
|
||||
* {@link Executor} must run callback sequentially, otherwise the order of
|
||||
* callbacks cannot be guaranteed.
|
||||
*/
|
||||
QosCallbackConnection(@NonNull final ConnectivityManager connectivityManager,
|
||||
@NonNull final QosCallback callback,
|
||||
@NonNull final Executor executor) {
|
||||
mConnectivityManager = Objects.requireNonNull(connectivityManager,
|
||||
"connectivityManager must be non-null");
|
||||
mCallback = Objects.requireNonNull(callback, "callback must be non-null");
|
||||
mExecutor = Objects.requireNonNull(executor, "executor must be non-null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when either the {@link EpsBearerQosSessionAttributes} has changed or on the first time
|
||||
* the attributes have become available.
|
||||
*
|
||||
* @param session the session that is now available
|
||||
* @param attributes the corresponding attributes of session
|
||||
*/
|
||||
@Override
|
||||
public void onQosEpsBearerSessionAvailable(@NonNull final QosSession session,
|
||||
@NonNull final EpsBearerQosSessionAttributes attributes) {
|
||||
|
||||
mExecutor.execute(() -> {
|
||||
final QosCallback callback = mCallback;
|
||||
if (callback != null) {
|
||||
callback.onQosSessionAvailable(session, attributes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the session is lost.
|
||||
*
|
||||
* @param session the session that was lost
|
||||
*/
|
||||
@Override
|
||||
public void onQosSessionLost(@NonNull final QosSession session) {
|
||||
mExecutor.execute(() -> {
|
||||
final QosCallback callback = mCallback;
|
||||
if (callback != null) {
|
||||
callback.onQosSessionLost(session);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is an error on the registered callback.
|
||||
*
|
||||
* @param errorType the type of error
|
||||
*/
|
||||
@Override
|
||||
public void onError(@QosCallbackException.ExceptionType final int errorType) {
|
||||
mExecutor.execute(() -> {
|
||||
final QosCallback callback = mCallback;
|
||||
if (callback != null) {
|
||||
// Messages no longer need to be received since there was an error.
|
||||
stopReceivingMessages();
|
||||
mConnectivityManager.unregisterQosCallback(callback);
|
||||
callback.onError(QosCallbackException.createException(errorType));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback will stop receiving messages.
|
||||
* <p/>
|
||||
* There are no synchronization guarantees on exactly when the callback will stop receiving
|
||||
* messages.
|
||||
*/
|
||||
void stopReceivingMessages() {
|
||||
mCallback = null;
|
||||
}
|
||||
}
|
||||
110
core/java/android/net/QosCallbackException.java
Normal file
110
core/java/android/net/QosCallbackException.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* This is the exception type passed back through the onError method on {@link QosCallback}.
|
||||
* {@link QosCallbackException#getCause()} contains the actual error that caused this exception.
|
||||
*
|
||||
* The possible exception types as causes are:
|
||||
* 1. {@link NetworkReleasedException}
|
||||
* 2. {@link SocketNotBoundException}
|
||||
* 3. {@link UnsupportedOperationException}
|
||||
* 4. {@link SocketLocalAddressChangedException}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class QosCallbackException extends Exception {
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = {"EX_TYPE_"}, value = {
|
||||
EX_TYPE_FILTER_NONE,
|
||||
EX_TYPE_FILTER_NETWORK_RELEASED,
|
||||
EX_TYPE_FILTER_SOCKET_NOT_BOUND,
|
||||
EX_TYPE_FILTER_NOT_SUPPORTED,
|
||||
EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ExceptionType {}
|
||||
|
||||
private static final String TAG = "QosCallbackException";
|
||||
|
||||
// Types of exceptions supported //
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_NONE = 0;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_NETWORK_RELEASED = 1;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
|
||||
|
||||
/**
|
||||
* Creates exception based off of a type and message. Not all types of exceptions accept a
|
||||
* custom message.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
@NonNull
|
||||
static QosCallbackException createException(@ExceptionType final int type) {
|
||||
switch (type) {
|
||||
case EX_TYPE_FILTER_NETWORK_RELEASED:
|
||||
return new QosCallbackException(new NetworkReleasedException());
|
||||
case EX_TYPE_FILTER_SOCKET_NOT_BOUND:
|
||||
return new QosCallbackException(new SocketNotBoundException());
|
||||
case EX_TYPE_FILTER_NOT_SUPPORTED:
|
||||
return new QosCallbackException(new UnsupportedOperationException(
|
||||
"This device does not support the specified filter"));
|
||||
case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED:
|
||||
return new QosCallbackException(
|
||||
new SocketLocalAddressChangedException());
|
||||
default:
|
||||
Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'");
|
||||
return new QosCallbackException(
|
||||
new RuntimeException("Unknown exception code: " + type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public QosCallbackException(@NonNull final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public QosCallbackException(@NonNull final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
62
core/java/android/net/QosFilter.java
Normal file
62
core/java/android/net/QosFilter.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
|
||||
/**
|
||||
* Provides the related filtering logic to the {@link NetworkAgent} to match {@link QosSession}s
|
||||
* to their related {@link QosCallback}.
|
||||
*
|
||||
* Used by the {@link com.android.server.ConnectivityService} to validate a {@link QosCallback}
|
||||
* is still able to receive a {@link QosSession}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract class QosFilter {
|
||||
|
||||
/**
|
||||
* The constructor is kept hidden from outside this package to ensure that all derived types
|
||||
* are known and properly handled when being passed to and from {@link NetworkAgent}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
QosFilter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The network used with this filter.
|
||||
*
|
||||
* @return the registered {@link Network}
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Network getNetwork();
|
||||
|
||||
/**
|
||||
* Validates that conditions have not changed such that no further {@link QosSession}s should
|
||||
* be passed back to the {@link QosCallback} associated to this filter.
|
||||
*
|
||||
* @return the error code when present, otherwise the filter is valid
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@QosCallbackException.ExceptionType
|
||||
public abstract int validate();
|
||||
}
|
||||
|
||||
113
core/java/android/net/QosFilterParcelable.java
Normal file
113
core/java/android/net/QosFilterParcelable.java
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Aware of how to parcel different types of {@link QosFilter}s. Any new type of qos filter must
|
||||
* have a specialized case written here.
|
||||
* <p/>
|
||||
* Specifically leveraged when transferring {@link QosFilter} from
|
||||
* {@link com.android.server.ConnectivityService} to {@link NetworkAgent} when the filter is first
|
||||
* registered.
|
||||
* <p/>
|
||||
* This is not meant to be used in other contexts.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class QosFilterParcelable implements Parcelable {
|
||||
|
||||
private static final String LOG_TAG = QosFilterParcelable.class.getSimpleName();
|
||||
|
||||
// Indicates that the filter was not successfully written to the parcel.
|
||||
private static final int NO_FILTER_PRESENT = 0;
|
||||
|
||||
// The parcel is of type qos socket filter.
|
||||
private static final int QOS_SOCKET_FILTER = 1;
|
||||
|
||||
private final QosFilter mQosFilter;
|
||||
|
||||
/**
|
||||
* The underlying qos filter.
|
||||
* <p/>
|
||||
* Null only in the case parceling failed.
|
||||
*/
|
||||
@Nullable
|
||||
public QosFilter getQosFilter() {
|
||||
return mQosFilter;
|
||||
}
|
||||
|
||||
public QosFilterParcelable(@NonNull final QosFilter qosFilter) {
|
||||
Objects.requireNonNull(qosFilter, "qosFilter must be non-null");
|
||||
|
||||
// NOTE: Normally a type check would belong here, but doing so breaks unit tests that rely
|
||||
// on mocking qos filter.
|
||||
mQosFilter = qosFilter;
|
||||
}
|
||||
|
||||
private QosFilterParcelable(final Parcel in) {
|
||||
final int filterParcelType = in.readInt();
|
||||
|
||||
switch (filterParcelType) {
|
||||
case QOS_SOCKET_FILTER: {
|
||||
mQosFilter = new QosSocketFilter(QosSocketInfo.CREATOR.createFromParcel(in));
|
||||
break;
|
||||
}
|
||||
|
||||
case NO_FILTER_PRESENT:
|
||||
default: {
|
||||
mQosFilter = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<QosFilterParcelable> CREATOR = new Creator<QosFilterParcelable>() {
|
||||
@Override
|
||||
public QosFilterParcelable createFromParcel(final Parcel in) {
|
||||
return new QosFilterParcelable(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QosFilterParcelable[] newArray(final int size) {
|
||||
return new QosFilterParcelable[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
if (mQosFilter instanceof QosSocketFilter) {
|
||||
dest.writeInt(QOS_SOCKET_FILTER);
|
||||
final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter;
|
||||
qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0);
|
||||
return;
|
||||
}
|
||||
dest.writeInt(NO_FILTER_PRESENT);
|
||||
Log.e(LOG_TAG, "Parceling failed, unknown type of filter present: " + mQosFilter);
|
||||
}
|
||||
}
|
||||
136
core/java/android/net/QosSession.java
Normal file
136
core/java/android/net/QosSession.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Provides identifying information of a QoS session. Sent to an application through
|
||||
* {@link QosCallback}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class QosSession implements Parcelable {
|
||||
|
||||
/**
|
||||
* The {@link QosSession} is a LTE EPS Session.
|
||||
*/
|
||||
public static final int TYPE_EPS_BEARER = 1;
|
||||
|
||||
private final int mSessionId;
|
||||
|
||||
private final int mSessionType;
|
||||
|
||||
/**
|
||||
* Gets the unique id of the session that is used to differentiate sessions across different
|
||||
* types.
|
||||
* <p/>
|
||||
* Note: Different qos sessions can be provided by different actors.
|
||||
*
|
||||
* @return the unique id
|
||||
*/
|
||||
public long getUniqueId() {
|
||||
return (long) mSessionType << 32 | mSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session id that is unique within that type.
|
||||
* <p/>
|
||||
* Note: The session id is set by the actor providing the qos. It can be either manufactured by
|
||||
* the actor, but also may have a particular meaning within that type. For example, using the
|
||||
* bearer id as the session id for {@link android.telephony.data.EpsBearerQosSessionAttributes}
|
||||
* is a straight forward way to keep the sessions unique from one another within that type.
|
||||
*
|
||||
* @return the id of the session
|
||||
*/
|
||||
public int getSessionId() {
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of session.
|
||||
*/
|
||||
@QosSessionType
|
||||
public int getSessionType() {
|
||||
return mSessionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link QosSession}.
|
||||
*
|
||||
* @param sessionId uniquely identifies the session across all sessions of the same type
|
||||
* @param sessionType the type of session
|
||||
*/
|
||||
public QosSession(final int sessionId, @QosSessionType final int sessionType) {
|
||||
//Ensures the session id is unique across types of sessions
|
||||
mSessionId = sessionId;
|
||||
mSessionType = sessionType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QosSession{"
|
||||
+ "mSessionId=" + mSessionId
|
||||
+ ", mSessionType=" + mSessionType
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotations for types of qos sessions.
|
||||
*/
|
||||
@IntDef(value = {
|
||||
TYPE_EPS_BEARER,
|
||||
})
|
||||
@interface QosSessionType {}
|
||||
|
||||
private QosSession(final Parcel in) {
|
||||
mSessionId = in.readInt();
|
||||
mSessionType = in.readInt();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<QosSession> CREATOR = new Creator<QosSession>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public QosSession createFromParcel(@NonNull final Parcel in) {
|
||||
return new QosSession(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public QosSession[] newArray(final int size) {
|
||||
return new QosSession[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
|
||||
dest.writeInt(mSessionId);
|
||||
dest.writeInt(mSessionType);
|
||||
}
|
||||
}
|
||||
128
core/java/android/net/QosSocketFilter.java
Normal file
128
core/java/android/net/QosSocketFilter.java
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Filters a {@link QosSession} according to the binding on the provided {@link Socket}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class QosSocketFilter extends QosFilter {
|
||||
|
||||
private static final String TAG = QosSocketFilter.class.getSimpleName();
|
||||
|
||||
@NonNull
|
||||
private final QosSocketInfo mQosSocketInfo;
|
||||
|
||||
/**
|
||||
* Creates a {@link QosSocketFilter} based off of {@link QosSocketInfo}.
|
||||
*
|
||||
* @param qosSocketInfo the information required to filter and validate
|
||||
*/
|
||||
public QosSocketFilter(@NonNull final QosSocketInfo qosSocketInfo) {
|
||||
Objects.requireNonNull(qosSocketInfo, "qosSocketInfo must be non-null");
|
||||
mQosSocketInfo = qosSocketInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parcelable qos socket info that was used to create the filter.
|
||||
*/
|
||||
@NonNull
|
||||
public QosSocketInfo getQosSocketInfo() {
|
||||
return mQosSocketInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs two validations:
|
||||
* 1. If the socket is not bound, then return
|
||||
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND}. This is detected
|
||||
* by checking the local address on the filter which becomes null when the socket is no
|
||||
* longer bound.
|
||||
* 2. In the scenario that the socket is now bound to a different local address, which can
|
||||
* happen in the case of UDP, then
|
||||
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned.
|
||||
* @return validation error code
|
||||
*/
|
||||
@Override
|
||||
public int validate() {
|
||||
final InetSocketAddress sa = getAddressFromFileDescriptor();
|
||||
if (sa == null) {
|
||||
return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
|
||||
}
|
||||
|
||||
if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) {
|
||||
return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
|
||||
}
|
||||
|
||||
return EX_TYPE_FILTER_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The local address of the socket's binding.
|
||||
*
|
||||
* Note: If the socket is no longer bound, null is returned.
|
||||
*
|
||||
* @return the local address
|
||||
*/
|
||||
@Nullable
|
||||
private InetSocketAddress getAddressFromFileDescriptor() {
|
||||
final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
|
||||
if (parcelFileDescriptor == null) return null;
|
||||
|
||||
final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
|
||||
if (fd == null) return null;
|
||||
|
||||
final SocketAddress address;
|
||||
try {
|
||||
address = Os.getsockname(fd);
|
||||
} catch (final ErrnoException e) {
|
||||
Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e);
|
||||
return null;
|
||||
}
|
||||
if (address instanceof InetSocketAddress) {
|
||||
return (InetSocketAddress) address;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The network used with this filter.
|
||||
*
|
||||
* @return the registered {@link Network}
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Network getNetwork() {
|
||||
return mQosSocketInfo.getNetwork();
|
||||
}
|
||||
}
|
||||
154
core/java/android/net/QosSocketInfo.java
Normal file
154
core/java/android/net/QosSocketInfo.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Used in conjunction with
|
||||
* {@link ConnectivityManager#registerQosCallback}
|
||||
* in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class QosSocketInfo implements Parcelable {
|
||||
|
||||
@NonNull
|
||||
private final Network mNetwork;
|
||||
|
||||
@NonNull
|
||||
private final ParcelFileDescriptor mParcelFileDescriptor;
|
||||
|
||||
@NonNull
|
||||
private final InetSocketAddress mLocalSocketAddress;
|
||||
|
||||
/**
|
||||
* The {@link Network} the socket is on.
|
||||
*
|
||||
* @return the registered {@link Network}
|
||||
*/
|
||||
@NonNull
|
||||
public Network getNetwork() {
|
||||
return mNetwork;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parcel file descriptor wrapped around the socket's file descriptor.
|
||||
*
|
||||
* @return the parcel file descriptor of the socket
|
||||
*/
|
||||
@NonNull
|
||||
ParcelFileDescriptor getParcelFileDescriptor() {
|
||||
return mParcelFileDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* The local address of the socket passed into {@link QosSocketInfo(Network, Socket)}.
|
||||
* The value does not reflect any changes that occur to the socket after it is first set
|
||||
* in the constructor.
|
||||
*
|
||||
* @return the local address of the socket
|
||||
*/
|
||||
@NonNull
|
||||
public InetSocketAddress getLocalSocketAddress() {
|
||||
return mLocalSocketAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}. The
|
||||
* {@link Socket} must remain bound in order to receive {@link QosSession}s.
|
||||
*
|
||||
* @param network the network
|
||||
* @param socket the bound {@link Socket}
|
||||
*/
|
||||
public QosSocketInfo(@NonNull final Network network, @NonNull final Socket socket)
|
||||
throws IOException {
|
||||
Objects.requireNonNull(socket, "socket cannot be null");
|
||||
|
||||
mNetwork = Objects.requireNonNull(network, "network cannot be null");
|
||||
mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
|
||||
mLocalSocketAddress =
|
||||
new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
|
||||
}
|
||||
|
||||
/* Parcelable methods */
|
||||
private QosSocketInfo(final Parcel in) {
|
||||
mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
|
||||
mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
|
||||
|
||||
final int addressLength = in.readInt();
|
||||
mLocalSocketAddress = readSocketAddress(in, addressLength);
|
||||
}
|
||||
|
||||
private InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
|
||||
final byte[] address = new byte[addressLength];
|
||||
in.readByteArray(address);
|
||||
final int port = in.readInt();
|
||||
|
||||
try {
|
||||
return new InetSocketAddress(InetAddress.getByAddress(address), port);
|
||||
} catch (final UnknownHostException e) {
|
||||
/* The catch block was purposely left empty. UnknownHostException will never be thrown
|
||||
since the address provided is numeric and non-null. */
|
||||
}
|
||||
return new InetSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
|
||||
mNetwork.writeToParcel(dest, 0);
|
||||
mParcelFileDescriptor.writeToParcel(dest, 0);
|
||||
|
||||
final byte[] address = mLocalSocketAddress.getAddress().getAddress();
|
||||
dest.writeInt(address.length);
|
||||
dest.writeByteArray(address);
|
||||
dest.writeInt(mLocalSocketAddress.getPort());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<QosSocketInfo> CREATOR =
|
||||
new Parcelable.Creator<QosSocketInfo>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public QosSocketInfo createFromParcel(final Parcel in) {
|
||||
return new QosSocketInfo(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public QosSocketInfo[] newArray(final int size) {
|
||||
return new QosSocketInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user