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

Commit 4fbdb2d5 authored by James.cf Lin's avatar James.cf Lin Committed by android-build-merger
Browse files

[RCS] Enable/Disable UCE capability after RcsFeature created am: 12a4f893

am: 2ad69a73

Change-Id: I99b31efd17a3c97ef6231cbce358b438eed14273
parents e339f654 2ad69a73
Loading
Loading
Loading
Loading
+67 −4
Original line number Diff line number Diff line
@@ -11,11 +11,12 @@
 * 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
 * limitations under the License.
 */

package com.android.ims;

import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
@@ -24,14 +25,14 @@ import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.ims.MmTelFeatureConnection.IFeatureUpdate;

import java.util.concurrent.Executor;

@@ -41,6 +42,20 @@ import java.util.concurrent.Executor;
public abstract class FeatureConnection {
    protected static final String TAG = "FeatureConnection";

    public interface IFeatureUpdate {
        /**
         * Called when the ImsFeature has changed its state. Use
         * {@link ImsFeature#getFeatureState()} to get the new state.
         */
        void notifyStateChanged();

        /**
         * Called when the ImsFeature has become unavailable due to the binder switching or app
         * crashing. A new ImsServiceProxy should be requested for that feature.
         */
        void notifyUnavailable();
    }

    protected static boolean sImsSupportedOnDevice = true;

    protected final int mSlotId;
@@ -55,6 +70,7 @@ public abstract class FeatureConnection {
    // ImsFeature Status from the ImsService. Cached.
    protected Integer mFeatureStateCached = null;
    protected IFeatureUpdate mStatusCallback;
    protected IImsRegistration mRegistrationBinder;
    protected final Object mLock = new Object();

    public FeatureConnection(Context context, int slotId, int featureType) {
@@ -78,6 +94,10 @@ public abstract class FeatureConnection {
        return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    }

    /**
     * Set the binder which type is IImsMmTelFeature or IImsRcsFeature to connect to MmTelFeature
     * or RcsFeature.
     */
    public void setBinder(IBinder binder) {
        synchronized (mLock) {
            mBinder = binder;
@@ -110,6 +130,7 @@ public abstract class FeatureConnection {
        synchronized (mLock) {
            if (mIsAvailable) {
                mIsAvailable = false;
                mRegistrationBinder = null;
                if (mBinder != null) {
                    mBinder.unlinkToDeath(mDeathRecipient, 0);
                }
@@ -120,11 +141,22 @@ public abstract class FeatureConnection {
        }
    }

    /**
     * The listener for ImsManger and RcsFeatureManager to receive IMS feature status changed.
     * @param callback Callback that will fire when the feature status has changed.
     */
    public void setStatusCallback(IFeatureUpdate callback) {
        mStatusCallback = callback;
    }

    @VisibleForTesting
    public IImsServiceFeatureCallback getListener() {
        return mListenerBinder;
    }

    /**
     * The callback to receive ImsFeature status changed.
     */
    private final IImsServiceFeatureCallback mListenerBinder =
        new IImsServiceFeatureCallback.Stub() {
            @Override
@@ -151,6 +183,37 @@ public abstract class FeatureConnection {
    protected abstract void handleImsFeatureRemovedCallback(int slotId, int feature);
    protected abstract void handleImsStatusChangedCallback(int slotId, int feature, int status);

    public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
        throws RemoteException {
        IImsRegistration registration = getRegistration();
        if (registration != null) {
            return registration.getRegistrationTechnology();
        } else {
            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
        }
    }

    protected @Nullable IImsRegistration getRegistration() {
        synchronized (mLock) {
            // null if cache is invalid;
            if (mRegistrationBinder != null) {
                return mRegistrationBinder;
            }
        }
        TelephonyManager tm = getTelephonyManager();
        // We don't want to synchronize on a binder call to another process.
        IImsRegistration regBinder = tm != null
            ? tm.getImsRegistration(mSlotId, mFeatureType) : null;
        synchronized (mLock) {
            // mRegistrationBinder may have changed while we tried to get the registration
            // interface.
            if (mRegistrationBinder == null) {
                mRegistrationBinder = regBinder;
            }
        }
        return mRegistrationBinder;
    }

    protected void checkServiceIsReady() throws RemoteException {
        if (!sImsSupportedOnDevice) {
            throw new RemoteException("IMS is not supported on this device.");
+288 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019 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;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.telephony.ims.feature.ImsFeature;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import java.util.concurrent.Executor;

/**
 * Helper class for managing a connection to the ImsFeature manager.
 */
public class FeatureConnector<T extends IFeatureConnector> extends Handler {
    private static final String TAG = "FeatureConnector";

    // 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;

    public interface Listener<T> {
        /**
         * Check if ImsFeature supported
         */
        boolean isSupported();

        /**
         * Get ImsFeature manager instance
         */
        T getFeatureManager();

        /**
         * ImsFeature manager is connected to the underlying IMS implementation.
         */
        void connectionReady(T manager) throws ImsException;

        /**
         * The underlying IMS implementation is unavailable and can not be used to communicate.
         */
        void connectionUnavailable();
    }

    public interface RetryTimeout {
        int get();
    }

    protected final int mPhoneId;
    protected final Context mContext;
    protected final Executor mExecutor;
    protected final Object mLock = new Object();
    protected final String mLogPrefix;

    @VisibleForTesting
    public Listener mListener;

    // The IMS feature manager which interacts with ImsService
    @VisibleForTesting
    public T mManager;

    protected int mRetryCount = 0;

    @VisibleForTesting
    public RetryTimeout mRetryTimeout = () -> {
        synchronized (mLock) {
            int timeout = (1 << mRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
            if (mRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
                mRetryCount++;
            }
            return timeout;
        }
    };

    public FeatureConnector(Context context, int phoneId, Listener listener) {
        mContext = context;
        mPhoneId = phoneId;
        mListener = listener;
        mExecutor = new HandlerExecutor(this);
        mLogPrefix = "?";
    }

    public FeatureConnector(Context context, int phoneId, Listener listener,
            String logPrefix) {
        mContext = context;
        mPhoneId = phoneId;
        mListener = listener;
        mExecutor = new HandlerExecutor(this);
        mLogPrefix = logPrefix;
    }

    @VisibleForTesting
    public FeatureConnector(Context context, int phoneId, Listener listener,
            Executor executor, String logPrefix) {
        mContext = context;
        mPhoneId = phoneId;
        mListener= listener;
        mExecutor = executor;
        mLogPrefix = logPrefix;
    }

    @VisibleForTesting
    public FeatureConnector(Context context, int phoneId, Listener listener,
            Executor executor, Looper looper) {
        super(looper);
        mContext = context;
        mPhoneId = phoneId;
        mListener= listener;
        mExecutor = executor;
        mLogPrefix = "?";
    }

    /**
     * Start the creation of a connection to the underlying ImsService implementation. When the
     * service is connected, {@link FeatureConnector.Listener#connectionReady(T manager)} will be
     * called with an active instance.
     *
     * If this device does not support an ImsStack (i.e. doesn't support
     * {@link PackageManager#FEATURE_TELEPHONY_IMS} feature), this method will do nothing.
     */
    public void connect() {
        Log.i(TAG, "connect");
        if (!isSupported()) {
            Log.i(TAG, "connect: NOT support!");
            return;
        }
        mRetryCount = 0;
        // Send a message to connect to the Ims Service and open a connection through
        // getImsService().
        post(mGetServiceRunnable);
    }

    // Check if this ImsFeature is supported or not.
    private boolean isSupported() {
        return mListener.isSupported();
    }

    /**
     * Disconnect from the ImsService Implementation and clean up. When this is complete,
     * {@link FeatureConnector.Listener#connectionUnavailable()} will be called one last time.
     */
    public void disconnect() {
        Log.i(TAG, "disconnect");
        removeCallbacks(mGetServiceRunnable);
        synchronized (mLock) {
            if (mManager != null) {
                mManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
            }
        }
        notifyNotReady();
    }

    private final Runnable mGetServiceRunnable = () -> {
        try {
            createImsService();
        } catch (ImsException e) {
            retryGetImsService();
        }
    };

    @VisibleForTesting
    public void createImsService() throws ImsException {
        synchronized (mLock) {
            Log.d(TAG, "createImsService");
            mManager = (T) mListener.getFeatureManager();
            // Adding to set, will be safe adding multiple times. If the ImsService is not
            // active yet, this method will throw an ImsException.
            mManager.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();
    }

    /**
     * Remove callback and re-running mGetServiceRunnable
     */
    public void retryGetImsService() {
        if (mManager != null) {
            // remove callback so we do not receive updates from old ImsServiceProxy when
            // switching between ImsServices.
            mManager.removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
            //Leave mImsManager as null, then CallStateException will be thrown when dialing
            mManager = null;
        }

        // Exponential backoff during retry, limited to 32 seconds.
        removeCallbacks(mGetServiceRunnable);
        int timeout = mRetryTimeout.get();
        postDelayed(mGetServiceRunnable, timeout);
        Log.i(TAG, getLogMessage("retryGetImsService: unavailable, retrying in " + timeout
            + " seconds"));
    }

    // Callback fires when IMS Feature changes state
    public FeatureConnection.IFeatureUpdate mNotifyStatusChangedCallback =
            new FeatureConnection.IFeatureUpdate() {
                @Override
                public void notifyStateChanged() {
                    mExecutor.execute(() -> {
                        try {
                            int status = ImsFeature.STATE_UNAVAILABLE;
                            synchronized (mLock) {
                                if (mManager != null) {
                                    status = mManager.getImsServiceState();
                                }
                            }
                            switch (status) {
                                case ImsFeature.STATE_READY: {
                                    notifyReady();
                                    break;
                                }
                                case ImsFeature.STATE_INITIALIZING:
                                    // fall through
                                case ImsFeature.STATE_UNAVAILABLE: {
                                    notifyNotReady();
                                    break;
                                }
                                default: {
                                    Log.w(TAG, "Unexpected State!");
                                }
                            }
                        } catch (ImsException e) {
                            // Could not get the ImsService, retry!
                            notifyNotReady();
                            retryGetImsService();
                        }
                    });
                }

                @Override
                public void notifyUnavailable() {
                    mExecutor.execute(() -> {
                        notifyNotReady();
                        retryGetImsService();
                    });
                }
            };

    private void notifyReady() throws ImsException {
        T manager;
        synchronized (mLock) {
            manager = mManager;
        }
        try {
            Log.i(TAG, "notifyReady");
            mListener.connectionReady(manager);
        }
        catch (ImsException e) {
            Log.w(TAG, getLogMessage("notifyReady exception: " + e.getMessage()));
            throw e;
        }
        // Only reset retry count if connectionReady does not generate an ImsException/
        synchronized (mLock) {
            mRetryCount = 0;
        }
    }

    protected void notifyNotReady() {
        Log.i(TAG, "notifyNotReady");
        mListener.connectionUnavailable();
    }

    protected String getLogMessage(String message) {
        return "Connector-[" + mLogPrefix + "] " + message;
    }
}
+24 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019 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;

public interface IFeatureConnector {
    int getImsServiceState() throws ImsException;
    void addNotifyStatusChangedCallbackIfAvailable(FeatureConnection.IFeatureUpdate callback)
            throws ImsException;
    void removeNotifyStatusChangedCallback(FeatureConnection.IFeatureUpdate callback);
}
 No newline at end of file
+10 −230

File changed.

Preview size limit exceeded, changes collapsed.

+0 −54
Original line number Diff line number Diff line
@@ -414,13 +414,11 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    private IFeatureUpdate mStatusCallback;
    // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
    private boolean mSupportsEmergencyCalling = false;

    // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If
    // it becomes disconnected, invalidate.
    private IImsRegistration mRegistrationBinder;
    private IImsConfig mConfigBinder;
    private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
    private final CapabilityCallbackManager mCapabilityCallbackManager;
@@ -454,20 +452,6 @@ public class MmTelFeatureConnection extends FeatureConnection {
        return serviceProxy;
    }

    public interface IFeatureUpdate {
        /**
         * Called when the ImsFeature has changed its state. Use
         * {@link ImsFeature#getFeatureState()} to get the new state.
         */
        void notifyStateChanged();

        /**
         * Called when the ImsFeature has become unavailable due to the binder switching or app
         * crashing. A new ImsServiceProxy should be requested for that feature.
         */
        void notifyUnavailable();
    }

    public MmTelFeatureConnection(Context context, int slotId) {
        super(context, slotId, ImsFeature.FEATURE_MMTEL);

@@ -497,27 +481,6 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    private @Nullable IImsRegistration getRegistration() {
        synchronized (mLock) {
            // null if cache is invalid;
            if (mRegistrationBinder != null) {
                return mRegistrationBinder;
            }
        }
        TelephonyManager tm = getTelephonyManager();
        // We don't want to synchronize on a binder call to another process.
        IImsRegistration regBinder = tm != null
                ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
        synchronized (mLock) {
            // mRegistrationBinder may have changed while we tried to get the registration
            // interface.
            if (mRegistrationBinder == null) {
                mRegistrationBinder = regBinder;
            }
        }
        return mRegistrationBinder;
    }

    private IImsConfig getConfig() {
        synchronized (mLock) {
            // null if cache is invalid;
@@ -729,16 +692,6 @@ public class MmTelFeatureConnection extends FeatureConnection {
        return getConfig();
    }

    public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech()
            throws RemoteException {
        IImsRegistration registration = getRegistration();
        if (registration != null) {
                return registration.getRegistrationTechnology();
        } else {
            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
        }
    }

    public IImsEcbm getEcbmInterface() throws RemoteException {
        synchronized (mLock) {
            checkServiceIsReady();
@@ -820,13 +773,6 @@ public class MmTelFeatureConnection extends FeatureConnection {
        }
    }

    /**
     * @param c Callback that will fire when the feature status has changed.
     */
    public void setStatusCallback(IFeatureUpdate c) {
        mStatusCallback = c;
    }

    @Override
    protected Integer retrieveFeatureState() {
        if (mBinder != null) {
Loading