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

Commit b6cf2533 authored by James.cf Lin's avatar James.cf Lin Committed by James Lin
Browse files

Use expired cache infomation instead if the result of the subscription request inconclusive

When the cnontact capability request result is unsuccessful and not a 404 error, the framework should use cached information even if the capabilities have expired. If the capability doesn't exist, use non-RCS result instead.

Bug: 204427048
Test: atest RcsUceAdapterTest --iterations 3
Change-Id: I90ca67b682807db4501bc36b3658a23f85912bb5
Merged-In: I90ca67b682807db4501bc36b3658a23f85912bb5
parent 99991f49
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -79,11 +79,23 @@ public class UceController {
         */
        List<EabCapabilityResult> getCapabilitiesFromCache(@NonNull List<Uri> uris);

        /**
         * Retrieve the capabilities associated with the given uris from the cache including
         * expired capabilities.
         */
        List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(@NonNull List<Uri> uris);

        /**
         * Retrieve the contact's capabilities from the availability cache.
         */
        EabCapabilityResult getAvailabilityFromCache(@NonNull Uri uri);

        /**
         * Retrieve the contact's capabilities from the availability cache including expired
         * capabilities
         */
        EabCapabilityResult getAvailabilityFromCacheIncludingExpired(@NonNull Uri uri);

        /**
         * Store the given capabilities to the cache.
         */
@@ -475,11 +487,21 @@ public class UceController {
            return mEabController.getCapabilities(uris);
        }

        @Override
        public List<EabCapabilityResult> getCapabilitiesFromCacheIncludingExpired(List<Uri> uris) {
            return mEabController.getCapabilitiesIncludingExpired(uris);
        }

        @Override
        public EabCapabilityResult getAvailabilityFromCache(Uri contactUri) {
            return mEabController.getAvailability(contactUri);
        }

        @Override
        public EabCapabilityResult getAvailabilityFromCacheIncludingExpired(Uri contactUri) {
            return mEabController.getAvailabilityIncludingExpired(contactUri);
        }

        @Override
        public void saveCapabilities(List<RcsContactUceCapability> contactCapabilities) {
            mEabController.saveCapabilities(contactCapabilities);
@@ -755,6 +777,7 @@ public class UceController {
    public void removeRequestDisallowedStatus() {
        logd("removeRequestDisallowedStatus");
        mDeviceState.resetDeviceState();
        mRequestManager.resetThrottlingList();
    }

    /**
+12 −0
Original line number Diff line number Diff line
@@ -34,11 +34,23 @@ public interface EabController extends ControllerBase {
     */
    @NonNull List<EabCapabilityResult> getCapabilities(@NonNull List<Uri> uris);

    /**
     * Get contact capabilities from cache including expired capabilities.
     * @param uris the uri list to get contact capabilities from cache.
     * @return The contact capabilities of the given uri list.
     */
    @NonNull List<EabCapabilityResult> getCapabilitiesIncludingExpired(@NonNull List<Uri> uris);

    /**
     * Retrieve the contact's capabilities from the availability cache.
     */
    @NonNull EabCapabilityResult getAvailability(@NonNull Uri contactUri);

    /**
     * Retrieve the contact's capabilities from the availability cache.
     */
    @NonNull EabCapabilityResult getAvailabilityIncludingExpired(@NonNull Uri contactUri);

    /**
     * Save the capabilities to the EAB database.
     */
+87 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;

/**
@@ -162,6 +163,29 @@ public class EabControllerImpl implements EabController {
        return capabilityResultList;
    }

    /**
     * Retrieve the contacts' capabilities from the EAB database including expired capabilities.
     */
    @Override
    public @NonNull List<EabCapabilityResult> getCapabilitiesIncludingExpired(
            @NonNull List<Uri> uris) {
        Objects.requireNonNull(uris);
        if (mIsSetDestroyedFlag) {
            Log.d(TAG, "EabController destroyed.");
            return generateDestroyedResult(uris);
        }

        Log.d(TAG, "getCapabilitiesIncludingExpired uri size=" + uris.size());
        List<EabCapabilityResult> capabilityResultList = new ArrayList();

        for (Uri uri : uris) {
            EabCapabilityResult result = generateEabResultIncludingExpired(uri,
                    this::isCapabilityExpired);
            capabilityResultList.add(result);
        }
        return capabilityResultList;
    }

    /**
     * Retrieve the contact's capabilities from the availability cache.
     */
@@ -178,6 +202,23 @@ public class EabControllerImpl implements EabController {
        return generateEabResult(contactUri, this::isAvailabilityExpired);
    }

    /**
     * Retrieve the contact's capabilities from the availability cache including expired
     * capabilities.
     */
    @Override
    public @NonNull EabCapabilityResult getAvailabilityIncludingExpired(@NonNull Uri contactUri) {
        Objects.requireNonNull(contactUri);
        if (mIsSetDestroyedFlag) {
            Log.d(TAG, "EabController destroyed.");
            return new EabCapabilityResult(
                contactUri,
                EabCapabilityResult.EAB_CONTROLLER_DESTROYED_FAILURE,
                null);
        }
        return generateEabResultIncludingExpired(contactUri, this::isAvailabilityExpired);
    }

    /**
     * Update the availability catch and save the capabilities to the EAB database.
     */
@@ -296,6 +337,52 @@ public class EabControllerImpl implements EabController {
        return result;
    }

    private EabCapabilityResult generateEabResultIncludingExpired(Uri contactUri,
            Predicate<Cursor> isExpiredMethod) {
        RcsUceCapabilityBuilderWrapper builder = null;
        EabCapabilityResult result;
        Optional<Boolean> isExpired = Optional.empty();

        // query EAB provider
        Uri queryUri = Uri.withAppendedPath(
                Uri.withAppendedPath(EabProvider.ALL_DATA_URI, String.valueOf(mSubId)),
                getNumberFromUri(mContext, contactUri));
        Cursor cursor = mContext.getContentResolver().query(queryUri, null, null, null, null);

        if (cursor != null && cursor.getCount() != 0) {
            while (cursor.moveToNext()) {
                // Record whether it has expired.
                if (!isExpired.isPresent()) {
                    isExpired = Optional.of(isExpiredMethod.test(cursor));
                }
                if (builder == null) {
                    builder = createNewBuilder(contactUri, cursor);
                } else {
                    updateCapability(contactUri, cursor, builder);
                }
            }
            cursor.close();

            // Determine the query result
            int eabResult = EabCapabilityResult.EAB_QUERY_SUCCESSFUL;
            if (isExpired.orElse(false)) {
                eabResult = EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE;
            }

            if (builder.getMechanism() == CAPABILITY_MECHANISM_PRESENCE) {
                PresenceBuilder presenceBuilder = builder.getPresenceBuilder();
                result = new EabCapabilityResult(contactUri, eabResult, presenceBuilder.build());
            } else {
                OptionsBuilder optionsBuilder = builder.getOptionsBuilder();
                result = new EabCapabilityResult(contactUri, eabResult, optionsBuilder.build());
            }
        } else {
            result = new EabCapabilityResult(contactUri,
                    EabCapabilityResult.EAB_CONTACT_NOT_FOUND_FAILURE, null);
        }
        return result;
    }

    private void updateCapability(Uri contactUri, Cursor cursor,
                RcsUceCapabilityBuilderWrapper builderWrapper) {
        if (builderWrapper.getMechanism() == CAPABILITY_MECHANISM_PRESENCE) {
+96 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.util.Log;

import com.android.ims.rcs.uce.UceDeviceState.DeviceStateResult;
import com.android.ims.rcs.uce.eab.EabCapabilityResult;
import com.android.ims.rcs.uce.presence.pidfparser.PidfParserUtils;
import com.android.ims.rcs.uce.request.UceRequestManager.RequestManagerCallback;
import com.android.ims.rcs.uce.util.UceUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -140,20 +141,36 @@ public abstract class CapabilityRequest implements UceRequest {
            return;
        }

        // Get the capabilities from the cache.
        final List<RcsContactUceCapability> cachedCapList
                = isSkipGettingFromCache() ? Collections.EMPTY_LIST : getCapabilitiesFromCache();
        // Get the contact capabilities from the cache including the expired capabilities.
        final List<EabCapabilityResult> eabResultList = getCapabilitiesFromCache();

        // Get all the unexpired capabilities from the EAB result list and add to the response.
        final List<RcsContactUceCapability> cachedCapList = isSkipGettingFromCache() ?
                Collections.EMPTY_LIST : getUnexpiredCapabilities(eabResultList);
        mRequestResponse.addCachedCapabilities(cachedCapList);

        logd("executeRequest: cached capabilities size=" + cachedCapList.size());

        // Get the rest contacts which are not in the cache or has expired.
        final List<Uri> expiredUris = getRequestingFromNetworkUris(cachedCapList);

        // For those uris that are not in the cache or have expired, we should request their
        // capabilities from the network. However, we still need to check whether these contacts
        // are in the throttling list. If the contact is in the throttling list, even if it has
        // expired, we will get the cached capabilities.
        final List<RcsContactUceCapability> throttlingUris =
                getFromThrottlingList(expiredUris, eabResultList);
        mRequestResponse.addCachedCapabilities(throttlingUris);

        logd("executeRequest: contacts in throttling list size=" + throttlingUris.size());

        // Notify that the cached capabilities are updated.
        if (!cachedCapList.isEmpty()) {
        if (!cachedCapList.isEmpty() || !throttlingUris.isEmpty()) {
            mRequestManagerCallback.notifyCachedCapabilitiesUpdated(mCoordinatorId, mTaskId);
        }

        // Get the rest contacts which need to request capabilities from the network.
        final List<Uri> requestCapUris = getRequestingFromNetworkUris(cachedCapList);
        List<Uri> requestCapUris = getRequestingFromNetworkUris(cachedCapList, throttlingUris);

        logd("executeRequest: requestCapUris size=" + requestCapUris.size());

@@ -193,21 +210,30 @@ public abstract class CapabilityRequest implements UceRequest {
    }

    // Get the cached capabilities by the given request type.
    private List<RcsContactUceCapability> getCapabilitiesFromCache() {
    private List<EabCapabilityResult> getCapabilitiesFromCache() {
        List<EabCapabilityResult> resultList = null;
        if (mRequestType == REQUEST_TYPE_CAPABILITY) {
            resultList = mRequestManagerCallback.getCapabilitiesFromCache(mUriList);
            resultList = mRequestManagerCallback.getCapabilitiesFromCacheIncludingExpired(mUriList);
        } else if (mRequestType == REQUEST_TYPE_AVAILABILITY) {
            // Always get the first element if the request type is availability.
            Uri uri = mUriList.get(0);
            EabCapabilityResult eabResult = mRequestManagerCallback.getAvailabilityFromCache(uri);
            EabCapabilityResult eabResult =
                    mRequestManagerCallback.getAvailabilityFromCacheIncludingExpired(uri);
            resultList = new ArrayList<>();
            resultList.add(eabResult);
        }
        if (resultList == null) {
            return Collections.emptyList();
        }
        return resultList.stream()
        return resultList;
    }

    /**
     * Get the unexpired contact capabilities from the given EAB result list.
     * @param list the query result from the EAB
     */
    private List<RcsContactUceCapability> getUnexpiredCapabilities(List<EabCapabilityResult> list) {
        return list.stream()
                .filter(Objects::nonNull)
                .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL)
                .map(EabCapabilityResult::getContactCapabilities)
@@ -226,6 +252,67 @@ public abstract class CapabilityRequest implements UceRequest {
                        .collect(Collectors.toList());
    }

    /**
     * Get the contact uris which cannot retrieve capabilities from the cache.
     * @param cachedCapList The capabilities which are already stored in the cache.
     * @param throttlingUris The capabilities which are in the throttling list.
     */
    private List<Uri> getRequestingFromNetworkUris(List<RcsContactUceCapability> cachedCapList,
            List<RcsContactUceCapability> throttlingUris) {
        // We won't request the network query for those contacts in the cache and in the
        // throttling list. Merging the two list and get the rest contact uris.
        List<RcsContactUceCapability> notNetworkQueryList = new ArrayList<>(cachedCapList);
        notNetworkQueryList.addAll(throttlingUris);
        return getRequestingFromNetworkUris(notNetworkQueryList);
    }

    /**
     * Get the contact capabilities for those uri are in the throttling list. If the contact uri is
     * in the throttling list, the capabilities will be retrieved from cache even if it has expired.
     * If the capabilities cannot be found, return the non-RCS contact capabilities instead.
     * @param expiredUris the expired/unknown uris to check whether are in the throttling list
     * @return the contact capabilities for the uris are in the throttling list
     */
    private List<RcsContactUceCapability> getFromThrottlingList(final List<Uri> expiredUris,
            final List<EabCapabilityResult> eabResultList) {
        List<RcsContactUceCapability> resultList = new ArrayList<>();
        List<RcsContactUceCapability> notFoundFromCacheList = new ArrayList<>();

        // Retrieve the uris put in the throttling list from the expired/unknown contacts.
        List<Uri> throttlingUris = mRequestManagerCallback.getInThrottlingListUris(expiredUris);

        // For these uris in the throttling list, check whether their capabilities are in the cache.
        List<EabCapabilityResult> throttlingUriFoundInEab = new ArrayList<>();
        for (Uri uri : throttlingUris) {
            for (EabCapabilityResult eabResult : eabResultList) {
                if (eabResult.getContact().equals(uri)) {
                    throttlingUriFoundInEab.add(eabResult);
                    break;
                }
            }
        }

        throttlingUriFoundInEab.forEach(eabResult -> {
            if (eabResult.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL ||
                eabResult.getStatus() == EabCapabilityResult.EAB_CONTACT_EXPIRED_FAILURE) {
                // The capabilities are found, add to the result list
                resultList.add(eabResult.getContactCapabilities());
            } else {
                // Cannot get the capabilities from cache, create the non-RCS capabilities instead.
                notFoundFromCacheList.add(PidfParserUtils.getNotFoundContactCapabilities(
                        eabResult.getContact()));
            }
        });

        if (!notFoundFromCacheList.isEmpty()) {
            resultList.addAll(notFoundFromCacheList);
        }

        logd("getFromThrottlingList: requesting uris in the list size=" + throttlingUris.size() +
                ", generate non-RCS size=" + notFoundFromCacheList.size());
        return resultList;
    }

    /**
     * Set the timeout timer of this request.
     */
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.ims.rcs.uce.request;

import android.net.Uri;
import android.util.Log;

import com.android.ims.rcs.uce.util.UceUtils;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * The class is used to store when the contact's capabilities request result is inconclusive.
 */
public class ContactThrottlingList {
    private static final String LOG_TAG = UceUtils.getLogPrefix() + "ThrottlingList";

    private static class ContactInfo {
        Uri mContactUri;
        int mSipCode;
        Instant mThrottleEndTimestamp;

        public ContactInfo(Uri contactUri, int sipCode, Instant timestamp) {
            mContactUri = contactUri;
            mSipCode = sipCode;
            mThrottleEndTimestamp = timestamp;
        }
    }

    private final int mSubId;
    private final List<ContactInfo> mThrottlingList = new ArrayList<>();

    public ContactThrottlingList(int subId) {
        mSubId = subId;
    }

    public synchronized void reset() {
        mThrottlingList.clear();
    }

    public synchronized void addToThrottlingList(List<Uri> uriList, int sipCode) {
        // Clean up the expired contacts before starting.
        cleanUpExpiredContacts();

        List<Uri> addToThrottlingList = getNotInThrottlingListUris(uriList);
        long expiration = UceUtils.getAvailabilityCacheExpiration(mSubId);
        Instant timestamp = Instant.now().plusSeconds(expiration);

        List<ContactInfo> list = addToThrottlingList.stream().map(uri ->
                new ContactInfo(uri, sipCode, timestamp)).collect(Collectors.toList());

        int previousSize = mThrottlingList.size();
        mThrottlingList.addAll(list);

        logd("addToThrottlingList: previous size=" + previousSize +
                ", current size=" + mThrottlingList.size() + ", expired time=" + timestamp);
    }

    private synchronized List<Uri> getNotInThrottlingListUris(List<Uri> uriList) {
        List<Uri> throttlingUris = mThrottlingList.stream().map(contactInfo ->
                contactInfo.mContactUri).collect(Collectors.toList());
        List<Uri> addToThrottlingUris = new ArrayList<>(uriList);
        addToThrottlingUris.removeAll(throttlingUris);
        return addToThrottlingUris;
    }

    public synchronized List<Uri> getInThrottlingListUris(List<Uri> uriList) {
        // Clean up the expired contacts before starting.
        cleanUpExpiredContacts();

        return uriList.stream()
                .filter(uri -> mThrottlingList.stream()
                    .anyMatch(contactInfo -> contactInfo.mContactUri.equals(uri)))
                    .collect(Collectors.toList());
    }

    /**
     * Clean up the expired contacts from the throttling list.
     */
    private synchronized void cleanUpExpiredContacts() {
        final int previousSize = mThrottlingList.size();
        List<ContactInfo> expiredContacts = mThrottlingList.stream()
                .filter(contactInfo -> Instant.now()
                        .isAfter(contactInfo.mThrottleEndTimestamp))
                        .collect(Collectors.toList());
        mThrottlingList.removeAll(expiredContacts);

        logd("cleanUpExpiredContacts: previous size=" + previousSize +
                ", current size=" + mThrottlingList.size());
    }

    private void logd(String log) {
        Log.d(LOG_TAG, getLogPrefix().append(log).toString());
    }

    private StringBuilder getLogPrefix() {
        StringBuilder builder = new StringBuilder("[");
        builder.append(mSubId);
        builder.append("] ");
        return builder;
    }
}
Loading