All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.ims.rcs.uce.request.CapabilityRequest Maven / Gradle / Ivy

/*
 * 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.telephony.ims.RcsUceAdapter;
import android.telephony.ims.RcsContactUceCapability;
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;

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

/**
 * The base class of the UCE request to request the capabilities from the carrier network.
 */
public abstract class CapabilityRequest implements UceRequest {

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

    protected final int mSubId;
    protected final long mTaskId;
    protected final List mUriList;
    protected final @UceRequestType int mRequestType;
    protected final RequestManagerCallback mRequestManagerCallback;
    protected final CapabilityRequestResponse mRequestResponse;

    protected volatile long mCoordinatorId;
    protected volatile boolean mIsFinished;
    protected volatile boolean mSkipGettingFromCache;

    public CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback) {
        mSubId = subId;
        mRequestType = type;
        mUriList = new ArrayList<>();
        mRequestManagerCallback = callback;
        mRequestResponse = new CapabilityRequestResponse();
        mTaskId = UceUtils.generateTaskId();
    }

    @VisibleForTesting
    public CapabilityRequest(int subId, @UceRequestType int type, RequestManagerCallback callback,
            CapabilityRequestResponse requestResponse) {
        mSubId = subId;
        mRequestType = type;
        mUriList = new ArrayList<>();
        mRequestManagerCallback = callback;
        mRequestResponse = requestResponse;
        mTaskId = UceUtils.generateTaskId();
    }

    @Override
    public void setRequestCoordinatorId(long coordinatorId) {
        mCoordinatorId = coordinatorId;
    }

    @Override
    public long getRequestCoordinatorId() {
        return mCoordinatorId;
    }

    @Override
    public long getTaskId() {
        return mTaskId;
    }

    @Override
    public void onFinish() {
        mIsFinished = true;
        // Remove the timeout timer of this request
        mRequestManagerCallback.removeRequestTimeoutTimer(mTaskId);
    }

    @Override
    public void setContactUri(List uris) {
        mUriList.addAll(uris);
        mRequestResponse.setRequestContacts(uris);
    }

    public List getContactUri() {
        return Collections.unmodifiableList(mUriList);
    }

    /**
     * Set to check if this request should be getting the capabilities from the cache. The flag is
     * set when the request is triggered by the capability polling service. The contacts from the
     * capability polling service are already expired, skip checking from the cache.
     */
    public void setSkipGettingFromCache(boolean skipFromCache) {
        mSkipGettingFromCache = skipFromCache;
    }

    /**
     * Return if the capabilities request should skip getting from the cache. The flag is set when
     * the request is triggered by the capability polling service and the request doesn't need to
     * check the cache again.
     */
    private boolean isSkipGettingFromCache() {
        return mSkipGettingFromCache;
    }

    /**
     * @return The RequestResponse instance associated with this request.
     */
    public CapabilityRequestResponse getRequestResponse() {
        return mRequestResponse;
    }

    /**
     * Start executing this request.
     */
    @Override
    public void executeRequest() {
        // Return if this request is not allowed to be executed.
        if (!isRequestAllowed()) {
            logd("executeRequest: The request is not allowed.");
            mRequestManagerCallback.notifyRequestError(mCoordinatorId, mTaskId);
            return;
        }

        // Get the contact capabilities from the cache including the expired capabilities.
        final List eabResultList = getCapabilitiesFromCache();

        // Get all the unexpired capabilities from the EAB result list and add to the response.
        final List 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 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 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() || !throttlingUris.isEmpty()) {
            mRequestManagerCallback.notifyCachedCapabilitiesUpdated(mCoordinatorId, mTaskId);
        }

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

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

        // Notify that it doesn't need to request capabilities from the network when all the
        // requested capabilities can be retrieved from cache. Otherwise, it needs to request
        // capabilities from the network for those contacts which cannot retrieve capabilities from
        // the cache.
        if (requestCapUris.isEmpty()) {
            mRequestManagerCallback.notifyNoNeedRequestFromNetwork(mCoordinatorId, mTaskId);
        } else {
            requestCapabilities(requestCapUris);
        }
    }

    // Check whether this request is allowed to be executed or not.
    private boolean isRequestAllowed() {
        if (mUriList == null || mUriList.isEmpty()) {
            logw("isRequestAllowed: uri is empty");
            mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
            return false;
        }

        if (mIsFinished) {
            logw("isRequestAllowed: This request is finished");
            mRequestResponse.setRequestInternalError(RcsUceAdapter.ERROR_GENERIC_FAILURE);
            return false;
        }

        DeviceStateResult deviceStateResult = mRequestManagerCallback.getDeviceState();
        if (deviceStateResult.isRequestForbidden()) {
            logw("isRequestAllowed: The device is disallowed.");
            mRequestResponse.setRequestInternalError(
                    deviceStateResult.getErrorCode().orElse(RcsUceAdapter.ERROR_GENERIC_FAILURE));
            return false;
        }
        return true;
    }

    // Get the cached capabilities by the given request type.
    private List getCapabilitiesFromCache() {
        List resultList = null;
        if (mRequestType == REQUEST_TYPE_CAPABILITY) {
            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.getAvailabilityFromCacheIncludingExpired(uri);
            resultList = new ArrayList<>();
            resultList.add(eabResult);
        }
        if (resultList == null) {
            return Collections.emptyList();
        }
        return resultList;
    }

    /**
     * Get the unexpired contact capabilities from the given EAB result list.
     * @param list the query result from the EAB
     */
    private List getUnexpiredCapabilities(List list) {
        return list.stream()
                .filter(Objects::nonNull)
                .filter(result -> result.getStatus() == EabCapabilityResult.EAB_QUERY_SUCCESSFUL)
                .map(EabCapabilityResult::getContactCapabilities)
                .filter(Objects::nonNull)
                .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.
     */
    private List getRequestingFromNetworkUris(List cachedCapList) {
        return mUriList.stream()
                .filter(uri -> cachedCapList.stream()
                        .noneMatch(cap -> cap.getContactUri().equals(uri)))
                        .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 getRequestingFromNetworkUris(List cachedCapList,
            List 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 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 getFromThrottlingList(final List expiredUris,
            final List eabResultList) {
        List resultList = new ArrayList<>();
        List notFoundFromCacheList = new ArrayList<>();

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

        // For these uris in the throttling list, check whether their capabilities are in the cache.
        List 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.
     */
    protected void setupRequestTimeoutTimer() {
        long timeoutAfterMs = UceUtils.getCapRequestTimeoutAfterMillis();
        logd("setupRequestTimeoutTimer(ms): " + timeoutAfterMs);
        mRequestManagerCallback.setRequestTimeoutTimer(mCoordinatorId, mTaskId, timeoutAfterMs);
    }

    /*
     * Requests capabilities from IMS. The inherited request is required to override this method
     * to define the behavior of requesting capabilities.
     */
    protected abstract void requestCapabilities(List requestCapUris);

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

    protected void logw(String log) {
        Log.w(LOG_TAG, getLogPrefix().append(log).toString());
    }

    protected void logi(String log) {
        Log.i(LOG_TAG, getLogPrefix().append(log).toString());
    }

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy