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

Commit a9ec7f3a authored by Etan Cohen's avatar Etan Cohen Committed by Gerrit - the friendly Code Review server
Browse files

IMS:Re-Initiate Conference Call from Leaf Node Causes Conference Call Crash.

During conference call, if there is an incoming call and user accepts it,
the ims FW triggers two accept commands since ims FW triggers the onCallHeld
multiple times for conference call.
The multiple accept commands causes the ims ril to respond failure. As the result,
the call drops and also crash occurs.
The fix is to block the multiple accepts in ims FW layer.

Bug: 17606426
Change-Id: I89904d4c64dad0d19398f74408ddc177f59c40ce

Propagating isMultiParty indicator from RIL up into Telephony layer.

- Changing ImsPhoneCall to return the multiparty bit from the ImsCall
instead of depending on the number of connections present.

Bug: 18058253
Change-Id: I0f81f16e38d3a907c8e81143b0f36d2d17e7f971

Implement test IMS conference event package harness.

Implement test conference event package intent receiver and parser which
allows the user to issue an intent to cause a test conference event
package file to be injected into the current ImsCall:

adb shell am broadcast
-a com.android.internal.telephony.TestConferenceEventPackage
-e filename test_filename.xml

Bug: 18119796
Change-Id: Ic51be9442ae5b991054ccfd7655c6efd76ffd8e0

Communicating participant changes to conference controller.

- Add event handlers to pass participant info to conference controller.

Bug: 18057361
Change-Id: Ifdd2d871994fc6e347f54d1e69686f7c68a7d6b0

Changed the prototype for onCallMerged.

The removed parameter is never used and it actually doesn't make
much sense as the layer that calls this callback doesn't have
any sensible second call to return to the handlers.

Bug: 18056632
Change-Id: I4abe92c020f64692370d9ab309e0d12877bdbb06

Support disconnecting conference participants from conference.

- Added implementation for onDisconnectConferenceParticipant method in
ImsPhoneConnction.  Calls the "removeParticipants" method provided by the
ImsCall to inform the conference server that a conference participant
should be disconnected.

Bug: 18176606
Change-Id: Icab779a49382765c198494c18d8adc276189e9c3

Conference event package performance improvement.

- Instead of sending each participant to the telephony conference
controller, all participants are sent at once.  This way the conference
only needs to be recalculated once.

Bug: 18057361
Change-Id: I2e81a14ac6ad2858cf70d98ba0b7e14f0bd55cb5

Clear disconnected connections when detaching.

This bug was happening because there was a disconnected connection
lingering on the background call after it is resumed. Because of
this lingering connection, its state remained on HOLD and did not
return to IDLE.

This caused the phone to get in a bad state, such that when trying
to unhold the conference later on, there foreground call is ACTIVE
and the background call is HOLD, which precludes unhold from working
properly.

This fix for the bug would clear disconnected connections from the
ImsPhoneCall after detaching a connection.

Note: clearDisconnected also updates the state to IDLE, which is why
I deleted that code.

Bug: 18291230
Change-Id: I09b5e274408235a9b6630d40bf3a3161676ffc79

Return MERGED_SUCCESSFULLY as a disconnect cause.

If a call was merged, change the disconnect cause to
IMS_MERGED_SUCCESSFULLY instead of NORMAL (because it was not
actually terminated as the result of a remote disconnect).

Bug: 18291234
Change-Id: I4cff83ace2aaa6062f4e182e9ac0f441bf77a23a

Add null-check to isMultiparty method, additional debug strings.

Bug: 18200934
Change-Id: I4b146046dc0d9f5ebb7c37151d95d81629a3dd25

Change-Id: I4b146046dc0d9f5ebb7c37151d95d81629a3dd25
parent b357bbe9
Loading
Loading
Loading
Loading
+25 −0
Original line number Original line Diff line number Diff line
@@ -16,8 +16,10 @@


package com.android.internal.telephony;
package com.android.internal.telephony;


import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.SystemClock;
import android.telecom.ConferenceParticipant;
import android.telephony.Rlog;
import android.telephony.Rlog;
import android.util.Log;
import android.util.Log;


@@ -47,6 +49,7 @@ public abstract class Connection {
                android.telecom.Connection.VideoProvider videoProvider);
                android.telecom.Connection.VideoProvider videoProvider);
        public void onAudioQualityChanged(int audioQuality);
        public void onAudioQualityChanged(int audioQuality);
        public void onCallSubstateChanged(int callSubstate);
        public void onCallSubstateChanged(int callSubstate);
        public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants);
    }
    }


    /**
    /**
@@ -66,6 +69,8 @@ public abstract class Connection {
        public void onAudioQualityChanged(int audioQuality) {}
        public void onAudioQualityChanged(int audioQuality) {}
        @Override
        @Override
        public void onCallSubstateChanged(int callSubstate) {}
        public void onCallSubstateChanged(int callSubstate) {}
        @Override
        public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {}
    }
    }


    public static final int AUDIO_QUALITY_STANDARD = 1;
    public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -588,6 +593,26 @@ public abstract class Connection {
        mDialString = oriNumber;
        mDialString = oriNumber;
    }
    }


    /**
     * Notifies listeners of a change to conference participant(s).
     *
     * @param conferenceParticipants The participant(s).
     */
    public void updateConferenceParticipants(List<ConferenceParticipant> conferenceParticipants) {
        for (Listener l : mListeners) {
            l.onConferenceParticipantsChanged(conferenceParticipants);
        }
    }

    /**
     * Notifies this Connection of a request to disconnect a participant of the conference managed
     * by the connection.
     *
     * @param endpoint the {@link Uri} of the participant to disconnect.
     */
    public void onDisconnectConferenceParticipant(Uri endpoint) {
    }

    /**
    /**
     * Build a human representation of a connection instance, suitable for debugging.
     * Build a human representation of a connection instance, suitable for debugging.
     * Don't log personal stuff unless in debug mode.
     * Don't log personal stuff unless in debug mode.
+68 −1
Original line number Original line Diff line number Diff line
@@ -23,18 +23,35 @@ import android.content.IntentFilter;
import android.os.Build;
import android.os.Build;
import android.telephony.Rlog;
import android.telephony.Rlog;


import com.android.internal.telephony.PhoneBase;
import com.android.ims.ImsCall;
import com.android.ims.ImsConferenceState;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.test.TestConferenceEventPackageParser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;


/**
/**
 * Telephony tester receives the following intents where {name} is the phone name
 * Telephony tester receives the following intents where {name} is the phone name
 *
 *
 * adb shell am broadcast -a com.android.internal.telephony.{name}.action_detached
 * adb shell am broadcast -a com.android.internal.telephony.{name}.action_detached
 * adb shell am broadcast -a com.android.internal.telephony.{name}.action_attached
 * adb shell am broadcast -a com.android.internal.telephony.{name}.action_attached
 * adb shell am broadcast -a com.android.internal.telephony.TestConferenceEventPackage -e filename
 *      test_filename.xml
 */
 */
public class TelephonyTester {
public class TelephonyTester {
    private static final String LOG_TAG = "TelephonyTester";
    private static final String LOG_TAG = "TelephonyTester";
    private static final boolean DBG = true;
    private static final boolean DBG = true;


    /**
     * Test-only intent used to send a test conference event package to the IMS framework.
     */
    private static final String ACTION_TEST_CONFERENCE_EVENT_PACKAGE =
            "com.android.internal.telephony.TestConferenceEventPackage";
    private static final String EXTRA_FILENAME = "filename";

    private PhoneBase mPhone;
    private PhoneBase mPhone;


    // The static intent receiver one for all instances and we assume this
    // The static intent receiver one for all instances and we assume this
@@ -50,6 +67,9 @@ public class TelephonyTester {
            } else if (action.equals(mPhone.getActionAttached())) {
            } else if (action.equals(mPhone.getActionAttached())) {
                log("simulate attaching");
                log("simulate attaching");
                mPhone.getServiceStateTracker().mAttachedRegistrants.notifyRegistrants();
                mPhone.getServiceStateTracker().mAttachedRegistrants.notifyRegistrants();
            } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) {
                log("inject simulated conference event package");
                handleTestConferenceEventPackage(context, intent.getStringExtra(EXTRA_FILENAME));
            } else {
            } else {
                if (DBG) log("onReceive: unknown action=" + action);
                if (DBG) log("onReceive: unknown action=" + action);
            }
            }
@@ -68,6 +88,11 @@ public class TelephonyTester {
            filter.addAction(mPhone.getActionAttached());
            filter.addAction(mPhone.getActionAttached());
            log("register for intent action=" + mPhone.getActionAttached());
            log("register for intent action=" + mPhone.getActionAttached());


            if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
                log("register for intent action=" + ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
                filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
            }

            phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
            phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
        }
        }
    }
    }
@@ -81,4 +106,46 @@ public class TelephonyTester {
    private static void log(String s) {
    private static void log(String s) {
        Rlog.d(LOG_TAG, s);
        Rlog.d(LOG_TAG, s);
    }
    }

    /**
     * Handles request to send a test conference event package to the active Ims call.
     *
     * @see com.android.internal.telephony.test.TestConferenceEventPackageParser
     * @param context The context.
     * @param fileName The name of the test conference event package file to read.
     */
    private void handleTestConferenceEventPackage(Context context, String fileName) {
        // Attempt to get the active IMS call before parsing the test XML file.
        ImsPhone imsPhone = (ImsPhone) mPhone;
        if (imsPhone == null) {
            return;
        }

        ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
        if (imsPhoneCall == null) {
            return;
        }

        ImsCall imsCall = imsPhoneCall.getImsCall();
        if (imsCall == null) {
            return;
        }

        File packageFile = new File(context.getFilesDir(), fileName);
        final FileInputStream is;
        try {
            is = new FileInputStream(packageFile);
        } catch (FileNotFoundException ex) {
            log("Test conference event package file not found: " + packageFile.getAbsolutePath());
            return;
        }

        TestConferenceEventPackageParser parser = new TestConferenceEventPackageParser(is);
        ImsConferenceState imsConferenceState = parser.parse();
        if (imsConferenceState == null) {
            return;
        }

        imsCall.conferenceStateUpdated(imsConferenceState);
    }
}
}
+20 −11
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.telephony.Rlog;
import android.telephony.Rlog;
import android.telephony.DisconnectCause;
import android.telephony.DisconnectCause;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Connection;
@@ -85,7 +86,12 @@ public class ImsPhoneCall extends Call {
    @Override
    @Override
    public boolean
    public boolean
    isMultiparty() {
    isMultiparty() {
        return mConnections.size() > 1;
        ImsCall imsCall = getImsCall();
        if (imsCall == null) {
            return false;
        }

        return imsCall.isMultiparty();
    }
    }


    /** Please note: if this is the foreground call and a
    /** Please note: if this is the foreground call and a
@@ -169,10 +175,7 @@ public class ImsPhoneCall extends Call {
    /*package*/ void
    /*package*/ void
    detach(ImsPhoneConnection conn) {
    detach(ImsPhoneConnection conn) {
        mConnections.remove(conn);
        mConnections.remove(conn);

        clearDisconnected();
        if (mConnections.size() == 0) {
            mState = State.IDLE;
        }
    }
    }


    /**
    /**
@@ -244,7 +247,17 @@ public class ImsPhoneCall extends Call {
        }
        }
    }
    }


    /*package*/ ImsCall
    /**
     * Retrieves the {@link ImsCall} for the current {@link ImsPhoneCall}.
     * <p>
     * Marked as {@code VisibleForTesting} so that the
     * {@link com.android.internal.telephony.TelephonyTester} class can inject a test conference
     * event package into a regular ongoing IMS call.
     *
     * @return The {@link ImsCall}.
     */
    @VisibleForTesting
    public ImsCall
    getImsCall() {
    getImsCall() {
        return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall();
        return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall();
    }
    }
@@ -295,11 +308,7 @@ public class ImsPhoneCall extends Call {


    /* package */ ImsPhoneConnection
    /* package */ ImsPhoneConnection
    getHandoverConnection() {
    getHandoverConnection() {
        ImsPhoneConnection conn = (ImsPhoneConnection) getEarliestConnection();
        return (ImsPhoneConnection) getEarliestConnection();
        if (conn != null) {
            conn.setMultiparty(isMultiparty());
        }
        return conn;
    }
    }


    void switchWith(ImsPhoneCall that) {
    void switchWith(ImsPhoneCall that) {
+34 −4
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.provider.Settings;
import android.provider.Settings;
import android.preference.PreferenceManager;
import android.preference.PreferenceManager;
import android.telecom.ConferenceParticipant;
import android.telecom.VideoProfile;
import android.telecom.VideoProfile;
import android.telephony.DisconnectCause;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneNumberUtils;
@@ -53,6 +54,7 @@ import com.android.ims.ImsManager;
import com.android.ims.ImsReasonInfo;
import com.android.ims.ImsReasonInfo;
import com.android.ims.ImsServiceClass;
import com.android.ims.ImsServiceClass;
import com.android.ims.ImsUtInterface;
import com.android.ims.ImsUtInterface;
import com.android.ims.internal.CallGroup;
import com.android.ims.internal.IImsVideoCallProvider;
import com.android.ims.internal.IImsVideoCallProvider;
import com.android.ims.internal.ImsCallSession;
import com.android.ims.internal.ImsCallSession;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
@@ -103,7 +105,6 @@ public final class ImsPhoneCallTracker extends CallTracker {


                    // Normal MT call
                    // Normal MT call
                    ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);
                    ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener);

                    ImsPhoneConnection conn = new ImsPhoneConnection(mPhone.getContext(), imsCall,
                    ImsPhoneConnection conn = new ImsPhoneConnection(mPhone.getContext(), imsCall,
                            ImsPhoneCallTracker.this, mRingingCall);
                            ImsPhoneCallTracker.this, mRingingCall);
                    addConnection(conn);
                    addConnection(conn);
@@ -1103,6 +1104,12 @@ public final class ImsPhoneCallTracker extends CallTracker {
                    cause = DisconnectCause.INCOMING_MISSED;
                    cause = DisconnectCause.INCOMING_MISSED;
                }
                }
            }
            }

            if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) {
                // Call was terminated while it is merged instead of a remote disconnect.
                cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY;
            }

            processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
            processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);


            if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) {
            if (reasonInfo.getCode() == ImsReasonInfo.CODE_USER_TERMINATED) {
@@ -1122,11 +1129,17 @@ public final class ImsPhoneCallTracker extends CallTracker {
                ImsPhoneCall.State oldState = mBackgroundCall.getState();
                ImsPhoneCall.State oldState = mBackgroundCall.getState();
                processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
                processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
                        DisconnectCause.NOT_DISCONNECTED);
                        DisconnectCause.NOT_DISCONNECTED);

                if (oldState == ImsPhoneCall.State.ACTIVE) {
                if (oldState == ImsPhoneCall.State.ACTIVE) {
                    if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING)
                    if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING)
                            || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) {
                            || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) {
                        boolean isOwner = true;
                        CallGroup callGroup =  imsCall.getCallGroup();
                        if (callGroup != null) {
                            isOwner = callGroup.isOwner(imsCall);
                        }
                        if (isOwner) {
                            sendEmptyMessage(EVENT_RESUME_BACKGROUND);
                            sendEmptyMessage(EVENT_RESUME_BACKGROUND);
                        }
                    } else {
                    } else {
                        //when multiple connections belong to background call,
                        //when multiple connections belong to background call,
                        //only the first callback reaches here
                        //only the first callback reaches here
@@ -1212,7 +1225,7 @@ public final class ImsPhoneCallTracker extends CallTracker {
        }
        }


        @Override
        @Override
        public void onCallMerged(ImsCall call, ImsCall newCall) {
        public void onCallMerged(ImsCall call) {
            if (DBG) log("onCallMerged");
            if (DBG) log("onCallMerged");


            mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
            mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
@@ -1225,6 +1238,23 @@ public final class ImsPhoneCallTracker extends CallTracker {
            if (DBG) log("onCallMergeFailed reasonCode=" + reasonInfo.getCode());
            if (DBG) log("onCallMergeFailed reasonCode=" + reasonInfo.getCode());
            mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
            mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
        }
        }

        /**
         * Called when the state of IMS conference participant(s) has changed.
         *
         * @param call the call object that carries out the IMS call.
         * @param participants the participant(s) and their new state information.
         */
        @Override
        public void onConferenceParticipantsStateChanged(ImsCall call,
                List<ConferenceParticipant> participants) {
            if (DBG) log("onConferenceParticipantsStateChanged");

            ImsPhoneConnection conn = findConnection(call);
            if (conn != null) {
                conn.updateConferenceParticipants(participants);
            }
        }
    };
    };


    /**
    /**
+47 −10
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.internal.telephony.imsphone;
package com.android.internal.telephony.imsphone;


import android.content.Context;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
@@ -25,6 +26,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager;
import android.os.Registrant;
import android.os.Registrant;
import android.os.SystemClock;
import android.os.SystemClock;
import android.telecom.Log;
import android.telephony.DisconnectCause;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.Rlog;
@@ -74,9 +76,6 @@ public class ImsPhoneConnection extends Connection {
    private int mCause = DisconnectCause.NOT_DISCONNECTED;
    private int mCause = DisconnectCause.NOT_DISCONNECTED;
    private PostDialState mPostDialState = PostDialState.NOT_STARTED;
    private PostDialState mPostDialState = PostDialState.NOT_STARTED;
    private UUSInfo mUusInfo;
    private UUSInfo mUusInfo;

    private boolean mIsMultiparty = false;

    private Handler mHandler;
    private Handler mHandler;


    private PowerManager.WakeLock mPartialWakeLock;
    private PowerManager.WakeLock mPartialWakeLock;
@@ -595,15 +594,9 @@ public class ImsPhoneConnection extends Connection {
        return null;
        return null;
    }
    }


    /* package */ void
    setMultiparty(boolean isMultiparty) {
        Rlog.d(LOG_TAG, "setMultiparty " + isMultiparty);
        mIsMultiparty = isMultiparty;
    }

    @Override
    @Override
    public boolean isMultiparty() {
    public boolean isMultiparty() {
        return mIsMultiparty;
        return mImsCall != null && mImsCall.isMultiparty();
    }
    }


    /*package*/ ImsCall getImsCall() {
    /*package*/ ImsCall getImsCall() {
@@ -724,5 +717,49 @@ public class ImsPhoneConnection extends Connection {
    public Bundle getCallExtras() {
    public Bundle getCallExtras() {
        return mCallExtras;
        return mCallExtras;
    }
    }

    /**
     * Notifies this Connection of a request to disconnect a participant of the conference managed
     * by the connection.
     *
     * @param endpoint the {@link android.net.Uri} of the participant to disconnect.
     */
    @Override
    public void onDisconnectConferenceParticipant(Uri endpoint) {
        ImsCall imsCall = getImsCall();
        if (imsCall == null) {
            return;
        }
        try {
            imsCall.removeParticipants(new String[]{endpoint.toString()});
        } catch (ImsException e) {
            // No session in place -- no change
            Rlog.e(LOG_TAG, "onDisconnectConferenceParticipant: no session in place. "+
                    "Failed to disconnect endpoint = " + endpoint);
        }
    }

    /**
     * Provides a string representation of the {@link ImsPhoneConnection}.  Primarily intended for
     * use in log statements.
     *
     * @return String representation of call.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ImsPhoneConnection objId: ");
        sb.append(System.identityHashCode(this));
        sb.append(" address:");
        sb.append(Log.pii(getAddress()));
        sb.append(" ImsCall:");
        if (mImsCall == null) {
            sb.append("null");
        } else {
            sb.append(mImsCall);
        }
        sb.append("]");
        return sb.toString();
    }
}
}
Loading