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

Commit 04e731bb authored by Tyler Gunn's avatar Tyler Gunn Committed by Android (Google) Code Review
Browse files

Merge "Add ImsExternalCallTracker and support for multiendpoint." into nyc-dev

parents c334d3e3 fec523bd
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -66,6 +66,18 @@ public abstract class Connection {
         * For an IMS call, indicates that the peer supports video.
         */
        public static final int SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 0x00000008;

        /**
         * Indicates that the connection is an external connection (e.g. an instance of the class
         * {@link com.android.internal.telephony.imsphone.ImsExternalConnection}.
         */
        public static final int IS_EXTERNAL_CONNECTION = 0x00000010;

        /**
         * Indicates that this external connection can be pulled from the remote device to the
         * local device.
         */
        public static final int IS_PULLABLE = 0x00000020;
    }

    /**
+5 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.ims.ImsManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriManager;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.uicc.IccCardProxy;
@@ -115,6 +116,10 @@ public class TelephonyComponentFactory {
        return new ImsPhoneCallTracker(imsPhone);
    }

    public ImsExternalCallTracker makeImsExternalCallTracker(ImsPhone imsPhone) {
        return new ImsExternalCallTracker(imsPhone);
    }

    public CdmaSubscriptionSourceManager
    getCdmaSubscriptionSourceManagerInstance(Context context, CommandsInterface ci, Handler h,
                                             int what, Object obj) {
+60 −0
Original line number Diff line number Diff line
@@ -20,11 +20,15 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.telephony.Rlog;

import com.android.ims.ImsCall;
import com.android.ims.ImsCallProfile;
import com.android.ims.ImsConferenceState;
import com.android.ims.ImsExternalCallState;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.test.TestConferenceEventPackageParser;
@@ -32,6 +36,8 @@ import com.android.internal.telephony.test.TestConferenceEventPackageParser;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

/**
 * Telephony tester receives the following intents where {name} is the phone name
@@ -50,7 +56,22 @@ public class TelephonyTester {
     */
    private static final String ACTION_TEST_CONFERENCE_EVENT_PACKAGE =
            "com.android.internal.telephony.TestConferenceEventPackage";

    /**
     * Test-only intent used to send a test dialog event package to the IMS framework.
     */
    private static final String ACTION_TEST_DIALOG_EVENT_PACKAGE =
            "com.android.internal.telephony.TestDialogEventPackage";

    private static final String EXTRA_FILENAME = "filename";
    private static final String EXTRA_STARTPACKAGE = "startPackage";
    private static final String EXTRA_SENDPACKAGE = "sendPackage";
    private static final String EXTRA_DIALOGID = "dialogId";
    private static final String EXTRA_NUMBER = "number";
    private static final String EXTRA_STATE = "state";
    private static final String EXTRA_CANPULL = "canPull";

    private static List<ImsExternalCallState> mImsExternalCallStates = null;

    private Phone mPhone;

@@ -70,6 +91,9 @@ public class TelephonyTester {
            } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) {
                log("inject simulated conference event package");
                handleTestConferenceEventPackage(context, intent.getStringExtra(EXTRA_FILENAME));
            } else if (action.equals(ACTION_TEST_DIALOG_EVENT_PACKAGE)) {
                log("handle test dialog event package intent");
                handleTestDialogEventPackageIntent(intent);
            } else {
                if (DBG) log("onReceive: unknown action=" + action);
            }
@@ -91,6 +115,8 @@ public class TelephonyTester {
            if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
                log("register for intent action=" + ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
                filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
                filter.addAction(ACTION_TEST_DIALOG_EVENT_PACKAGE);
                mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
            }

            phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
@@ -148,4 +174,38 @@ public class TelephonyTester {

        imsCall.conferenceStateUpdated(imsConferenceState);
    }

    /**
     * Handles intents containing test dialog event package data.
     *
     * @param intent
     */
    private void handleTestDialogEventPackageIntent(Intent intent) {
        ImsPhone imsPhone = (ImsPhone) mPhone;
        if (imsPhone == null) {
            return;
        }
        ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
        if (externalCallTracker == null) {
            return;
        }

        if (intent.hasExtra(EXTRA_STARTPACKAGE)) {
            mImsExternalCallStates.clear();
        } else if (intent.hasExtra(EXTRA_SENDPACKAGE)) {
            externalCallTracker.refreshExternalCallState(mImsExternalCallStates);
            mImsExternalCallStates.clear();
        } else if (intent.hasExtra(EXTRA_DIALOGID)) {
            ImsExternalCallState state = new ImsExternalCallState(
                    intent.getIntExtra(EXTRA_DIALOGID, 0),
                    Uri.parse(intent.getStringExtra(EXTRA_NUMBER)),
                    intent.getBooleanExtra(EXTRA_CANPULL, true),
                    intent.getIntExtra(EXTRA_STATE,
                            ImsExternalCallState.CALL_STATE_CONFIRMED),
                    ImsCallProfile.CALL_TYPE_VOICE,
                    false /* isHeld */
                    );
            mImsExternalCallStates.add(state);
        }
    }
}
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.internal.telephony.imsphone;

import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;

import java.util.List;

/**
 * Companion class for {@link ImsExternalConnection}; represents an external call which was
 * received via {@link com.android.ims.ImsExternalCallState} info.
 */
public class ImsExternalCall extends Call {

    private Phone mPhone;

    public ImsExternalCall(Phone phone, ImsExternalConnection connection) {
        mPhone = phone;
        mConnections.add(connection);
    }

    @Override
    public List<Connection> getConnections() {
        return mConnections;
    }

    @Override
    public Phone getPhone() {
        return mPhone;
    }

    @Override
    public boolean isMultiparty() {
        return false;
    }

    @Override
    public void hangup() throws CallStateException {

    }

    /**
     * Sets the call state to active.
     */
    public void setActive() {
        setState(State.ACTIVE);
    }

    /**
     * Sets the call state to terminated.
     */
    public void setTerminated() {
        setState(State.DISCONNECTED);
    }
}
+193 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.internal.telephony.imsphone;

import com.android.ims.ImsExternalCallState;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;

import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
import android.util.ArrayMap;
import android.util.Log;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Responsible for tracking external calls known to the system.
 */
public class ImsExternalCallTracker {

    public final static String TAG = "ImsExternalCallTracker";

    /**
     * Extra key used when informing telecom of a new external call using the
     * {@link android.telecom.TelecomManager#addNewUnknownCall(PhoneAccountHandle, Bundle)} API.
     * Used to ensure that when Telecom requests the {@link android.telecom.ConnectionService} to
     * create the connection for the unknown call that we can determine which
     * {@link ImsExternalConnection} in {@link #mExternalConnections} is the one being requested.
     */
    public final static String EXTRA_IMS_EXTERNAL_CALL_ID =
            "android.telephony.ImsExternalCallTracker.extra.EXTERNAL_CALL_ID";

    /**
     * Contains a list of the external connections known by the ImsPhoneCallTracker.  These are
     * connections which originated from a dialog event package and reside on another device.
     * Used in multi-endpoint (VoLTE for internet connected endpoints) scenarios.
     */
    private Map<Integer, ImsExternalConnection> mExternalConnections =
            new ArrayMap<Integer, ImsExternalConnection>();

    private ImsPhone mPhone;

    public ImsExternalCallTracker(ImsPhone phone) {
        mPhone = phone;
    }

    /**
     * Called when the IMS stack receives a new dialog event package.  Triggers the creation and
     * update of {@link ImsExternalConnection}s to represent the dialogs in the dialog event
     * package data.
     *
     * @param externalCallStates the {@link ImsExternalCallState} information for the dialog event
     *                           package.
     */
    public void refreshExternalCallState(List<ImsExternalCallState> externalCallStates) {
        Log.d(TAG, "refreshExternalCallState: depSize = " + externalCallStates.size());

        // Check to see if any call Ids are no longer present in the external call state.  If they
        // are, the calls are terminated and should be removed.
        Iterator<Map.Entry<Integer, ImsExternalConnection>> connectionIterator =
                mExternalConnections.entrySet().iterator();
        boolean wasCallRemoved = false;
        while (connectionIterator.hasNext()) {
            Map.Entry<Integer, ImsExternalConnection> entry = connectionIterator.next();
            int callId = entry.getKey().intValue();

            if (!containsCallId(externalCallStates, callId)) {
                ImsExternalConnection externalConnection = entry.getValue();
                externalConnection.setTerminated();
                connectionIterator.remove();
                wasCallRemoved = true;
            }
        }
        // If one or more calls were removed, trigger a notification that will cause the
        // TelephonyConnection instancse to refresh their state with Telecom.
        if (wasCallRemoved) {
            mPhone.notifyPreciseCallStateChanged();
        }

        // Check for new calls, and updates to existing ones.
        for (ImsExternalCallState callState : externalCallStates) {
            if (!mExternalConnections.containsKey(callState.getCallId())) {
                Log.d(TAG, "refreshExternalCallState: got = " + callState);
                // If there is a new entry and it is already terminated, don't bother adding it to
                // telecom.
                if (callState.getCallState() != ImsExternalCallState.CALL_STATE_CONFIRMED) {
                    continue;
                }
                createExternalConnection(callState);
            } else{
                updateExistingConnection(mExternalConnections.get(callState.getCallId()),
                        callState);
            }
        }
    }

    /**
     * Finds an external connection given a call Id.
     *
     * @param callId The call Id.
     * @return The {@link Connection}, or {@code null} if no match found.
     */
    public Connection getConnectionById(int callId) {
        return mExternalConnections.get(callId);
    }

    /**
     * Given an {@link ImsExternalCallState} instance obtained from a dialog event package,
     * creates a new instance of {@link ImsExternalConnection} to represent the connection, and
     * initiates the addition of the new call to Telecom as an unknown call.
     *
     * @param state External call state from a dialog event package.
     */
    private void createExternalConnection(ImsExternalCallState state) {
        Log.i(TAG, "createExternalConnection");

        ImsExternalConnection connection = new ImsExternalConnection(mPhone,
                state.getCallId(), /* Dialog event package call id */
                state.getAddress().getSchemeSpecificPart() /* phone number */,
                state.isCallPullable());

        // Add to list of tracked connections.
        mExternalConnections.put(connection.getCallId(), connection);

        // Note: The notification of unknown connection is ultimately handled by
        // PstnIncomingCallNotifier#addNewUnknownCall.  That method will ensure that an extra is set
        // containing the ImsExternalConnection#mCallId so that we have a means of reconciling which
        // unknown call was added.
        mPhone.notifyUnknownConnection(connection);
    }

    /**
     * Given an existing {@link ImsExternalConnection}, applies any changes found found in a
     * {@link ImsExternalCallState} instance received from a dialog event package to the connection.
     *
     * @param connection The connection to apply changes to.
     * @param state The new dialog state for the connection.
     */
    private void updateExistingConnection(ImsExternalConnection connection,
            ImsExternalCallState state) {
        Call.State existingState = connection.getState();
        Call.State newState = state.getCallState() == ImsExternalCallState.CALL_STATE_CONFIRMED ?
                Call.State.ACTIVE : Call.State.DISCONNECTED;

        if (existingState != newState) {
            if (newState == Call.State.ACTIVE) {
                connection.setActive();
            } else {
                connection.setTerminated();
                mExternalConnections.remove(connection);
                mPhone.notifyPreciseCallStateChanged();
            }
        }

        connection.setIsPullable(state.isCallPullable());
    }

    /**
     * Determines if a list of call states obtained from a dialog event package contacts an existing
     * call Id.
     *
     * @param externalCallStates The dialog event package state information.
     * @param callId The call Id.
     * @return {@code true} if the state information contains the call Id, {@code false} otherwise.
     */
    private boolean containsCallId(List<ImsExternalCallState> externalCallStates, int callId) {
        for (ImsExternalCallState state : externalCallStates) {
            if (state.getCallId() == callId) {
                return true;
            }
        }

        return false;
    }
}
Loading