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

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

[RCS UCE] Implement the device capabilities publishing.

Bug: 170476615
Test: atest
Change-Id: I87c4ed63a96aec6e64c473c7954257fb68b58589
parent e482d456
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -32,8 +32,10 @@ import android.telephony.ims.aidl.IRcsFeatureListener;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.ImsFeature;


import com.android.ims.rcs.uce.RcsCapabilityExchangeImplAdapter.PublishResponseCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
import com.android.telephony.Rlog;

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


/**
/**
@@ -222,6 +224,11 @@ public class RcsFeatureConnection extends FeatureConnection {
        }
        }
    }
    }


    public void requestPublication(String pidfXml, PublishResponseCallback responseCallback)
            throws RemoteException {
        // TODO: add the new API: requestPublication
    }

    public void requestCapabilities(List<Uri> uris, int taskId) throws RemoteException {
    public void requestCapabilities(List<Uri> uris, int taskId) throws RemoteException {
        synchronized (mLock) {
        synchronized (mLock) {
            checkServiceIsReady();
            checkServiceIsReady();
+6 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ import android.telephony.ims.stub.RcsSipOptionsImplBase;
import android.util.Log;
import android.util.Log;


import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.ims.rcs.uce.RcsCapabilityExchangeImplAdapter.PublishResponseCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephony;
import com.android.telephony.Rlog;
import com.android.telephony.Rlog;
@@ -430,6 +431,11 @@ public class RcsFeatureManager implements FeatureUpdates {
        mRcsFeatureConnection.requestPublication(capabilities, taskId);
        mRcsFeatureConnection.requestPublication(capabilities, taskId);
    }
    }


    public void requestPublication(String pidfXml, PublishResponseCallback responseCallback)
            throws RemoteException {
        mRcsFeatureConnection.requestPublication(pidfXml, responseCallback);
    }

    public void requestCapabilities(List<Uri> uris, int taskId) throws RemoteException {
    public void requestCapabilities(List<Uri> uris, int taskId) throws RemoteException {
        mRcsFeatureConnection.requestCapabilities(uris, taskId);
        mRcsFeatureConnection.requestCapabilities(uris, taskId);
    }
    }
+21 −0
Original line number Original line Diff line number Diff line
@@ -94,6 +94,27 @@ public interface PublishController extends ControllerBase {
         */
         */
        void requestPublishFromInternal(@PublishTriggerType int type, long delay);
        void requestPublishFromInternal(@PublishTriggerType int type, long delay);


        /**
         * Receive the command error callback of the request from ImsService.
         */
        void onRequestCommandError(PublishRequestResponse requestResponse);

        /**
         * Receive the network response callback fo the request from ImsService.
         */
        void onRequestNetworkResp(PublishRequestResponse requestResponse);

        /**
         * Set the timer to cancel the request. This timer is to prevent taking too long for
         * waiting the response callback.
         */
        void setupRequestCanceledTimer(long taskId, long delay);

        /**
         * Clear the request canceled timer. This api will be called if the request is finished.
         */
        void clearRequestCanceledTimer();

        /**
        /**
         * Update the publish request result.
         * Update the publish request result.
         */
         */
+80 −1
Original line number Original line Diff line number Diff line
@@ -212,6 +212,30 @@ public class PublishControllerImpl implements PublishController {
                    mPublishHandler.requestPublish(type, delay);
                    mPublishHandler.requestPublish(type, delay);
                }
                }


                @Override
                public void onRequestCommandError(PublishRequestResponse requestResponse) {
                    logd("onRequestCommandError: taskId=" + requestResponse.getTaskId());
                    mPublishHandler.onRequestCommandError(requestResponse);
                }

                @Override
                public void onRequestNetworkResp(PublishRequestResponse requestResponse) {
                    logd("onRequestNetworkResp: taskId=" + requestResponse.getTaskId());
                    mPublishHandler.onRequestNetworkResponse(requestResponse);
                }

                @Override
                public void setupRequestCanceledTimer(long taskId, long delay) {
                    logd("setupRequestCanceledTimer: taskId=" + taskId + ", delay=" + delay);
                    mPublishHandler.setRequestCanceledTimer(taskId, delay);
                }

                @Override
                public void clearRequestCanceledTimer() {
                    logd("clearRequestCanceledTimer");
                    mPublishHandler.clearRequestCanceledTimer();
                }

                @Override
                @Override
                public void updatePublishRequestResult(@PublishState int publishState) {
                public void updatePublishRequestResult(@PublishState int publishState) {
                    logd("updatePublishRequestResult: " + publishState);
                    logd("updatePublishRequestResult: " + publishState);
@@ -231,6 +255,9 @@ public class PublishControllerImpl implements PublishController {
    private class PublishHandler extends Handler {
    private class PublishHandler extends Handler {
        private static final int MSG_PUBLISH_STATE_CHANGED = 1;
        private static final int MSG_PUBLISH_STATE_CHANGED = 1;
        private static final int MSG_REQUEST_PUBLISH = 2;
        private static final int MSG_REQUEST_PUBLISH = 2;
        private static final int MSG_REQUEST_CMD_ERROR = 3;
        private static final int MSG_REQUEST_SIP_RESPONSE = 4;
        private static final int MSG_REQUEST_CANCELED = 5;


        public PublishHandler(Looper looper) {
        public PublishHandler(Looper looper) {
            super(looper);
            super(looper);
@@ -250,6 +277,21 @@ public class PublishControllerImpl implements PublishController {
                    int type = (Integer) message.obj;
                    int type = (Integer) message.obj;
                    handleRequestPublishMessage(type);
                    handleRequestPublishMessage(type);
                    break;
                    break;

                case MSG_REQUEST_CMD_ERROR:
                    PublishRequestResponse cmdErrorResponse = (PublishRequestResponse) message.obj;
                    mPublishProcessor.onCommandError(cmdErrorResponse);
                    break;

                case MSG_REQUEST_SIP_RESPONSE:
                    PublishRequestResponse networkResponse = (PublishRequestResponse) message.obj;
                    mPublishProcessor.onNetworkResponse(networkResponse);
                    break;

                case MSG_REQUEST_CANCELED:
                    long taskId = (Long) message.obj;
                    handleRequestCanceledMessage(taskId);
                    break;
            }
            }
        }
        }


@@ -297,6 +339,39 @@ public class PublishControllerImpl implements PublishController {
                sendMessage(message);
                sendMessage(message);
            }
            }
        }
        }

        public void onRequestCommandError(PublishRequestResponse requestResponse) {
            if (mIsDestroyedFlag) return;

            Message message = obtainMessage();
            message.what = MSG_REQUEST_CMD_ERROR;
            message.obj = requestResponse;
            sendMessage(message);
        }

        public void onRequestNetworkResponse(PublishRequestResponse requestResponse) {
            if (mIsDestroyedFlag) return;

            Message message = obtainMessage();
            message.what = MSG_REQUEST_SIP_RESPONSE;
            message.obj = requestResponse;
            sendMessage(message);
        }

        public void setRequestCanceledTimer(long taskId, long delay) {
            if (mIsDestroyedFlag) return;
            removeMessages(MSG_REQUEST_CANCELED, (Long) taskId);

            Message message = obtainMessage();
            message.what = MSG_REQUEST_CANCELED;
            message.obj = (Long) taskId;
            sendMessageDelayed(message, delay);
        }

        public void clearRequestCanceledTimer() {
            if (mIsDestroyedFlag) return;
            removeMessages(MSG_REQUEST_CANCELED);
        }
    }
    }


    /**
    /**
@@ -325,10 +400,14 @@ public class PublishControllerImpl implements PublishController {


    public void handleRequestPublishMessage(@PublishTriggerType int type) {
    public void handleRequestPublishMessage(@PublishTriggerType int type) {
        if (mIsDestroyedFlag) return;
        if (mIsDestroyedFlag) return;
        logd("handleRequestPublishMessage");
        mPublishProcessor.doPublish(type);
        mPublishProcessor.doPublish(type);
    }
    }


    public void handleRequestCanceledMessage(long taskId) {
        if (mIsDestroyedFlag) return;
        mPublishProcessor.cancelPublishRequest(taskId);
    }

    @VisibleForTesting
    @VisibleForTesting
    public void setPublishStateCallback(RemoteCallbackList<IRcsUcePublishStateCallback> list) {
    public void setPublishStateCallback(RemoteCallbackList<IRcsUcePublishStateCallback> list) {
        mPublishStateCallbacks = list;
        mPublishStateCallbacks = list;
+369 −5
Original line number Original line Diff line number Diff line
@@ -17,27 +17,81 @@
package com.android.ims.rcs.uce.presence.publish;
package com.android.ims.rcs.uce.presence.publish;


import android.content.Context;
import android.content.Context;
import android.os.RemoteException;
import android.telephony.ims.RcsContactUceCapability;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;


import com.android.ims.RcsFeatureManager;
import com.android.ims.RcsFeatureManager;
import com.android.ims.rcs.uce.presence.pidfparser.PidfParser;
import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
import com.android.ims.rcs.uce.presence.publish.PublishController.PublishControllerCallback;
import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType;
import com.android.ims.rcs.uce.presence.publish.PublishController.PublishTriggerType;
import com.android.ims.rcs.uce.util.UceUtils;
import com.android.internal.annotations.VisibleForTesting;


import java.io.PrintWriter;

/**
 * Send the publish request and handle the response of the publish request result.
 */
public class PublishProcessor {
public class PublishProcessor {


    private static final String LOG_TAG = "PublishProcessor";

    // The length of time waiting for the response callback.
    private static final long RESPONSE_CALLBACK_WAITING_TIME = 60000L;

    private final int mSubId;
    private final Context mContext;
    private volatile boolean mIsDestroyed;
    private volatile RcsFeatureManager mRcsFeatureManager;

    // Manage the state of the publish processor.
    private PublishProcessorState mProcessorState;

    // The information of the device's capabilities.
    private DeviceCapabilityInfo mDeviceCapabilities;

    // The callback of the PublishController
    private PublishControllerCallback mPublishCtrlCallback;

    private final LocalLog mLocalLog = new LocalLog(20);

    public PublishProcessor(Context context, int subId, DeviceCapabilityInfo capabilityInfo,
    public PublishProcessor(Context context, int subId, DeviceCapabilityInfo capabilityInfo,
            PublishControllerCallback callback) {
            PublishControllerCallback publishCtrlCallback) {
        mSubId = subId;
        mContext = context;
        mDeviceCapabilities = capabilityInfo;
        mPublishCtrlCallback = publishCtrlCallback;
        mProcessorState = new PublishProcessorState();
    }
    }


    /**
     * The RcsFeature has been connected to the framework.
     */
    public void onRcsConnected(RcsFeatureManager featureManager) {
    public void onRcsConnected(RcsFeatureManager featureManager) {
        // TODO: Implement this method
        mLocalLog.log("onRcsConnected");
        logi("onRcsConnected");
        mRcsFeatureManager = featureManager;
    }
    }


    /**
     * The framework has lost the binding to the RcsFeature.
     */
    public void onRcsDisconnected() {
    public void onRcsDisconnected() {
        // TODO: Implement this method
        mLocalLog.log("onRcsDisconnected");
        logi("onRcsDisconnected");
        mRcsFeatureManager = null;
    }
    }


    /**
     * Set the destroy flag
     */
    public void onDestroy() {
    public void onDestroy() {
        // TODO: Implement this method
        mLocalLog.log("onDestroy");
        logi("onDestroy");
        mIsDestroyed = true;
    }
    }


    /**
    /**
@@ -45,6 +99,316 @@ public class PublishProcessor {
     * @param triggerType The type of triggering the publish request.
     * @param triggerType The type of triggering the publish request.
     */
     */
    public void doPublish(@PublishTriggerType int triggerType) {
    public void doPublish(@PublishTriggerType int triggerType) {
        // TODO: Implement this method
        if (mIsDestroyed) return;

        mLocalLog.log("doPublish: trigger type=" + triggerType);
        logi("doPublish: trigger type=" + triggerType);

        // Check if it should reset the retry count.
        if (isResetRetryNeeded(triggerType)) {
            mProcessorState.resetRetryCount();
        }

        // Return if this request is not allowed to execute.
        if (!isRequestAllowed()) {
            mLocalLog.log("doPublish: The request is not allowed");
            return;
        }

        // Get the latest device's capabilities.
        RcsContactUceCapability deviceCapability =
                mDeviceCapabilities.getDeviceCapabilities(mContext);
        if (deviceCapability == null) {
            logw("doPublish: device capability is null");
            return;
        }

        // Convert the device's capabilities to pidf format.
        String pidfXml = PidfParser.convertToPidf(deviceCapability);
        if (TextUtils.isEmpty(pidfXml)) {
            logw("doPublish: pidfXml is empty");
            return;
        }

        // Publish to the Presence server.
        publishCapabilities(pidfXml);
    }

    // Check if the giving trigger type should reset the retry count.
    private boolean isResetRetryNeeded(@PublishTriggerType int triggerType) {
        // Do no reset the retry count if the request is triggered by the previous failed retry.
        if (triggerType == PublishController.PUBLISH_TRIGGER_RETRY) {
            return false;
        }
        return true;
    }

    // Check if the publish request is allowed to execute.
    private boolean isRequestAllowed() {
        // Check if the instance is destroyed.
        if (mIsDestroyed) {
            logd("isPublishAllowed: This instance is already destroyed");
            return false;
        }

        // Check if it has provisioned. When the provisioning changes, a new publish request will
        // be triggered.
        if (!UceUtils.isEabProvisioned(mContext, mSubId)) {
            logd("isPublishAllowed: NOT provisioned");
            return false;
        }

        // Do not request publish if the IMS is not registered. When the IMS is registered
        // afterward, a new publish request will be triggered.
        if (!mDeviceCapabilities.isImsRegistered()) {
            logd("isPublishAllowed: IMS is not registered");
            return false;
        }

        // Set the pending flag if there's already a request running now.
        if (mProcessorState.isPublishingNow()) {
            logd("isPublishAllowed: There is already a request running now");
            mProcessorState.setPendingRequest(true);
            return false;
        }

        // Check if the publishing request has reached the maximum number of retries.
        if (mProcessorState.isReachMaximumRetries()) {
            logd("isPublishAllowed: It has reached maximum number of retries");
            return false;
        }

        // Skip this request and re-send the request with the delay time if the publish request
        // executes too frequently.
        if (!mProcessorState.isCurrentTimeAllowed()) {
            logd("isPublishAllowed: Current time is not allowed, resend this request");
            long delayTime = mProcessorState.getDelayTimeToAllowPublish();
            mPublishCtrlCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_RETRY, delayTime);
            return false;
        }
        return true;
    }

    // Publish the device capabilities with the given pidf
    private void publishCapabilities(String pidfXml) {
        if (mIsDestroyed) {
            logw("publishCapabilities: This instance is already destroyed");
            return;
        }

        // Check if the RCS is connected.
        RcsFeatureManager featureManager = mRcsFeatureManager;
        if (featureManager == null) {
            mLocalLog.log("publish capabilities: not connected");
            logw("publishCapabilities: NOT connected");
            return;
        }

        PublishRequestResponse requestResponse = null;
        try {
            // Set publishing flag
            mProcessorState.setPublishingFlag(true);

            // Clear the pending request flag since we're publishing the latest device's capability
            mProcessorState.setPendingRequest(false);

            // Generate a unique taskId to track this request.
            long taskId = mProcessorState.generatePublishTaskId();
            requestResponse = new PublishRequestResponse(mPublishCtrlCallback, taskId);

            mLocalLog.log("publish capabilities: taskId=" + taskId);
            logi("publishCapabilities: taskId=" + taskId);

            // request publication
            featureManager.requestPublication(pidfXml, requestResponse);

            // Send a request canceled timer to avoid waiting too long for the response callback.
            mPublishCtrlCallback.setupRequestCanceledTimer(taskId, RESPONSE_CALLBACK_WAITING_TIME);

        } catch (RemoteException e) {
            mLocalLog.log("publish capability exception: " + e.getMessage());
            logw("publishCapabilities: exception=" + e.getMessage());
            // Exception occurred, end this request.
            setRequestEnded(requestResponse);
            checkAndSendPendingRequest();
        }
   }

    /**
     * Handle the command error callback of the publish request. This method is called by the
     * handler of the PublishController.
     */
    public void onCommandError(PublishRequestResponse requestResponse) {
        if (!checkRequestRespValid(requestResponse)) {
            mLocalLog.log("Command error callback is invalid");
            logw("onCommandError: request response is invalid");
            setRequestEnded(requestResponse);
            checkAndSendPendingRequest();
            return;
        }

        mLocalLog.log("Receive command error code=" + requestResponse.getCmdErrorCode());
        logd("onCommandError: " + requestResponse.toString());

        if (requestResponse.needRetry()) {
            // Increase the retry count
            mProcessorState.increaseRetryCount();

            // Reset the pending flag since it is going to resend a publish request.
            mProcessorState.setPendingRequest(false);

            // Resend a publish request
            long delayTime = mProcessorState.getDelayTimeToAllowPublish();
            mPublishCtrlCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_RETRY, delayTime);
        } else {
            // Update the publish state if the request is failed and doesn't need to retry.
            int publishState = requestResponse.getPublishStateByCmdErrorCode();
            mPublishCtrlCallback.updatePublishRequestResult(publishState);

            // Check if there is a pending request
            checkAndSendPendingRequest();
        }

        // End this request
        setRequestEnded(requestResponse);
    }

    /**
     * Handle the network response callback of the publish request. This method is called by the
     * handler of the PublishController.
     */
    public void onNetworkResponse(PublishRequestResponse requestResponse) {
        if (!checkRequestRespValid(requestResponse)) {
            mLocalLog.log("Network response callback is invalid");
            logw("onNetworkResponse: request response is invalid");
            setRequestEnded(requestResponse);
            checkAndSendPendingRequest();
            return;
        }

        mLocalLog.log("Receive network response code=" + requestResponse.getNetworkRespSipCode());
        logd("onNetworkResponse: " + requestResponse.toString());

        if (requestResponse.needRetry()) {
            // Increase the retry count
            mProcessorState.increaseRetryCount();

            // Reset the pending flag since it is going to resend a publish request.
            mProcessorState.setPendingRequest(false);

            // Resend a publish request
            long delayTime = mProcessorState.getDelayTimeToAllowPublish();
            mPublishCtrlCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_RETRY, delayTime);
        } else {
            // Reset the retry count if the publish is success.
            if (requestResponse.isRequestSuccess()) {
                mProcessorState.resetRetryCount();
            }
            // Update the publish state if the request doesn't need to retry.
            int publishResult = requestResponse.getPublishStateByNetworkResponse();
            mPublishCtrlCallback.updatePublishRequestResult(publishResult);

            // Check if there is a pending request
            checkAndSendPendingRequest();
        }

        // End this request
        setRequestEnded(requestResponse);
    }

    // Check if the request response callback is valid.
    private boolean checkRequestRespValid(PublishRequestResponse requestResponse) {
        if (requestResponse == null) {
            logd("checkRequestRespValid: request response is null");
            return false;
        }

        if (!mProcessorState.isPublishingNow()) {
            logd("checkRequestRespValid: the request is finished");
            return false;
        }

        // Abandon this response callback if the current taskId is different to the response
        // callback taskId. This response callback is obsoleted.
        long taskId = mProcessorState.getCurrentTaskId();
        long responseTaskId = requestResponse.getTaskId();
        if (taskId != responseTaskId) {
            logd("checkRequestRespValid: invalid taskId! current taskId=" + taskId
                    + ", response callback taskId=" + responseTaskId);
            return false;
        }

        if (mIsDestroyed) {
            logd("checkRequestRespValid: is already destroyed! taskId=" + taskId);
            return false;
        }
        return true;
    }

    /**
     * Cancel the publishing request since it has token too long for waiting the response callback.
     * This method is called by the handler of the PublishController.
     */
    public void cancelPublishRequest(long taskId) {
        mLocalLog.log("cancel publish request: taskId=" + taskId);
        logd("cancelPublishRequest: taskId=" + taskId);
        setRequestEnded(null);
        checkAndSendPendingRequest();
    }

    private void setRequestEnded(PublishRequestResponse requestResponse) {
        long taskId = -1L;
        if (requestResponse != null) {
            requestResponse.onDestroy();
            taskId = requestResponse.getTaskId();
        }
        mProcessorState.setPublishingFlag(false);
        mPublishCtrlCallback.clearRequestCanceledTimer();

        mLocalLog.log("Set request ended: taskId=" + taskId);
        logd("setRequestEnded: taskId=" + taskId);
    }

    private void checkAndSendPendingRequest() {
        if (mIsDestroyed) return;
        if (mProcessorState.hasPendingRequest()) {
            logd("checkAndSendPendingRequest: send pending request");
            mProcessorState.setPublishingFlag(false);

            long delayTime = mProcessorState.getDelayTimeToAllowPublish();
            mPublishCtrlCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_RETRY, delayTime);
        }
    }

    @VisibleForTesting
    public void setProcessorState(PublishProcessorState processorState) {
        mProcessorState = processorState;
    }

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

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

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

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

    public void dump(PrintWriter printWriter) {
        mLocalLog.dump(printWriter);
    }
    }
}
}
Loading