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

Commit cee4c3af authored by James Lin's avatar James Lin Committed by Gerrit Code Review
Browse files

Merge "[RCS UCE] Implement the SubscribeController for the APIs...

Merge "[RCS UCE] Implement the SubscribeController for the APIs requestCapabilities and requestAvailability"
parents 9d48e878 1b836b46
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -31,8 +31,8 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IPublishResponseCallback;
import android.telephony.ims.aidl.IRcsFeatureListener;
import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.aidl.ISubscribeResponseCallback;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;

import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
@@ -241,10 +241,11 @@ public class RcsFeatureConnection extends FeatureConnection {
        }
    }

    public void requestCapabilities(List<Uri> uris, int taskId) throws RemoteException {
    public void requestCapabilities(List<Uri> uris, ISubscribeResponseCallback c)
            throws RemoteException {
        synchronized (mLock) {
            checkServiceIsReady();
            getServiceInterface(mBinder).requestCapabilities(uris, taskId);
            getServiceInterface(mBinder).subscribeForCapabilities(uris, c);
        }
    }

+4 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IPublishResponseCallback;
import android.telephony.ims.aidl.IRcsFeatureListener;
import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.aidl.ISubscribeResponseCallback;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
@@ -461,8 +462,9 @@ public class RcsFeatureManager implements FeatureUpdates {
        mRcsFeatureConnection.requestPublication(pidfXml, responseCallback);
    }

    public void requestCapabilities(List<Uri> uris, int taskId) throws RemoteException {
        mRcsFeatureConnection.requestCapabilities(uris, taskId);
    public void requestCapabilities(List<Uri> uris, ISubscribeResponseCallback c)
            throws RemoteException {
        mRcsFeatureConnection.requestCapabilities(uris, c);
    }

    /**
+168 −46
Original line number Diff line number Diff line
@@ -44,8 +44,12 @@ import com.android.ims.rcs.uce.presence.publish.PublishController;
import com.android.ims.rcs.uce.presence.publish.PublishControllerImpl;
import com.android.ims.rcs.uce.presence.subscribe.SubscribeController;
import com.android.ims.rcs.uce.presence.subscribe.SubscribeControllerImpl;
import com.android.ims.rcs.uce.request.UceRequestManager;
import com.android.internal.annotations.VisibleForTesting;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;

/**
@@ -59,7 +63,7 @@ public class UceController {

    /**
     * The callback interface is called by the internal controllers to receive information from
     * others controllers
     * others controllers.
     */
    public interface UceControllerCallback {
        /**
@@ -82,6 +86,25 @@ public class UceController {
         */
        RcsContactUceCapability getDeviceCapabilities();

        /**
         * The network reply that the request is forbidden.
         * @param isForbidden If UCE requests are forbidden by the network.
         * @param retryAfterMillis The time to wait for the retry.
         */
        void updateRequestForbidden(boolean isForbidden, long retryAfterMillis);

        /**
         * Check if UCE request is forbidden by the network.
         * @return true when the UCE is forbidden by the network
         */
        boolean isRequestForbiddenByNetwork();

        /**
         * Get the milliseconds need to wait for retry.
         * @return The milliseconds need to wait
         */
        long getRetryAfterMillis();

        /**
         * Trigger the capabilities request with OPTIONS
         */
@@ -109,15 +132,16 @@ public class UceController {
    }

    /**
     * Used to inject RequestTaskManger instances for testing.
     * Used to inject RequestManger instances for testing.
     */
    @VisibleForTesting
    public interface RequestTaskManagerFactory {
        UceRequestTaskManager createTaskManager(Context context, int subId, Looper looper);
    public interface RequestManagerFactory {
        UceRequestManager createRequestManager(Context context, int subId, Looper looper,
                UceControllerCallback callback);
    }

    private RequestTaskManagerFactory mTaskManagerFactory = (context, subId, looper) ->
            new UceRequestTaskManager(context, subId, looper);
    private RequestManagerFactory mRequestManagerFactory = (context, subId, looper, callback) ->
            new UceRequestManager(context, subId, looper, callback);

    /**
     * Used to inject Controller instances for testing.
@@ -133,13 +157,13 @@ public class UceController {
        /**
         * @return an {@link PublishController} associated with the subscription id specified.
         */
        PublishController createPublishController(Context context, int subId, Looper looper);
        PublishController createPublishController(Context context, int subId,
                UceControllerCallback c, Looper looper);

        /**
         * @return an {@link SubscribeController} associated with the subscription id specified.
         */
        SubscribeController createSubscribeController(Context context, int subId,
                UceControllerCallback c, Looper looper);
        SubscribeController createSubscribeController(Context context, int subId);

        /**
         * @return an {@link OptionsController} associated with the subscription id specified.
@@ -157,14 +181,13 @@ public class UceController {

        @Override
        public PublishController createPublishController(Context context, int subId,
                Looper looper) {
            return new PublishControllerImpl(context, subId, looper);
                UceControllerCallback c, Looper looper) {
            return new PublishControllerImpl(context, subId, c, looper);
        }

        @Override
        public SubscribeController createSubscribeController(Context context, int subId,
                UceControllerCallback c, Looper looper) {
            return new SubscribeControllerImpl(context, subId, c, looper);
        public SubscribeController createSubscribeController(Context context, int subId) {
            return new SubscribeControllerImpl(context, subId);
        }

        @Override
@@ -184,28 +207,33 @@ public class UceController {
    private PublishController mPublishController;
    private SubscribeController mSubscribeController;
    private OptionsController mOptionsController;
    private UceRequestTaskManager mTaskManager;
    private UceRequestManager mRequestManager;

    // The server state for UCE requests.
    private ServerState mServerState;

    public UceController(Context context, int subId) {
        mSubId = subId;
        mContext = context;
        mServerState = new ServerState();
        logi("create");

        initLooper();
        initRequestTaskManager();
        initControllers();
        initRequestManager();
    }

    @VisibleForTesting
    public UceController(Context context, int subId, ControllerFactory controllerFactory,
            RequestTaskManagerFactory taskManagerFactory) {
    public UceController(Context context, int subId, ServerState serverState,
            ControllerFactory controllerFactory, RequestManagerFactory requestManagerFactory) {
        mSubId = subId;
        mContext = context;
        mServerState = serverState;
        mControllerFactory = controllerFactory;
        mTaskManagerFactory = taskManagerFactory;
        mRequestManagerFactory = requestManagerFactory;
        initLooper();
        initRequestTaskManager();
        initControllers();
        initRequestManager();
    }

    private void initLooper() {
@@ -215,20 +243,22 @@ public class UceController {
        mLooper = handlerThread.getLooper();
    }

    private void initRequestTaskManager() {
        mTaskManager = mTaskManagerFactory.createTaskManager(mContext, mSubId, mLooper);
    }

    private void initControllers() {
        mEabController = mControllerFactory.createEabController(mContext, mSubId, mCtrlCallback,
                mLooper);
        mPublishController = mControllerFactory.createPublishController(mContext, mSubId, mLooper);
        mSubscribeController = mControllerFactory.createSubscribeController(mContext, mSubId,
        mPublishController = mControllerFactory.createPublishController(mContext, mSubId,
                mCtrlCallback, mLooper);
        mSubscribeController = mControllerFactory.createSubscribeController(mContext, mSubId);
        mOptionsController = mControllerFactory.createOptionsController(mContext, mSubId,
                mCtrlCallback, mLooper);
    }

    private void initRequestManager() {
        mRequestManager = mRequestManagerFactory.createRequestManager(mContext, mSubId, mLooper,
                mCtrlCallback);
        mRequestManager.setSubscribeController(mSubscribeController);
    }

    /**
     * The RcsFeature has been connected to the framework. This method runs on main thread.
     */
@@ -267,12 +297,11 @@ public class UceController {
    public void onDestroy() {
        logi("onDestroy");
        mIsDestroyedFlag = true;
        mTaskManager.onDestroy();
        mRequestManager.onDestroy();
        mEabController.onDestroy();
        mPublishController.onDestroy();
        mSubscribeController.onDestroy();
        mOptionsController.onDestroy();

        mLooper.quit();
    }

@@ -301,6 +330,21 @@ public class UceController {
            return mPublishController.getDeviceCapabilities();
        }

        @Override
        public void updateRequestForbidden(boolean isForbidden, long retryAfterMillis) {
            onRequestForbidden(isForbidden, retryAfterMillis);
        }

        @Override
        public boolean isRequestForbiddenByNetwork() {
            return mServerState.isRequestForbidden();
        }

        @Override
        public long getRetryAfterMillis() {
            return mServerState.getRetryAfterMillis();
        }

        @Override
        public void requestCapabilitiesByOptions(@NonNull Uri uri,
                @NonNull RcsContactUceCapability ownCapabilities,
@@ -359,7 +403,7 @@ public class UceController {
    /**
     * Request to get the contacts' capabilities. This method will retrieve the capabilities from
     * the cache If the capabilities are out of date, it will trigger another request to get the
     * latest contact's capabilities from the carrier network.
     * latest contact's capabilities from the network.
     */
    public void requestCapabilities(@NonNull List<Uri> uriList,
            @NonNull IRcsUceControllerCallback c) throws RemoteException {
@@ -377,20 +421,28 @@ public class UceController {
            return;
        }

        // Check if UCE requests are forbidden by the network.
        if (mServerState.isRequestForbidden()) {
            long retryAfter = mServerState.getRetryAfterMillis();
            logw("requestCapabilities: The request is forbidden, retryAfter=" + retryAfter);
            c.onError(RcsUceAdapter.ERROR_FORBIDDEN, retryAfter);
            return;
        }

        // Trigger the capabilities request task
        logd("requestCapabilities: " + uriList.size());
        mTaskManager.triggerCapabilityRequestTask(mCtrlCallback, uriList, c);
        mRequestManager.sendCapabilityRequest(uriList, c);
    }

    /**
     * Request to get the contact's capabilities. It will check the availability cache first. If
     * the capability in the availability cache is expired then it will retrieve the capability
     * from the carrier network.
     * from the network.
     */
    public void requestAvailability(@NonNull Uri uri, @NonNull IRcsUceControllerCallback c)
            throws RemoteException {
        if (uri == null || c == null) {
            logw("requestCapabilities: parameter is empty");
            logw("requestAvailability: parameter is empty");
            if (c != null) {
                c.onError(RcsUceAdapter.ERROR_GENERIC_FAILURE, 0L);
            }
@@ -403,9 +455,17 @@ public class UceController {
            return;
        }

        // Check if UCE requests are forbidden by the network.
        if (mServerState.isRequestForbidden()) {
            long retryAfter = mServerState.getRetryAfterMillis();
            logw("requestAvailability: The request is forbidden, retryAfter=" + retryAfter);
            c.onError(RcsUceAdapter.ERROR_FORBIDDEN, retryAfter);
            return;
        }

        // Trigger the availability request task
        logd("requestAvailability");
        mTaskManager.triggerAvailabilityRequestTask(mCtrlCallback, uri, c);
        mRequestManager.sendAvailabilityRequest(uri, c);
    }

    /**
@@ -421,7 +481,7 @@ public class UceController {
     * capabilities has been unpublished from the network.
     */
    public void onUnpublish() {
        logd("onUnpublish");
        logi("onUnpublish");
        mPublishController.onUnpublish();
    }

@@ -431,10 +491,18 @@ public class UceController {
     */
    public void retrieveOptionsCapabilitiesForRemote(@NonNull Uri contactUri,
            @NonNull List<String> remoteCapabilities, @NonNull IOptionsRequestCallback c) {
        logd("retrieveOptionsCapabilitiesForRemote");
        logi("retrieveOptionsCapabilitiesForRemote");
        mOptionsController.retrieveCapabilitiesForRemote(contactUri, remoteCapabilities, c);
    }

    /**
     * Update the Request forbidden state.
     */
    public void onRequestForbidden(boolean isForbidden, long retryAfterMillis) {
        logi("onRequestForbidden: forbidden=" + isForbidden + ", retry=" + retryAfterMillis);
        mServerState.forbidUceRequest(isForbidden, retryAfterMillis);
    }

    /**
     * Register a {@link PublishStateCallback} to receive the published state changed.
     */
@@ -456,20 +524,17 @@ public class UceController {
        return mPublishController.getUcePublishState();
    }

    @VisibleForTesting
    public void setRequestTaskManagerFactory(RequestTaskManagerFactory factory) {
        mTaskManagerFactory = factory;
    }

    @VisibleForTesting
    public void setControllerFactory(ControllerFactory factory) {
        mControllerFactory = factory;
    }

    /**
     * Get the subscription ID.
     */
    public int getSubId() {
        return mSubId;
    }

    /**
     * Check if the UceController is available.
     * @return true if the RCS is connected without destroyed.
     */
    public boolean isUnavailable() {
        if (!mIsRcsConnected || mIsDestroyedFlag) {
            return true;
@@ -477,6 +542,63 @@ public class UceController {
        return false;
    }

    /**
     * The internal class to store the server state which sent from the network. It will help to
     * check if the network allows the UCE request.
     */
    @VisibleForTesting
    public static class ServerState {
        // If UCE requests are forbidden by the network.
        private boolean mIsForbidden;

        // The timestamp when the network allows the UCE requests. This value may be null if the
        // network doesn't specified any retryAfter info.
        private Instant mAllowedTimestamp;

        private final Object mNetworkStateLock = new Object();

        public ServerState() {
            mIsForbidden = false;
            mAllowedTimestamp = null;
        }

        public void forbidUceRequest(boolean isForbidden, long retryAfterMillis) {
            synchronized (mNetworkStateLock) {
                mIsForbidden = isForbidden;
                if (!mIsForbidden) {
                    mAllowedTimestamp = null;
                } else {
                    mAllowedTimestamp = Instant.now().plus(retryAfterMillis, ChronoUnit.MILLIS);
                }
                Log.d(LOG_TAG, "forbidUceRequest: " + mIsForbidden
                        + ", time=" + mAllowedTimestamp);
            }
        }

        public boolean isRequestForbidden() {
            synchronized (mNetworkStateLock) {
                if (mIsForbidden && mAllowedTimestamp != null) {
                    return Instant.now().isBefore(mAllowedTimestamp);
                }
                return mIsForbidden;
            }
        }

        public long getRetryAfterMillis() {
            synchronized (mNetworkStateLock) {
                if (!mIsForbidden || mAllowedTimestamp == null) {
                    return 0L;
                }
                Duration duration = Duration.between(Instant.now(), mAllowedTimestamp);
                long retryAfterMillis = duration.toMillis();
                if (retryAfterMillis < 0) {
                    return 0L;
                }
                return retryAfterMillis;
            }
        }
    }

    private void logd(String log) {
        Log.d(LOG_TAG, getLogPrefix().append(log).toString());
    }
+0 −64
Original line number Diff line number Diff line
/*
 * 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 com.android.ims.rcs.uce;

import android.content.Context;
import android.net.Uri;
import android.os.Looper;
import android.telephony.ims.aidl.IRcsUceControllerCallback;

import com.android.ims.rcs.uce.UceController.UceControllerCallback;

import java.util.List;

/**
 * The interface of managing the capability request and the availability request.
 */
public class UceRequestTaskManager {

    private final Context mContext;
    private final int mSubId;
    private final Looper mLooper;

    public UceRequestTaskManager(Context context, int subId, Looper looper) {
        mContext = context;
        mSubId = subId;
        mLooper = looper;
    }

    /**
     * Trigger the capability request task.
     */
    public void triggerCapabilityRequestTask(UceControllerCallback controller, List<Uri> uriList,
            IRcsUceControllerCallback callback) {
        // TODO: Implement this method
    }
    /**
     * Trigger the availability request task.
     */
    public void triggerAvailabilityRequestTask(UceControllerCallback controller, Uri uri,
            IRcsUceControllerCallback callback) {
        // TODO: Implement this method
    }

    /**
     * Notify the task manager to destroy.
     */
    public void onDestroy() {
        // TODO: Implement this method
    }
}
+26 −0
Original line number Diff line number Diff line
@@ -38,9 +38,19 @@ import com.android.ims.rcs.uce.presence.pidfparser.pidf.Status;
import com.android.ims.rcs.uce.presence.pidfparser.pidf.Timestamp;
import com.android.ims.rcs.uce.presence.pidfparser.pidf.Tuple;

import java.util.Arrays;
import java.util.List;

/**
 * The utils to help the PIDF parsing process.
 */
public class PidfParserUtils {

    /*
     * The resource terminated reason with NOT FOUND
     */
    private static String[] REQUEST_RESULT_REASON_NOT_FOUND = { "noresource", "rejected" };

    /**
     * Convert the giving class RcsContactUceCapability to the class Presence.
     */
@@ -270,4 +280,20 @@ public class PidfParserUtils {
        }
        return null;
    }

    /**
     * Get the terminated capability which disable all the capabilities.
     */
    public static RcsContactUceCapability getTerminatedCapability(Uri contact, String reason) {
        if (reason == null) reason = "";
        int requestResult = (Arrays.stream(REQUEST_RESULT_REASON_NOT_FOUND)
                    .anyMatch(reason::equalsIgnoreCase) == true) ?
                            RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND :
                                    RcsContactUceCapability.REQUEST_RESULT_UNKNOWN;

        RcsContactUceCapability.PresenceBuilder builder =
                new RcsContactUceCapability.PresenceBuilder(
                        contact, RcsContactUceCapability.SOURCE_TYPE_NETWORK, requestResult);
        return builder.build();
    }
}
Loading