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

Commit b03dbaac authored by Brad Ebinger's avatar Brad Ebinger
Browse files

Refactor IMS to use Helper class for polling

Reduces duplicated logic in the platform by
moving polling code into helper class.

Bug: 72643077
Test: Manual, Telephony unit tests
Change-Id: Id3f20080ceafcd6b9778e9ee5620868b9c171fe7
parent a8edd920
Loading
Loading
Loading
Loading
+25 −111
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
package com.android.internal.telephony;

import android.os.RemoteException;
import android.os.Message;
import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.aidl.IImsSmsListener;
@@ -26,12 +26,10 @@ import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
import android.provider.Telephony.Sms.Intents;
import android.util.Pair;

import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.ims.MmTelFeatureConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.util.SMSDispatcherUtil;
@@ -47,12 +45,8 @@ import java.util.concurrent.atomic.AtomicInteger;
 * @hide
 */
public class ImsSmsDispatcher extends SMSDispatcher {
    // Initial condition for ims connection retry.
    private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
    // Ceiling bitshift amount for service query timeout, calculated as:
    // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
    // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
    private static final int CEILING_SERVICE_RETRY_COUNT = 6;

    private static final String TAG = "ImsSmsDispacher";

    @VisibleForTesting
    public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
@@ -62,20 +56,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
    private volatile boolean mIsSmsCapable;
    private volatile boolean mIsImsServiceUp;
    private volatile boolean mIsRegistered;
    private volatile int mImsServiceRetryCount;

    /**
     * Default implementation of interface that calculates the ImsService retry timeout.
     * Override-able for testing.
     */
    private IRetryTimeout mRetryTimeout = () -> {
        int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
        if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
            mImsServiceRetryCount++;
        }
        return timeout;
    };

    private final ImsManager.Connector mImsManagerConnector;
    /**
     * Listen to the IMS service state change
     *
@@ -120,45 +101,6 @@ public class ImsSmsDispatcher extends SMSDispatcher {
                }
    };

    // Callback fires when ImsManager MMTel Feature changes state
    private MmTelFeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
            new MmTelFeatureConnection.IFeatureUpdate() {
                @Override
                public void notifyStateChanged() {
                    try {
                        int status = getImsManager().getImsServiceState();
                        Rlog.d(TAG, "Status Changed: " + status);
                        switch (status) {
                            case android.telephony.ims.feature.ImsFeature.STATE_READY: {
                                synchronized (mLock) {
                                    setListeners();
                                    mIsImsServiceUp = true;
                                }
                                break;
                            }
                            case android.telephony.ims.feature.ImsFeature.STATE_INITIALIZING:
                                // fall through
                            case ImsFeature.STATE_UNAVAILABLE:
                                synchronized (mLock) {
                                    mIsImsServiceUp = false;
                                }
                                break;
                            default: {
                                Rlog.w(TAG, "Unexpected State!");
                            }
                        }
                    } catch (ImsException e) {
                        // Could not get the ImsService, retry!
                        retryGetImsService();
                    }
                }

                @Override
                public void notifyUnavailable() {
                    retryGetImsService();
                }
            };

    private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
        @Override
        public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
@@ -235,36 +177,26 @@ public class ImsSmsDispatcher extends SMSDispatcher {
    public ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
        super(phone, smsDispatchersController);

        mImsServiceRetryCount = 0;
        // Send a message to connect to the Ims Service and open a connection through
        // getImsService().
        sendEmptyMessage(EVENT_GET_IMS_SERVICE);
        mImsManagerConnector = new ImsManager.Connector(mContext, mPhone.getPhoneId(),
                new ImsManager.Connector.Listener() {
                    @Override
                    public void connectionReady(ImsManager manager) throws ImsException {
                        Rlog.d(TAG, "ImsManager: connection ready.");
                        synchronized (mLock) {
                            setListeners();
                            mIsImsServiceUp = true;
                        }
                    }

                    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_GET_IMS_SERVICE:
                try {
                    getImsService();
                } catch (ImsException e) {
                    Rlog.e(TAG, "setListeners: " + e);
                    retryGetImsService();
                }
                break;
            default:
                super.handleMessage(msg);
                    public void connectionUnavailable() {
                        Rlog.d(TAG, "ImsManager: connection unavailable.");
                        synchronized (mLock) {
                            mIsImsServiceUp = false;
                        }
                    }

    private void getImsService() throws ImsException {
        Rlog.d(TAG, "getImsService");
        // Adding to set, will be safe adding multiple times. If the ImsService is not active yet,
        // this method will throw an ImsException.
        getImsManager().addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
        // Wait for ImsService.STATE_READY to start listening for SMS.
        // Call the callback right away for compatibility with older devices that do not use states.
        mNotifyStatusChangedCallback.notifyStateChanged();
                });
        mImsManagerConnector.connect();
    }

    private void setListeners() throws ImsException {
@@ -272,25 +204,12 @@ public class ImsSmsDispatcher extends SMSDispatcher {
        getImsManager().addCapabilitiesCallback(mCapabilityCallback);
        getImsManager().setSmsListener(mImsSmsListener);
        getImsManager().onSmsReady();
        mImsServiceRetryCount = 0;
    }

    private void retryGetImsService() {
        // The binder connection is already up. Do not try to get it again.
        if (getImsManager().isServiceAvailable()) {
            return;
        }
        // remove callback so we do not receive updates from old MmTelFeatureConnection when switching
        // between ImsServices.
        getImsManager().removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
        // Exponential backoff during retry, limited to 32 seconds.
        Rlog.e(TAG, "getImsService: Retrying getting ImsService...");
        removeMessages(EVENT_GET_IMS_SERVICE);
        sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get());
    }

    public boolean isAvailable() {
        synchronized (mLock) {
            Rlog.d(TAG, "isAvailable: up=" + mIsImsServiceUp + ", reg= " + mIsRegistered
                    + ", cap= " + mIsSmsCapable);
            return mIsImsServiceUp && mIsRegistered && mIsSmsCapable;
        }
    }
@@ -382,9 +301,4 @@ public class ImsSmsDispatcher extends SMSDispatcher {
    protected boolean isCdmaMo() {
        return mSmsDispatchersController.isCdmaFormat(getFormat());
    }

    @VisibleForTesting
    public interface IRetryTimeout {
        int get();
    }
}
+28 −97
Original line number Diff line number Diff line
@@ -71,7 +71,6 @@ import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.ims.ImsMultiEndpoint;
import com.android.ims.ImsUtInterface;
import com.android.ims.MmTelFeatureConnection;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
@@ -252,20 +251,12 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
    private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21;
    private static final int EVENT_VT_DATA_USAGE_UPDATE = 22;
    private static final int EVENT_DATA_ENABLED_CHANGED = 23;
    private static final int EVENT_GET_IMS_SERVICE = 24;
    private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25;
    private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26;
    private static final int EVENT_SUPP_SERVICE_INDICATION = 27;

    private static final int TIMEOUT_HANGUP_PENDINGMO = 500;

    // Initial condition for ims connection retry.
    private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
    // Ceiling bitshift amount for service query timeout, calculated as:
    // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
    // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
    private static final int CEILING_SERVICE_RETRY_COUNT = 6;

    private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms

    //***** Instance Variables
@@ -304,7 +295,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {

    private PhoneConstants.State mState = PhoneConstants.State.IDLE;

    private int mImsServiceRetryCount;
    private ImsManager mImsManager;
    private ImsUtInterface mUtInterface;

@@ -640,58 +630,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        return PhoneNumberUtils.isEmergencyNumber(string);
    };

    // Callback fires when ImsManager MMTel Feature changes state
    private MmTelFeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
            new MmTelFeatureConnection.IFeatureUpdate() {
                @Override
                public void notifyStateChanged() {
                    try {
                        int status = mImsManager.getImsServiceState();
                        log("Status Changed: " + status);
                        switch (status) {
                            case ImsFeature.STATE_READY: {
                                startListeningForCalls();
                                break;
                            }
                            case ImsFeature.STATE_INITIALIZING:
                                // fall through
                            case ImsFeature.STATE_UNAVAILABLE: {
                                stopListeningForCalls();
                                break;
                            }
                            default: {
                                Log.w(LOG_TAG, "Unexpected State!");
                            }
                        }
                    } catch (ImsException e) {
                        // Could not get the ImsService, retry!
                        retryGetImsService();
                    }
                }

                @Override
                public void notifyUnavailable() {
                    retryGetImsService();
                }
            };

    @VisibleForTesting
    public interface IRetryTimeout {
        int get();
    }

    /**
     * Default implementation of interface that calculates the ImsService retry timeout.
     * Override-able for testing.
     */
    @VisibleForTesting
    public IRetryTimeout mRetryTimeout = () -> {
        int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
        if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
            mImsServiceRetryCount++;
        }
        return timeout;
    };
    private final ImsManager.Connector mImsManagerConnector;

    //***** Events

@@ -712,8 +651,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        mPhone.getDefaultPhone().registerForDataEnabledChanged(
                this, EVENT_DATA_ENABLED_CHANGED, null);

        mImsServiceRetryCount = 0;

        final TelecomManager telecomManager =
                (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
        mDefaultDialerUid.set(
@@ -723,9 +660,20 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        mVtDataUsageSnapshot = new NetworkStats(currentTime, 1);
        mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);

        // Send a message to connect to the Ims Service and open a connection through
        // getImsService().
        sendEmptyMessage(EVENT_GET_IMS_SERVICE);
        mImsManagerConnector = new ImsManager.Connector(phone.getContext(), phone.getPhoneId(),
                new ImsManager.Connector.Listener() {
                    @Override
                    public void connectionReady(ImsManager manager) throws ImsException {
                        mImsManager = manager;
                        startListeningForCalls();
                    }

                    @Override
                    public void connectionUnavailable() {
                        stopListeningForCalls();
                    }
                });
        mImsManagerConnector.connect();
    }

    /**
@@ -747,6 +695,14 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        mPhoneNumberUtilsProxy = phoneNumberUtilsProxy;
    }

    /**
     * Test-only method used to set the ImsService retry timeout.
     */
    @VisibleForTesting
    public void setRetryTimeout(ImsManager.Connector.RetryTimeout retryTimeout) {
        mImsManagerConnector.mRetryTimeout = retryTimeout;
    }

    private int getPackageUid(Context context, String pkg) {
        if (pkg == null) {
            return NetworkStats.UID_ALL;
@@ -763,19 +719,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        return uid;
    }

    private void getImsService() throws ImsException {
        if (DBG) log("getImsService");
        mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
        // Adding to set, will be safe adding multiple times. If the ImsService is not active yet,
        // this method will throw an ImsException.
        mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
        // Wait for ImsService.STATE_READY to start listening for calls.
        // Call the callback right away for compatibility with older devices that do not use states.
        mNotifyStatusChangedCallback.notifyStateChanged();
    }

    private void startListeningForCalls() throws ImsException {
        mImsServiceRetryCount = 0;
        log("startListeningForCalls");
        mImsManager.open(mMmTelFeatureListener);
        mImsManager.addRegistrationCallback(mImsRegistrationCallback);
        mImsManager.addCapabilitiesCallback(mImsCapabilityCallback);
@@ -815,6 +760,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
    }

    private void stopListeningForCalls() {
        log("stopListeningForCalls");
        resetImsCapabilities();
        // Only close on valid session.
        if (mImsManager != null) {
@@ -840,7 +786,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        }
        mPhone.getContext().unregisterReceiver(mReceiver);
        mPhone.getDefaultPhone().unregisterForDataEnabledChanged(this);
        removeMessages(EVENT_GET_IMS_SERVICE);
        mImsManagerConnector.disconnect();
    }

    @Override
@@ -3004,14 +2950,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
                    onDataEnabledChanged(p.first, p.second);
                }
                break;
            case EVENT_GET_IMS_SERVICE:
                try {
                    getImsService();
                } catch (ImsException e) {
                    loge("getImsService: " + e);
                    retryGetImsService();
                }
                break;
            case EVENT_CHECK_FOR_WIFI_HANDOVER:
                if (msg.obj instanceof ImsCall) {
                    ImsCall imsCall = (ImsCall) msg.obj;
@@ -3250,15 +3188,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        if (mImsManager.isServiceAvailable()) {
            return;
        }
        // remove callback so we do not receive updates from old ImsServiceProxy when switching
        // between ImsServices.
        mImsManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
        //Leave mImsManager as null, then CallStateException will be thrown when dialing
        mImsManager = null;
        // Exponential backoff during retry, limited to 32 seconds.
        loge("getImsService: Retrying getting ImsService...");
        removeMessages(EVENT_GET_IMS_SERVICE);
        sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get());

        mImsManagerConnector.connect();
    }

    private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
+5 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
@@ -218,6 +219,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {

        doReturn(mImsConfig).when(mImsManager).getConfigInterface();

        doNothing().when(mImsManager).addNotifyStatusChangedCallbackIfAvailable(any());

        mImsCTHandlerThread = new ImsCTHandlerThread(this.getClass().getSimpleName());
        mImsCTHandlerThread.start();

@@ -607,7 +610,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
    public void testDialImsServiceUnavailable() throws ImsException {
        doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
                mImsManager).createCallProfile(anyInt(), anyInt());
        mCTUT.mRetryTimeout = () -> 0; //ms
        mCTUT.setRetryTimeout(() -> 0);
        assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());

@@ -636,7 +639,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
                mImsManager).setUiTTYMode(nullable(Context.class), anyInt(),
                nullable(Message.class));
        // Remove retry timeout delay
        mCTUT.mRetryTimeout = () -> 0; //ms
        mCTUT.setRetryTimeout(() -> 0); //ms

        mCTUT.setUiTTYMode(0, new Message());