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

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

[RCS] Initialize RcsFeatureManager and RcsFeatureConnection instance am: 79e54d94 am: b72a24c7

am: e7c3a3b3

Change-Id: I2b3c3d725f06f1234be100c11fb3361fa8688bb4
parents 228c18ed e7c3a3b3
Loading
Loading
Loading
Loading
+210 −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.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.feature.ImsFeature;
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;

/**
 * Base class of MmTelFeatureConnection and RcsFeatureConnection.
 */
public abstract class FeatureConnection {
    protected static final String TAG = "FeatureConnection";

    protected static boolean sImsSupportedOnDevice = true;

    protected final int mSlotId;
    protected final int mFeatureType;
    protected Context mContext;
    protected IBinder mBinder;
    @VisibleForTesting
    public Executor mExecutor;

    // We are assuming the feature is available when started.
    protected volatile boolean mIsAvailable = true;
    // ImsFeature Status from the ImsService. Cached.
    protected Integer mFeatureStateCached = null;
    protected IFeatureUpdate mStatusCallback;
    protected final Object mLock = new Object();

    public FeatureConnection(Context context, int slotId, int featureType) {
        mSlotId = slotId;
        mContext = context;
        mFeatureType = featureType;

        // Callbacks should be scheduled on the main thread.
        if (context.getMainLooper() != null) {
            mExecutor = context.getMainExecutor();
        } else {
            // Fallback to the current thread.
            if (Looper.myLooper() == null) {
                Looper.prepare();
            }
            mExecutor = new HandlerExecutor(new Handler(Looper.myLooper()));
        }
    }

    protected TelephonyManager getTelephonyManager() {
        return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    }

    public void setBinder(IBinder binder) {
        synchronized (mLock) {
            mBinder = binder;
            try {
                if (mBinder != null) {
                    mBinder.linkToDeath(mDeathRecipient, 0);
                }
            } catch (RemoteException e) {
                // No need to do anything if the binder is already dead.
            }
        }
    }

    protected final IBinder.DeathRecipient mDeathRecipient = () -> {
        Log.w(TAG, "DeathRecipient triggered, binder died.");
        if (mContext != null && Looper.getMainLooper() != null) {
            // Move this signal to the main thread, notifying ImsManager of the Binder
            // death on another thread may lead to deadlocks.
            mContext.getMainExecutor().execute(this::onRemovedOrDied);
            return;
        }
        // No choice - execute on the current Binder thread.
        onRemovedOrDied();
    };

    /**
     * Called when the MmTelFeature/RcsFeature has either been removed by Telephony or crashed.
     */
    protected void onRemovedOrDied() {
        synchronized (mLock) {
            if (mIsAvailable) {
                mIsAvailable = false;
                if (mBinder != null) {
                    mBinder.unlinkToDeath(mDeathRecipient, 0);
                }
                if (mStatusCallback != null) {
                    mStatusCallback.notifyUnavailable();
                }
            }
        }
    }

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

    private final IImsServiceFeatureCallback mListenerBinder =
        new IImsServiceFeatureCallback.Stub() {
            @Override
            public void imsFeatureCreated(int slotId, int feature) {
                mExecutor.execute(() -> {
                    handleImsFeatureCreatedCallback(slotId, feature);
                });
            }
            @Override
            public void imsFeatureRemoved(int slotId, int feature) {
                mExecutor.execute(() -> {
                    handleImsFeatureRemovedCallback(slotId, feature);
                });
            }
            @Override
            public void imsStatusChanged(int slotId, int feature, int status) {
                mExecutor.execute(() -> {
                    handleImsStatusChangedCallback(slotId, feature, status);
                });
            }
        };

    protected abstract void handleImsFeatureCreatedCallback(int slotId, int feature);
    protected abstract void handleImsFeatureRemovedCallback(int slotId, int feature);
    protected abstract void handleImsStatusChangedCallback(int slotId, int feature, int status);

    protected void checkServiceIsReady() throws RemoteException {
        if (!sImsSupportedOnDevice) {
            throw new RemoteException("IMS is not supported on this device.");
        }
        if (!isBinderReady()) {
            throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
        }
    }

    /**
     * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
     * method returns false, it doesn't mean that the Binder connection is not available (use
     * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
     * at this time.
     *
     * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
     * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
     */
    public boolean isBinderReady() {
        return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
    }

    /**
     * @return false if the binder connection is no longer alive.
     */
    public boolean isBinderAlive() {
        return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
    }

    /**
     * @return an integer describing the current Feature Status, defined in
     * {@link ImsFeature.ImsState}.
     */
    public int getFeatureState() {
        synchronized (mLock) {
            if (isBinderAlive() && mFeatureStateCached != null) {
                return mFeatureStateCached;
            }
        }
        // Don't synchronize on Binder call.
        Integer state = retrieveFeatureState();
        synchronized (mLock) {
            if (state == null) {
                return ImsFeature.STATE_UNAVAILABLE;
            }
            // Cache only non-null value for feature status.
            mFeatureStateCached = state;
        }
        Log.i(TAG, "getFeatureState - returning " + ImsFeature.STATE_LOG_MAP.get(state));
        return state;
    }

    /**
     * Internal method used to retrieve the feature status from the corresponding ImsService.
     */
    protected abstract Integer retrieveFeatureState();
}
+69 −193
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.ims;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
@@ -58,16 +57,14 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
 * the platform currently supports: MMTel and RCS.
 * @hide
 * the platform currently supports: MMTel
 */

public class MmTelFeatureConnection {
public class MmTelFeatureConnection extends FeatureConnection {
    protected static final String TAG = "MmTelFeatureConnection";

    // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection.
@@ -417,38 +414,14 @@ public class MmTelFeatureConnection {
        }
    }

    protected final int mSlotId;
    protected IBinder mBinder;
    private Context mContext;
    private Executor mExecutor;

    // We are assuming the feature is available when started.
    private volatile boolean mIsAvailable = true;
    // ImsFeature Status from the ImsService. Cached.
    private Integer mFeatureStateCached = null;
    private IFeatureUpdate mStatusCallback;
    private final Object mLock = new Object();
    // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent.
    private boolean mSupportsEmergencyCalling = false;
    private static boolean sImsSupportedOnDevice = true;

    // 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 IBinder.DeathRecipient mDeathRecipient = () -> {
        Log.w(TAG, "DeathRecipient triggered, binder died.");
        if (mContext != null && Looper.getMainLooper() != null) {
            // Move this signal to the main thread, notifying ImsManager of the Binder
            // death on another thread may lead to deadlocks.
            mContext.getMainExecutor().execute(this::onRemovedOrDied);
            return;
        }
        // No choice - execute on the current Binder thread.
        onRemovedOrDied();
    };

    private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager;
    private final CapabilityCallbackManager mCapabilityCallbackManager;
    private final ProvisioningCallbackManager mProvisioningCallbackManager;
@@ -461,7 +434,7 @@ public class MmTelFeatureConnection {
            return serviceProxy;
        }

        TelephonyManager tm  = getTelephonyManager(context);
        TelephonyManager tm = serviceProxy.getTelephonyManager();
        if (tm == null) {
            Rlog.w(TAG, "create: TelephonyManager is null!");
            // Binder can be unset in this case because it will be torn down/recreated as part of
@@ -481,10 +454,6 @@ public class MmTelFeatureConnection {
        return serviceProxy;
    }

    public static TelephonyManager getTelephonyManager(Context context) {
        return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    }

    public interface IFeatureUpdate {
        /**
         * Called when the ImsFeature has changed its state. Use
@@ -499,99 +468,16 @@ public class MmTelFeatureConnection {
        void notifyUnavailable();
    }

    private final IImsServiceFeatureCallback mListenerBinder =
            new IImsServiceFeatureCallback.Stub() {

        @Override
        public void imsFeatureCreated(int slotId, int feature) {
                mExecutor.execute(() -> {
                // The feature has been enabled. This happens when the feature is first created and
                // may happen when the feature is re-enabled.
                synchronized (mLock) {
                    if(mSlotId != slotId) {
                        return;
                    }
                    switch (feature) {
                        case ImsFeature.FEATURE_MMTEL: {
                            if (!mIsAvailable) {
                                Log.i(TAG, "MmTel enabled on slotId: " + slotId);
                                mIsAvailable = true;
                            }
                            break;
                        }
                        case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
                            mSupportsEmergencyCalling = true;
                            Log.i(TAG, "Emergency calling enabled on slotId: " + slotId);
                            break;
                        }
                    }
                }
            });
        }

        @Override
        public void imsFeatureRemoved(int slotId, int feature) {
            mExecutor.execute(() -> {
                synchronized (mLock) {
                    if (mSlotId != slotId) {
                        return;
                    }
                    switch (feature) {
                        case ImsFeature.FEATURE_MMTEL: {
                            Log.i(TAG, "MmTel removed on slotId: " + slotId);
                            onRemovedOrDied();
                            break;
                        }
                        case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
                            mSupportsEmergencyCalling = false;
                            Log.i(TAG, "Emergency calling disabled on slotId: " + slotId);
                            break;
                        }
                    }
                }
            });
        }

        @Override
        public void imsStatusChanged(int slotId, int feature, int status) {
            mExecutor.execute(() -> {
                synchronized (mLock) {
                    Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: "
                            + ImsFeature.FEATURE_LOG_MAP.get(feature) +
                            " status: " + ImsFeature.STATE_LOG_MAP.get(status));
                    if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
                        mFeatureStateCached = status;
                        if (mStatusCallback != null) {
                            mStatusCallback.notifyStateChanged();
                        }
                    }
                }
            });
        }
    };

    public MmTelFeatureConnection(Context context, int slotId) {
        mSlotId = slotId;
        mContext = context;
        // Callbacks should be scheduled on the main thread.
        if (context.getMainLooper() != null) {
            mExecutor = context.getMainExecutor();
        } else {
            // Fallback to the current thread.
            if (Looper.myLooper() == null) {
                Looper.prepare();
            }
            mExecutor = new HandlerExecutor(new Handler(Looper.myLooper()));
        }
        super(context, slotId, ImsFeature.FEATURE_MMTEL);

        mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock);
        mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock);
        mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock);
    }

    /**
     * Called when the MmTelFeature has either been removed by Telephony or crashed.
     */
    private void onRemovedOrDied() {
    @Override
    protected void onRemovedOrDied() {
        synchronized (mLock) {
            mRegistrationCallbackManager.close();
            mCapabilityCallbackManager.close();
@@ -618,7 +504,7 @@ public class MmTelFeatureConnection {
                return mRegistrationBinder;
            }
        }
        TelephonyManager tm = getTelephonyManager(mContext);
        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;
@@ -639,7 +525,7 @@ public class MmTelFeatureConnection {
                return mConfigBinder;
            }
        }
        TelephonyManager tm = getTelephonyManager(mContext);
        TelephonyManager tm = getTelephonyManager();
        IImsConfig configBinder = tm != null
                ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null;
        synchronized (mLock) {
@@ -651,27 +537,71 @@ public class MmTelFeatureConnection {
        return mConfigBinder;
    }

    public boolean isEmergencyMmTelAvailable() {
        return mSupportsEmergencyCalling;
    @Override
    protected void handleImsFeatureCreatedCallback(int slotId, int feature) {
        // The feature has been enabled. This happens when the feature is first created and
        // may happen when the feature is re-enabled.
        synchronized (mLock) {
            if(mSlotId != slotId) {
                return;
            }
            switch (feature) {
                case ImsFeature.FEATURE_MMTEL: {
                    if (!mIsAvailable) {
                        Log.i(TAG, "MmTel enabled on slotId: " + slotId);
                        mIsAvailable = true;
                    }
                    break;
                }
                case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
                    mSupportsEmergencyCalling = true;
                    Log.i(TAG, "Emergency calling enabled on slotId: " + slotId);
                    break;
                }
            }
        }
    }

    public IImsServiceFeatureCallback getListener() {
        return mListenerBinder;
    @Override
    protected void handleImsFeatureRemovedCallback(int slotId, int feature) {
        synchronized (mLock) {
            if (mSlotId != slotId) {
                return;
            }
            switch (feature) {
                case ImsFeature.FEATURE_MMTEL: {
                    Log.i(TAG, "MmTel removed on slotId: " + slotId);
                    onRemovedOrDied();
                    break;
                }
                case ImsFeature.FEATURE_EMERGENCY_MMTEL: {
                    mSupportsEmergencyCalling = false;
                    Log.i(TAG, "Emergency calling disabled on slotId: " + slotId);
                    break;
                }
            }
        }
    }

    public void setBinder(IBinder binder) {
    @Override
    protected void handleImsStatusChangedCallback(int slotId, int feature, int status) {
        synchronized (mLock) {
            mBinder = binder;
            try {
                if (mBinder != null) {
                    mBinder.linkToDeath(mDeathRecipient, 0);
            Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: "
                + ImsFeature.FEATURE_LOG_MAP.get(feature) +
                " status: " + ImsFeature.STATE_LOG_MAP.get(status));
            if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) {
                mFeatureStateCached = status;
                if (mStatusCallback != null) {
                    mStatusCallback.notifyStateChanged();
                }
            } catch (RemoteException e) {
                // No need to do anything if the binder is already dead.
            }
        }
    }

    public boolean isEmergencyMmTelAvailable() {
        return mSupportsEmergencyCalling;
    }

    /**
     * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the
     * framework. Calling this method multiple times will reset the listener attached to the
@@ -891,32 +821,14 @@ public class MmTelFeatureConnection {
    }

    /**
     * @return an integer describing the current Feature Status, defined in
     * {@link ImsFeature.ImsState}.
     * @param c Callback that will fire when the feature status has changed.
     */
    public int getFeatureState() {
        synchronized (mLock) {
            if (isBinderAlive() && mFeatureStateCached != null) {
                return mFeatureStateCached;
            }
        }
        // Don't synchronize on Binder call.
        Integer state = retrieveFeatureState();
        synchronized (mLock) {
            if (state == null) {
                return ImsFeature.STATE_UNAVAILABLE;
            }
            // Cache only non-null value for feature status.
            mFeatureStateCached = state;
        }
        Log.i(TAG, "getFeatureState - returning " + ImsFeature.STATE_LOG_MAP.get(state));
        return state;
    public void setStatusCallback(IFeatureUpdate c) {
        mStatusCallback = c;
    }

    /**
     * Internal method used to retrieve the feature status from the corresponding ImsService.
     */
    private Integer retrieveFeatureState() {
    @Override
    protected Integer retrieveFeatureState() {
        if (mBinder != null) {
            try {
                return getServiceInterface(mBinder).getFeatureState();
@@ -927,42 +839,6 @@ public class MmTelFeatureConnection {
        return null;
    }

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

    /**
     * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
     * method returns false, it doesn't mean that the Binder connection is not available (use
     * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
     * at this time.
     *
     * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
     * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}.
     */
    public boolean isBinderReady() {
        return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY;
    }

    /**
     * @return false if the binder connection is no longer alive.
     */
    public boolean isBinderAlive() {
        return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
    }

    private void checkServiceIsReady() throws RemoteException {
        if (!sImsSupportedOnDevice) {
            throw new RemoteException("IMS is not supported on this device.");
        }
        if (!isBinderReady()) {
            throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
        }
    }

    private IImsMmTelFeature getServiceInterface(IBinder b) {
        return IImsMmTelFeature.Stub.asInterface(b);
    }
+183 −0

File added.

Preview size limit exceeded, changes collapsed.

+104 −0

File added.

Preview size limit exceeded, changes collapsed.