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

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

Add the throttling mechanism to get rate limited for the PUBLISH requests.

Bug: 179311083
Test: atest -c CtsTelephonyTestCases:android.telephony.ims.cts.ImsServiceTest
Merged-In: If0cedcddbb24e7c16b28a3e520377e180321ff03
Change-Id: If0cedcddbb24e7c16b28a3e520377e180321ff03
parent e6a96d3b
Loading
Loading
Loading
Loading
+2 −25
Original line number Diff line number Diff line
@@ -116,17 +116,6 @@ public class UceController {
         */
        void refreshCapabilities(@NonNull List<Uri> contactNumbers,
                @NonNull IRcsUceControllerCallback callback) throws RemoteException;

        /**
         * The method is called when the EabController and the PublishController want to receive
         * published state changes.
         */
        void registerPublishStateCallback(@NonNull IRcsUcePublishStateCallback c);

        /**
         * Remove the existing PublishStateCallback.
         */
        void unregisterPublishStateCallback(@NonNull IRcsUcePublishStateCallback c);
    }

    /**
@@ -358,18 +347,6 @@ public class UceController {
            logd("refreshCapabilities: " + contactNumbers.size());
            UceController.this.requestCapabilitiesInternal(contactNumbers, true, callback);
        }

        @Override
        public void registerPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) {
            logd("UceControllerCallback: registerPublishStateCallback");
            UceController.this.registerPublishStateCallback(c);
        }

        @Override
        public void unregisterPublishStateCallback(@NonNull IRcsUcePublishStateCallback c) {
            logd("UceControllerCallback: unregisterPublishStateCallback");
            UceController.this.unregisterPublishStateCallback(c);
        }
    };

    @VisibleForTesting
@@ -467,7 +444,7 @@ public class UceController {
        if (mServerState.isRequestForbidden()) {
            Integer errorCode = mServerState.getForbiddenErrorCode();
            long retryAfter = mServerState.getRetryAfterMillis();
            logw("requestCapabilities: The request is forbidden, errorCode=" + errorCode
            logw("requestAvailability: The request is forbidden, errorCode=" + errorCode
                + ", retryAfter=" + retryAfter);
            errorCode = (errorCode != null) ? errorCode : RcsUceAdapter.ERROR_FORBIDDEN;
            c.onError(errorCode, retryAfter);
@@ -539,7 +516,7 @@ public class UceController {

    /**
     * Check if the UceController is available.
     * @return true if the RCS is connected without destroyed.
     * @return true if RCS is connected without destroyed.
     */
    public boolean isUnavailable() {
        if (!mIsRcsConnected || mIsDestroyedFlag) {
+130 −47
Original line number Diff line number Diff line
@@ -23,11 +23,14 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.provider.Telephony;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
@@ -57,9 +60,7 @@ public class DeviceCapabilityListener {

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

    // Delay to send the registered changed because the registered state changed of MMTEL and RCS
    // may be called at the same time.
    private static final long DELAY_SEND_IMS_REGISTERED_CHANGED_MSG = 500L;
    private static final long REGISTER_IMS_CHANGED_DELAY = 15000L;  // 15 seconds

    /**
     * Used to inject ImsMmTelManager instances for testing.
@@ -85,49 +86,78 @@ public class DeviceCapabilityListener {
        ProvisioningManager getProvisioningManager(int subId);
    }

    // The handler to re-register ims provision callback.
    private class RegisterCallbackHandler extends Handler {
    /*
     * Handle registering IMS callback and triggering the publish request because of the
     * capabilities changed.
     */
    private class DeviceCapabilityHandler extends Handler {
        private static final long TRIGGER_PUBLISH_REQUEST_DELAY_MS = 500L;

        private static final int EVENT_REGISTER_IMS_CONTENT_CHANGE = 1;
        private static final long REGISTER_IMS_CHANGED_DELAY = 5000L;  // 5 seconds
        private static final int EVENT_UNREGISTER_IMS_CHANGE = 2;
        private static final int EVENT_REQUEST_PUBLISH = 3;

        RegisterCallbackHandler(Looper looper) {
        DeviceCapabilityHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            logd("handleMessage: " + msg.what);
            if (mIsDestroyed) return;
            switch (msg.what) {
                case EVENT_REGISTER_IMS_CONTENT_CHANGE:
                    registerImsProvisionCallback();
                    break;
                case EVENT_UNREGISTER_IMS_CHANGE:
                    unregisterImsProvisionCallback();
                    break;
                case EVENT_REQUEST_PUBLISH:
                    int triggerType = msg.arg1;
                    mCallback.requestPublishFromInternal(triggerType);
                    break;
            }
        }

        public void sendRegisterImsContentChangedMessage() {
        public void sendRegisterImsContentChangedMessage(long delay) {
            // Remove the existing message and send a new one with the delayed time.
            removeMessages(EVENT_REGISTER_IMS_CONTENT_CHANGE);
            Message msg = obtainMessage(EVENT_REGISTER_IMS_CONTENT_CHANGE);
            sendMessageDelayed(msg, REGISTER_IMS_CHANGED_DELAY);
            sendMessageDelayed(msg, delay);
        }

        public void removeRegisterImsContentChangedMessage() {
            removeMessages(EVENT_REGISTER_IMS_CONTENT_CHANGE);
        }

        public void sendUnregisterImsCallbackMessage() {
            removeMessages(EVENT_REGISTER_IMS_CONTENT_CHANGE);
            sendEmptyMessage(EVENT_UNREGISTER_IMS_CHANGE);
        }

        public void sendTriggeringPublishMessage(int type) {
            // Remove the existing message and resend a new message.
            removeMessages(EVENT_REQUEST_PUBLISH);
            Message message = obtainMessage();
            message.what = EVENT_REQUEST_PUBLISH;
            message.arg1 = type;
            sendMessageDelayed(message, TRIGGER_PUBLISH_REQUEST_DELAY_MS);
        }
    }

    private final int mSubId;
    private final Context mContext;
    private final LocalLog mLocalLog = new LocalLog(UceUtils.LOG_SIZE);
    private volatile boolean mInitialized;

    // The listener is destroyed
    private volatile boolean mIsDestroyed;
    private volatile boolean mIsRcsConnected;
    private volatile boolean mIsImsCallbackRegistered;

    // The callback to trigger the internal publish request
    private final PublishControllerCallback mCallback;
    private final DeviceCapabilityInfo mCapabilityInfo;
    private final RegisterCallbackHandler mHandler;
    private final HandlerThread mHandlerThread;
    private final DeviceCapabilityHandler mHandler;
    private final HandlerExecutor mHandlerExecutor;

    private ImsMmTelManager mImsMmTelManager;
@@ -146,7 +176,7 @@ public class DeviceCapabilityListener {
    private final Object mLock = new Object();

    public DeviceCapabilityListener(Context context, int subId, DeviceCapabilityInfo info,
            PublishControllerCallback callback, Looper looper) {
            PublishControllerCallback callback) {
        mSubId = subId;
        logi("create");

@@ -154,7 +184,10 @@ public class DeviceCapabilityListener {
        mCallback = callback;
        mCapabilityInfo = info;
        mInitialized = false;
        mHandler = new RegisterCallbackHandler(looper);

        mHandlerThread = new HandlerThread("DeviceCapListenerThread");
        mHandlerThread.start();
        mHandler = new DeviceCapabilityHandler(mHandlerThread.getLooper());
        mHandlerExecutor = new HandlerExecutor(mHandler);
    }

@@ -180,6 +213,18 @@ public class DeviceCapabilityListener {
        }
    }

    // The RcsFeature has been connected to the framework
    public void onRcsConnected() {
        mIsRcsConnected = true;
        mHandler.sendRegisterImsContentChangedMessage(0L);
    }

    // The framework has lost the binding to the RcsFeature.
    public void onRcsDisconnected() {
        mIsRcsConnected = false;
        mHandler.sendUnregisterImsCallbackMessage();
    }

    /**
     * Notify the instance is destroyed
     */
@@ -192,6 +237,7 @@ public class DeviceCapabilityListener {
            mInitialized = false;
            unregisterReceivers();
            unregisterImsProvisionCallback();
            mHandlerThread.quit();
        }
    }

@@ -228,6 +274,11 @@ public class DeviceCapabilityListener {
    }

    private void registerImsProvisionCallback() {
        if (mIsImsCallbackRegistered) {
            logd("registerImsProvisionCallback: already registered.");
            return;
        }

        logd("registerImsProvisionCallback");
        try {
            // Register mmtel callback
@@ -247,12 +298,18 @@ public class DeviceCapabilityListener {
            // Register provisioning changed callback
            mProvisioningManager.registerProvisioningChangedCallback(mHandlerExecutor,
                    mProvisionChangedCallback);

            // Set the IMS callback is registered.
            mIsImsCallbackRegistered = true;
        } catch (ImsException e) {
            logw("registerImsProvisionCallback error: " + e);
            // Unregister the callback
            unregisterImsProvisionCallback();
            // Retry registering IMS content change callback
            mHandler.sendRegisterImsContentChangedMessage();

            // Retry registering IMS callback only when the RCS is connected.
            if (mIsRcsConnected) {
                mHandler.sendRegisterImsContentChangedMessage(REGISTER_IMS_CHANGED_DELAY);
            }
        }
    }

@@ -291,6 +348,9 @@ public class DeviceCapabilityListener {
        } catch (RuntimeException e) {
            logw("unregister provisioning callback error: " + e.getMessage());
        }

        // Clear the IMS callback registered flag.
        mIsImsCallbackRegistered = false;
    }

    @VisibleForTesting
@@ -382,6 +442,7 @@ public class DeviceCapabilityListener {
                public void onRegistered(ImsRegistrationAttributes attributes) {
                    synchronized (mLock) {
                        logi("onRcsRegistered: " + attributes);
                        if (!mIsImsCallbackRegistered) return;
                        handleImsRcsRegistered(attributes);
                    }
                }
@@ -390,6 +451,7 @@ public class DeviceCapabilityListener {
                public void onUnregistered(ImsReasonInfo info) {
                    synchronized (mLock) {
                        logi("onRcsUnregistered: " + info);
                        if (!mIsImsCallbackRegistered) return;
                        handleImsRcsUnregistered();
                    }
                }
@@ -399,10 +461,12 @@ public class DeviceCapabilityListener {
    public final RegistrationManager.RegistrationCallback mMmtelRegistrationCallback =
            new RegistrationManager.RegistrationCallback() {
                @Override
                public void onRegistered(int imsTransportType) {
                public void onRegistered(@TransportType int transportType) {
                    synchronized (mLock) {
                        logi("onMmTelRegistered: " + imsTransportType);
                        handleImsMmtelRegistered(imsTransportType);
                        String type = AccessNetworkConstants.transportTypeToString(transportType);
                        logi("onMmTelRegistered: " + type);
                        if (!mIsImsCallbackRegistered) return;
                        handleImsMmtelRegistered(transportType);
                    }
                }

@@ -410,6 +474,7 @@ public class DeviceCapabilityListener {
                public void onUnregistered(ImsReasonInfo info) {
                    synchronized (mLock) {
                        logi("onMmTelUnregistered: " + info);
                        if (!mIsImsCallbackRegistered) return;
                        handleImsMmtelUnregistered();
                    }
                }
@@ -441,44 +506,46 @@ public class DeviceCapabilityListener {
                        case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
                        case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
                            handleProvisioningChanged();
                        case ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS:
                            handlePublishThrottleChanged(value);
                            break;
                    }
                }
            };

    private void handleTtyPreferredModeChanged(int preferredMode) {
        logi("TTY preferred mode changed: " + preferredMode);
        boolean isChanged = mCapabilityInfo.updateTtyPreferredMode(preferredMode);
        logi("TTY preferred mode changed: " + preferredMode + ", isChanged=" + isChanged);
        if (isChanged) {
            mCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_TTY_PREFERRED_CHANGE, 0L);
            mHandler.sendTriggeringPublishMessage(
                PublishController.PUBLISH_TRIGGER_TTY_PREFERRED_CHANGE);
        }
    }

    private void handleAirplaneModeChanged(boolean state) {
        logi("Airplane mode changed: " + state);
        boolean isChanged = mCapabilityInfo.updateAirplaneMode(state);
        logi("Airplane mode changed: " + state + ", isChanged="+ isChanged);
        if (isChanged) {
            mCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE, 0L);
            mHandler.sendTriggeringPublishMessage(
                    PublishController.PUBLISH_TRIGGER_AIRPLANE_MODE_CHANGE);
        }
    }

    private void handleMobileDataChanged(boolean isEnabled) {
        logi("Mobile data changed: " + isEnabled);
        boolean isChanged = mCapabilityInfo.updateMobileData(isEnabled);
        logi("Mobile data changed: " + isEnabled + ", isChanged=" + isChanged);
        if (isChanged) {
            mCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_MOBILE_DATA_CHANGE, 0L);
            mHandler.sendTriggeringPublishMessage(
                    PublishController.PUBLISH_TRIGGER_MOBILE_DATA_CHANGE);
        }
    }

    private void handleVtSettingChanged(boolean isEnabled) {
        logi("VT setting changed: " + isEnabled);
        boolean isChanged = mCapabilityInfo.updateVtSetting(isEnabled);
        logi("VT setting changed: " + isEnabled + ", isChanged=" + isChanged);
        if (isChanged) {
            mCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_VT_SETTING_CHANGE, 0L);
            mHandler.sendTriggeringPublishMessage(
                    PublishController.PUBLISH_TRIGGER_VT_SETTING_CHANGE);
        }
    }

@@ -487,9 +554,8 @@ public class DeviceCapabilityListener {
     */
    private void handleImsMmtelRegistered(int imsTransportType) {
        mCapabilityInfo.updateImsMmtelRegistered(imsTransportType);
        mCallback.requestPublishFromInternal(
                PublishController.PUBLISH_TRIGGER_MMTEL_REGISTERED,
                DELAY_SEND_IMS_REGISTERED_CHANGED_MSG);
        mHandler.sendTriggeringPublishMessage(
                PublishController.PUBLISH_TRIGGER_MMTEL_REGISTERED);
    }

    /*
@@ -497,41 +563,53 @@ public class DeviceCapabilityListener {
     */
    private void handleImsMmtelUnregistered() {
        mCapabilityInfo.updateImsMmtelUnregistered();
        mCallback.requestPublishFromInternal(
                PublishController.PUBLISH_TRIGGER_MMTEL_UNREGISTERED, 0L);
        mHandler.sendTriggeringPublishMessage(
                PublishController.PUBLISH_TRIGGER_MMTEL_UNREGISTERED);
    }

    private void handleMmtelCapabilitiesStatusChanged(MmTelCapabilities capabilities) {
        boolean isChanged = mCapabilityInfo.updateMmtelCapabilitiesChanged(capabilities);
        logi("MMTel capabilities status changed: isChanged=" + isChanged);
        if (isChanged) {
            mCallback.requestPublishFromInternal(
                    PublishController.PUBLISH_TRIGGER_MMTEL_CAPABILITY_CHANGE, 0L);
            mHandler.sendTriggeringPublishMessage(
                    PublishController.PUBLISH_TRIGGER_MMTEL_CAPABILITY_CHANGE);
        }
    }

    /*
     * This method is called when the RCS is registered.
     * This method is called when RCS is registered.
     */
    private void handleImsRcsRegistered(ImsRegistrationAttributes attr) {
        mCapabilityInfo.updateImsRcsRegistered(attr);
        mCallback.requestPublishFromInternal(
                PublishController.PUBLISH_TRIGGER_RCS_REGISTERED,
                DELAY_SEND_IMS_REGISTERED_CHANGED_MSG);
        mHandler.sendTriggeringPublishMessage(PublishController.PUBLISH_TRIGGER_RCS_REGISTERED);
    }

    /*
     * This method is called when the RCS is unregistered.
     * This method is called when RCS is unregistered.
     */
    private void handleImsRcsUnregistered() {
        mCapabilityInfo.updateImsRcsUnregistered();
        mCallback.requestPublishFromInternal(
                PublishController.PUBLISH_TRIGGER_RCS_UNREGISTERED, 0L);
        mHandler.sendTriggeringPublishMessage(PublishController.PUBLISH_TRIGGER_RCS_UNREGISTERED);
    }

    /*
     * This method is called when the provisioning is changed
     */
    private void handleProvisioningChanged() {
        mCallback.requestPublishFromInternal(
                PublishController.PUBLISH_TRIGGER_PROVISIONING_CHANGE, 0L);
        mHandler.sendTriggeringPublishMessage(
                PublishController.PUBLISH_TRIGGER_PROVISIONING_CHANGE);
    }

    /*
     * Update the publish throttle.
     */
    private void handlePublishThrottleChanged(int value) {
        mCallback.updatePublishThrottle(value);
    }

    @VisibleForTesting
    public Handler getHandler() {
        return mHandler;
    }

    @VisibleForTesting
@@ -549,6 +627,11 @@ public class DeviceCapabilityListener {
        mProvisioningMgrFactory = factory;
    }

    @VisibleForTesting
    public void setImsCallbackRegistered(boolean registered) {
        mIsImsCallbackRegistered = registered;
    }

    private void logd(String log) {
        Log.d(LOG_TAG, getLogPrefix().append(log).toString());
        mLocalLog.log("[D] " + log);
+6 −1
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ public interface PublishController extends ControllerBase {
        /**
         * Request publish from local.
         */
        void requestPublishFromInternal(@PublishTriggerType int type, long delay);
        void requestPublishFromInternal(@PublishTriggerType int type);

        /**
         * Receive the command error callback of the request from ImsService.
@@ -122,6 +122,11 @@ public interface PublishController extends ControllerBase {
         * Update the publish request result.
         */
        void updatePublishRequestResult(int publishState, Instant updatedTimestamp, String pidfXml);

        /**
         * Update the value of the publish throttle.
         */
        void updatePublishThrottle(int value);
    }

    /**
+47 −44

File changed.

Preview size limit exceeded, changes collapsed.

+116 −93

File changed.

Preview size limit exceeded, changes collapsed.

Loading