blob: d4386ff73084090fd43a0e0a5b449aedb041de5d [file] [log] [blame]
/*
* 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.telephony.ims.cts;
import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_FOUND;
import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND;
import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_CACHED;
import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_NETWORK;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_FETCH_ERROR;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_GENERIC_FAILURE;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_INSUFFICIENT_MEMORY;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_INVALID_PARAM;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_LOST_NETWORK_CONNECTION;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_NOT_FOUND;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_NOT_SUPPORTED;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_NO_CHANGE;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_REQUEST_TIMEOUT;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_SERVICE_UNAVAILABLE;
import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_SERVICE_UNKNOWN;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.PersistableBundle;
import android.provider.Telephony;
import android.telecom.PhoneAccount;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.RcsContactPresenceTuple;
import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
import android.telephony.ims.RcsContactUceCapability;
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.CapabilityExchangeEventListener;
import android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.BlockedNumberUtil;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.internal.os.SomeArgs;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
public class RcsUceAdapterTest {
private static final String FEATURE_TAG_CHAT =
"+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
private static final String FEATURE_TAG_FILE_TRANSFER =
"+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"";
private static final String FEATURE_TAG_MMTEL_AUDIO_CALL =
"+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
private static final String FEATURE_TAG_MMTEL_VIDEO_CALL =
"+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\";video";
private static int sTestSlot = 0;
private static int sTestSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private static final Uri LISTENER_URI = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
private static HandlerThread sHandlerThread;
private static ImsServiceConnector sServiceConnector;
private static CarrierConfigReceiver sReceiver;
private static boolean sDeviceUceEnabled;
private static String sTestPhoneNumber;
private static String sTestContact2;
private static String sTestContact3;
private static Uri sTestNumberUri;
private static Uri sTestContact2Uri;
private static Uri sTestContact3Uri;
private ContentObserver mUceObserver;
private static class CarrierConfigReceiver extends BroadcastReceiver {
private CountDownLatch mLatch = new CountDownLatch(1);
private final int mSubId;
CarrierConfigReceiver(int subId) {
mSubId = subId;
}
@Override
public void onReceive(Context context, Intent intent) {
if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, -1);
if (mSubId == subId) {
mLatch.countDown();
}
}
}
void clearQueue() {
mLatch = new CountDownLatch(1);
}
void waitForCarrierConfigChanged() throws Exception {
mLatch.await(5000, TimeUnit.MILLISECONDS);
}
}
@BeforeClass
public static void beforeAllTests() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
sTestSub = ImsUtils.getPreferredActiveSubId();
sTestSlot = SubscriptionManager.getSlotIndex(sTestSub);
if (Looper.getMainLooper() == null) {
Looper.prepareMainLooper();
}
sHandlerThread = new HandlerThread("CtsTelephonyTestCases");
sHandlerThread.start();
sServiceConnector = new ImsServiceConnector(InstrumentationRegistry.getInstrumentation());
sServiceConnector.clearAllActiveImsServices(sTestSlot);
// Save the original config of device uce enabled and override it.
sDeviceUceEnabled = sServiceConnector.getDeviceUceEnabled();
sServiceConnector.setDeviceUceEnabled(true);
sReceiver = new RcsUceAdapterTest.CarrierConfigReceiver(sTestSub);
IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
// ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
InstrumentationRegistry.getInstrumentation().getContext()
.registerReceiver(sReceiver, filter);
// Initialize the test phone numbers
initPhoneNumbers();
}
@AfterClass
public static void afterAllTests() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
// // Restore all ImsService configurations that existed before the test.
if (sServiceConnector != null) {
sServiceConnector.disconnectServices();
sServiceConnector.setDeviceUceEnabled(sDeviceUceEnabled);
}
sServiceConnector = null;
// Ensure there are no CarrierConfig overrides as well as reset the ImsResolver in case the
// ImsService override changed in CarrierConfig while we were overriding it.
overrideCarrierConfig(null);
if (sReceiver != null) {
InstrumentationRegistry.getInstrumentation().getContext().unregisterReceiver(sReceiver);
sReceiver = null;
}
if (sHandlerThread != null) {
sHandlerThread.quit();
}
}
@Before
public void beforeTest() {
if (!ImsUtils.shouldTestImsService()) {
return;
}
if (!SubscriptionManager.isValidSubscriptionId(sTestSub)) {
fail("This test requires that there is a SIM in the device!");
}
}
@After
public void afterTest() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
// Unbind the ImsService after the test completes.
if (sServiceConnector != null) {
sServiceConnector.disconnectCarrierImsService();
sServiceConnector.disconnectDeviceImsService();
}
overrideCarrierConfig(null);
// Remove all the test contacts from EAB database
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
@Test
public void testCapabilityDiscoveryIntentReceiverExists() {
if (!ImsUtils.shouldTestImsService()) {
return;
}
PackageManager packageManager = getContext().getPackageManager();
ResolveInfo info = packageManager.resolveActivity(
new Intent(ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN),
PackageManager.MATCH_DEFAULT_ONLY);
assertNotNull(ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN
+ " Intent action must be handled by an appropriate settings application.", info);
assertNotEquals(ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN
+ " activity intent filter must have a > 0 priority.", 0, info.priority);
}
@Test
public void testGetAndSetUceSetting() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter adapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("RcsUceAdapter can not be null!", adapter);
Boolean isEnabled = null;
try {
isEnabled = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
adapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
"android.permission.READ_PHONE_STATE");
assertNotNull(isEnabled);
// Ensure the ContentObserver gets the correct callback based on the change.
LinkedBlockingQueue<Uri> queue = new LinkedBlockingQueue<>(1);
registerUceObserver(queue::offer);
boolean userSetIsEnabled = isEnabled;
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
adapter, a -> a.setUceSettingEnabled(!userSetIsEnabled), ImsException.class,
"android.permission.MODIFY_PHONE_STATE");
Uri result = queue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertNotNull(result);
assertTrue("Unexpected URI, should only receive URIs with prefix " + LISTENER_URI,
result.isPathPrefixMatch(LISTENER_URI));
// Verify the subId associated with the Observer is correct.
List<String> pathSegments = result.getPathSegments();
String subId = pathSegments.get(pathSegments.size() - 1);
assertEquals("Subscription ID contained in ContentObserver URI doesn't match the "
+ "subscription that has changed.",
String.valueOf(sTestSub), subId);
Boolean setResult = ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
adapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
"android.permission.READ_PHONE_STATE");
assertNotNull(setResult);
assertEquals("Incorrect setting!", !userSetIsEnabled, setResult);
} catch (ImsException e) {
if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
fail("failed getting UCE setting with code: " + e.getCode());
}
} finally {
if (isEnabled != null) {
boolean userSetIsEnabled = isEnabled;
// set back to user preference
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
adapter, a -> a.setUceSettingEnabled(userSetIsEnabled), ImsException.class,
"android.permission.MODIFY_PHONE_STATE");
}
unregisterUceObserver();
}
}
@Test
public void testMethodPermissions() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
Collection<Uri> numbers = new ArrayList<>(1);
numbers.add(sTestNumberUri);
// isUceSettingEnabled - read
Boolean isUceSettingEnabledResult = null;
try {
isUceSettingEnabledResult =
ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
uceAdapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
"android.permission.READ_PHONE_STATE");
assertNotNull("result from isUceSettingEnabled should not be null",
isUceSettingEnabledResult);
} catch (SecurityException e) {
fail("isUceSettingEnabled should succeed with READ_PHONE_STATE.");
} catch (ImsException e) {
// unsupported is a valid fail cause.
if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
fail("isUceSettingEnabled failed with code " + e.getCode());
}
}
// isUceSettingEnabled - read_privileged
try {
isUceSettingEnabledResult =
ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
uceAdapter, RcsUceAdapter::isUceSettingEnabled, ImsException.class,
"android.permission.READ_PRIVILEGED_PHONE_STATE");
assertNotNull("result from isUceSettingEnabled should not be null",
isUceSettingEnabledResult);
} catch (SecurityException e) {
fail("isUceSettingEnabled should succeed with READ_PRIVILEGED_PHONE_STATE.");
} catch (ImsException e) {
// unsupported is a valid fail cause.
if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
fail("isUceSettingEnabled failed with code " + e.getCode());
}
}
// setUceSettingEnabled
boolean isUceSettingEnabled =
(isUceSettingEnabledResult == null ? false : isUceSettingEnabledResult);
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
(m) -> m.setUceSettingEnabled(isUceSettingEnabled), ImsException.class,
"android.permission.MODIFY_PHONE_STATE");
} catch (SecurityException e) {
fail("setUceSettingEnabled should succeed with MODIFY_PHONE_STATE.");
} catch (ImsException e) {
// unsupported is a valid fail cause.
if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
fail("setUceSettingEnabled failed with code " + e.getCode());
}
}
// getUcePublishState without permission
try {
uceAdapter.getUcePublishState();
fail("getUcePublishState should require READ_PRIVILEGED_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
// getUcePublishState with permission
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
RcsUceAdapter::getUcePublishState, ImsException.class,
"android.permission.READ_PRIVILEGED_PHONE_STATE");
} catch (SecurityException e) {
fail("getUcePublishState should succeed with READ_PRIVILEGED_PHONE_STATE.");
} catch (ImsException e) {
// ImsExceptions are still valid because it means the permission check passed.
}
final RcsUceAdapter.OnPublishStateChangedListener publishStateListener = (state) -> { };
// addOnPublishStateChangedListener without permission
try {
uceAdapter.addOnPublishStateChangedListener(Runnable::run, publishStateListener);
fail("addOnPublishStateChangedListener should require "
+ "READ_PRIVILEGED_PHONE_STATE");
} catch (SecurityException e) {
// expected
}
// addOnPublishStateChangedListener with permission.
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
(m) -> m.addOnPublishStateChangedListener(Runnable::run, publishStateListener),
ImsException.class,
"android.permission.READ_PRIVILEGED_PHONE_STATE");
} catch (SecurityException e) {
fail("addOnPublishStateChangedListener should succeed with "
+ "READ_PRIVILEGED_PHONE_STATE.");
} catch (ImsException e) {
// ImsExceptions are still valid because it means the permission check passed.
}
// removeOnPublishStateChangedListener without permission
try {
uceAdapter.removeOnPublishStateChangedListener(publishStateListener);
fail("removeOnPublishStateChangedListener should require "
+ "READ_PRIVILEGED_PHONE_STATE");
} catch (SecurityException e) {
// expected
}
// Prepare the callback of the capability request
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
}
@Override
public void onComplete() {
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
}
};
// requestCapabilities without permission
try {
uceAdapter.requestCapabilities(numbers, Runnable::run , callback);
fail("requestCapabilities should require ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
} catch (SecurityException e) {
//expected
}
// requestAvailability without permission
try {
uceAdapter.requestAvailability(sTestNumberUri, Runnable::run, callback);
fail("requestAvailability should require ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
} catch (SecurityException e) {
//expected
}
// requestCapabilities in the foreground
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
(m) -> m.requestCapabilities(numbers, Runnable::run, callback),
ImsException.class,
"android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
} catch (SecurityException e) {
fail("requestCapabilities should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
} catch (ImsException e) {
// ImsExceptions are still valid because it means the permission check passed.
}
// requestAvailability in the foreground
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
(m) -> m.requestAvailability(sTestNumberUri, Runnable::run, callback),
ImsException.class,
"android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
} catch (SecurityException e) {
fail("requestAvailability should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
} catch (ImsException e) {
// ImsExceptions are still valid because it means the permission check passed.
}
overrideCarrierConfig(null);
}
@Test
public void testCapabilitiesRequestAllowed() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
// Start cap exchange disabled and enable later.
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL,
false);
overrideCarrierConfig(bundle);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Prepare the test contact and the callback
Collection<Uri> numbers = new ArrayList<>(1);
numbers.add(sTestNumberUri);
ArrayList<String> pidfXmlList = new ArrayList<>(1);
pidfXmlList.add(getPidfXmlData(sTestNumberUri, true, true));
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
}
};
// The API requestCapabilities should fail when it doesn't grant the permission.
try {
uceAdapter.requestCapabilities(numbers, Runnable::run, callback);
fail("requestCapabilities requires ACCESS_RCS_USER_CAPABILITY_EXCHANGE permission.");
} catch (SecurityException e) {
//expected
}
// The API requestAvailability should fail when it doesn't grant the permission.
try {
uceAdapter.requestAvailability(sTestNumberUri, Runnable::run, callback);
fail("requestAvailability requires ACCESS_RCS_USER_CAPABILITY_EXCHANGE permission.");
} catch (SecurityException e) {
//expected
}
// Trigger carrier config changed
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, true);
overrideCarrierConfig(bundle);
// Connect to the TestImsService
connectTestImsService();
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the callback "onError" is called with the error code NOT_ENABLED because
// the carrier config KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL is still false.
try {
assertEquals(RcsUceAdapter.ERROR_NOT_ENABLED, waitForIntResult(errorQueue));
} catch (Exception e) {
fail("requestCapabilities with command error failed: " + e);
} finally {
capabilityQueue.clear();
completeQueue.clear();
errorQueue.clear();
}
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the error code NOT_ENABLED because
// the carrier config KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL is still false.
try {
assertEquals(RcsUceAdapter.ERROR_NOT_ENABLED, waitForIntResult(errorQueue));
} catch (Exception e) {
fail("requestAvailability with command error failed: " + e);
} finally {
capabilityQueue.clear();
completeQueue.clear();
errorQueue.clear();
}
// Override another carrier config KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL,
true);
overrideCarrierConfig(bundle);
// Prepare the network response is 200 OK and the capabilities update
int networkRespCode = 200;
String networkRespReason = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the contact capability is received and the onCompleted is called.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact: " + sTestNumberUri, capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
waitForResult(completeQueue);
errorQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the contact capability is received and the onCompleted is called.
capability = waitForResult(capabilityQueue);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
waitForResult(completeQueue);
overrideCarrierConfig(null);
}
@Test
public void testCapabilitiesRequestWithCmdError() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Connect to the TestImsService
setupTestImsService(uceAdapter, true, true, false);
Collection<Uri> contacts = Collections.singletonList(sTestNumberUri);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
// Prepare queues to receive the callback
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> retryAfterQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
}
@Override
public void onComplete() {
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
retryAfterQueue.offer(retryAfterMilliseconds);
}
};
// Prepare a map and define each command error code and its associated result.
Map<Integer, Integer> cmdErrorMap = new HashMap<>();
cmdErrorMap.put(COMMAND_CODE_SERVICE_UNKNOWN, RcsUceAdapter.ERROR_GENERIC_FAILURE);
cmdErrorMap.put(COMMAND_CODE_GENERIC_FAILURE, RcsUceAdapter.ERROR_GENERIC_FAILURE);
cmdErrorMap.put(COMMAND_CODE_INVALID_PARAM, RcsUceAdapter.ERROR_GENERIC_FAILURE);
cmdErrorMap.put(COMMAND_CODE_FETCH_ERROR, RcsUceAdapter.ERROR_GENERIC_FAILURE);
cmdErrorMap.put(COMMAND_CODE_REQUEST_TIMEOUT, RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
cmdErrorMap.put(COMMAND_CODE_INSUFFICIENT_MEMORY, RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY);
cmdErrorMap.put(COMMAND_CODE_LOST_NETWORK_CONNECTION, RcsUceAdapter.ERROR_LOST_NETWORK);
cmdErrorMap.put(COMMAND_CODE_NOT_SUPPORTED, RcsUceAdapter.ERROR_GENERIC_FAILURE);
cmdErrorMap.put(COMMAND_CODE_NOT_FOUND, RcsUceAdapter.ERROR_NOT_FOUND);
cmdErrorMap.put(COMMAND_CODE_SERVICE_UNAVAILABLE, RcsUceAdapter.ERROR_SERVER_UNAVAILABLE);
cmdErrorMap.put(COMMAND_CODE_NO_CHANGE, RcsUceAdapter.ERROR_GENERIC_FAILURE);
// Verify each command error code and the expected callback result
cmdErrorMap.forEach((cmdError, expectedCallbackResult) -> {
// Setup the capabilities request that will be failed with the given command error code
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onCommandError(cmdError);
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestCapabilities with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
}
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestAvailability with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
}
});
overrideCarrierConfig(null);
}
@Test
public void testCapabilitiesRequestWithResponseError() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* options */);
Collection<Uri> numbers = new ArrayList<>(1);
numbers.add(sTestNumberUri);
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> retryAfterQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
}
@Override
public void onComplete() {
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
retryAfterQueue.offer(retryAfterMilliseconds);
}
};
Map<Entry<Integer, String>, Integer> networkRespError = new HashMap<>();
// 408 Request Timeout
networkRespError.put(new Entry<Integer, String>() {
@Override
public Integer getKey() {
return 408;
}
@Override
public String getValue() {
return "Request Timeout";
}
@Override
public String setValue(String value) {
return value;
}
}, RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
// 423 Interval Too Short
networkRespError.put(new Entry<Integer, String>() {
@Override
public Integer getKey() {
return 423;
}
@Override
public String getValue() {
return "Interval Too Short";
}
@Override
public String setValue(String value) {
return value;
}
}, RcsUceAdapter.ERROR_GENERIC_FAILURE);
// 500 Server Internal Error
networkRespError.put(new Entry<Integer, String>() {
@Override
public Integer getKey() {
return 500;
}
@Override
public String getValue() {
return "Service Unavailable";
}
@Override
public String setValue(String value) {
return value;
}
}, RcsUceAdapter.ERROR_SERVER_UNAVAILABLE);
// 503 Service Unavailable
networkRespError.put(new Entry<Integer, String>() {
@Override
public Integer getKey() {
return 503;
}
@Override
public String getValue() {
return "Service Unavailable";
}
@Override
public String setValue(String value) {
return value;
}
}, RcsUceAdapter.ERROR_SERVER_UNAVAILABLE);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
networkRespError.forEach((networkResp, expectedCallbackResult) -> {
// Set the capabilities request failed with the given SIP code (without Reason header)
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkResp.getKey(), networkResp.getValue());
});
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestCapabilities with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestAvailability with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
/*
* Set the capabilities request failed with the given SIP code (with Reason header)
*/
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
int networkRespCode = 200;
String networkReason = "OK";
cb.onNetworkResponse(networkRespCode, networkReason,
networkResp.getKey(), networkResp.getValue());
});
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestCapabilities with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestAvailability with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
});
// Set the capabilities request will be failed with the 403 sip code
int networkResp = 403;
String networkRespReason = "";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkResp, networkRespReason);
});
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the error code FORBIDDEN
try {
assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestAvailability with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the capabilities request is sill failed because the ImsService has returned
// the 403 error before.
try {
assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(retryAfterQueue));
} catch (Exception e) {
fail("requestCapabilities with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
overrideCarrierConfig(null);
}
@Test
public void testRequestCapabilitiesWithPresenceMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Override the carrier config to support group subscribe.
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
overrideCarrierConfig(bundle);
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare three contacts
final Uri contact1 = sTestNumberUri;
final Uri contact2 = sTestContact2Uri;
final Uri contact3 = sTestContact3Uri;
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(contact1);
contacts.add(contact2);
contacts.add(contact3);
ArrayList<String> pidfXmlList = new ArrayList<>(3);
pidfXmlList.add(getPidfXmlData(contact1, true, true));
pidfXmlList.add(getPidfXmlData(contact2, true, false));
pidfXmlList.add(getPidfXmlData(contact3, false, false));
// Setup the network response is 200 OK and notify capabilities update
int networkRespCode = 200;
String networkRespReason = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
List<RcsContactUceCapability> resultCapList = new ArrayList<>();
// Verify that all the three contact's capabilities are received
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact", capability);
resultCapList.add(capability);
// Verify the first contact capabilities from the received capabilities list
RcsContactUceCapability resultCapability = getContactCapability(resultCapList, contact1);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
resultCapList.clear();
removeTestContactFromEab();
// Setup the callback that some of the contacts are terminated.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(404, "NOT FOUND");
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify the contacts are not found.
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
// Verify the first contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact1);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
resultCapList.clear();
removeTestContactFromEab();
// Setup the callback that some of the contacts are terminated.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
List<Uri> uriList = new ArrayList(uris);
cb.onNetworkResponse(networkRespCode, networkRespReason);
// Notify capabilities updated for the first contact
String pidfXml = pidfXmlList.get(0);
cb.onNotifyCapabilitiesUpdate(Collections.singletonList(pidfXml));
List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
for (int i = 1; i < uriList.size(); i++) {
Pair<Uri, String> pair = Pair.create(uriList.get(i), "noresource");
terminatedResources.add(pair);
}
cb.onResourceTerminated(terminatedResources);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify the first contact is found.
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
// Verify the reset contacts are not found.
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
// Verify the first contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact1);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
overrideCarrierConfig(null);
}
@Test
public void testRequestCapabilitiesFromCacheWithPresenceMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare three contacts
final Uri contact1 = sTestNumberUri;
final Uri contact2 = sTestContact2Uri;
final Uri contact3 = sTestContact3Uri;
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(contact1);
contacts.add(contact2);
contacts.add(contact3);
ArrayList<String> pidfXmlList = new ArrayList<>(3);
pidfXmlList.add(getPidfXmlData(contact1, true, true));
pidfXmlList.add(getPidfXmlData(contact2, true, false));
pidfXmlList.add(getPidfXmlData(contact3, false, false));
// Setup the network response is 200 OK and notify capabilities update
int networkRespCode = 200;
String networkRespReason = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
List<RcsContactUceCapability> resultCapList = new ArrayList<>();
// Verify that all the three contact's capabilities are received
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Cannot receive the first capabilities result.", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Cannot receive the second capabilities result.", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Cannot receive the third capabilities result.", capability);
resultCapList.add(capability);
// Verify contact1's capabilities from the received capabilities list
RcsContactUceCapability resultCapability = getContactCapability(resultCapList, contact1);
assertNotNull("Cannot find the contact: " + contact1, resultCapability);
verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
// Verify contact2's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact: " + contact2, resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, false);
// Verify contact3's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact: " + contact3, resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
resultCapList.clear();
// The request should not be called because the capabilities should be retrieved from cache.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
fail("The request should not be called.");
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify that all the three contact's capabilities are received
capability = waitForResult(capabilityQueue);
assertNotNull("Cannot receive the first capabilities result.", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Cannot receive the second capabilities result.", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Cannot receive the third capabilities result.", capability);
resultCapList.add(capability);
// Verify contact1's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact1);
assertNotNull("Cannot find the contact: " + contact1, resultCapability);
verifyCapabilityResult(resultCapability, contact1, SOURCE_TYPE_CACHED, REQUEST_RESULT_FOUND,
true, true);
// Verify contact2's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact: " + contact2, resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_CACHED, REQUEST_RESULT_FOUND,
true, false);
// Verify contact3's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact: " + contact3, resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_CACHED, REQUEST_RESULT_FOUND,
false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
resultCapList.clear();
removeTestContactFromEab();
overrideCarrierConfig(null);
}
@Test
public void testIndividualRequestCapabilities() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Override the carrier config to not support group subscribe.
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, false);
overrideCarrierConfig(bundle);
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Long> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
List<RcsContactUceCapability> capabilityQueue = new ArrayList<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.add(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(new Long(errorCode));
errorQueue.offer(retryAfterMilliseconds);
}
};
// Prepare three contacts
final Uri contact1 = sTestNumberUri;
final Uri contact2 = sTestContact2Uri;
final Uri contact3 = sTestContact3Uri;
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(contact1);
contacts.add(contact2);
contacts.add(contact3);
List<String> pidfXml1 = Collections.singletonList(getPidfXmlData(contact1, true, true));
List<String> pidfXml2 = Collections.singletonList(getPidfXmlData(contact2, true, false));
List<String> pidfXml3 = Collections.singletonList(getPidfXmlData(contact3, false, false));
// Setup the network response is 200 OK and notify capabilities update
int networkRespCode = 200;
String networkRespReason = "OK";
AtomicInteger receiveRequestCount = new AtomicInteger(0);
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
receiveRequestCount.incrementAndGet();
cb.onNetworkResponse(networkRespCode, networkRespReason);
assertEquals(1, uris.size());
String uriPart = uris.iterator().next().getSchemeSpecificPart();
if (contact1.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
cb.onNotifyCapabilitiesUpdate(pidfXml1);
} else if (contact2.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
cb.onNotifyCapabilitiesUpdate(pidfXml2);
} else if (contact3.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
cb.onNotifyCapabilitiesUpdate(pidfXml3);
}
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify the onCompleted is called
waitForResult(completeQueue);
// Verify the capability request has been split to individual requests.
assertEquals(contacts.size(), receiveRequestCount.get());
// verify the capabilities result
assertEquals(contacts.size(), capabilityQueue.size());
for (RcsContactUceCapability capability : capabilityQueue) {
Uri contact = capability.getContactUri();
if (contact1.equals(contact)) {
verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
} else if (contact2.equals(contact)) {
verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, false);
} else if (contact3.equals(contact)) {
verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, false, false);
} else {
fail("The contact of the capabilities result is invalid.");
}
}
errorQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
receiveRequestCount.set(0);
// Setup the callback that some of the contacts are terminated.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
receiveRequestCount.incrementAndGet();
cb.onNetworkResponse(networkRespCode, networkRespReason);
assertEquals(1, uris.size());
String uriPart = uris.iterator().next().getSchemeSpecificPart();
if (contact1.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
cb.onNotifyCapabilitiesUpdate(pidfXml1);
} else {
// Notify resources terminated for the reset contacts
List<Uri> uriList = new ArrayList(uris);
List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
for (int i = 0; i < uriList.size(); i++) {
Pair<Uri, String> pair = Pair.create(uriList.get(i), "noresource");
terminatedResources.add(pair);
}
cb.onResourceTerminated(terminatedResources);
}
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify the onCompleted is called
waitForResult(completeQueue);
// Verify the capability request has been split to individual requests.
assertEquals(contacts.size(), receiveRequestCount.get());
// verify the capabilities result
assertEquals(contacts.size(), capabilityQueue.size());
for (RcsContactUceCapability capability : capabilityQueue) {
Uri contact = capability.getContactUri();
if (contact1.equals(contact)) {
verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
} else if (contact2.equals(contact)) {
verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, true, false);
} else if (contact3.equals(contact)) {
verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false,
false);
} else {
fail("The contact of the capabilities result is invalid.");
}
}
overrideCarrierConfig(null);
}
@Test
public void testRequestCapabilitiesWithOptionsMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
// The test contact
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(sTestNumberUri);
// The result callback
BlockingQueue<Long> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(new Long(errorCode));
errorQueue.offer(retryAfterMilliseconds);
}
};
// Set the result of the network response is 200 OK.
final List<String> featureTags = new ArrayList<>();
featureTags.add(FEATURE_TAG_CHAT);
featureTags.add(FEATURE_TAG_FILE_TRANSFER);
featureTags.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
featureTags.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
int sipCode = 200;
String reason = "OK";
optionsCallback.onNetworkResponse(sipCode, reason, featureTags);
});
// Request capabilities by calling the API requestCapabilities.
requestCapabilities(uceAdapter, contacts, callback);
// Verify the callback "onCapabilitiesReceived" is called.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
// Verify the callback "onComplete" is called.
assertNotNull(waitForResult(completeQueue));
assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
assertEquals(sTestNumberUri, capability.getContactUri());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.getCapabilityMechanism());
Set<String> resultFeatureTags = capability.getFeatureTags();
assertEquals(featureTags.size(), resultFeatureTags.size());
for (String featureTag : featureTags) {
if (!resultFeatureTags.contains(featureTag)) {
fail("Cannot find feature tag in the result");
}
}
errorQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
// Remove the test contact capabilities
removeTestContactFromEab();
// Request capabilities by calling the API requestAvailability.
requestAvailability(uceAdapter, sTestContact2Uri, callback);
// Verify the callback "onCapabilitiesReceived" is called.
capability = waitForResult(capabilityQueue);
// Verify the callback "onComplete" is called.
waitForResult(completeQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
assertEquals(sTestContact2Uri, capability.getContactUri());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.getCapabilityMechanism());
resultFeatureTags = capability.getFeatureTags();
assertEquals(featureTags.size(), resultFeatureTags.size());
for (String featureTag : featureTags) {
if (!resultFeatureTags.contains(featureTag)) {
fail("Cannot find feature tag in the result");
}
}
errorQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
// Remove the test contact capabilities
removeTestContactFromEab();
// Set the OPTIONS result is failed.
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
int sipCode = 400;
String reason = "Bad Request";
optionsCallback.onNetworkResponse(sipCode, reason, Collections.EMPTY_LIST);
});
// Request capabilities by calling the API requestCapabilities.
requestCapabilities(uceAdapter, contacts, callback);
// Verify the callback "onError" is called.
assertEquals(RcsUceAdapter.ERROR_GENERIC_FAILURE, waitForLongResult(errorQueue));
// The callback "onCapabilitiesReceived" should be called with NOT FOUND
capability = waitForResult(capabilityQueue);
assertEquals(RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND,
capability.getRequestResult());
overrideCarrierConfig(null);
}
@Test
public void testRequestCapabilitiesFromCacheWithOptionsMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
// The result callback
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> retryAfterQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
retryAfterQueue.offer(retryAfterMilliseconds);
}
};
// Set the result of the network response is 200 OK.
final List<String> featureTags = new ArrayList<>();
featureTags.add(FEATURE_TAG_CHAT);
featureTags.add(FEATURE_TAG_FILE_TRANSFER);
featureTags.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
featureTags.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
int sipCode = 200;
String reason = "OK";
optionsCallback.onNetworkResponse(sipCode, reason, featureTags);
});
// Request capabilities with the for the first time.
requestCapabilities(uceAdapter, Collections.singletonList(sTestNumberUri), callback);
// Verify the callback "onCapabilitiesReceived" is called.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
// Verify the callback "onComplete" is called.
assertNotNull(waitForResult(completeQueue));
assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
assertEquals(sTestNumberUri, capability.getContactUri());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.getCapabilityMechanism());
Set<String> resultFeatureTags = capability.getFeatureTags();
assertEquals(featureTags.size(), resultFeatureTags.size());
for (String featureTag : featureTags) {
if (!resultFeatureTags.contains(featureTag)) {
fail("Cannot find feature tag in the result");
}
}
errorQueue.clear();
retryAfterQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
// Request capabilities for the second time.
requestAvailability(uceAdapter, sTestContact2Uri, callback);
// Verify the callback "onCapabilitiesReceived" is called.
capability = waitForResult(capabilityQueue);
// Verify the callback "onComplete" is called.
waitForResult(completeQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
assertEquals(sTestContact2Uri, capability.getContactUri());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.getCapabilityMechanism());
resultFeatureTags = capability.getFeatureTags();
assertEquals(featureTags.size(), resultFeatureTags.size());
for (String featureTag : featureTags) {
if (!resultFeatureTags.contains(featureTag)) {
fail("Cannot find feature tag in the result");
}
}
errorQueue.clear();
retryAfterQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
// Set the OPTIONS result is failed because the capabilities should be retrieved from cache.
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
fail("The Options request should not be called.");
});
// The contact to requeste the capabilities is the same as the first time.
requestCapabilities(uceAdapter, Collections.singletonList(sTestNumberUri), callback);
// Verify the callback "onCapabilitiesReceived" is called.
capability = waitForResult(capabilityQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
// Verify the callback "onComplete" is called.
assertNotNull(waitForResult(completeQueue));
// Verify the capabilities are retrieved from the cache.
assertEquals(RcsContactUceCapability.SOURCE_TYPE_CACHED, capability.getSourceType());
assertEquals(sTestNumberUri, capability.getContactUri());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.getCapabilityMechanism());
resultFeatureTags = capability.getFeatureTags();
assertEquals(featureTags.size(), resultFeatureTags.size());
for (String featureTag : featureTags) {
if (!resultFeatureTags.contains(featureTag)) {
fail("Cannot find feature tag in the result");
}
}
errorQueue.clear();
retryAfterQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
overrideCarrierConfig(null);
}
@Test
public void testIndividualRequestCapabilitiesWithOptionsMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
// Prepare three test contacts
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(sTestNumberUri);
contacts.add(sTestContact2Uri);
contacts.add(sTestContact3Uri);
// The result callback
BlockingQueue<Long> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
List<RcsContactUceCapability> capabilityQueue = new ArrayList<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.add(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(new Long(errorCode));
errorQueue.offer(retryAfterMilliseconds);
}
};
// Set the result of the network response is 200 OK.
final List<String> featureTags = new ArrayList<>();
featureTags.add(FEATURE_TAG_CHAT);
featureTags.add(FEATURE_TAG_FILE_TRANSFER);
featureTags.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
featureTags.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
AtomicInteger receiveRequestCount = new AtomicInteger(0);
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
receiveRequestCount.incrementAndGet();
int sipCode = 200;
String reason = "OK";
optionsCallback.onNetworkResponse(sipCode, reason, featureTags);
});
// Request capabilities by calling the API requestCapabilities.
requestCapabilities(uceAdapter, contacts, callback);
// Verify the callback "onComplete" is called.
assertNotNull(waitForResult(completeQueue));
// Verify the capability request has been split to individual requests.
assertEquals(contacts.size(), receiveRequestCount.get());
// Verify the result
verifyOptionsCapabilityResult(capabilityQueue, contacts,
RcsContactUceCapability.SOURCE_TYPE_NETWORK,
RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
RcsContactUceCapability.REQUEST_RESULT_FOUND, featureTags);
errorQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
receiveRequestCount.set(0);
removeTestContactFromEab();
// Set the OPTIONS result is failed.
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
receiveRequestCount.incrementAndGet();
int sipCode = 400;
String reason = "Bad Request";
optionsCallback.onNetworkResponse(sipCode, reason, Collections.EMPTY_LIST);
});
// Request capabilities by calling the API requestCapabilities.
requestCapabilities(uceAdapter, contacts, callback);
// Verify the callback "onError" is called.
assertEquals(RcsUceAdapter.ERROR_GENERIC_FAILURE, waitForLongResult(errorQueue));
// Verify the result
verifyOptionsCapabilityResult(capabilityQueue, contacts,
RcsContactUceCapability.SOURCE_TYPE_NETWORK,
RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND, Collections.EMPTY_LIST);
errorQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
receiveRequestCount.set(0);
removeTestContactFromEab();
overrideCarrierConfig(null);
}
@Test
public void testOptionsRequestFromNetwork() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
CapabilityExchangeEventListener eventListener =
sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
final Uri contact = sTestContact2Uri;
Set<String> remoteCapabilities = new ArraySet<>();
remoteCapabilities.add(FEATURE_TAG_CHAT);
remoteCapabilities.add(FEATURE_TAG_FILE_TRANSFER);
remoteCapabilities.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
remoteCapabilities.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
BlockingQueue<Pair<RcsContactUceCapability, Boolean>> respToCapRequestQueue =
new LinkedBlockingQueue<>();
OptionsRequestCallback callback = new OptionsRequestCallback() {
@Override
public void onRespondToCapabilityRequest(RcsContactUceCapability capabilities,
boolean isBlocked) {
respToCapRequestQueue.offer(new Pair<>(capabilities, isBlocked));
}
@Override
public void onRespondToCapabilityRequestWithError(int sipCode, String reason) {
}
};
// Notify the remote capability request
eventListener.onRemoteCapabilityRequest(contact, remoteCapabilities, callback);
// Verify receive the result
Pair<RcsContactUceCapability, Boolean> capability = waitForResult(respToCapRequestQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
assertEquals(RcsContactUceCapability.SOURCE_TYPE_CACHED, capability.first.getSourceType());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND,
capability.first.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.first.getCapabilityMechanism());
// Should not report blocked
assertFalse("This number is not blocked, so the API should not report blocked",
capability.second);
overrideCarrierConfig(null);
}
@Test
public void testOptionsRequestFromNetworkBlocked() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
CapabilityExchangeEventListener eventListener =
sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
final Uri contact = sTestNumberUri;
Set<String> remoteCapabilities = new ArraySet<>();
remoteCapabilities.add(FEATURE_TAG_CHAT);
remoteCapabilities.add(FEATURE_TAG_FILE_TRANSFER);
remoteCapabilities.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
remoteCapabilities.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
BlockingQueue<Pair<RcsContactUceCapability, Boolean>> respToCapRequestQueue =
new LinkedBlockingQueue<>();
OptionsRequestCallback callback = new OptionsRequestCallback() {
@Override
public void onRespondToCapabilityRequest(RcsContactUceCapability capabilities,
boolean isBlocked) {
respToCapRequestQueue.offer(new Pair<>(capabilities, isBlocked));
}
@Override
public void onRespondToCapabilityRequestWithError(int sipCode, String reason) {
}
};
// Must be default SMS app to block numbers
sServiceConnector.setDefaultSmsApp();
Uri blockedUri = BlockedNumberUtil.insertBlockedNumber(getContext(), sTestPhoneNumber);
assertNotNull("could not block number", blockedUri);
try {
// Notify the remote capability request
eventListener.onRemoteCapabilityRequest(contact, remoteCapabilities, callback);
// Verify receive the result
Pair<RcsContactUceCapability, Boolean> capability =
waitForResult(respToCapRequestQueue);
assertNotNull("RcsContactUceCapability should not be null", capability);
assertEquals(RcsContactUceCapability.SOURCE_TYPE_CACHED,
capability.first.getSourceType());
assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND,
capability.first.getRequestResult());
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
capability.first.getCapabilityMechanism());
// Should report blocked
assertTrue("this number is blocked, so API should report blocked",
capability.second);
} finally {
BlockedNumberUtil.deleteBlockedNumber(getContext(), blockedUri);
sServiceConnector.restoreDefaultSmsApp();
}
overrideCarrierConfig(null);
}
@Test
public void testForbidCapabilitiesRequest() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Override the carrier config of SIP 489 request forbidden.
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.Ims.KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, true);
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
overrideCarrierConfig(bundle);
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> retryAfterQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
List<RcsContactUceCapability> capabilityQueue = new ArrayList<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.add(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMillis) {
errorQueue.offer(errorCode);
retryAfterQueue.offer(retryAfterMillis);
}
};
// Prepare two contacts
final Uri contact1 = sTestNumberUri;
final Uri contact2 = sTestContact2Uri;
Collection<Uri> contacts = new ArrayList<>(2);
contacts.add(contact1);
contacts.add(contact2);
// Prepare the network response.
final int sipCodeBadEvent = 489;
final int sipCodeForbidden = 403;
AtomicInteger subscribeRequestCount = new AtomicInteger(0);
// Prepare a map to define the sip code and its associated result.
Map<Integer, Integer> networkSipCodeMap = new HashMap<>();
networkSipCodeMap.put(sipCodeBadEvent, RcsUceAdapter.ERROR_FORBIDDEN);
networkSipCodeMap.put(sipCodeForbidden, RcsUceAdapter.ERROR_FORBIDDEN);
// Verify each command error code and the expected callback result
networkSipCodeMap.forEach((sipCode, expectedCallbackResult) -> {
// Setup the capabilities request response with the given sip code.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
subscribeRequestCount.incrementAndGet();
cb.onNetworkResponse(sipCode, "");
});
try {
// Request contact uce capabilities
requestCapabilities(uceAdapter, contacts, callback);
// Verify that the callback "onError" is called with the error code FORBIDDEN
assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
// Verify the retryAfter value
long retryAfterMillis = waitForLongResult(retryAfterQueue);
if (sipCode == sipCodeForbidden) {
assertEquals(0L, retryAfterMillis);
} else if (sipCode == sipCodeBadEvent) {
assertTrue(retryAfterMillis > 0L);
}
} catch (Exception e) {
fail("testForbiddenResponseToCapabilitiesRequest with command error failed: " + e);
} finally {
errorQueue.clear();
retryAfterQueue.clear();
capabilityQueue.clear();
completeQueue.clear();
subscribeRequestCount.set(0);
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
});
overrideCarrierConfig(null);
}
@Test
public void testTerminatedCallbackWithCapabilitiesRequest() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
ArrayList<String> pidfXmlList = new ArrayList<>(1);
pidfXmlList.add(getPidfXmlData(sTestNumberUri, true, true));
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMillis) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMillis);
}
};
// Prepare the test contact and the callback
Collection<Uri> numbers = new ArrayList<>(1);
numbers.add(sTestNumberUri);
// Prepare the network response is 200 OK and the capabilities update
int networkRespCode = 200;
String networkRespReason = "OK";
Map<SomeArgs, SomeArgs> terminatedMap = new HashMap<>();
SomeArgs deactivatedArgs = SomeArgs.obtain();
deactivatedArgs.arg1 = "deactivated";
deactivatedArgs.arg2 = Long.valueOf(3000L);
SomeArgs deactivatedExpectedArgs = SomeArgs.obtain();
deactivatedExpectedArgs.argi1 = RcsUceAdapter.ERROR_GENERIC_FAILURE;
deactivatedExpectedArgs.arg1 = Long.valueOf(3000L);
terminatedMap.put(deactivatedArgs, deactivatedExpectedArgs);
SomeArgs probationArgs = SomeArgs.obtain();
probationArgs.arg1 = "probation";
probationArgs.arg2 = Long.valueOf(4000L);
SomeArgs probationExpectedArgs = SomeArgs.obtain();
probationExpectedArgs.argi1 = RcsUceAdapter.ERROR_GENERIC_FAILURE;
probationExpectedArgs.arg1 = Long.valueOf(4000L);
terminatedMap.put(probationArgs, probationExpectedArgs);
SomeArgs rejectedArgs = SomeArgs.obtain();
rejectedArgs.arg1 = "rejected";
rejectedArgs.arg2 = Long.valueOf(5000L);
SomeArgs rejectedExpectedArgs = SomeArgs.obtain();
rejectedExpectedArgs.argi1 = RcsUceAdapter.ERROR_NOT_AUTHORIZED;
rejectedExpectedArgs.arg1 = Long.valueOf(0L);
terminatedMap.put(rejectedArgs, rejectedExpectedArgs);
SomeArgs timeoutArgs = SomeArgs.obtain();
timeoutArgs.arg1 = "timeout";
timeoutArgs.arg2 = Long.valueOf(6000L);
SomeArgs timeoutExpectedArgs = SomeArgs.obtain();
timeoutExpectedArgs.argi1 = RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
timeoutExpectedArgs.arg1 = Long.valueOf(6000L);
terminatedMap.put(timeoutArgs, timeoutExpectedArgs);
SomeArgs giveupArgs = SomeArgs.obtain();
giveupArgs.arg1 = "giveup";
giveupArgs.arg2 = Long.valueOf(7000L);
SomeArgs giveupExpectedArgs = SomeArgs.obtain();
giveupExpectedArgs.argi1 = RcsUceAdapter.ERROR_NOT_AUTHORIZED;
giveupExpectedArgs.arg1 = Long.valueOf(7000L);
terminatedMap.put(giveupArgs, giveupExpectedArgs);
SomeArgs noresourceArgs = SomeArgs.obtain();
noresourceArgs.arg1 = "noresource";
noresourceArgs.arg2 = Long.valueOf(8000L);
SomeArgs noresourceExpectedArgs = SomeArgs.obtain();
noresourceExpectedArgs.argi1 = RcsUceAdapter.ERROR_NOT_FOUND;
noresourceExpectedArgs.arg1 = Long.valueOf(0L);
terminatedMap.put(giveupArgs, giveupExpectedArgs);
SomeArgs emptyReasonArgs = SomeArgs.obtain();
emptyReasonArgs.arg1 = "";
emptyReasonArgs.arg2 = Long.valueOf(9000L);
SomeArgs emptyReasonExpectedArgs = SomeArgs.obtain();
emptyReasonExpectedArgs.argi1 = RcsUceAdapter.ERROR_GENERIC_FAILURE;
emptyReasonExpectedArgs.arg1 = Long.valueOf(9000L);
terminatedMap.put(emptyReasonArgs, emptyReasonExpectedArgs);
// Verify each subscription terminated and the expected result
terminatedMap.forEach((reason, expectedResult) -> {
String terminatedReason = (String) reason.arg1;
Long terminatedRetryAfterMillis = (Long) reason.arg2;
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated(terminatedReason, terminatedRetryAfterMillis);
});
requestCapabilities(uceAdapter, numbers, callback);
try {
// Verify that the contact capability is received and the onCompleted is called.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact: " + sTestNumberUri,
capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
int expectedErrorCode = expectedResult.argi1;
Long expectedRetryAfter = (Long) expectedResult.arg1;
assertEquals(expectedErrorCode, waitForIntResult(errorQueue));
assertEquals(expectedRetryAfter.longValue(), (waitForLongResult(errorRetryQueue)));
} catch (Exception e) {
fail("Unexpected exception " + e);
}
reason.recycle();
expectedResult.recycle();
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
});
/*
* Verify the subscribe request is successful when: A) The terminated is timeout and
* B) The retryAfter is 0L and C) All the capabilities have been received.
*/
String terminatedReason = "timeout";
long terminatedRetryAfterMillis = 0L;
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated(terminatedReason, terminatedRetryAfterMillis);
});
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the contact capability is received and the onCompleted is called.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact: " + sTestNumberUri, capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
assertTrue(waitForResult(completeQueue));
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
/*
* Set the subscribe request is failed because NOT all of the capabilities have been
* received.
*/
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onTerminated(terminatedReason, terminatedRetryAfterMillis);
});
requestCapabilities(uceAdapter, numbers, callback);
/*
* Verify the request is failed because NOT all of the capabilities have been received.
*/
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, (waitForLongResult(errorRetryQueue)));
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
overrideCarrierConfig(null);
}
@Test
public void testTimeoutToRequestCapabilitiesWithPresenceMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare three contacts
final Uri contact1 = sTestNumberUri;
final Uri contact2 = sTestContact2Uri;
final Uri contact3 = sTestContact3Uri;
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(contact1);
contacts.add(contact2);
contacts.add(contact3);
// Setup the ImsService doesn't trigger any callbacks.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
// It won't trigger any callbacks.
});
try {
setCapabilitiesRequestTimeout(3000L);
requestCapabilities(uceAdapter, contacts, callback);
// Verify that the clients receive the TIMEOUT error code.
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, (waitForLongResult(errorRetryQueue)));
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
setCapabilitiesRequestTimeout(-1L);
}
// Setup the ImsService only trigger the network response callback. However it doesn't
// trigger the onTerminated callback
int networkRespCode = 200;
String networkRespReason = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
});
try {
setCapabilitiesRequestTimeout(3000L);
requestCapabilities(uceAdapter, contacts, callback);
// Verify that the clients receive the TIMEOUT error code.
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, (waitForLongResult(errorRetryQueue)));
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
setCapabilitiesRequestTimeout(-1L);
}
overrideCarrierConfig(null);
}
@Test
public void testRequestTimeoutWithPresenceMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare the test contact
Collection<Uri> contacts = new ArrayList<>();
contacts.add(sTestNumberUri);
// Setup the ImsService doesn't trigger any callbacks.
AtomicInteger subscribeRequestCount = new AtomicInteger(0);
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
subscribeRequestCount.incrementAndGet();
// It won't trigger any callbacks to the framework.
});
// Set the timeout for 3 seconds
setCapabilitiesRequestTimeout(3000L);
// Request capabilities
requestCapabilities(uceAdapter, contacts, callback);
try {
// Verify the error code REQUEST_TIMEOUT is received
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(errorRetryQueue));
// Verify the capabilities can still be received.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
subscribeRequestCount.set(0);
}
// Request the capabilities with the same contact again.
requestCapabilities(uceAdapter, contacts, callback);
try {
// Verify that the caller can received the capabilities callback.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the complete callback will be called.
waitForResult(completeQueue);
// Verify that the ImsService didn't received the request because the capabilities
// should be retrieved from the cache.
assertEquals(0, subscribeRequestCount.get());
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
subscribeRequestCount.set(0);
setCapabilitiesRequestTimeout(-1L);
}
overrideCarrierConfig(null);
}
@Test
public void testTimeoutToRequestCapabilitiesWithOptionsMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
// The test contact
Collection<Uri> contacts = new ArrayList<>(3);
contacts.add(sTestNumberUri);
// The result callback
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Setup the ImsService doesn't trigger any callbacks.
capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
// It won't trigger any callbacks.
});
try {
setCapabilitiesRequestTimeout(3000L);
requestCapabilities(uceAdapter, contacts, callback);
// Verify that the clients receive the TIMEOUT error code.
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, (waitForLongResult(errorRetryQueue)));
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
setCapabilitiesRequestTimeout(-1L);
}
overrideCarrierConfig(null);
}
@Test
public void testRequestCapabilitiesWithUriFormatChanged() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Override the carrier config to support group subscribe.
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
overrideCarrierConfig(bundle);
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare three contacts
final Uri contact1TelScheme = sTestNumberUri;
final Uri contact1SipScheme = Uri.fromParts(PhoneAccount.SCHEME_SIP,
sTestPhoneNumber + "@test.cts;user=phone", null);
final Uri contact2 = sTestContact2Uri;
final Uri contact3 = sTestContact3Uri;
Collection<Uri> contacts = new ArrayList<>(3);
// The first contact is using the tel scheme
contacts.add(contact1TelScheme);
contacts.add(contact2);
contacts.add(contact3);
ArrayList<String> pidfXmlList = new ArrayList<>(3);
// ImsService replies the pidf xml data with the SIP scheme
pidfXmlList.add(getPidfXmlData(contact1SipScheme, true, true));
pidfXmlList.add(getPidfXmlData(contact2, true, false));
pidfXmlList.add(getPidfXmlData(contact3, false, false));
// Setup the network response is 200 OK and notify capabilities update
int networkRespCode = 200;
String networkRespReason = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
List<RcsContactUceCapability> resultCapList = new ArrayList<>();
// Verify that all the three contact's capabilities are received
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received for contact", capability);
resultCapList.add(capability);
// Verify the first contact capabilities from the received capabilities list
RcsContactUceCapability resultCapability =
getContactCapability(resultCapList, contact1SipScheme);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact1SipScheme, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, false);
// Verify the third contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
resultCapList.clear();
removeTestContactFromEab();
// Setup the callback that some of the contacts are terminated.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(404, "NOT FOUND");
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify the contacts are not found.
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
// Verify the first contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact1TelScheme);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact1TelScheme, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
resultCapList.clear();
removeTestContactFromEab();
// Setup the callback that some of the contacts are terminated.
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
List<Uri> uriList = new ArrayList(uris);
cb.onNetworkResponse(networkRespCode, networkRespReason);
// Notify capabilities updated for the first contact
String pidfXml = pidfXmlList.get(0);
cb.onNotifyCapabilitiesUpdate(Collections.singletonList(pidfXml));
List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
for (int i = 1; i < uriList.size(); i++) {
Pair<Uri, String> pair = Pair.create(uriList.get(i), "noresource");
terminatedResources.add(pair);
}
cb.onResourceTerminated(terminatedResources);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
// Verify the first contact is found.
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
// Verify the reset contacts are not found.
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
resultCapList.add(capability);
// Verify the first contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact1SipScheme);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact1SipScheme, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact2);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact2, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, true, false);
// Verify the second contact capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, contact3);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, contact3, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
overrideCarrierConfig(null);
}
@Test
public void testReceivingEmptyPidfXml() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare the test contact
Collection<Uri> contacts = new ArrayList<>(1);
contacts.add(sTestNumberUri);
// Prepare the empty PIDF xml
ArrayList<String> pidfXmlList = new ArrayList<>(1);
pidfXmlList.add("");
// Setup the network response is 200 OK, empty PIDF data and the reason of onTerminated
// is "TIMEOUT"
int networkRespCode = 200;
String networkRespReason = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode, networkRespReason);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("TIMEOUT", 0L);
});
requestCapabilities(uceAdapter, contacts, callback);
try {
// Verify the contact capabilities is received and the result is NOT FOUND.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received.", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the callback "onCompleted" is called
waitForResult(completeQueue);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
}
// Setup the network response is 404 NOT FOUND
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(404, "NOT FOUND");
});
requestCapabilities(uceAdapter, contacts, callback);
try {
// Verify the contact capabilities is received and the result is NOT FOUND.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities is not received.", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify that the callback "onComplete" is called
waitForResult(completeQueue);
} catch (Exception e) {
fail("requestCapabilities is failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
}
// Setup the network response is 405 Method Not Allowed
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(405, "Method Not Allowed");
});
requestCapabilities(uceAdapter, contacts, callback);
try {
// Verify the contact capabilities is received and the result is NOT FOUND.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities is not received.", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify that the callback "onComplete" is called
waitForResult(completeQueue);
} catch (Exception e) {
fail("requestCapabilities is failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
completeQueue.clear();
capabilityQueue.clear();
removeTestContactFromEab();
}
overrideCarrierConfig(null);
}
@Test
public void testContactInThrottlingState() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Reset the UCE device state.
removeUceRequestDisallowedStatus();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* options */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// Prepare the test contact
Collection<Uri> numbers = new ArrayList<>(1);
numbers.add(sTestNumberUri);
// Setup the network response is 408 Request Timeout.
int networkRespCode = 408;
String networkRespReason = "Request Timeout";
AtomicInteger subscribeRequestCount = new AtomicInteger(0);
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
subscribeRequestCount.incrementAndGet();
cb.onNetworkResponse(networkRespCode, networkRespReason);
});
// Request contact capabilities
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(errorRetryQueue));
// Verify the caller can received the capabilities callback.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verity the ImsService received the request.
assertTrue(subscribeRequestCount.get() > 0);
} catch (Exception e) {
fail("testContactsInThrottlingState with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
capabilityQueue.clear();
completeQueue.clear();
subscribeRequestCount.set(0);
}
// Request the capabilities again with the same contact.
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the result.
try {
// Verify that the caller can received the capabilities callback.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_CACHED,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the complete callback will be called.
waitForResult(completeQueue);
// Verify that the ImsService didn't received the request because the capabilities
// should be retrieved from the cache.
assertEquals(0, subscribeRequestCount.get());
} catch (Exception e) {
fail("testContactsInThrottlingState with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
subscribeRequestCount.set(0);
// reset the cache and throttling list
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
// Request availability.
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(errorRetryQueue));
// Verify the caller can received the capabilities callback.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verity the ImsService received the request.
assertTrue(subscribeRequestCount.get() > 0);
} catch (Exception e) {
fail("requestAvailability with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
capabilityQueue.clear();
completeQueue.clear();
subscribeRequestCount.set(0);
}
// Request availability again with the same contact.
requestAvailability(uceAdapter, sTestNumberUri, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
// Verify that the caller can received the capabilities callback.
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_CACHED,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify the complete callback will be called.
waitForResult(completeQueue);
// Verify that the ImsService didn't received the request because the capabilities
// should be retrieved from the cache.
assertEquals(0, subscribeRequestCount.get());
} catch (Exception e) {
fail("testContactsInThrottlingState with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
subscribeRequestCount.set(0);
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
overrideCarrierConfig(null);
}
@Test
public void testRequestResultInconclusive() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
assertNotNull("UCE adapter should not be null!", uceAdapter);
// Remove the test contact capabilities
removeTestContactFromEab();
// Reset the UCE device state.
removeUceRequestDisallowedStatus();
// Connect to the ImsService
setupTestImsService(uceAdapter, true, true /* presence cap */, false /* options */);
TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
.getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@Override
public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
capabilities.forEach(c -> capabilityQueue.offer(c));
}
@Override
public void onComplete() {
completeQueue.offer(true);
}
@Override
public void onError(int errorCode, long retryAfterMilliseconds) {
errorQueue.offer(errorCode);
errorRetryQueue.offer(retryAfterMilliseconds);
}
};
// In the first round, prepare the test account
Collection<Uri> numbers = new ArrayList<>();
numbers.add(sTestNumberUri);
ArrayList<String> pidfXmlList = new ArrayList<>();
pidfXmlList.add(getPidfXmlData(sTestNumberUri, true, true));
// Setup the network response is 200 OK for the first request
final int networkRespCode200 = 200;
final String networkRespReasonOK = "OK";
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
cb.onNetworkResponse(networkRespCode200, networkRespReasonOK);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("", 0L);
});
// Request contact capabilities
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the contact capability is received and the onCompleted is called.
try {
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
waitForResult(completeQueue);
} catch (Exception e) {
fail("testRequestResultInconclusive with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
capabilityQueue.clear();
completeQueue.clear();
numbers.clear();
pidfXmlList.clear();
}
// Request the second contacts and this time, the network respons is 408 Request Timeout
numbers.add(sTestContact2Uri);
final int networkRespCode408 = 408;
final String networkRespReasonTimeout = "Request Timeout";
AtomicInteger subscribeRequestCount = new AtomicInteger(0);
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
subscribeRequestCount.incrementAndGet();
cb.onNetworkResponse(networkRespCode408, networkRespReasonTimeout);
});
// Request contact capabilities again with different contact
requestCapabilities(uceAdapter, numbers, callback);
// Verify that the callback "onError" is called with the expected error code.
try {
assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
assertEquals(0L, waitForLongResult(errorRetryQueue));
RcsContactUceCapability capability = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability);
verifyCapabilityResult(capability, sTestContact2Uri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_NOT_FOUND, false, false);
assertTrue(subscribeRequestCount.get() > 0);
} catch (Exception e) {
fail("testRequestResultInconclusive with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
capabilityQueue.clear();
completeQueue.clear();
numbers.clear();
pidfXmlList.clear();
subscribeRequestCount.set(0);
}
// Request three contacts at a time in the third round.
numbers.add(sTestNumberUri);
numbers.add(sTestContact2Uri);
numbers.add(sTestContact3Uri);
// The first two contact capabilities can be retrieved from the cache. However, the third
// contact capabilities will be provided by the ImsService
pidfXmlList.add(getPidfXmlData(sTestContact3Uri, true, true));
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
subscribeRequestCount.incrementAndGet();
assertNotNull("The uris of capabilities request cannot be null", uris);
List<Uri> uriList = new ArrayList(uris);
// Verify that only uri need to be queried from the network
assertEquals(1, uriList.size());
assertEquals(sTestContact3Uri, uriList.get(0));
cb.onNetworkResponse(networkRespCode200, networkRespReasonOK);
cb.onNotifyCapabilitiesUpdate(pidfXmlList);
cb.onTerminated("", 0L);
});
requestCapabilities(uceAdapter, numbers, callback);
List<RcsContactUceCapability> resultCapList = new ArrayList<>();
// Verify that the contact capability is received and the onCompleted is called.
try {
RcsContactUceCapability capability1 = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability1);
resultCapList.add(capability1);
RcsContactUceCapability capability2 = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability2);
resultCapList.add(capability2);
RcsContactUceCapability capability3 = waitForResult(capabilityQueue);
assertNotNull("Capabilities were not received", capability3);
resultCapList.add(capability3);
// Verify contact1's capabilities from the received capabilities list
RcsContactUceCapability resultCapability =
getContactCapability(resultCapList, sTestNumberUri);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, sTestNumberUri, SOURCE_TYPE_CACHED,
REQUEST_RESULT_FOUND, true, true);
// Verify contact2's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, sTestContact2Uri);
assertNotNull("Cannot find the contact", resultCapability);
verifyCapabilityResult(resultCapability, sTestContact2Uri, SOURCE_TYPE_CACHED,
REQUEST_RESULT_NOT_FOUND, false, false);
// Verify contact3's capabilities from the received capabilities list
resultCapability = getContactCapability(resultCapList, sTestContact3Uri);
assertNotNull("Cannot find the contact", sTestContact3Uri);
verifyCapabilityResult(resultCapability, sTestContact3Uri, SOURCE_TYPE_NETWORK,
REQUEST_RESULT_FOUND, true, true);
// Verify the onCompleted is called
waitForResult(completeQueue);
} catch (Exception e) {
fail("testRequestResultInconclusive with command error failed: " + e);
} finally {
errorQueue.clear();
errorRetryQueue.clear();
capabilityQueue.clear();
completeQueue.clear();
numbers.clear();
pidfXmlList.clear();
removeTestContactFromEab();
removeUceRequestDisallowedStatus();
}
overrideCarrierConfig(null);
}
private void setupTestImsService(RcsUceAdapter uceAdapter, boolean presencePublishEnabled,
boolean presenceCapExchangeEnabled, boolean sipOptionsEnabled) throws Exception {
// Trigger carrier config changed
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
presencePublishEnabled);
bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL,
presenceCapExchangeEnabled);
bundle.putBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, sipOptionsEnabled);
overrideCarrierConfig(bundle);
// Connect to the TestImsService
connectTestImsService();
}
private String getPidfXmlData(Uri contact, boolean audioSupported, boolean videoSupported) {
StringBuilder pidfBuilder = new StringBuilder();
pidfBuilder.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>")
.append("<presence entity=\"").append(contact).append("\"")
.append(" xmlns=\"urn:ietf:params:xml:ns:pidf\"")
.append(" xmlns:op=\"urn:oma:xml:prs:pidf:oma-pres\"")
.append(" xmlns:caps=\"urn:ietf:params:xml:ns:pidf:caps\">")
.append("<tuple id=\"tid0\"><status><basic>open</basic></status>")
.append("<op:service-description>")
.append("<op:service-id>service_id_01</op:service-id>")
.append("<op:version>1.0</op:version>")
.append("<op:description>description_test1</op:description>")
.append("</op:service-description>")
.append("<caps:servcaps>")
.append("<caps:audio>").append(audioSupported).append("</caps:audio>")
.append("<caps:video>").append(videoSupported).append("</caps:video>")
.append("</caps:servcaps>")
.append("<contact>").append(contact).append("</contact>")
.append("</tuple></presence>");
return pidfBuilder.toString();
}
private RcsContactUceCapability getContactCapability(
List<RcsContactUceCapability> resultCapList, Uri targetUri) {
if (resultCapList == null) {
return null;
}
return resultCapList.stream()
.filter(capability -> targetUri.equals(capability.getContactUri()))
.findFirst()
.orElse(null);
}
private void verifyCapabilityResult(RcsContactUceCapability resultCapability, Uri expectedUri,
int expectedSourceType, int expectedResult, boolean expectedAudioSupported,
boolean expectedVideoSupported) {
// Verify the contact URI
assertEquals(expectedUri, resultCapability.getContactUri());
// Verify the source type is the network type.
assertEquals(expectedSourceType,
resultCapability.getSourceType());
// Verify the request result is expected.
final int requestResult = resultCapability.getRequestResult();
assertEquals(expectedResult, requestResult);
// Return directly if the result is not found.
if (requestResult == REQUEST_RESULT_NOT_FOUND) {
return;
}
// Verify the mechanism is presence
assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE,
resultCapability.getCapabilityMechanism());
RcsContactPresenceTuple presenceTuple =
resultCapability.getCapabilityTuple("service_id_01");
assertNotNull("Contact Presence tuple should not be null!", presenceTuple);
ServiceCapabilities capabilities = presenceTuple.getServiceCapabilities();
assertNotNull("Service capabilities should not be null!", capabilities);
// Verify if the audio is supported
assertEquals(expectedAudioSupported, capabilities.isAudioCapable());
// Verify if the video is supported
assertEquals(expectedVideoSupported, capabilities.isVideoCapable());
}
private void verifyOptionsCapabilityResult(List<RcsContactUceCapability> resultCapList,
Collection<Uri> expectedUriList, int expectedSourceType, int expectedMechanism,
int expectedResult, List<String> expectedFeatureTags) {
assertEquals(resultCapList.size(), expectedUriList.size());
assertTrue(resultCapList.stream().map(capability -> capability.getContactUri())
.anyMatch(expectedUriList::contains));
resultCapList.stream().map(capability -> capability.getSourceType())
.forEach(sourceType -> assertEquals((int) sourceType, (int) expectedSourceType));
resultCapList.stream().map(capability -> capability.getCapabilityMechanism())
.forEach(mechanism -> assertEquals((int) mechanism, (int) expectedMechanism));
resultCapList.stream().map(capability -> capability.getRequestResult())
.forEach(result -> assertEquals((int) result, (int) expectedResult));
resultCapList.stream().map(capability -> capability.getFeatureTags())
.forEach(featureTags -> {
assertEquals((int) featureTags.size(), (int) expectedFeatureTags.size());
assertTrue(featureTags.containsAll(expectedFeatureTags));
});
}
private void registerUceObserver(Consumer<Uri> resultConsumer) {
mUceObserver = new ContentObserver(new Handler(sHandlerThread.getLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
resultConsumer.accept(uri);
}
};
getContext().getContentResolver().registerContentObserver(LISTENER_URI,
true /*notifyForDecendents*/, mUceObserver);
}
private void unregisterUceObserver() {
if (mUceObserver != null) {
getContext().getContentResolver().unregisterContentObserver(mUceObserver);
}
}
private int waitForIntResult(BlockingQueue<Integer> queue) throws Exception {
Integer result = queue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
return result != null ? result : Integer.MAX_VALUE;
}
private long waitForLongResult(BlockingQueue<Long> queue) throws Exception {
Long result = queue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
return result != null ? result : Long.MAX_VALUE;
}
private <T> T waitForResult(BlockingQueue<T> queue) throws Exception {
return queue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
private void connectTestImsService() throws Exception {
assertTrue(sServiceConnector.connectCarrierImsService(new ImsFeatureConfiguration.Builder()
.addFeature(sTestSlot, ImsFeature.FEATURE_MMTEL)
.addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
.build()));
// The RcsFeature is created when the ImsService is bound. If it wasn't created, then the
// Framework did not call it.
assertTrue("Did not receive createRcsFeature", sServiceConnector.getCarrierService()
.waitForLatchCountdown(TestImsService.LATCH_CREATE_RCS));
assertTrue("Did not receive RcsFeature#onReady", sServiceConnector.getCarrierService()
.waitForLatchCountdown(TestImsService.LATCH_RCS_READY));
// Make sure the RcsFeature was created in the test service.
assertNotNull("Device ImsService created, but TestDeviceImsService#createRcsFeature was not"
+ "called!", sServiceConnector.getCarrierService().getRcsFeature());
assertTrue("Did not receive RcsFeature#setCapabilityExchangeEventListener",
sServiceConnector.getCarrierService().waitForLatchCountdown(
TestImsService.LATCH_UCE_LISTENER_SET));
int serviceSlot = sServiceConnector.getCarrierService().getRcsFeature().getSlotIndex();
assertEquals("The slot specified for the test (" + sTestSlot + ") does not match the "
+ "assigned slot (" + serviceSlot + "+ for the associated RcsFeature",
sTestSlot, serviceSlot);
}
private static void initPhoneNumbers() {
// Generate a random phone number
sTestPhoneNumber = generateRandomPhoneNumber();
sTestNumberUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, sTestPhoneNumber, null);
sTestContact2 = generateRandomContact(5);
sTestContact2Uri = Uri.fromParts(PhoneAccount.SCHEME_SIP, sTestContact2, null);
sTestContact3 = generateRandomContact(6);
sTestContact3Uri = Uri.fromParts(PhoneAccount.SCHEME_SIP, sTestContact3, null);
}
private static String generateRandomPhoneNumber() {
Random random = new Random();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 10; i++) {
builder.append(random.nextInt(10));
}
return builder.toString();
}
private static String generateRandomContact(int length) {
Random random = new Random();
StringBuilder builder = new StringBuilder("TestContact");
for (int i = 0; i < length; i++) {
int asciiNum = random.nextInt(26) + 65; // ascii 65
builder.append((char) asciiNum);
}
return builder.toString();
}
private static void overrideCarrierConfig(PersistableBundle bundle) throws Exception {
CarrierConfigManager carrierConfigManager = InstrumentationRegistry.getInstrumentation()
.getContext().getSystemService(CarrierConfigManager.class);
sReceiver.clearQueue();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(carrierConfigManager,
(m) -> m.overrideConfig(sTestSub, bundle));
sReceiver.waitForCarrierConfigChanged();
}
private static void removeTestContactFromEab() {
try {
StringBuilder builder = new StringBuilder();
builder.append(sTestPhoneNumber)
.append(",").append(sTestContact2)
.append(",").append(sTestContact3);
sServiceConnector.removeEabContacts(sTestSlot, builder.toString());
} catch (Exception e) {
Log.w("RcsUceAdapterTest", "Cannot remove test contacts from eab database: " + e);
}
}
private static void removeUceRequestDisallowedStatus() {
try {
sServiceConnector.removeUceRequestDisallowedStatus(sTestSlot);
} catch (Exception e) {
Log.w("RcsUceAdapterTest", "Cannot remove request disallowed status: " + e);
}
}
private static void setCapabilitiesRequestTimeout(long timeoutAfterMillis) {
try {
sServiceConnector.setCapabilitiesRequestTimeout(sTestSlot, timeoutAfterMillis);
} catch (Exception e) {
Log.w("RcsUceAdapterTest", "Cannot set capabilities request timeout: " + e);
}
}
private void requestCapabilities(RcsUceAdapter uceAdapter, Collection<Uri> numbers,
RcsUceAdapter.CapabilitiesCallback callback) {
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
uceAdapter,
adapter -> adapter.requestCapabilities(numbers, Runnable::run, callback),
ImsException.class,
"android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
} catch (SecurityException e) {
fail("requestCapabilities should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE. "
+ "Exception: " + e);
} catch (ImsException e) {
fail("requestCapabilities failed " + e);
}
}
private void requestAvailability(RcsUceAdapter uceAdapter, Uri number,
RcsUceAdapter.CapabilitiesCallback callback) {
try {
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
uceAdapter,
adapter -> adapter.requestAvailability(number, Runnable::run, callback),
ImsException.class,
"android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
} catch (SecurityException e) {
fail("requestAvailability should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE. "
+ "Exception: " + e);
} catch (ImsException e) {
fail("requestAvailability failed " + e);
}
}
}