Add Qos Callback support

* Provide App Developers Qos related info associated to
  a bound socket through ConnectivityManager
* Qos sessions are generated and filtered by Network Agents
  and sent back through the Connectivity Service to the
  API consumer.
* The structure of the code within com.android.server
  is designed to support different types of filters in the
  the future.
* The first type of Qos Attributes are related to EPS
  Bearers in order support RCS.

Bug: 155176305
Test: Added to cts/NetworkAgentTest
Test: Added to ConnectivityServiceTest
Change-Id: I145dd065d9deeee449eb9695ab3f6c8556ee7c09
This commit is contained in:
Daniel Bright
2020-06-15 16:10:01 -07:00
parent f2a4eeb55d
commit f9e945b074
18 changed files with 2020 additions and 49 deletions

View File

@@ -167,6 +167,7 @@ import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
import android.net.IQosCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
@@ -190,6 +191,9 @@ import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkTestResultParcelable;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
import android.net.QosFilter;
import android.net.QosSession;
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
@@ -218,6 +222,7 @@ import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -226,10 +231,12 @@ import android.security.Credentials;
import android.security.KeyStore;
import android.system.Os;
import android.telephony.TelephonyManager;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -251,6 +258,7 @@ import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -368,6 +376,8 @@ public class ConnectivityServiceTest {
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
private QosCallbackMockHelper mQosCallbackMockHelper;
private QosCallbackTracker mQosCallbackTracker;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@@ -1395,6 +1405,7 @@ public class ConnectivityServiceTest {
mService.systemReadyInternal();
mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
mQosCallbackTracker = mock(QosCallbackTracker.class);
// Ensure that the default setting for Captive Portals is used for most tests
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
@@ -1470,6 +1481,11 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
if (mQosCallbackMockHelper != null) {
mQosCallbackMockHelper.tearDown();
mQosCallbackMockHelper = null;
}
mMockVpn.disconnect();
waitForIdle();
@@ -4379,7 +4395,7 @@ public class ConnectivityServiceTest {
}
private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception {
// Ensure the network is disconnected before we do anything.
// Ensure the network is disconnected before anything else occurs
if (mWiFiNetworkAgent != null) {
assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
}
@@ -8512,7 +8528,7 @@ public class ConnectivityServiceTest {
TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null,
0, INVALID_UID);
0, INVALID_UID, mQosCallbackTracker);
}
@Test
@@ -8890,7 +8906,7 @@ public class ConnectivityServiceTest {
@Test
public void testInvalidRequestTypes() {
final int[] invalidReqTypeInts = new int[] {-1, NetworkRequest.Type.NONE.ordinal(),
final int[] invalidReqTypeInts = new int[]{-1, NetworkRequest.Type.NONE.ordinal(),
NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length};
final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
@@ -8903,4 +8919,151 @@ public class ConnectivityServiceTest {
);
}
}
private class QosCallbackMockHelper {
@NonNull public final QosFilter mFilter;
@NonNull public final IQosCallback mCallback;
@NonNull public final TestNetworkAgentWrapper mAgentWrapper;
@NonNull private final List<IQosCallback> mCallbacks = new ArrayList();
QosCallbackMockHelper() throws Exception {
Log.d(TAG, "QosCallbackMockHelper: ");
mFilter = mock(QosFilter.class);
// Ensure the network is disconnected before anything else occurs
assertNull(mCellNetworkAgent);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
verifyActiveNetwork(TRANSPORT_CELLULAR);
waitForIdle();
final Network network = mCellNetworkAgent.getNetwork();
final Pair<IQosCallback, IBinder> pair = createQosCallback();
mCallback = pair.first;
when(mFilter.getNetwork()).thenReturn(network);
when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
mAgentWrapper = mCellNetworkAgent;
}
void registerQosCallback(@NonNull final QosFilter filter,
@NonNull final IQosCallback callback) {
mCallbacks.add(callback);
final NetworkAgentInfo nai =
mService.getNetworkAgentInfoForNetwork(filter.getNetwork());
mService.registerQosCallbackInternal(filter, callback, nai);
}
void tearDown() {
for (int i = 0; i < mCallbacks.size(); i++) {
mService.unregisterQosCallback(mCallbacks.get(i));
}
}
}
private Pair<IQosCallback, IBinder> createQosCallback() {
final IQosCallback callback = mock(IQosCallback.class);
final IBinder binder = mock(Binder.class);
when(callback.asBinder()).thenReturn(binder);
when(binder.isBinderAlive()).thenReturn(true);
return new Pair<>(callback, binder);
}
@Test
public void testQosCallbackRegistration() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper;
when(mQosCallbackMockHelper.mFilter.validate())
.thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 =
(NetworkAgentWrapper.CallbackType.OnQosCallbackRegister)
wrapper.getCallbackHistory().poll(1000, x -> true);
assertNotNull(cbRegister1);
final int registerCallbackId = cbRegister1.mQosCallbackId;
mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback);
final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister;
cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister)
wrapper.getCallbackHistory().poll(1000, x -> true);
assertNotNull(cbUnregister);
assertEquals(registerCallbackId, cbUnregister.mQosCallbackId);
assertNull(wrapper.getCallbackHistory().poll(200, x -> true));
}
@Test
public void testQosCallbackNoRegistrationOnValidationError() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
when(mQosCallbackMockHelper.mFilter.validate())
.thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
verify(mQosCallbackMockHelper.mCallback)
.onError(eq(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED));
}
@Test
public void testQosCallbackAvailableAndLost() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
final int sessionId = 10;
final int qosCallbackId = 1;
when(mQosCallbackMockHelper.mFilter.validate())
.thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes(
1, 2, 3, 4, 5, new ArrayList<>());
mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
waitForIdle();
verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session ->
session.getSessionId() == sessionId
&& session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes));
mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
.sendQosSessionLost(qosCallbackId, sessionId);
waitForIdle();
verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
session.getSessionId() == sessionId
&& session.getSessionType() == QosSession.TYPE_EPS_BEARER));
}
@Test
public void testQosCallbackTooManyRequests() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
when(mQosCallbackMockHelper.mFilter.validate())
.thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
for (int i = 0; i < 100; i++) {
final Pair<IQosCallback, IBinder> pair = createQosCallback();
try {
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, pair.first);
} catch (ServiceSpecificException e) {
assertEquals(e.errorCode, ConnectivityManager.Errors.TOO_MANY_REQUESTS);
if (i < 50) {
fail("TOO_MANY_REQUESTS thrown too early, the count is " + i);
}
// As long as there is at least 50 requests, it is safe to assume it works.
// Note: The count isn't being tested precisely against 100 because the counter
// is shared with request network.
return;
}
}
fail("TOO_MANY_REQUESTS never thrown");
}
}

View File

@@ -78,6 +78,7 @@ public class LingerMonitorTest {
@Mock Context mCtx;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
@Mock QosCallbackTracker mQosCallbackTracker;
@Before
public void setUp() {
@@ -358,7 +359,7 @@ public class LingerMonitorTest {
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
mConnService, mNetd, mDnsResolver, mNMS, NetworkProvider.ID_NONE,
Binder.getCallingUid());
Binder.getCallingUid(), mQosCallbackTracker);
nai.everValidated = true;
return nai;
}