Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 239efaec authored by Brad Ebinger's avatar Brad Ebinger Committed by Android (Google) Code Review
Browse files

Merge "Shortcut and return caps of contacts who have non-expired cap info"

parents e33c346e 82d096e4
Loading
Loading
Loading
Loading
+85 −3
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@@ -59,6 +60,14 @@ public class UceRequestManager {

    private static final String LOG_TAG = UceUtils.getLogPrefix() + "UceRequestManager";

    /**
     * When enabled, skip the request queue for requests that have numbers with valid cached
     * capabilities and return that cached info directly.
     * Note: This also has a CTS test associated with it, so this can not be disabled without
     * disabling the corresponding RcsUceAdapterTest#testCacheQuerySuccessWhenNetworkBlocked test.
     */
    private static final boolean FEATURE_SHORTCUT_QUEUE_FOR_CACHED_CAPS = true;

    /**
     * Testing interface used to mock UceUtils in testing.
     */
@@ -487,12 +496,27 @@ public class UceRequestManager {
    private void sendRequestInternal(@UceRequestType int type, List<Uri> uriList,
            boolean skipFromCache, IRcsUceControllerCallback callback) throws RemoteException {
        UceRequestCoordinator requestCoordinator = null;
        List<Uri> nonCachedUris = uriList;
        if (FEATURE_SHORTCUT_QUEUE_FOR_CACHED_CAPS && !skipFromCache) {
            nonCachedUris = sendCachedCapInfoToRequester(type, uriList, callback);
            if (uriList.size() != nonCachedUris.size()) {
                logd("sendRequestInternal: shortcut queue for caps - request reduced from "
                        + uriList.size() + " entries to " + nonCachedUris.size() + " entries");
            } else {
                logd("sendRequestInternal: shortcut queue for caps - no cached caps.");
            }
            if (nonCachedUris.isEmpty()) {
                logd("sendRequestInternal: shortcut complete, sending success result");
                callback.onComplete();
                return;
            }
        }
        if (sUceUtilsProxy.isPresenceCapExchangeEnabled(mContext, mSubId) &&
                sUceUtilsProxy.isPresenceSupported(mContext, mSubId)) {
            requestCoordinator = createSubscribeRequestCoordinator(type, uriList, skipFromCache,
                    callback);
            requestCoordinator = createSubscribeRequestCoordinator(type, nonCachedUris,
                    skipFromCache, callback);
        } else if (sUceUtilsProxy.isSipOptionsSupported(mContext, mSubId)) {
            requestCoordinator = createOptionsRequestCoordinator(type, uriList, callback);
            requestCoordinator = createOptionsRequestCoordinator(type, nonCachedUris, callback);
        }

        if (requestCoordinator == null) {
@@ -513,6 +537,64 @@ public class UceRequestManager {
        addRequestCoordinator(requestCoordinator);
    }

    /**
     * Try to get the valid capabilities associated with the URI List specified from the EAB cache.
     * If one or more of the numbers from the URI List have valid cached capabilities, return them
     * to the requester now and remove them from the returned List of URIs that will require a
     * network query.
     * @param type The type of query
     * @param uriList The List of URIs that we want to send cached capabilities for
     * @param callback The callback used to communicate with the remote requester
     * @return The List of URIs that were not found in the capability cache and will require a
     * network query.
     */
    private List<Uri> sendCachedCapInfoToRequester(int type, List<Uri> uriList,
            IRcsUceControllerCallback callback) {
        List<Uri> nonCachedUris = new ArrayList<>(uriList);
        List<RcsContactUceCapability> numbersWithCachedCaps =
                getCapabilitiesFromCache(type, nonCachedUris);
        try {
            if (!numbersWithCachedCaps.isEmpty()) {
                logd("sendCachedCapInfoToRequester: cached caps found for "
                        + numbersWithCachedCaps.size() + " entries. Notifying requester.");
                // Notify caller of the numbers that have cached caps
                callback.onCapabilitiesReceived(numbersWithCachedCaps);
            }
        } catch (RemoteException e) {
            logw("sendCachedCapInfoToRequester, error sending cap info back to requester: " + e);
        }
        // remove these numbers from the numbers pending a cap query from the network.
        for (RcsContactUceCapability c : numbersWithCachedCaps) {
            nonCachedUris.removeIf(uri -> c.getContactUri().equals(uri));
        }
        return nonCachedUris;
    }

    /**
     * Get the capabilities for the List of given URIs
     * @param requestType The request type, used to determine if the cached info is "fresh" enough.
     * @param uriList The List of URIs that we will be requesting cached capabilities for.
     * @return A list of capabilities corresponding to the subset of numbers that still have
     * valid cache data associated with them.
     */
    private List<RcsContactUceCapability> getCapabilitiesFromCache(int requestType,
            List<Uri> uriList) {
        List<EabCapabilityResult> resultList = Collections.emptyList();
        if (requestType == UceRequest.REQUEST_TYPE_CAPABILITY) {
            resultList = mRequestMgrCallback.getCapabilitiesFromCache(uriList);
        } else if (requestType == UceRequest.REQUEST_TYPE_AVAILABILITY) {
            // Always get the first element if the request type is availability.
            resultList = Collections.singletonList(
                    mRequestMgrCallback.getAvailabilityFromCache(uriList.get(0)));
        }
        // Map from EabCapabilityResult -> RcsContactUceCapability.
        // Pull out only items that have valid cache data.
        return resultList.stream().filter(Objects::nonNull)
                .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL)
                .map(EabCapabilityResult::getContactCapabilities)
                .filter(Objects::nonNull).collect(Collectors.toList());
    }

    private UceRequestCoordinator createSubscribeRequestCoordinator(final @UceRequestType int type,
            final List<Uri> uriList, boolean skipFromCache, IRcsUceControllerCallback callback) {
        SubscribeRequestCoordinator.Builder builder;
+114 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.ims.rcs.uce.request;

import static android.telephony.ims.RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE;
import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_CACHED;

import static com.android.ims.rcs.uce.request.UceRequestCoordinator.REQUEST_UPDATE_CACHED_CAPABILITY_UPDATE;
import static com.android.ims.rcs.uce.request.UceRequestCoordinator.REQUEST_UPDATE_CAPABILITY_UPDATE;
@@ -49,12 +50,15 @@ import androidx.test.filters.SmallTest;
import com.android.ims.ImsTestBase;
import com.android.ims.rcs.uce.UceController;
import com.android.ims.rcs.uce.UceController.UceControllerCallback;
import com.android.ims.rcs.uce.eab.EabCapabilityResult;
import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
import com.android.ims.rcs.uce.request.UceRequestManager.UceUtilsProxy;
import com.android.ims.rcs.uce.util.FeatureTags;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.junit.After;
import org.junit.Before;
@@ -132,6 +136,116 @@ public class UceRequestManagerTest extends ImsTestBase {
        verify(mCapabilitiesCallback).onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
    }

    /**
     * Test cache hit shortcut, where we return valid cache results when they exist before adding
     * the request to the queue.
     */
    @Test
    @SmallTest
    public void testCacheHitShortcut() throws Exception {
        UceRequestManager requestManager = getUceRequestManager();
        requestManager.setsUceUtilsProxy(getUceUtilsProxy(true, true, true, false, true, 10));
        Handler handler = requestManager.getUceRequestHandler();

        List<Uri> uriList = new ArrayList<>();
        uriList.add(Uri.fromParts("sip", "test", null));

        // Simulate a cache entry for each item in the uriList
        List<EabCapabilityResult> cachedNumbers = uriList.stream().map(uri ->
                        new EabCapabilityResult(uri, EabCapabilityResult.EAB_QUERY_SUCCESSFUL,
                                new RcsContactUceCapability.PresenceBuilder(uri,
                                        CAPABILITY_MECHANISM_PRESENCE, SOURCE_TYPE_CACHED).build()))
                .collect(Collectors.toList());
        doReturn(cachedNumbers).when(mCallback).getCapabilitiesFromCache(uriList);

        requestManager.sendCapabilityRequest(uriList, false, mCapabilitiesCallback);
        waitForHandlerAction(handler, 500L);
        // Extract caps from EabCapabilityResult and ensure the Lists match.
        verify(mCapabilitiesCallback).onCapabilitiesReceived(
                cachedNumbers.stream().map(EabCapabilityResult::getContactCapabilities).collect(
                Collectors.toList()));
        verify(mCapabilitiesCallback).onComplete();
        // The cache should have been hit, so no network requests should have been generated.
        verify(mRequestRepository, never()).addRequestCoordinator(any());
    }

    /**
     * Test cache hit shortcut, but in this case the cache result was expired. This should generate
     * a network request.
     */
    @Test
    @SmallTest
    public void testCacheExpiredShortcut() throws Exception {
        UceRequestManager requestManager = getUceRequestManager();
        requestManager.setsUceUtilsProxy(getUceUtilsProxy(true, true, true, false, true, 10));
        Handler handler = requestManager.getUceRequestHandler();

        List<Uri> uriList = new ArrayList<>();
        uriList.add(Uri.fromParts("sip", "test", null));

        // Simulate a cache entry for each item in the uriList
        List<EabCapabilityResult> cachedNumbers = uriList.stream().map(uri ->
                        new EabCapabilityResult(uri,
                                EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE,
                                new RcsContactUceCapability.PresenceBuilder(uri,
                                        CAPABILITY_MECHANISM_PRESENCE, SOURCE_TYPE_CACHED).build()))
                .collect(Collectors.toList());
        doReturn(cachedNumbers).when(mCallback).getCapabilitiesFromCache(uriList);

        requestManager.sendCapabilityRequest(uriList, false, mCapabilitiesCallback);
        waitForHandlerAction(handler, 500L);
        // Extract caps from EabCapabilityResult and ensure the Lists match.
        verify(mCapabilitiesCallback, never()).onCapabilitiesReceived(any());
        verify(mCapabilitiesCallback, never()).onComplete();
        // A network request should have been generated for the expired contact.
        verify(mRequestRepository).addRequestCoordinator(any());
    }

    /**
     * Test cache hit shortcut, where we return valid cache results when they exist before adding
     * the request to the queue. This case also tests the case where one entry of requested caps is
     * in the cache and the other isn't. We should receive a response for cached caps immediately
     * and generate a network request for the one that isnt.
     */
    @Test
    @SmallTest
    public void testCacheHitShortcutForSubsetOfCaps() throws Exception {
        UceRequestManager requestManager = getUceRequestManager();
        requestManager.setsUceUtilsProxy(getUceUtilsProxy(true, true, true, false, true, 10));
        Handler handler = requestManager.getUceRequestHandler();

        List<Uri> uriList = new ArrayList<>();
        Uri uri1 = Uri.fromParts("sip", "cachetest", null);
        uriList.add(uri1);

        // Simulate a cache entry for each item in the uriList
        List<EabCapabilityResult> cachedNumbers = new ArrayList<>();
        EabCapabilityResult cachedItem = new EabCapabilityResult(uri1,
                EabCapabilityResult.EAB_QUERY_SUCCESSFUL,
                new RcsContactUceCapability.PresenceBuilder(uri1, CAPABILITY_MECHANISM_PRESENCE,
                        SOURCE_TYPE_CACHED).build());
        cachedNumbers.add(cachedItem);

        // Add an entry that is not part of cache
        Uri uri2 = Uri.fromParts("sip", "nettest", null);
        uriList.add(uri2);
        cachedNumbers.add(new EabCapabilityResult(uri2,
                EabCapabilityResult.EAB_CONTACT_NOT_FOUND_FAILURE,
                new RcsContactUceCapability.PresenceBuilder(uri2, CAPABILITY_MECHANISM_PRESENCE,
                        SOURCE_TYPE_CACHED).build()));
        doReturn(cachedNumbers).when(mCallback).getCapabilitiesFromCache(uriList);

        requestManager.sendCapabilityRequest(uriList, false, mCapabilitiesCallback);
        waitForHandlerAction(handler, 500L);
        // Extract caps from EabCapabilityResult and ensure the Lists match.
        verify(mCapabilitiesCallback).onCapabilitiesReceived(
                Collections.singletonList(cachedItem.getContactCapabilities()));
        verify(mCapabilitiesCallback, never()).onComplete();
        // The cache should have been hit, but there was also entry that was not in the cache, so
        // ensure that is requested.
        verify(mRequestRepository).addRequestCoordinator(any());
    }

    @Test
    @SmallTest
    public void testRequestManagerCallback() throws Exception {